mirror of
https://github.com/Blinue/Magpie.git
synced 2026-06-24 02:04:10 +00:00
源窗口最小化不再终止缩放 (#1219)
* fix: 优化窗口移动检测 * fix: 优化拖拽窗口时缩放行为 窗口模式立即缩放,全屏模式将等待拖拽结束 * feat: 源窗口最小化后等待其还原 * fix: 修复显示消息时窗口被销毁然后立即显示新消息会导致崩溃 * feat: 源窗口在最小化然后还原后可以还原缩放尺寸 * refactor: 微小重构 * feat: 缩放时禁用源窗口的窗口动画 * refactor: 禁用/还原窗口动画的逻辑集中在 WindowAnimationDisabler 类 * feat: 不再禁用窗口动画 Win10 的动画很突兀,Win11 却还行
This commit is contained in:
parent
18f3749502
commit
f116629169
6 changed files with 140 additions and 82 deletions
|
|
@ -54,9 +54,14 @@ bool ScalingRuntime::Start(HWND hwndSrc, ScalingOptions&& options) {
|
|||
assert(!options.screenshotsDir.empty() && options.showToast && options.showError && options.save);
|
||||
|
||||
_Dispatcher().TryEnqueue([this, hwndSrc, options(std::move(options))]() mutable {
|
||||
ScalingWindow& scalingWindow = ScalingWindow::Get();
|
||||
if (scalingWindow.IsSrcRepositioning()) {
|
||||
scalingWindow.CleanAfterSrcRepositioned();
|
||||
}
|
||||
|
||||
// 初始化时视为处于缩放状态
|
||||
_IsScaling(true);
|
||||
ScalingWindow::Get().Start(hwndSrc, std::move(options));
|
||||
scalingWindow.Start(hwndSrc, std::move(options));
|
||||
});
|
||||
|
||||
return true;
|
||||
|
|
@ -88,15 +93,24 @@ void ScalingRuntime::Stop() {
|
|||
// -1: 应取消缩放
|
||||
// 0: 仍在调整中
|
||||
// 1: 调整完毕
|
||||
static int GetSrcRepositionState(HWND hwndSrc, bool allowScalingMaximized) noexcept {
|
||||
if (!IsWindow(hwndSrc) || GetForegroundWindow() != hwndSrc) {
|
||||
static int GetSrcRepositionState(HWND hwndSrc) noexcept {
|
||||
if (!IsWindow(hwndSrc)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (UINT showCmd = Win32Helper::GetWindowShowCmd(hwndSrc); showCmd != SW_NORMAL) {
|
||||
if (showCmd != SW_SHOWMAXIMIZED || !allowScalingMaximized) {
|
||||
return -1;
|
||||
}
|
||||
if (Win32Helper::IsWindowHung(hwndSrc)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const UINT showCmd = Win32Helper::GetWindowShowCmd(hwndSrc);
|
||||
if (showCmd == SW_HIDE) {
|
||||
return -1;
|
||||
} else if (showCmd == SW_SHOWMAXIMIZED) {
|
||||
// 窗口最大化则尝试缩放,失败会显示错误消息
|
||||
return 1;
|
||||
} else if (showCmd == SW_SHOWMINIMIZED) {
|
||||
// 窗口最小化则继续等待
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 检查源窗口是否正在调整大小或移动
|
||||
|
|
@ -156,7 +170,7 @@ void ScalingRuntime::_ScalingThreadProc() noexcept {
|
|||
DispatchMessage(&msg);
|
||||
}
|
||||
|
||||
_IsScaling(scalingWindow || scalingWindow.IsSrcRepositioning());
|
||||
_IsScaling(scalingWindow);
|
||||
|
||||
if (scalingWindow) {
|
||||
const auto now = steady_clock::now();
|
||||
|
|
@ -175,10 +189,7 @@ void ScalingRuntime::_ScalingThreadProc() noexcept {
|
|||
const DWORD restMs = DWORD((rest.count() + ratio - 1) / ratio);
|
||||
MsgWaitForMultipleObjectsEx(0, nullptr, restMs, QS_ALLINPUT, MWMO_INPUTAVAILABLE);
|
||||
} else if (scalingWindow.IsSrcRepositioning()) {
|
||||
const int state = GetSrcRepositionState(
|
||||
scalingWindow.SrcTracker().Handle(),
|
||||
scalingWindow.Options().IsAllowScalingMaximized()
|
||||
);
|
||||
const int state = GetSrcRepositionState(scalingWindow.SrcTracker().Handle());
|
||||
if (state == 0) {
|
||||
// 等待调整完成
|
||||
MsgWaitForMultipleObjectsEx(0, nullptr, 10, QS_ALLINPUT, MWMO_INPUTAVAILABLE);
|
||||
|
|
|
|||
|
|
@ -57,6 +57,20 @@ ScalingError ScalingWindow::_StartImpl(HWND hwndSrc) noexcept {
|
|||
Win32Helper::IsProcessElevated() ? "是" : "否"
|
||||
));
|
||||
|
||||
#if _DEBUG
|
||||
OutputDebugString(fmt::format(L"可执行文件路径: {}\n窗口类: {}\n",
|
||||
Win32Helper::GetWindowPath(hwndSrc), Win32Helper::GetWindowClassName(hwndSrc)).c_str());
|
||||
#endif
|
||||
|
||||
_runtimeError = ScalingError::NoError;
|
||||
_isFirstFrame = true;
|
||||
_isResizingOrMoving = false;
|
||||
_isPreparingForResizing = false;
|
||||
_isMovingDueToSrcMoved = false;
|
||||
_shouldWaitForRender = false;
|
||||
_areResizeHelperWindowsVisible = false;
|
||||
_isSrcRepositioning = false;
|
||||
|
||||
if (_options.IsWindowedMode()) {
|
||||
if (_options.Is3DGameMode()) {
|
||||
return ScalingError::Windowed3DGameMode;
|
||||
|
|
@ -72,15 +86,6 @@ ScalingError ScalingWindow::_StartImpl(HWND hwndSrc) noexcept {
|
|||
|
||||
InitMessage();
|
||||
|
||||
_runtimeError = ScalingError::NoError;
|
||||
_isFirstFrame = true;
|
||||
_isResizingOrMoving = false;
|
||||
_isPreparingForResizing = false;
|
||||
_isMovingDueToSrcMoved = false;
|
||||
_shouldWaitForRender = false;
|
||||
_areResizeHelperWindowsVisible = false;
|
||||
_isSrcRepositioning = false;
|
||||
|
||||
if (ScalingError error = _srcTracker.Set(hwndSrc, _options); error != ScalingError::NoError) {
|
||||
Logger::Get().Error("初始化 SrcTracker 失败");
|
||||
return error;
|
||||
|
|
@ -96,10 +101,10 @@ ScalingError ScalingWindow::_StartImpl(HWND hwndSrc) noexcept {
|
|||
}
|
||||
}
|
||||
|
||||
#if _DEBUG
|
||||
OutputDebugString(fmt::format(L"可执行文件路径: {}\n窗口类: {}\n",
|
||||
Win32Helper::GetWindowPath(hwndSrc), Win32Helper::GetWindowClassName(hwndSrc)).c_str());
|
||||
#endif
|
||||
if (_srcTracker.IsMoving() && !_options.IsWindowedMode()) {
|
||||
_isSrcRepositioning = true;
|
||||
return ScalingError::NoError;
|
||||
}
|
||||
|
||||
[[maybe_unused]] static Ignore _ = []() {
|
||||
WNDCLASSEXW wcex{
|
||||
|
|
@ -158,25 +163,30 @@ ScalingError ScalingWindow::_StartImpl(HWND hwndSrc) noexcept {
|
|||
// 填入渲染矩形尺寸
|
||||
int windowWidth = 0;
|
||||
int windowHeight = 0;
|
||||
if (_options.initialWindowedScaleFactor < 1.0f) {
|
||||
// 根据屏幕的工作区尺寸计算
|
||||
MONITORINFO mi{ .cbSize = sizeof(mi) };
|
||||
if (GetMonitorInfo(hMon, &mi)) {
|
||||
const SIZE monitorSize = Win32Helper::GetSizeOfRect(mi.rcWork);
|
||||
const float srcAspectRatio = (float)srcSize.cy / srcSize.cx;
|
||||
if (_lastWindowedRendererWidth == 0) {
|
||||
if (_options.initialWindowedScaleFactor < 1.0f) {
|
||||
// 根据屏幕的工作区尺寸计算
|
||||
MONITORINFO mi{ .cbSize = sizeof(mi) };
|
||||
if (GetMonitorInfo(hMon, &mi)) {
|
||||
const SIZE monitorSize = Win32Helper::GetSizeOfRect(mi.rcWork);
|
||||
const float srcAspectRatio = (float)srcSize.cy / srcSize.cx;
|
||||
|
||||
// 放大到显示器的 3/4,且最少放大 1/3 倍
|
||||
if ((float)monitorSize.cy / monitorSize.cx > srcAspectRatio) {
|
||||
windowWidth = std::max(monitorSize.cx * 3 / 4, srcSize.cx * 4 / 3);
|
||||
// 放大到显示器的 3/4,且最少放大 1/3 倍
|
||||
if ((float)monitorSize.cy / monitorSize.cx > srcAspectRatio) {
|
||||
windowWidth = std::max(monitorSize.cx * 3 / 4, srcSize.cx * 4 / 3);
|
||||
} else {
|
||||
windowHeight = std::max(monitorSize.cy * 3 / 4, srcSize.cy * 4 / 3);
|
||||
}
|
||||
} else {
|
||||
windowHeight = std::max(monitorSize.cy * 3 / 4, srcSize.cy * 4 / 3);
|
||||
Logger::Get().Win32Error("GetMonitorInfo 失败");
|
||||
windowWidth = srcSize.cx;
|
||||
}
|
||||
} else {
|
||||
Logger::Get().Win32Error("GetMonitorInfo 失败");
|
||||
windowWidth = srcSize.cx;
|
||||
windowWidth = (LONG)std::lroundf(srcSize.cx * _options.initialWindowedScaleFactor);
|
||||
}
|
||||
} else {
|
||||
windowWidth = (LONG)std::lroundf(srcSize.cx * _options.initialWindowedScaleFactor);
|
||||
// 恢复上次窗口模式缩放尺寸
|
||||
windowWidth = _lastWindowedRendererWidth;
|
||||
}
|
||||
|
||||
if (!_CalcWindowedScalingWindowSize(windowWidth, windowHeight, true)) {
|
||||
|
|
@ -282,7 +292,7 @@ ScalingError ScalingWindow::_StartImpl(HWND hwndSrc) noexcept {
|
|||
|
||||
LogRects(_srcTracker.SrcRect(), _rendererRect, _windowRect);
|
||||
|
||||
if (!_options.IsWindowedMode() && !_options.IsAllowScalingMaximized()) {
|
||||
if (!_options.RealIsAllowScalingMaximized()) {
|
||||
// 检查源窗口是否是无边框全屏窗口
|
||||
if (srcWindowKind == SrcWindowKind::NoDecoration && _srcTracker.WindowRect() == _rendererRect) {
|
||||
Logger::Get().Info("源窗口已全屏");
|
||||
|
|
@ -343,6 +353,9 @@ void ScalingWindow::SwitchScalingState(bool isWindowedMode) noexcept {
|
|||
|
||||
// 源窗口在前台时按快捷键可以切换全屏/窗口模式缩放
|
||||
_isSrcRepositioning = true;
|
||||
if (_options.IsWindowedMode()) {
|
||||
_lastWindowedRendererWidth = _rendererRect.right - _rendererRect.left;
|
||||
}
|
||||
Destroy();
|
||||
_options.IsWindowedMode(isWindowedMode);
|
||||
RestartAfterSrcRepositioned();
|
||||
|
|
@ -386,6 +399,7 @@ void ScalingWindow::RestartAfterSrcRepositioned() noexcept {
|
|||
|
||||
void ScalingWindow::CleanAfterSrcRepositioned() noexcept {
|
||||
_options = {};
|
||||
_lastWindowedRendererWidth = 0;
|
||||
_isSrcRepositioning = false;
|
||||
}
|
||||
|
||||
|
|
@ -831,11 +845,10 @@ LRESULT ScalingWindow::_MessageHandler(UINT msg, WPARAM wParam, LPARAM lParam) n
|
|||
_renderer.reset();
|
||||
Logger::Get().Info("Renderer 已析构");
|
||||
|
||||
// 如果正在源窗口正在调整,暂时不清理这些成员
|
||||
if (!_isSrcRepositioning) {
|
||||
// 缩放结束时保存配置
|
||||
_options.save(_options, NULL);
|
||||
_options = {};
|
||||
CleanAfterSrcRepositioned();
|
||||
}
|
||||
|
||||
// 还原时钟精度
|
||||
|
|
@ -1256,23 +1269,32 @@ bool ScalingWindow::_UpdateSrcState(
|
|||
return false;
|
||||
}
|
||||
|
||||
bool srcMinimized = false;
|
||||
bool srcRectChanged = false;
|
||||
bool srcSizeChanged = false;
|
||||
bool srcMovingChanged = false;
|
||||
if (!_srcTracker.UpdateState(hwndFore, _options.IsWindowedMode(), _isResizingOrMoving,
|
||||
srcFocusedChanged, srcOwnedWindowFocusedChanged,
|
||||
srcRectChanged, srcSizeChanged, srcMovingChanged)) {
|
||||
srcMinimized, srcRectChanged, srcSizeChanged, srcMovingChanged)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (srcSizeChanged || (!_options.IsWindowedMode() && srcRectChanged)) {
|
||||
// 不要立刻设置 _isSrcRepositioning,销毁窗口是异步的
|
||||
if (srcMinimized || srcSizeChanged || (!_options.IsWindowedMode() && srcRectChanged)) {
|
||||
// 不要立刻设置 _isSrcSizing,销毁窗口是异步的
|
||||
isSrcRepositioning = true;
|
||||
|
||||
if (srcSizeChanged) {
|
||||
// 源窗口大小改变则清除记忆
|
||||
_lastWindowedRendererWidth = 0;
|
||||
} else if (srcMinimized) {
|
||||
if (_options.IsWindowedMode()) {
|
||||
_lastWindowedRendererWidth = _rendererRect.right - _rendererRect.left;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// DirectFlip 可能使窗口移动很卡,目前发现缩放 Magpie 主窗口有这个
|
||||
// 问题。因此源窗口移动过程中临时禁用 DirectFlip。
|
||||
if (srcMovingChanged) {
|
||||
assert(_options.IsWindowedMode());
|
||||
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ private:
|
|||
|
||||
void _UpdateWindowRectFromWindowPos(const WINDOWPOS& windowPos) noexcept;
|
||||
|
||||
void _DelayedStop(bool onSrcHung = false, bool isSrcRepositioning = false) const noexcept;
|
||||
void _DelayedStop(bool onSrcHung = false, bool onSrcRepositioning = false) const noexcept;
|
||||
|
||||
static inline std::atomic<uint32_t> _runId = 0;
|
||||
static inline winrt::DispatcherQueue _dispatcher{ nullptr };
|
||||
|
|
@ -182,6 +182,9 @@ private:
|
|||
|
||||
ScalingError _runtimeError = ScalingError::NoError;
|
||||
|
||||
// 窗口缩放时切换到全屏缩放或最小化前保存尺寸供以后恢复
|
||||
LONG _lastWindowedRendererWidth = 0;
|
||||
|
||||
// 第一帧渲染完成后再显示
|
||||
bool _isFirstFrame = false;
|
||||
bool _isResizingOrMoving = false;
|
||||
|
|
|
|||
|
|
@ -37,10 +37,19 @@ static bool CheckIL(HWND hwndSrc) noexcept {
|
|||
return GetWindowIntegrityLevel(hwndSrc, windowIL) && windowIL <= thisIL;
|
||||
}
|
||||
|
||||
static bool IsWindowMoving(HWND hWnd) noexcept {
|
||||
GUITHREADINFO guiThreadInfo{ .cbSize = sizeof(GUITHREADINFO) };
|
||||
if (GetGUIThreadInfo(GetWindowThreadProcessId(hWnd, nullptr), &guiThreadInfo)) {
|
||||
return guiThreadInfo.flags & GUI_INMOVESIZE;
|
||||
} else {
|
||||
Logger::Get().Win32Error("GetGUIThreadInfo 失败");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ScalingError SrcTracker::Set(HWND hWnd, const ScalingOptions& options) noexcept {
|
||||
_hWnd = hWnd;
|
||||
_isMoving = false;
|
||||
|
||||
|
||||
// 这里不检查源窗口是否挂起,将在创建缩放窗口前检查
|
||||
|
||||
if (!IsWindow(_hWnd)) {
|
||||
|
|
@ -81,6 +90,8 @@ ScalingError SrcTracker::Set(HWND hWnd, const ScalingOptions& options) noexcept
|
|||
_isFocused = hwndFore == hWnd;
|
||||
_UpdateIsOwnedWindowFocused(hwndFore);
|
||||
|
||||
_isMoving = IsWindowMoving(_hWnd);
|
||||
|
||||
if (!GetWindowRect(hWnd, &_windowRect)) {
|
||||
Logger::Get().Win32Error("GetWindowRect 失败");
|
||||
return ScalingError::ScalingFailedGeneral;
|
||||
|
|
@ -173,6 +184,7 @@ bool SrcTracker::UpdateState(
|
|||
bool isResizingOrMoving,
|
||||
bool& focusedChanged,
|
||||
bool& ownedWindowFocusedChanged,
|
||||
bool& minimized,
|
||||
bool& rectChanged,
|
||||
bool& sizeChanged,
|
||||
bool& movingChanged
|
||||
|
|
@ -180,12 +192,7 @@ bool SrcTracker::UpdateState(
|
|||
assert(!focusedChanged && !ownedWindowFocusedChanged && !rectChanged && !sizeChanged && !movingChanged);
|
||||
|
||||
if (!IsWindow(_hWnd)) {
|
||||
Logger::Get().Error("源窗口已销毁");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsWindowVisible(_hWnd)) {
|
||||
Logger::Get().Error("源窗口已隐藏");
|
||||
Logger::Get().Info("源窗口已销毁");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -194,7 +201,7 @@ bool SrcTracker::UpdateState(
|
|||
// 格,因此即使源窗口挂起一段时间,只要用户不做额外的操作就不会结束缩放,
|
||||
// 直到源窗口被替换为幽灵窗口。
|
||||
if (IsHungAppWindow(_hWnd)) {
|
||||
Logger::Get().Error("源窗口已挂起");
|
||||
Logger::Get().Info("源窗口已挂起");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -206,21 +213,39 @@ bool SrcTracker::UpdateState(
|
|||
ownedWindowFocusedChanged = _UpdateIsOwnedWindowFocused(hwndFore);
|
||||
|
||||
const bool oldMaximized = _isMaximized;
|
||||
UINT showCmd = Win32Helper::GetWindowShowCmd(_hWnd);
|
||||
if (showCmd == SW_SHOWMINIMIZED) {
|
||||
Logger::Get().Error("源窗口处于最小化状态");
|
||||
const UINT showCmd = Win32Helper::GetWindowShowCmd(_hWnd);
|
||||
if (showCmd == SW_HIDE) {
|
||||
Logger::Get().Info("源窗口已隐藏");
|
||||
return false;
|
||||
} else if (showCmd == SW_SHOWMINIMIZED) {
|
||||
Logger::Get().Info("源窗口已最小化");
|
||||
_isMaximized = false;
|
||||
minimized = true;
|
||||
} else {
|
||||
_isMaximized = showCmd == SW_SHOWMAXIMIZED;
|
||||
}
|
||||
_isMaximized = showCmd == SW_SHOWMAXIMIZED;
|
||||
|
||||
RECT curWindowRect;
|
||||
if (!GetWindowRect(_hWnd, &curWindowRect)) {
|
||||
Logger::Get().Win32Error("GetWindowRect 失败");
|
||||
return false;
|
||||
if (minimized) {
|
||||
WINDOWPLACEMENT wp{ sizeof(wp) };
|
||||
if (!GetWindowPlacement(_hWnd, &wp)) {
|
||||
Logger::Get().Win32Error("GetWindowPlacement 失败");
|
||||
return false;
|
||||
}
|
||||
curWindowRect = wp.rcNormalPosition;
|
||||
} else {
|
||||
if (!GetWindowRect(_hWnd, &curWindowRect)) {
|
||||
Logger::Get().Win32Error("GetWindowRect 失败");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
sizeChanged = oldMaximized != _isMaximized ||
|
||||
Win32Helper::GetSizeOfRect(curWindowRect) != Win32Helper::GetSizeOfRect(_windowRect);
|
||||
if (sizeChanged) {
|
||||
rectChanged = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 缩放窗口正在调整大小或被拖动时源窗口的移动是异步的,暂时不检查源窗口是否移动
|
||||
if (isResizingOrMoving) {
|
||||
|
|
@ -232,20 +257,6 @@ bool SrcTracker::UpdateState(
|
|||
rectChanged = oldMaximized != _isMaximized || curWindowRect != _windowRect;
|
||||
|
||||
if (isWindowedMode && !sizeChanged) {
|
||||
bool isMoving = false;
|
||||
GUITHREADINFO guiThreadInfo{ .cbSize = sizeof(GUITHREADINFO) };
|
||||
if (GetGUIThreadInfo(GetWindowThreadProcessId(_hWnd, nullptr), &guiThreadInfo)) {
|
||||
isMoving = guiThreadInfo.flags & GUI_INMOVESIZE;
|
||||
} else {
|
||||
Logger::Get().Win32Error("GetGUIThreadInfo 失败");
|
||||
}
|
||||
|
||||
// 处理自己实现拖拽逻辑的窗口:将鼠标左键按下视为开始拖拽,释放视为拖拽结束。
|
||||
// 可能会有误判,但幸好后果不太严重。
|
||||
if (_isMoving || (!_isMoving && rectChanged)) {
|
||||
isMoving = isMoving || IsPrimaryMouseButtonDown();
|
||||
}
|
||||
|
||||
if (rectChanged) {
|
||||
const LONG offsetX = curWindowRect.left - _windowRect.left;
|
||||
const LONG offsetY = curWindowRect.top - _windowRect.top;
|
||||
|
|
@ -253,6 +264,10 @@ bool SrcTracker::UpdateState(
|
|||
Win32Helper::OffsetRect(_srcRect, offsetX, offsetY);
|
||||
}
|
||||
|
||||
// 处理自己实现拖拽逻辑的窗口:将鼠标左键按下视为开始拖拽,释放视为拖拽结束。
|
||||
// 可能会有误判,但幸好后果不太严重。
|
||||
const bool isMoving = !minimized &&
|
||||
(IsWindowMoving(_hWnd) || (rectChanged && IsPrimaryMouseButtonDown()));
|
||||
if (_isMoving != isMoving) {
|
||||
movingChanged = true;
|
||||
_isMoving = isMoving;
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ public:
|
|||
bool isResizingOrMoving,
|
||||
bool& focusedChanged,
|
||||
bool& ownedWindowFocusedChanged,
|
||||
bool& minimized,
|
||||
bool& rectChanged,
|
||||
bool& sizeChanged,
|
||||
bool& movingChanged
|
||||
|
|
|
|||
|
|
@ -247,10 +247,16 @@ fire_and_forget ToastPage::ShowMessageOnWindow(std::wstring title, std::wstring
|
|||
co_return;
|
||||
}
|
||||
|
||||
if (!IsWindow((HWND)hwndTarget) || !IsWindow(_hwndToast) || !Win32Helper::GetWindowFrameRect((HWND)hwndTarget, frameRect)) {
|
||||
// 附加的窗口已经关闭,toast 也应关闭,_oldTeachingTip 用于延长生命周期避免崩溃
|
||||
UnloadObject(curTeachingTip);
|
||||
_oldTeachingTip = std::move(curTeachingTip);
|
||||
if (!IsWindow((HWND)hwndTarget) || !IsWindow(_hwndToast) ||
|
||||
!Win32Helper::GetWindowFrameRect((HWND)hwndTarget, frameRect))
|
||||
{
|
||||
// 附加的窗口关闭后 toast 也应关闭。应检查 curTeachingTip 是否已经在新的调用中被卸载,
|
||||
// 见函数开头的 UnloadObject。
|
||||
if (curTeachingTip.IsLoaded()) {
|
||||
UnloadObject(curTeachingTip);
|
||||
// 延长生命周期避免崩溃
|
||||
_oldTeachingTip = std::move(curTeachingTip);
|
||||
}
|
||||
co_return;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue