mirror of
https://github.com/Blinue/Magpie.git
synced 2026-06-24 02:04:10 +00:00
644 lines
18 KiB
C++
644 lines
18 KiB
C++
#include "pch.h"
|
||
#include "App.h"
|
||
#include "Utils.h"
|
||
#include "GraphicsCaptureFrameSource.h"
|
||
#include "GDIFrameSource.h"
|
||
#include "DwmSharedSurfaceFrameSource.h"
|
||
#include "DesktopDuplicationFrameSource.h"
|
||
#include "ExclModeHack.h"
|
||
|
||
|
||
extern std::shared_ptr<spdlog::logger> logger;
|
||
|
||
const UINT WM_DESTORYHOST = RegisterWindowMessage(L"MAGPIE_WM_DESTORYHOST");
|
||
|
||
static constexpr const wchar_t* HOST_WINDOW_CLASS_NAME = L"Window_Magpie_967EB565-6F73-4E94-AE53-00CC42592A22";
|
||
static constexpr const wchar_t* DDF_WINDOW_CLASS_NAME = L"Window_Magpie_C322D752-C866-4630-91F5-32CB242A8930";
|
||
static constexpr const wchar_t* HOST_WINDOW_TITLE = L"Magpie_Host";
|
||
|
||
|
||
App::~App() {
|
||
MagUninitialize();
|
||
winrt::uninit_apartment();
|
||
}
|
||
|
||
bool App::Initialize(HINSTANCE hInst) {
|
||
SPDLOG_LOGGER_INFO(logger, "正在初始化 App");
|
||
|
||
_hInst = hInst;
|
||
|
||
// 初始化 COM
|
||
winrt::init_apartment(winrt::apartment_type::multi_threaded);
|
||
|
||
_RegisterWndClasses();
|
||
|
||
// 供隐藏光标和 MagCallback 抓取模式使用
|
||
if (!MagInitialize()) {
|
||
SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("MagInitialize 失败"));
|
||
}
|
||
|
||
SPDLOG_LOGGER_INFO(logger, "App 初始化成功");
|
||
return true;
|
||
}
|
||
|
||
bool App::Run(
|
||
HWND hwndSrc,
|
||
const std::string& effectsJson,
|
||
UINT captureMode,
|
||
int frameRate,
|
||
float cursorZoomFactor,
|
||
UINT cursorInterpolationMode,
|
||
UINT adapterIdx,
|
||
UINT multiMonitorUsage,
|
||
const RECT& cropBorders,
|
||
UINT flags
|
||
) {
|
||
_hwndSrc = hwndSrc;
|
||
_captureMode = captureMode;
|
||
_frameRate = frameRate;
|
||
_cursorZoomFactor = cursorZoomFactor;
|
||
_cursorInterpolationMode = cursorInterpolationMode;
|
||
_adapterIdx = adapterIdx;
|
||
_multiMonitorUsage = multiMonitorUsage;
|
||
_cropBorders = cropBorders;
|
||
_flags = flags;
|
||
|
||
SPDLOG_LOGGER_INFO(logger, fmt::format("运行时参数:\n\thwndSrc:{}\n\tcaptureMode:{}\n\tadjustCursorSpeed:{}\n\tshowFPS:{}\n\tframeRate:{}\n\tdisableLowLatency:{}\n\tbreakpointMode:{}\n\tdisableWindowResizing:{}\n\tdisableDirectFlip:{}\n\tconfineCursorIn3DGames:{}\n\tadapterIdx:{}\n\tcropTitleBarOfUWP:{}\n\tmultiMonitorUsage: {}\n\tnoCursor: {}\n\tdisableEffectCache: {}\n\tsimulateExclusiveFullscreen: {}\n\tcursorInterpolationMode: {}\n\tcropLeft: {}\n\tcropTop: {}\n\tcropRight: {}\n\tcropBottom: {}", (void*)hwndSrc, captureMode, IsAdjustCursorSpeed(), IsShowFPS(), frameRate, IsDisableLowLatency(), IsBreakpointMode(), IsDisableWindowResizing(), IsDisableDirectFlip(), IsConfineCursorIn3DGames(), adapterIdx, IsCropTitleBarOfUWP(), multiMonitorUsage, IsNoCursor(), IsDisableEffectCache(), IsSimulateExclusiveFullscreen(), cursorInterpolationMode, cropBorders.left, cropBorders.top, cropBorders.right, cropBorders.bottom));
|
||
|
||
SetErrorMsg(ErrorMessages::GENERIC);
|
||
|
||
// 禁用窗口大小调整
|
||
if (IsDisableWindowResizing()) {
|
||
LONG_PTR style = GetWindowLongPtr(hwndSrc, GWL_STYLE);
|
||
if (style & WS_THICKFRAME) {
|
||
if (SetWindowLongPtr(hwndSrc, GWL_STYLE, style ^ WS_THICKFRAME)) {
|
||
// 不重绘边框,以防某些窗口状态不正确
|
||
// if (!SetWindowPos(hwndSrc, 0, 0, 0, 0, 0,
|
||
// SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED)) {
|
||
// SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("SetWindowPos 失败"));
|
||
// }
|
||
|
||
SPDLOG_LOGGER_INFO(logger, "已禁用窗口大小调整");
|
||
_windowResizingDisabled = true;
|
||
} else {
|
||
SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("禁用窗口大小调整失败"));
|
||
}
|
||
}
|
||
}
|
||
|
||
// 模拟独占全屏
|
||
// 必须在主窗口创建前,否则 SHQueryUserNotificationState 可能返回 QUNS_BUSY 而不是 QUNS_RUNNING_D3D_FULL_SCREEN
|
||
ExclModeHack exclMode;
|
||
|
||
if (!_CreateHostWnd()) {
|
||
SPDLOG_LOGGER_CRITICAL(logger, "创建主窗口失败");
|
||
_OnQuit();
|
||
return false;
|
||
}
|
||
|
||
_renderer.reset(new Renderer());
|
||
if (!_renderer->Initialize()) {
|
||
SPDLOG_LOGGER_CRITICAL(logger, "初始化 Renderer 失败,正在清理");
|
||
Close();
|
||
_Run();
|
||
return false;
|
||
}
|
||
|
||
_srcFrameRect = {};
|
||
|
||
switch (captureMode) {
|
||
case 0:
|
||
_frameSource.reset(new GraphicsCaptureFrameSource());
|
||
break;
|
||
case 1:
|
||
_frameSource.reset(new DesktopDuplicationFrameSource());
|
||
break;
|
||
case 2:
|
||
_frameSource.reset(new GDIFrameSource());
|
||
break;
|
||
case 3:
|
||
_frameSource.reset(new DwmSharedSurfaceFrameSource());
|
||
break;
|
||
default:
|
||
SPDLOG_LOGGER_CRITICAL(logger, "未知的捕获模式,即将退出");
|
||
Close();
|
||
_Run();
|
||
return false;
|
||
}
|
||
|
||
if (!_frameSource->Initialize()) {
|
||
SPDLOG_LOGGER_CRITICAL(logger, "初始化 FrameSource 失败,即将退出");
|
||
Close();
|
||
_Run();
|
||
return false;
|
||
}
|
||
|
||
if (_srcFrameRect == RECT{}) {
|
||
// FrameSource 初始化完成后计算窗口边框,因为初始化过程中可能改变窗口位置
|
||
if (!UpdateSrcFrameRect()) {
|
||
SPDLOG_LOGGER_ERROR(logger, "UpdateSrcFrameRect 失败");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
SPDLOG_LOGGER_INFO(logger, fmt::format("源窗口尺寸:{}x{}",
|
||
_srcFrameRect.right - _srcFrameRect.left, _srcFrameRect.bottom - _srcFrameRect.top));
|
||
|
||
if (!_renderer->InitializeEffectsAndCursor(effectsJson)) {
|
||
SPDLOG_LOGGER_CRITICAL(logger, "初始化效果失败,即将退出");
|
||
Close();
|
||
_Run();
|
||
return false;
|
||
}
|
||
|
||
// 禁用窗口圆角
|
||
if (_frameSource->HasRoundCornerInWin11()) {
|
||
const auto& version = Utils::GetOSVersion();
|
||
bool isWin11 = Utils::CompareVersion(
|
||
version.dwMajorVersion, version.dwMinorVersion,
|
||
version.dwBuildNumber, 10, 0, 22000) >= 0;
|
||
|
||
if (isWin11) {
|
||
INT attr = DWMWCP_DONOTROUND;
|
||
HRESULT hr = DwmSetWindowAttribute(hwndSrc, DWMWA_WINDOW_CORNER_PREFERENCE, &attr, sizeof(attr));
|
||
if (FAILED(hr)) {
|
||
SPDLOG_LOGGER_ERROR(logger, MakeComErrorMsg("禁用窗口圆角失败", hr));
|
||
} else {
|
||
SPDLOG_LOGGER_INFO(logger, "已禁用窗口圆角");
|
||
_roundCornerDisabled = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
_Run();
|
||
|
||
return true;
|
||
}
|
||
|
||
void App::_Run() {
|
||
SPDLOG_LOGGER_INFO(logger, "开始接收窗口消息");
|
||
|
||
while (true) {
|
||
MSG msg;
|
||
while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
|
||
if (msg.message == WM_QUIT) {
|
||
_OnQuit();
|
||
return;
|
||
}
|
||
|
||
TranslateMessage(&msg);
|
||
DispatchMessage(&msg);
|
||
}
|
||
|
||
_renderer->Render();
|
||
|
||
// 第二帧(等待时或完成后)创建 DDF 窗口
|
||
// 如果在 Run 中创建会有短暂的灰屏
|
||
// 选择第二帧的原因:当 GetFrameCount() 返回 1 时第一帧可能处于等待状态而没有渲染,见 Renderer::Render()
|
||
if (_renderer->GetTimer().GetFrameCount() == 2 && !_hwndDDF && IsDisableDirectFlip() && !IsBreakpointMode()) {
|
||
if (!_DisableDirectFlip()) {
|
||
SPDLOG_LOGGER_ERROR(logger, "_DisableDirectFlip 失败");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
ComPtr<IWICImagingFactory2> App::GetWICImageFactory() {
|
||
if (_wicImgFactory == nullptr) {
|
||
HRESULT hr = CoCreateInstance(
|
||
CLSID_WICImagingFactory,
|
||
NULL,
|
||
CLSCTX_INPROC_SERVER,
|
||
IID_PPV_ARGS(&_wicImgFactory)
|
||
);
|
||
|
||
if (FAILED(hr)) {
|
||
SPDLOG_LOGGER_ERROR(logger, MakeComErrorMsg("创建 WICImagingFactory 失败", hr));
|
||
return nullptr;
|
||
}
|
||
}
|
||
|
||
return _wicImgFactory;
|
||
}
|
||
|
||
bool App::RegisterTimer(UINT uElapse, std::function<void()> cb) {
|
||
if (!SetTimer(_hwndHost, _nextTimerId, uElapse, nullptr)) {
|
||
SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("SetTimer 失败"));
|
||
return false;
|
||
}
|
||
|
||
++_nextTimerId;
|
||
_timerCbs.emplace_back(std::move(cb));
|
||
|
||
return true;
|
||
}
|
||
|
||
|
||
LRESULT DDFWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||
if (msg == WM_DESTROY) {
|
||
return 0;
|
||
}
|
||
|
||
return DefWindowProc(hWnd, msg, wParam, lParam);
|
||
}
|
||
|
||
// 注册窗口类
|
||
void App::_RegisterWndClasses() const {
|
||
WNDCLASSEX wcex = {};
|
||
wcex.cbSize = sizeof(WNDCLASSEX);
|
||
wcex.lpfnWndProc = _HostWndProcStatic;
|
||
wcex.hInstance = _hInst;
|
||
wcex.lpszClassName = HOST_WINDOW_CLASS_NAME;
|
||
|
||
if (!RegisterClassEx(&wcex)) {
|
||
// 忽略此错误,因为可能是重复注册产生的错误
|
||
SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("注册主窗口类失败"));
|
||
} else {
|
||
SPDLOG_LOGGER_INFO(logger, "已注册主窗口类");
|
||
}
|
||
|
||
wcex.lpfnWndProc = DDFWndProc;
|
||
wcex.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
|
||
wcex.lpszClassName = DDF_WINDOW_CLASS_NAME;
|
||
|
||
if (!RegisterClassEx(&wcex)) {
|
||
SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("注册 DDF 窗口类失败"));
|
||
} else {
|
||
SPDLOG_LOGGER_INFO(logger, "已注册 DDF 窗口类");
|
||
}
|
||
}
|
||
|
||
static BOOL CALLBACK MonitorEnumProc(HMONITOR, HDC, LPRECT monitorRect, LPARAM data) {
|
||
RECT* params = (RECT*)data;
|
||
|
||
if (Utils::CheckOverlap(params[0], *monitorRect)) {
|
||
UnionRect(¶ms[1], monitorRect, ¶ms[1]);
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static bool CalcHostWndRect(HWND hWnd, UINT multiMonitorMode, RECT& result) {
|
||
switch (multiMonitorMode) {
|
||
case 0:
|
||
{
|
||
// 使用距离源窗口最近的显示器
|
||
HMONITOR hMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
|
||
if (!hMonitor) {
|
||
SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("MonitorFromWindow 失败"));
|
||
return false;
|
||
}
|
||
|
||
MONITORINFO mi{};
|
||
mi.cbSize = sizeof(mi);
|
||
if (!GetMonitorInfo(hMonitor, &mi)) {
|
||
SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("GetMonitorInfo 失败"));
|
||
return false;
|
||
}
|
||
result = mi.rcMonitor;
|
||
|
||
break;
|
||
}
|
||
case 1:
|
||
{
|
||
// 使用源窗口跨越的所有显示器
|
||
|
||
// [0] 存储源窗口坐标,[1] 存储计算结果
|
||
RECT params[2]{};
|
||
|
||
if (!Utils::GetWindowFrameRect(hWnd, params[0])) {
|
||
SPDLOG_LOGGER_ERROR(logger, "GetWindowFrameRect 失败");
|
||
return false;
|
||
}
|
||
|
||
if (!EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)¶ms)) {
|
||
SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("EnumDisplayMonitors 失败"));
|
||
return false;
|
||
}
|
||
|
||
result = params[1];
|
||
if (result.right - result.left <= 0 || result.bottom - result.top <= 0) {
|
||
SPDLOG_LOGGER_ERROR(logger, "计算主窗口坐标失败");
|
||
return false;
|
||
}
|
||
|
||
break;
|
||
}
|
||
case 2:
|
||
{
|
||
// 使用所有显示器(Virtual Screen)
|
||
int vsWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
|
||
int vsHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
|
||
int vsX = GetSystemMetrics(SM_XVIRTUALSCREEN);
|
||
int vsY = GetSystemMetrics(SM_YVIRTUALSCREEN);
|
||
result = { vsX, vsY, vsX + vsWidth, vsY + vsHeight };
|
||
|
||
break;
|
||
}
|
||
default:
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
// 创建主窗口
|
||
bool App::_CreateHostWnd() {
|
||
if (FindWindow(HOST_WINDOW_CLASS_NAME, nullptr)) {
|
||
SPDLOG_LOGGER_CRITICAL(logger, "已存在主窗口");
|
||
return false;
|
||
}
|
||
|
||
if (!CalcHostWndRect(_hwndSrc, GetMultiMonitorUsage(), _hostWndRect)) {
|
||
SPDLOG_LOGGER_ERROR(logger, "CalcHostWndRect 失败");
|
||
return false;
|
||
}
|
||
|
||
// 主窗口没有覆盖 Virtual Screen 则使用多屏幕模式
|
||
// 打开断点模式时不使用多屏幕模式
|
||
_isMultiMonitorMode = !IsBreakpointMode() && GetMultiMonitorUsage() != 2 &&
|
||
((_hostWndRect.right - _hostWndRect.left) < GetSystemMetrics(SM_CXVIRTUALSCREEN) ||
|
||
(_hostWndRect.bottom - _hostWndRect.top) < GetSystemMetrics(SM_CYVIRTUALSCREEN));
|
||
|
||
_hwndHost = CreateWindowEx(
|
||
(IsBreakpointMode() ? 0 : WS_EX_TOPMOST) | WS_EX_NOACTIVATE | WS_EX_LAYERED | WS_EX_TRANSPARENT,
|
||
HOST_WINDOW_CLASS_NAME,
|
||
HOST_WINDOW_TITLE,
|
||
WS_CLIPCHILDREN | WS_POPUP | WS_VISIBLE,
|
||
_hostWndRect.left,
|
||
_hostWndRect.top,
|
||
_hostWndRect.right - _hostWndRect.left,
|
||
_hostWndRect.bottom - _hostWndRect.top,
|
||
NULL,
|
||
NULL,
|
||
_hInst,
|
||
NULL
|
||
);
|
||
if (!_hwndHost) {
|
||
SPDLOG_LOGGER_CRITICAL(logger, MakeWin32ErrorMsg("创建主窗口失败"));
|
||
return false;
|
||
}
|
||
|
||
SPDLOG_LOGGER_INFO(logger, fmt::format("主窗口尺寸:{}x{}",
|
||
_hostWndRect.right - _hostWndRect.left, _hostWndRect.bottom - _hostWndRect.top));
|
||
|
||
// 设置窗口不透明
|
||
// 不完全透明时可关闭 DirectFlip
|
||
if (!SetLayeredWindowAttributes(_hwndHost, 0, IsDisableDirectFlip() ? 254 : 255, LWA_ALPHA)) {
|
||
SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("SetLayeredWindowAttributes 失败"));
|
||
}
|
||
|
||
if (!ShowWindow(_hwndHost, SW_NORMAL)) {
|
||
SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("ShowWindow 失败"));
|
||
}
|
||
|
||
SPDLOG_LOGGER_INFO(logger, "已创建主窗口");
|
||
return true;
|
||
}
|
||
|
||
bool App::_DisableDirectFlip() {
|
||
// 没有显式关闭 DirectFlip 的方法
|
||
// 将全屏窗口设为稍微透明,以灰色全屏窗口为背景
|
||
_hwndDDF = CreateWindowEx(
|
||
WS_EX_NOACTIVATE | WS_EX_LAYERED | WS_EX_TRANSPARENT,
|
||
DDF_WINDOW_CLASS_NAME,
|
||
NULL,
|
||
WS_CLIPCHILDREN | WS_POPUP | WS_VISIBLE,
|
||
_hostWndRect.left,
|
||
_hostWndRect.top,
|
||
_hostWndRect.right - _hostWndRect.left,
|
||
_hostWndRect.bottom - _hostWndRect.top,
|
||
NULL,
|
||
NULL,
|
||
_hInst,
|
||
NULL
|
||
);
|
||
|
||
if (!_hwndDDF) {
|
||
SPDLOG_LOGGER_CRITICAL(logger, MakeWin32ErrorMsg("创建 DDF 窗口失败"));
|
||
return false;
|
||
}
|
||
|
||
// 设置窗口不透明
|
||
if (!SetLayeredWindowAttributes(_hwndDDF, 0, 255, LWA_ALPHA)) {
|
||
SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("SetLayeredWindowAttributes 失败"));
|
||
}
|
||
|
||
if (_frameSource->IsScreenCapture()) {
|
||
const RTL_OSVERSIONINFOW& version = Utils::GetOSVersion();
|
||
if (Utils::CompareVersion(version.dwMajorVersion, version.dwMinorVersion, version.dwBuildNumber, 10, 0, 19041) >= 0) {
|
||
// 使 DDF 窗口无法被捕获到
|
||
if (!SetWindowDisplayAffinity(_hwndDDF, WDA_EXCLUDEFROMCAPTURE)) {
|
||
SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("SetWindowDisplayAffinity 失败"));
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!ShowWindow(_hwndDDF, SW_NORMAL)) {
|
||
SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("ShowWindow 失败"));
|
||
}
|
||
|
||
if (!SetWindowPos(_hwndDDF, _hwndHost, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW)) {
|
||
SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("SetWindowPos 失败"));
|
||
}
|
||
|
||
SPDLOG_LOGGER_INFO(logger, "已创建 DDF 主窗口");
|
||
return true;
|
||
}
|
||
|
||
LRESULT App::_HostWndProcStatic(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||
return GetInstance()._HostWndProc(hWnd, msg, wParam, lParam);
|
||
}
|
||
|
||
|
||
LRESULT App::_HostWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
|
||
if (message == WM_DESTORYHOST) {
|
||
SPDLOG_LOGGER_INFO(logger, "收到 MAGPIE_WM_DESTORYHOST 消息,即将销毁主窗口");
|
||
Close();
|
||
return 0;
|
||
}
|
||
|
||
switch (message) {
|
||
case WM_DESTROY:
|
||
{
|
||
// 有两个退出路径:
|
||
// 1. 前台窗口发生改变
|
||
// 2. 收到_WM_DESTORYMAG 消息
|
||
PostQuitMessage(0);
|
||
return 0;
|
||
}
|
||
case WM_TIMER:
|
||
{
|
||
if (hWnd != _hwndHost || wParam <= 0 || wParam > _timerCbs.size()) {
|
||
break;
|
||
}
|
||
|
||
_timerCbs[wParam - 1]();
|
||
return 0;
|
||
}
|
||
default:
|
||
break;
|
||
}
|
||
|
||
return DefWindowProc(hWnd, message, wParam, lParam);
|
||
}
|
||
|
||
void App::_OnQuit() {
|
||
// 释放资源
|
||
_frameSource = nullptr;
|
||
_renderer = nullptr;
|
||
|
||
// 计时器资源在窗口销毁时自动释放
|
||
_nextTimerId = 1;
|
||
_timerCbs.clear();
|
||
|
||
// 还原窗口圆角
|
||
if (_roundCornerDisabled) {
|
||
_roundCornerDisabled = false;
|
||
|
||
INT attr = DWMWCP_DEFAULT;
|
||
HRESULT hr = DwmSetWindowAttribute(_hwndSrc, DWMWA_WINDOW_CORNER_PREFERENCE, &attr, sizeof(attr));
|
||
if (FAILED(hr)) {
|
||
SPDLOG_LOGGER_INFO(logger, MakeComErrorMsg("取消禁用窗口圆角失败", hr));
|
||
} else {
|
||
SPDLOG_LOGGER_INFO(logger, "已取消禁用窗口圆角");
|
||
}
|
||
}
|
||
|
||
// 还原窗口大小调整
|
||
if (_windowResizingDisabled) {
|
||
_windowResizingDisabled = false;
|
||
|
||
LONG_PTR style = GetWindowLongPtr(_hwndSrc, GWL_STYLE);
|
||
if (!(style & WS_THICKFRAME)) {
|
||
if (SetWindowLongPtr(_hwndSrc, GWL_STYLE, style | WS_THICKFRAME)) {
|
||
if (!SetWindowPos(_hwndSrc, 0, 0, 0, 0, 0,
|
||
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED)) {
|
||
SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("SetWindowPos 失败"));
|
||
}
|
||
|
||
SPDLOG_LOGGER_INFO(logger, "已取消禁用窗口大小调整");
|
||
} else {
|
||
SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("取消禁用窗口大小调整失败"));
|
||
}
|
||
}
|
||
}
|
||
|
||
SPDLOG_LOGGER_INFO(logger, "主窗口已销毁");
|
||
}
|
||
|
||
void App::Close() {
|
||
if (_hwndDDF) {
|
||
DestroyWindow(_hwndDDF);
|
||
}
|
||
if (_hwndHost) {
|
||
DestroyWindow(_hwndHost);
|
||
}
|
||
}
|
||
|
||
struct EnumChildWndParam {
|
||
const wchar_t* clientWndClassName = nullptr;
|
||
std::vector<HWND> childWindows;
|
||
};
|
||
|
||
static BOOL CALLBACK EnumChildProc(
|
||
_In_ HWND hwnd,
|
||
_In_ LPARAM lParam
|
||
) {
|
||
std::wstring className(256, 0);
|
||
int num = GetClassName(hwnd, &className[0], (int)className.size());
|
||
if (num == 0) {
|
||
SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("GetClassName 失败"));
|
||
return TRUE;
|
||
}
|
||
className.resize(num);
|
||
|
||
EnumChildWndParam* param = (EnumChildWndParam*)lParam;
|
||
if (className == param->clientWndClassName) {
|
||
param->childWindows.push_back(hwnd);
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static HWND FindClientWindow(HWND hwndSrc, const wchar_t* clientWndClassName) {
|
||
// 查找所有窗口类名为 ApplicationFrameInputSinkWindow 的子窗口
|
||
// 该子窗口一般为客户区
|
||
EnumChildWndParam param{};
|
||
param.clientWndClassName = clientWndClassName;
|
||
EnumChildWindows(hwndSrc, EnumChildProc, (LPARAM)¶m);
|
||
|
||
if (param.childWindows.empty()) {
|
||
// 未找到符合条件的子窗口
|
||
return hwndSrc;
|
||
}
|
||
|
||
if (param.childWindows.size() == 1) {
|
||
return param.childWindows[0];
|
||
}
|
||
|
||
// 如果有多个匹配的子窗口,取最大的(一般不会出现)
|
||
int maxSize = 0, maxIdx = 0;
|
||
for (int i = 0; i < param.childWindows.size(); ++i) {
|
||
RECT rect;
|
||
if (!GetClientRect(param.childWindows[i], &rect)) {
|
||
continue;
|
||
}
|
||
|
||
int size = rect.right - rect.left + rect.bottom - rect.top;
|
||
if (size > maxSize) {
|
||
maxSize = size;
|
||
maxIdx = i;
|
||
}
|
||
}
|
||
|
||
return param.childWindows[maxIdx];
|
||
}
|
||
|
||
bool App::UpdateSrcFrameRect() {
|
||
_srcFrameRect = {};
|
||
|
||
if (IsCropTitleBarOfUWP()) {
|
||
std::wstring className(256, 0);
|
||
int num = GetClassName(_hwndSrc, &className[0], (int)className.size());
|
||
if (num > 0) {
|
||
className.resize(num);
|
||
if (App::GetInstance().IsCropTitleBarOfUWP() &&
|
||
(className == L"ApplicationFrameWindow" || className == L"Windows.UI.Core.CoreWindow")
|
||
) {
|
||
// "Modern App"
|
||
// 客户区窗口类名为 ApplicationFrameInputSinkWindow
|
||
HWND hwndClient = FindClientWindow(_hwndSrc, L"ApplicationFrameInputSinkWindow");
|
||
if (hwndClient) {
|
||
if (!Utils::GetClientScreenRect(hwndClient, _srcFrameRect)) {
|
||
SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("GetClientScreenRect 失败"));
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("GetClassName 失败"));
|
||
}
|
||
}
|
||
|
||
if (_srcFrameRect == RECT{}) {
|
||
if (!Utils::GetClientScreenRect(_hwndSrc, _srcFrameRect)) {
|
||
SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("GetClientScreenRect 失败"));
|
||
return false;
|
||
}
|
||
}
|
||
|
||
_srcFrameRect = {
|
||
_srcFrameRect.left + _cropBorders.left,
|
||
_srcFrameRect.top + _cropBorders.top,
|
||
_srcFrameRect.right - _cropBorders.right,
|
||
_srcFrameRect.bottom - _cropBorders.bottom
|
||
};
|
||
|
||
if (_srcFrameRect.right - _srcFrameRect.left <= 0 || _srcFrameRect.bottom - _srcFrameRect.top <= 0) {
|
||
SetErrorMsg(ErrorMessages::FAILED_TO_CROP);
|
||
SPDLOG_LOGGER_ERROR(logger, "裁剪窗口失败");
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|