透明 CWnd 过期时删除打开对话框的窗口句柄
Posted
技术标签:
【中文标题】透明 CWnd 过期时删除打开对话框的窗口句柄【英文标题】:Transparent CWnd deletes open dialog's window handle when it expires 【发布时间】:2020-11-15 22:41:23 【问题描述】:我正在使用透明窗口在我的应用程序中显示不显眼的消息。这一切都很好,但有一件事。如果我在到期时打开了一个模态对话框,则对话框窗口将被破坏。这当然会导致崩溃,因为没有可供对话消息循环使用的 wnd 句柄。我已经检查过了,它发生在几个不同的对话框中,比如 AboutDlg。我猜测由于模态消息循环而出现了问题。如果我没有打开对话框,应用程序会继续正常运行。我在DestroyWindow
中尝试过,但没有帮助:
SetWindowLong(GetSafeHwnd(), GWL_EXSTYLE, 0);
我正在检查应用程序启动时的更新。如果有更新,我会使用以下命令创建窗口:
pFrm->SetInfoPtr(InfoWnd::InitWindow(pFrm->GetActiveView(), IDB_NEWVERSION));
如果父桌面所在的位置,它可以独立存在。但是如果在一个视图上并且当主框架移动或调整大小时......所以现在主框架将是InfoWnd
感知的。这可能是解决方案的一部分。
这是窗口的代码。
info_window.h
class InfoWnd : public CWnd
protected:
static InfoWnd* self_ptr;
CWnd* parent = nullptr;
HDC memdc = nullptr;
static BLENDFUNCTION blend;
int width;
int height;
CBitmap m_bitmap;
InfoWnd() ;
public:
static auto GetSelfPointer() return &self_ptr;
virtual ~InfoWnd() self_ptr = nullptr;
virtual void PostNcDestroy() self_ptr = nullptr; delete this;
static InfoWnd** InitWindow(CWnd* parnt, UINT bitmapID);
void MoveInfoWindow(LPCRECT lpRect);
protected:
BOOL Create(CWnd* pParentWnd = NULL);
DECLARE_MESSAGE_MAP()
afx_msg void OnPaint();
afx_msg void OnTimer(UINT nIDEvent);
;
info_window.cpp
BEGIN_MESSAGE_MAP(InfoWnd, CWnd)
ON_WM_CREATE()
ON_WM_PAINT()
ON_WM_TIMER()
END_MESSAGE_MAP()
InfoWnd** InfoWnd::InitWindow(CWnd* parent, UINT bitmapID)
assert(!self_ptr);
self_ptr = new InfoWnd;
if (!self_ptr->m_bitmap.LoadBitmap(bitmapID))
delete self_ptr;
self_ptr = nullptr;
return nullptr;
BITMAP bm;
self_ptr->m_bitmap.GetBitmap(&bm);
self_ptr->width = bm.bmWidth;
self_ptr->height = bm.bmHeight;
self_ptr->parent = parent;
self_ptr->Create(parent);
return &self_ptr;
BOOL InfoWnd::Create(CWnd* pParentWnd)
parent = pParentWnd;
CRect parentRect;
pParentWnd->GetWindowRect(parentRect);
BOOL result = CreateEx( WS_EX_TOPMOST,
AfxRegisterWndClass(0, AfxGetApp()->LoadStandardCursor(IDC_CROSS)),
NULL, WS_POPUP | WS_VISIBLE, 0, 0, width, height, pParentWnd->GetSafeHwnd(), NULL);
if (!result)
return FALSE;
std::cout << "InfoWnd: " << GetSafeHwnd() << std::endl;
MoveWindow(parentRect.right - width, parentRect.top, parentRect.right, parentRect.top);
blend.BlendOp = AC_SRC_OVER; // the only BlendOp defined in Windows 2000
blend.BlendFlags = 0; // nothing else is special ...
blend.AlphaFormat = 0; // ...
blend.SourceConstantAlpha = 255; // the initial alpha value
ShowWindow(SW_SHOW);
SetTimer(1, 200, NULL);
return TRUE;
void InfoWnd::MoveInfoWindow(LPCRECT lpRect)
MoveWindow(lpRect->right - width, lpRect->top, lpRect->right, lpRect->top);
void InfoWnd::OnPaint()
CPaintDC dc(this);
CDC dcImage;
if (!dcImage.CreateCompatibleDC(&dc))
return;
BITMAP bm;
m_bitmap.GetBitmap(&bm);
CBitmap* pOldBitmap = dcImage.SelectObject(&m_bitmap);
dc.BitBlt(0, 0, bm.bmWidth, bm.bmHeight, &dcImage, 0, 0, SRCCOPY);
dcImage.SelectObject(pOldBitmap);
void InfoWnd::OnTimer(UINT nIDEvent)
if (blend.SourceConstantAlpha)
if (blend.SourceConstantAlpha == 255)
SetWindowLong(GetSafeHwnd(), GWL_EXSTYLE, WS_EX_LAYERED | WS_EX_NOACTIVATE | WS_EX_TRANSPARENT);
if (blend.SourceConstantAlpha)
UpdateLayeredWindow( NULL, NULL, NULL, NULL, NULL, NULL, &blend, ULW_ALPHA);
blend.SourceConstantAlpha-= 5;
return;
KillTimer(nIDEvent);
//this will be replaced with a post message to the parent?
DestroyWindow();
【问题讨论】:
那么……您还需要杀死导致问题的对话框,以便对话框在透明窗口之前终止。一些带有 WM_CLOSE 和计时器的消息发布可能是为了优雅地退出 嗨@Sven Nilsson 通知窗口的想法是用户可以继续他们想做的任何事情。喜欢工具->下载新版本。如果没有更好的解决方案,我可能要做的就是在用户“做事”时销毁通知窗口。但这本身就是一个有趣的挑战。但我不能把他们踢出他们呼吁的对话。 没有足够的代码来显示完整图片,但您是否尝试过将其设为WS_CHILD
而不是 WS_POPUP
。
嗨@dxiv 我不能让它成为一个孩子,我的用户仍在运行 Windows 7。这是分层/透明窗口的本质。当我开始这个时,我真的想让它成为一个子窗口。
@lakeweb 然后使用spy++ 检查模态对话框。我的猜测是,透明窗口最终会成为他们祖先某个地方的父母或所有者,您需要找到一种方法来防止这种情况发生。
【参考方案1】:
原来答案很简单。没有钩子或黑客。 CMyApp::OnIdle(..)
在有一个模态对话框时不参与。所以一个安全的地方来破坏信息窗口。 info_window.h
已包含在此翻译单元中,因为这是创建窗口的位置。
在上面的例子中,删除InfoWnd::OnTimer(..)
中的DestroyWindow()
在声明中添加一个成员(注意,有一个成员,但现在只返回指针):
bool IsDone() const return !blend.SourceConstantAlpha;
static auto GetSelfPointer() return self_ptr;
然后在应用程序中OnIdle
:
if(InfoWnd::GetSelfPointer() && InfoWnd::GetSelfPointer()->IsDone())
InfoWnd::GetSelfPointer()->DestroyWindow();
指向指针的原因仍然被MainFrame使用。它通过检查 self_ptr 不为空来检查信息窗口是否有效。
CMainFrame::OnMoving()
if (*pInfoWnd)
//Move the window
【讨论】:
以上是关于透明 CWnd 过期时删除打开对话框的窗口句柄的主要内容,如果未能解决你的问题,请参考以下文章