diff --git a/source/Controller.ps1 b/source/Controller.ps1 index 9996eed..ecd596e 100644 --- a/source/Controller.ps1 +++ b/source/Controller.ps1 @@ -1,75 +1,29 @@ -$QuickEditCodeSnippet=@" -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Runtime.InteropServices; - -public static class DisableConsoleQuickEdit -{ - const uint ENABLE_QUICK_EDIT = 0x0040; - const int STD_INPUT_HANDLE = -10; -[DllImport("kernel32.dll", SetLastError = true)] - static extern IntPtr GetStdHandle(int nStdHandle); -[DllImport("kernel32.dll")] - static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode); -[DllImport("kernel32.dll")] - static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode); - - public static bool SetQuickEdit(bool reset) - { - - IntPtr consoleHandle = GetStdHandle(STD_INPUT_HANDLE); - uint consoleMode; - if (!GetConsoleMode(consoleHandle, out consoleMode)) - { - return false; - } - if (reset) - { - consoleMode &= ~ENABLE_QUICK_EDIT; - } - else - { - consoleMode |= ENABLE_QUICK_EDIT; - } - if (!SetConsoleMode(consoleHandle, consoleMode)) - { - return false; - } - return true; - } +$Config = @{ +# 自行指定游戏根目录和游戏启动命令示例: +# GameRootDirectory = "C:\Users\Silver\Games\Tiancity\csol\"; +# LaunchGameCmd = '"C:\Program Files (x86)\TCGame\tcgame.exe" cso'; + MaxWaitTimeInRoom = 600; } -"@ -$QuickEditMode = Add-Type -TypeDefinition $QuickEditCodeSnippet -Language CSharp +$Parameters = @() -function Set-QuickEdit() +foreach ($key in $Config.Keys) { -[CmdletBinding()] -param( -[Parameter(Mandatory=$False, HelpMessage="This switch will disable Console QuickEdit mode.")] - [switch]$DisableQuickEdit=$False -) -if([DisableConsoleQuickEdit]::SetQuickEdit($DisableQuickEdit)) - { - Write-Output "[MESSAGE] Console Quick-Edit Mode disabled successfully." - } - else - { - Write-Output "[ERROR] Attempt to disable Console Quick-Edit Mode failed." - } + $val = $Config[$key] + if ($val -ne $Null -and $val.Length -gt 0) + { + $Parameters += "--$key" + $Parameters += "$val" + } } + $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) if (-not $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { - $location = (Get-Location).ToString() - Start-Process -FilePath powershell -Verb RunAs -ArgumentList "-NoExit -Command `"& {Set-Location `'$location`'; .\Controller.ps1}`"" + Start-Process -FilePath powershell -Verb RunAs -ArgumentList "-NoExit -Command `"& { Set-Location `'$PSScriptRoot`'; .\Controller.ps1 }`"" } else { - Set-QuickEdit -DisableQuickEdit - .\Controller.exe -} \ No newline at end of file + .\Controller.exe $Parameters +} diff --git a/source/Controller/Main.cpp b/source/Controller/Main.cpp index e36b378..d7391f4 100644 --- a/source/Controller/Main.cpp +++ b/source/Controller/Main.cpp @@ -48,15 +48,13 @@ int wmain(int argc, wchar_t **argv) std::string game_root_dir; std::string launch_game_cmd; uint32_t max_wait_time_in_room{15 * 60}; - app.add_option("--GameRootDirectory", game_root_dir, "Game root path, e.g., \"C:\\csol\""); + app.add_option("--GameRootDirectory", game_root_dir, "游戏根目录。例如:\"C:\\csol\"。"); app.add_option( "--LaunchGameCmd", launch_game_cmd, - "Command line to launch the game, e.g., \"C:\\TCGAME\\tcgame.exe\" cso. Note that the program path must be " - "parenthesized, otherwise the controller will fail to aoto-restart the game."); + "启动游戏的命令(含命令行参数)。例如:\"C:\\TCGAME\\tcgame.exe\" cso。注意,若启动命令含有命令行参数,则启动器路径必须用双引号包含,否则掉线自动重连将失败。"); app.add_option( "--MaxWaitTimeInRoom", max_wait_time_in_room, - "Maximum time to wait in a room, in seconds, 900 seconds by default. Once the in-room waiting time exceeds " - "this value, controller will instruct the executor to leave the game room and create a new room."); + "在房间中最长等待时间(单位为秒)。当等待时间超过该时间,将自动离开当前房间并创建新房间挂机。"); CLI11_PARSE(app, argc, argv); try { diff --git a/source/Controller/WindowCapture.cpp b/source/Controller/WindowCapture.cpp index 61673b8..911ec78 100644 --- a/source/Controller/WindowCapture.cpp +++ b/source/Controller/WindowCapture.cpp @@ -43,7 +43,12 @@ WindowCapture& WindowCapture::operator=(WindowCapture wc) swap(*this, wc); return *this; } - +/* + *@brief 截取指定窗口的图像。 + *@param `hWnd` 窗口句柄 + *@return `WindowCapture` 对象 + *@remark `hWnd` 可以为 `nullptr`,此时本函数将截取桌面窗口(覆盖整个屏幕)的图像。 + */ WindowCapture& WindowCapture::Capture(HWND hWnd) { if (!hWnd) @@ -73,19 +78,26 @@ WindowCapture& WindowCapture::Capture(HWND hWnd) { throw Exception("WindowCapture::Capture(HWND):窗口处于最小化状态,无法捕获。"); } + + /* 获取窗口的相对区域 */ RECT rcClient; if (!GetClientRect(hWnd, &rcClient)) { throw Exception("WindowCapture::Capture(HWND):GetClientRect 失败。错误代码:%lu。", GetLastError()); } + + /* 获取窗口左上角绝对坐标 */ + POINT ptLeftTopOfClient{ 0, 0 }; /* 需要将各字段坐标初始化为 0,表示左上角坐标 */ + ClientToScreen(hWnd, &ptLeftTopOfClient); + + /* 从整个屏幕截图中裁剪出客户端窗口部分 */ SetStretchBltMode(hdcWindow.get(), HALFTONE); - if (!StretchBlt(hdcWindow.get(), + if (!StretchBlt(hdcWindow.get(), /* 窗口部分 */ 0, 0, rcClient.right, rcClient.bottom, - hdcScreen.get(), - 0, 0, - GetSystemMetrics(SM_CXSCREEN), - GetSystemMetrics(SM_CYSCREEN), + hdcScreen.get(), /* 整个屏幕 */ + ptLeftTopOfClient.x, ptLeftTopOfClient.y, + rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, SRCCOPY)) { throw Exception("WindowCapture::Capture(HWND):SetStretchBltMode 失败。错误代码:%lu。", GetLastError()); @@ -108,14 +120,15 @@ WindowCapture& WindowCapture::Capture(HWND hWnd) { throw Exception("WindowCapture::Capture(const RECT&):BitBlt 失败。错误代码:%lu。", GetLastError()); } + BITMAP bmpScreen{}; - GetObject(hbmScreen.get(), sizeof(BITMAP), &bmpScreen); + GetObjectW(hbmScreen.get(), sizeof(BITMAP), &bmpScreen); /* 获取位图信息 */ m_BitmapInfoHeader.biSize = sizeof(BITMAPINFOHEADER); m_BitmapInfoHeader.biWidth = bmpScreen.bmWidth; m_BitmapInfoHeader.biHeight = bmpScreen.bmHeight; m_BitmapInfoHeader.biPlanes = 1; - m_BitmapInfoHeader.biBitCount = 32; + m_BitmapInfoHeader.biBitCount = 32; /* 每个像素 32 位 */ m_BitmapInfoHeader.biCompression = BI_RGB; m_BitmapInfoHeader.biSizeImage = 0; m_BitmapInfoHeader.biXPelsPerMeter = 0; @@ -123,7 +136,7 @@ WindowCapture& WindowCapture::Capture(HWND hWnd) m_BitmapInfoHeader.biClrUsed = 0; m_BitmapInfoHeader.biClrImportant = 0; - dwBmpSize = ((bmpScreen.bmWidth * m_BitmapInfoHeader.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight; + dwBmpSize = ((bmpScreen.bmWidth * m_BitmapInfoHeader.biBitCount + 31) / 32) /* 宽度向上取整 */ * 4 * bmpScreen.bmHeight; if (dwBmpSize > m_BufferCapacity) { @@ -132,16 +145,16 @@ WindowCapture& WindowCapture::Capture(HWND hWnd) } if (!GetDIBits(hdcWindow.get(), hbmScreen.get(), 0, - (UINT)bmpScreen.bmHeight, + static_cast(bmpScreen.bmHeight), m_pData, - (BITMAPINFO*)&m_BitmapInfoHeader, DIB_RGB_COLORS)) + reinterpret_cast(&m_BitmapInfoHeader), DIB_RGB_COLORS)) { throw Exception("WindowCapture::Capture(const RECT&):GetDIBits 失败。错误代码:%lu。", GetLastError()); } dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); - m_BitmapFileHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER); + m_BitmapFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); m_BitmapFileHeader.bfSize = dwSizeofDIB; m_BitmapFileHeader.bfType = 0x4D42; // BM. m_BitmapFileHeader.bfReserved1 = 0; @@ -156,18 +169,9 @@ void WindowCapture::Save(std::filesystem::path p) { throw Exception("WindowCapture::Save(std::filesystem::path):CreateFileW 失败。错误代码:%lu。", GetLastError()); } - /* mandatory in Windows 7 */ - //DWORD dwBytesWritten1; - //DWORD dwBytesWritten2; - //DWORD dwBytesWritten3; - //if (WriteFile(hFile.get(), &m_BitmapFileHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten1, NULL) && - // WriteFile(hFile.get(), &m_BitmapInfoHeader, sizeof(BITMAPINFOHEADER), &dwBytesWritten2, NULL) && - // WriteFile(hFile.get(), m_pData, m_BitmapFileHeader.bfSize, &dwBytesWritten3, NULL) - //) - //{ - // throw Exception("WindowCapture::Save(std::filesystem::path):WriteFile 失败。错误代码:%lu。", GetLastError()); - //} + Save(hFile.get()); } + void WindowCapture::Save(HANDLE hFile) { if (!hFile || hFile == INVALID_HANDLE_VALUE)