Metal Gear Solid V: The Phantom Pain · handler “Metal Gear Online 3”
Version 1
Release notes
First release
- Released
- Size
- 27.7 MB
- Downloads
- 3
- Check
- Unverified
- SHA-256
- e22c08d84f8daa09b42462c252a2c246a09049f1f2927535c98b7d1c60609143
Files 21
| Path | Size |
|---|---|
| Metal Gear Solid V/downgrade.xdelta | 27.4 MB |
| Metal Gear Solid V/Goldberg SteamEmu Saves/287700/local/MGO_GAME_DATA0 | 16 KB |
| Metal Gear Solid V/Goldberg SteamEmu Saves/287700/local/MGO_GAME_DATA1 | 16 KB |
| Metal Gear Solid V/Goldberg SteamEmu Saves/287700/local/PERSONAL_DATA0 | 3.0 KB |
| Metal Gear Solid V/Goldberg SteamEmu Saves/287700/local/PERSONAL_DATA1 | 3.0 KB |
| Metal Gear Solid V/Goldberg SteamEmu Saves/287700/local/TPP_CONFIG_DATA0 | 2.0 KB |
| Metal Gear Solid V/Goldberg SteamEmu Saves/287700/local/TPP_CONFIG_DATA1 | 2.0 KB |
| Metal Gear Solid V/Goldberg SteamEmu Saves/287700/local/TPP_GRAPHICS_CONFIG | 762 B |
| Metal Gear Solid V/Goldberg SteamEmu Saves/287700/remote/MGO_GAME_DATA | 16 KB |
| Metal Gear Solid V/Goldberg SteamEmu Saves/287700/remote/PERSONAL_DATA | 3.0 KB |
| Metal Gear Solid V/Goldberg SteamEmu Saves/287700/remote/TPP_CONFIG_DATA | 2.0 KB |
| Metal Gear Solid V/MGSV_TPP_WideScreen_Patch.cfg | 1.3 KB |
| Metal Gear Solid V/MGS_V_Patch.exe | 109 KB |
| Metal Gear Solid V/rez.ps1 | 5.0 KB |
| Metal Gear Solid V/WinCrop/WinCrop.deps.json | 413 B |
| Metal Gear Solid V/WinCrop/WinCrop.dll | 8.0 KB |
| Metal Gear Solid V/WinCrop/WinCrop.exe | 146 KB |
| Metal Gear Solid V/WinCrop/WinCrop.pdb | 12 KB |
| Metal Gear Solid V/WinCrop/WinCrop.runtimeconfig.json | 147 B |
| Metal Gear Solid V/xdelta.exe | 610 KB |
| handler.js | 30 KB |
handler.js
Plain textHub.Handler.Version = 1; // Released at https://hub.splitscreen.me/ on Mon Mar 11 2024 20:54:29 GMT+0000 (Coordinated Universal Time).
Hub.Handler.Id = "D5QoyqGRxeBETvuRg";
Hub.Maintainer.Name = "happyexpress";
Hub.Maintainer.Id = "yasq5hW6LH5sjwQty";
Game.Description =
"Requirements: \n" +
"1. Docker Desktop for Windows https://www.docker.com/products/docker-desktop/ \n" +
"2. .NET 6.0 Runtime https://dotnet.microsoft.com/en-us/download/dotnet/6.0 \n" +
"3. MGO3 Server Emulator https://github.com/mxmstr/mgsv_emulator/releases/ \n" +
" \n" +
"Steps: \n" +
"-Open Docker Desktop. \n" +
"-Extract MGO3.Server.Emulator.zip and run RunServer.bat. \n" +
"-Open Nucleus as Administrator. \n" +
"-Start the handler and wait for each instance to boot and get resized. \n" +
"-Inputs will lock automatically. Press the End key to unlock them when you want to end the session. \n" +
"-Once ingame, Player 1 can press Start -> Create Match to create a lobby. \n" +
"-Everyone else can press Start -> Select Match to join it.";
Game.FileSymlinkExclusions = ["mgsvmgo.exe"];
Game.KillMutex = ["MGO 3.0"];
Game.PartialMutexSearch = true;
Game.GameName = "Metal Gear Solid V - Metal Gear Online 3";
Game.HandlerInterval = 100;
Game.SupportsKeyboard = false;
Game.SymlinkExe = false;
Game.SymlinkGame = true;
// Game.KeepSymLinkOnExit = true;
Game.GUID = "MGSV";
Game.ExecutableName = "mgsvmgo.exe";
Game.MaxPlayers = 16;
Game.MaxPlayersOneMonitor = 8;
Game.Hook.DInputEnabled = false;
Game.UseDInputBlocker = false;
Game.Hook.DInputForceDisable = false;
Game.Hook.XInputEnabled = false;
Game.Hook.XInputReroute = false;
Game.Hook.CustomDllEnabled = false;
Game.XInputPlusDll = [];
// Game.ProcessChangesAtEnd = true;
Game.PauseBetweenStarts = 0;
Game.PauseBetweenContextAndLaunch = 0;
Game.PauseBetweenProcessGrab = 15;
//Game.PromptBetweenInstances = true;
Game.RequiresAdmin = true;
Game.UseNucleusEnvironment = true;
Game.UseGoldberg = true;
Game.SteamID = "287700";
Game.GoldbergNoLocalSave = true;
Game.UserProfileSavePathNoCopy = true;
Game.DocumentsConfigPathNoCopy = true;
Game.ThirdPartyLaunch = true;
Game.IgnoreThirdPartyPrompt = true;
//Game.ForceEnvironmentUse = true;
Game.DontResize = true;
Game.DontReposition = true;
Game.DontRemoveBorders = true;
Game.SetWindowHook = true;
// Game.NotTopMost = true;
// Game.HideDesktop = false;
Game.SupportsMultipleKeyboardsAndMice = true;
Game.HookSetCursorPos = false;
Game.HookGetCursorPos = false;
Game.HookGetKeyState = false;
Game.HookGetAsyncKeyState = false;
Game.HookGetKeyboardState = false;
Game.HookFilterRawInput = false;
Game.HookFilterMouseMessages = false;
Game.HookUseLegacyInput = false;
Game.HookDontUpdateLegacyInMouseMsg = false;
Game.HookMouseVisibility = false;
Game.SendNormalMouseInput = false;
Game.SendNormalKeyboardInput = false;
Game.SendScrollWheel = false;
Game.ForwardRawKeyboardInput = false;
Game.ForwardRawMouseInput = false;
Game.DrawFakeMouseCursor = false;
Game.LockInputAtStart = true;
Game.LockInputSuspendsExplorer = true;
Game.ProtoInput.FreezeExternalInputWhenInputNotLocked = true;
Game.LockInputToggleKey = 0x23;
Game.ProtoInput.InjectStartup = false;
Game.ProtoInput.InjectRuntime_RemoteLoadMethod = false;
Game.ProtoInput.InjectRuntime_EasyHookMethod = true;
Game.ProtoInput.InjectRuntime_EasyHookStealthMethod = false;
Game.ProtoInput.RegisterRawInputHook = true;
Game.ProtoInput.GetRawInputDataHook = true;
Game.ProtoInput.MessageFilterHook = true;
Game.ProtoInput.ClipCursorHook = true;
Game.ProtoInput.FocusHooks = true;
Game.ProtoInput.SendMouseWheelMessages = true;
Game.ProtoInput.SendMouseButtonMessages = true;
Game.ProtoInput.SendMouseMovementMessages = true;
Game.ProtoInput.SendKeyboardButtonMessages = true;
Game.ProtoInput.EnableFocusMessageLoop = false;
Game.ProtoInput.DrawFakeCursor = false;
Game.ProtoInput.BlockedMessages = [0x0008];
Game.ProtoInput.RenameHandlesHook = false;
Game.ProtoInput.RenameHandles = [];
Game.ProtoInput.RenameNamedPipes = [];
Game.ProtoInput.XinputHook = true;
Game.ProtoInput.UseOpenXinput = true;
Game.ProtoInput.UseDinputRedirection = false;
Game.ProtoInput.AutoHideTaskbar = true;
var ResAnswers = ["1080p", "900p", "720p", "576p", "1440p", "2160p"];
var ResValues = [
[1920, 1080],
[1600, 900],
[1280, 720],
[1024, 576],
[2560, 1440],
[3840, 2160]
];
Game.AddOption("Internal Resolution", "Don't use anything below 1080p when using the horizontal 2-player layout, as the cropping won't work correctly.", "InternalResolution", ResAnswers);
var GfxAnswers = ["None", "Low", "Medium", "High"];
Game.AddOption("Graphics Preset", "Set the graphics settings to a preset.", "GraphicsPreset", GfxAnswers);
// depth_of_field | effect | lighting | model_detail | motion_blur_amount | postprocess | shadow | ssao | texture | texture_filtering | volumetric_clouds
const GFXPresets = {
High: ["Enable", "High", "High", "High", "Large", "High", "High", "High", "High", "High", "Off"],
Medium: ["Enable", "Low", "Low", "Medium", "Large", "High", "Medium", "Off", "Medium", "Medium", "Off"],
Low: ["Enable", "Low", "Low", "Low", "Medium", "Low", "Low", "Off", "Low", "Medium", "Off"]
};
var instanceFolder = "";
Game.Play = function() {
instanceFolder = Context.GetFolder(Nucleus.Folder.InstancedGameFolder);
var userRoot = Context.EnvironmentPlayer;
var downgrade = true;
var InternalResolution = ResValues[ResAnswers.indexOf(Context.Options["InternalResolution"])];
var GFXPresetOption = Context.Options["GraphicsPreset"];
var GFXPreset = GFXPresetOption == "None" ? null : GFXPresets[GFXPresetOption];
const copiedFiles = ["downgrade.xdelta", "xdelta.exe", "MGS_V_Patch.exe", "MGSV_TPP_WideScreen_Patch.cfg"];
for (let index = 0; index < copiedFiles.length; ++index) {
const fileName = copiedFiles[index];
var savePath = instanceFolder + "\\" + fileName;
var savePkgOrigin = System.IO.Path.Combine(Game.Folder, fileName);
System.IO.File.Copy(savePkgOrigin, savePath, true);
}
// Start the server
// RunCMD("server", [
// "cd " + Game.Folder
// //,"@echo off "
// ,"SETLOCAL EnableDelayedExpansion "
// ," "
// ,"REM Check if the container \"mgo\" exists "
// ,"FOR /f \"tokens=*\" %%i IN ('docker ps -a -q --filter \"name=^mgo$\" 2^>nul') DO SET MGO_CONTAINER_ID=%%i "
// ," "
// ,"REM If the container does not exist, load the image and create the container "
// ,"IF \"!MGO_CONTAINER_ID!\"==\"\" ( "
// ," echo Container \"mgo\" does not exist. Loading image and creating container. "
// ," docker load -i mgo.tar "
// ," docker run -m 512m -d -p 80:80 --name mgo mgo "
// ,") ELSE ( "
// ," echo Container \"mgo\" already exists. Skipping image load. "
// ," REM Optional: Start the container if it's not running "
// ," docker start mgo "
// ,") "
// ," "
// ,"ENDLOCAL "
// ,"PAUSE;"
// ]);
if (!System.IO.Directory.Exists(userRoot + "\\AppData\\Roaming\\Goldberg SteamEmu Saves\\287700\\local")) {
// If this folder doesn't exist, we can assume first time setup needs to be done
RunCMD("copysaves", ['xcopy /E /I /Y "' + Game.Folder + "\\Goldberg SteamEmu Saves" + '" "' + userRoot + "\\AppData\\Roaming\\Goldberg SteamEmu Saves" + '"']);
Context.Wait(2000);
}
var path = userRoot + "\\AppData\\Roaming\\Goldberg SteamEmu Saves\\287700\\local\\TPP_GRAPHICS_CONFIG";
var lines = (GFXPreset == null
? []
: [
'5| "depth_of_field" : "' + GFXPreset[0] + '",',
'6| "effect" : "' + GFXPreset[1] + '",',
'7| "lighting" : "' + GFXPreset[2] + '",',
'8| "model_detail" : "' + GFXPreset[3] + '",',
'9| "motion_blur_amount" : "' + GFXPreset[4] + '",',
'10| "postprocess" : "' + GFXPreset[5] + '",',
'11| "shadow" : "' + GFXPreset[6] + '",',
'12| "ssao" : "' + GFXPreset[7] + '",',
'13| "texture" : "' + GFXPreset[8] + '",',
'14| "texture_filtering" : "' + GFXPreset[9] + '",',
'15| "volumetric_clouds" : "' + GFXPreset[10] + '"'
//]);
]
).concat(['21| "height" : ' + InternalResolution[1] + ",", '23| "width" : ' + InternalResolution[0] + ",", '24| "window_mode" : "FullScreenWindowed"']);
Context.ReplaceLinesInTextFile(path, lines);
// Downgrade the copied exe to the previous version
var savePath = instanceFolder + "\\mgsvmgoOld.exe";
var savePkgOrigin = Context.RootInstallFolder + "\\mgsvmgo.exe";
System.IO.File.Copy(savePkgOrigin, savePath, true);
RunCMD(
"downgrade",
downgrade
? [
"(xdelta.exe -d -s mgsvmgoOld.exe downgrade.xdelta mgsvmgo.exe);",
'IF %ERRORLEVEL% NEQ 0 (%SystemRoot%\\Sysnative\\msg.exe * "EXE downgrade failed! Are you sure you have the latest legit Steam version?"); '
//,"PAUSE;"
]
: ['ren "mgsvmgoOld.exe" "mgsvmgo.exe"']
);
Context.Wait(3000);
var path = instanceFolder + "\\MGSV_TPP_WideScreen_Patch.cfg";
var lines = ["4|horizontalRes=" + Context.Width, "5|verticalRes=" + Context.Height];
Context.ReplaceLinesInTextFile(path, lines);
var url = "http://127.0.0.1:80";
var urlStartOffset = 0x1271a8;
var urlEndOffset = 0x1271d7;
// Apply widescreen patch, apply url patch, start game, wait and then crop window
RunPowershell(
"start",
HexEditFunction.concat([
"echo 'yes' | .\\MGS_V_Patch.exe",
"ReplaceHexBlockInExe " + '-filePath "' + instanceFolder + '\\mgsvmgo.exe" ' + '-replacementHex "' + url + '" ' + '-startOffset "' + urlStartOffset + '" ' + '-endOffset "' + urlEndOffset + '" '
])
.concat(SetupEnvironmentFunction)
.concat([
"SetupEnvironment -NucleusEnvironmentRoot " + Context.EnvironmentRoot + " -playerNickname " + Context.Nickname,
'$process = Start-Process -FilePath "mgsvmgo.exe" -PassThru ' + '-ArgumentList " /AppData 99c85cdbf2c837d50d37c82af2c837d5c12d5e80fbc837d5f2c837d5f2c837d5f2 " ',
"Start-Sleep -Seconds 10",
'."' + Game.Folder + '\\WinCrop\\WinCrop.exe" $process.Id ' + Context.Width + " " + Context.Height + " " + Context.PosX + " " + Context.PosY
])
);
};
Game.ProtoInput.OnInputLocked = function() {
for (var i = 0; i < PlayerList.Count; i++) {
var player = PlayerList[i];
ProtoInput.InstallHook(player.ProtoInputInstanceHandle, ProtoInput.Values.GetCursorPosHookID);
ProtoInput.InstallHook(player.ProtoInputInstanceHandle, ProtoInput.Values.SetCursorPosHookID);
ProtoInput.InstallHook(player.ProtoInputInstanceHandle, ProtoInput.Values.GetKeyStateHookID);
ProtoInput.InstallHook(player.ProtoInputInstanceHandle, ProtoInput.Values.GetAsyncKeyStateHookID);
ProtoInput.InstallHook(player.ProtoInputInstanceHandle, ProtoInput.Values.GetKeyboardStateHookID);
ProtoInput.InstallHook(player.ProtoInputInstanceHandle, ProtoInput.Values.CursorVisibilityStateHookID);
ProtoInput.InstallHook(player.ProtoInputInstanceHandle, ProtoInput.Values.FocusHooksHookID);
ProtoInput.EnableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.RawInputFilterID);
// Avoid the mouse move filter unless absolutely necessary as it can massively affect performance if the game gets primary input from mouse move moessages
//ProtoInput.EnableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.MouseMoveFilterID);
ProtoInput.EnableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.MouseActivateFilterID);
ProtoInput.EnableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.WindowActivateFilterID);
ProtoInput.EnableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.WindowActivateAppFilterID);
ProtoInput.EnableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.MouseWheelFilterID);
ProtoInput.EnableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.MouseButtonFilterID);
ProtoInput.EnableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.KeyboardButtonFilterID);
ProtoInput.SetDrawFakeCursor(player.ProtoInputInstanceHandle, false);
ProtoInput.StartFocusMessageLoop(player.ProtoInputInstanceHandle, 5, true, true, true, true, true);
// Disable the bypass: let the input be processed by Proto Input
ProtoInput.SetRawInputBypass(player.ProtoInputInstanceHandle, false);
}
};
Game.ProtoInput.OnInputUnlocked = function() {
for (var i = 0; i < PlayerList.Count; i++) {
var player = PlayerList[i];
ProtoInput.UninstallHook(player.ProtoInputInstanceHandle, ProtoInput.Values.GetCursorPosHookID);
ProtoInput.UninstallHook(player.ProtoInputInstanceHandle, ProtoInput.Values.SetCursorPosHookID);
ProtoInput.UninstallHook(player.ProtoInputInstanceHandle, ProtoInput.Values.GetKeyStateHookID);
ProtoInput.UninstallHook(player.ProtoInputInstanceHandle, ProtoInput.Values.GetAsyncKeyStateHookID);
ProtoInput.UninstallHook(player.ProtoInputInstanceHandle, ProtoInput.Values.GetKeyboardStateHookID);
ProtoInput.UninstallHook(player.ProtoInputInstanceHandle, ProtoInput.Values.CursorVisibilityStateHookID);
// Intentionally disable focus so all the instances don't respond to input at the same time
ProtoInput.UninstallHook(player.ProtoInputInstanceHandle, ProtoInput.Values.FocusHooksHookID);
ProtoInput.DisableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.RawInputFilterID);
ProtoInput.DisableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.MouseMoveFilterID);
ProtoInput.DisableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.MouseActivateFilterID);
ProtoInput.DisableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.WindowActivateFilterID);
ProtoInput.DisableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.WindowActivateAppFilterID);
ProtoInput.DisableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.MouseWheelFilterID);
ProtoInput.DisableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.MouseButtonFilterID);
ProtoInput.DisableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.KeyboardButtonFilterID);
ProtoInput.SetDrawFakeCursor(player.ProtoInputInstanceHandle, false);
// Intentionally disable focus so all the instances don't respond to input at the same time
ProtoInput.StopFocusMessageLoop(player.ProtoInputInstanceHandle);
// Enable the bypass: allow any raw input to pass
ProtoInput.SetRawInputBypass(player.ProtoInputInstanceHandle, true);
}
};
function RunCMD(fileName, cmdScript) {
var cmdFile = instanceFolder + "\\" + fileName + ".bat";
Context.WriteTextFile(cmdFile, cmdScript);
Context.RunAdditionalFiles(["all|" + cmdFile], false, 0, true, false);
}
function RunPowershell(fileName, psScript) {
var psFile = instanceFolder + "\\" + fileName + ".ps1";
Context.WriteTextFile(psFile, psScript);
var cmdScript = [
'%SystemRoot%\\sysnative\\WindowsPowerShell\\v1.0\\powershell.exe -ExecutionPolicy RemoteSigned -command ".\\' + fileName + '.ps1"'
//,"PAUSE;"
];
RunCMD(fileName, cmdScript);
}
const HexEditFunction = [
" function ReplaceHexBlockInExe { ",
" param ( ",
" [string]$filePath, ",
" [int]$startOffset, ",
" [int]$endOffset, ",
" [string]$replacementHex ",
" ) ",
" ",
" if (-not (Test-Path -Path $filePath -PathType Leaf)) { ",
' Write-Host "File not found: $filePath" ',
" return ",
" } ",
" ",
" $binaryData = [System.IO.File]::ReadAllBytes($filePath) ",
" ",
" if ($startOffset -lt 0 -or $startOffset -ge $binaryData.Length -or ",
" $endOffset -lt 0 -or $endOffset -ge $binaryData.Length -or ",
" $startOffset -gt $endOffset) { ",
' Write-Host "Invalid range specified." ',
" return ",
" } ",
" ",
" $blockLength = $endOffset - $startOffset + 1 ",
" $zeroBytes = [byte[]]@(0) * $blockLength ",
" $replacementBytes = [System.Text.Encoding]::UTF8.GetBytes($replacementHex) ",
" [Array]::Copy($zeroBytes, 0, $binaryData, $startOffset, $blockLength) ",
" [Array]::Copy($replacementBytes, 0, $binaryData, $startOffset, $replacementBytes.Length) ",
" [System.IO.File]::WriteAllBytes($filePath, $binaryData) ",
" ",
' Write-Host "Hex block replaced and updated in $filePath." ',
" } "
];
const SetupEnvironmentFunction = [
" function SetupEnvironment { ",
" param ( ",
" [string]$NucleusEnvironmentRoot, ",
" [string]$playerNickname ",
" ) ",
'Write-Host "Setting up Nucleus environment" ',
" ",
"$username = $env:USERNAME ",
" ",
"try { ",
" $username = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name.Split('\\')[1] ",
"} catch { ",
" Write-Host \"ERROR - getting current user's username, defaulting to using environment's username\" ",
"} ",
" ",
"# Set environment variables ",
'$env:APPDATA = "$NucleusEnvironmentRoot\\$playerNickname\\AppData\\Roaming" ',
'$env:LOCALAPPDATA = "$NucleusEnvironmentRoot\\$playerNickname\\AppData\\Local" ',
'$env:USERPROFILE = "$NucleusEnvironmentRoot\\$playerNickname" ',
'$env:HOMEPATH = "Users\\$username\\NucleusCoop\\$playerNickname" ',
" ",
"# Create directories ",
"$directories = @( ",
' "$NucleusEnvironmentRoot", ',
' "$NucleusEnvironmentRoot\\$playerNickname", ',
' "$NucleusEnvironmentRoot\\$playerNickname\\AppData\\Roaming", ',
' "$NucleusEnvironmentRoot\\$playerNickname\\AppData\\Local" ',
") ",
" ",
"foreach ($directory in $directories) { ",
" New-Item -ItemType Directory -Force -Path $directory ",
"} ",
"} "
];
const ResizeWindowFunction = [
"function ResizeWindow { ",
" param ( ",
" [Parameter(Mandatory=$true)] ",
" [int]$ProcessId, ",
" ",
" [Parameter(Mandatory=$true)] ",
" [int]$Left, ",
" ",
" [Parameter(Mandatory=$true)] ",
" [int]$Top, ",
" ",
" [Parameter(Mandatory=$true)] ",
" [int]$Width, ",
" ",
" [Parameter(Mandatory=$true)] ",
" [int]$Height ",
" ) ",
" ",
" # Get the process by ID ",
" $process = Get-Process -Id $ProcessId -ErrorAction SilentlyContinue ",
" ",
" if ($process -ne $null) { ",
" # Get the window handle ",
" $hwnd = $process.MainWindowHandle ",
" ",
" # Move and resize the window ",
" $result = [User32]::MoveWindow($hwnd, $Left, $Top, $Width, $Height, $true) ",
" ",
" if ($result -eq 0) { ",
' Write-Host "Window repositioned and resized successfully." ',
" } else { ",
' Write-Host "Failed to reposition and resize the window. Error code: $result" ',
" } ",
" } else { ",
' Write-Host "Process with ID $ProcessId not found." ',
" } ",
"} ",
" ",
"# Define User32 class for MoveWindow function ",
'Add-Type @" ',
" using System; ",
" using System.Runtime.InteropServices; ",
" ",
" public class User32 { ",
' [DllImport("user32.dll", SetLastError = true)] ',
" public static extern int MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, [MarshalAs(UnmanagedType.Bool)] bool bRepaint); ",
" } ",
'"@ '
];