透明 CWnd 在过期时删除打开对话框的 window 句柄
Transparent CWnd deletes open dialog's window handle when it expires
我正在使用透明 window 在我的应用程序中显示不显眼的消息。一切正常,但有一点。如果我在模式对话框过期时打开它,对话框 window 就会被销毁。这当然会导致崩溃,因为没有用于对话消息循环的 wnd 句柄。我已经检查过了,它发生在几个不同的对话框中,比如 AboutDlg。我猜测由于模态消息循环而出现问题。如果我没有打开对话框,应用程序会继续正常运行。我在 DestroyWindow
中试过这个,但没有帮助:
SetWindowLong(GetSafeHwnd(), GWL_EXSTYLE, 0);
我正在检查应用程序启动时的更新。如果有更新,我会创建 window:
pFrm->SetInfoPtr(InfoWnd::InitWindow(pFrm->GetActiveView(), IDB_NEWVERSION));
如果父级在桌面上,它可以独立存在。但是如果在视图上方并且当主框架移动或调整大小时...那么现在主框架将 InfoWnd
感知。这可能是解决方案的一部分。
这是 window 的代码。
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();
}
原来答案很简单。没有钩子或黑客。 CMyApp::OnIdle(..)
在有模式对话框时不参与。因此,销毁信息的安全场所 window。 info_window.h
已包含在此翻译单元中,因为这是创建 window 的地方。
在上面的例子中,删除在InfoWnd::OnTimer(..)
中找到的DestroyWindow()
在声明中添加一个成员(注意,有一个,但现在只是 returns 指针):
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 是否为空来检查信息 window 是否有效。
CMainFrame::OnMoving(){
if (*pInfoWnd){
//Move the window
}
}
我正在使用透明 window 在我的应用程序中显示不显眼的消息。一切正常,但有一点。如果我在模式对话框过期时打开它,对话框 window 就会被销毁。这当然会导致崩溃,因为没有用于对话消息循环的 wnd 句柄。我已经检查过了,它发生在几个不同的对话框中,比如 AboutDlg。我猜测由于模态消息循环而出现问题。如果我没有打开对话框,应用程序会继续正常运行。我在 DestroyWindow
中试过这个,但没有帮助:
SetWindowLong(GetSafeHwnd(), GWL_EXSTYLE, 0);
我正在检查应用程序启动时的更新。如果有更新,我会创建 window:
pFrm->SetInfoPtr(InfoWnd::InitWindow(pFrm->GetActiveView(), IDB_NEWVERSION));
如果父级在桌面上,它可以独立存在。但是如果在视图上方并且当主框架移动或调整大小时...那么现在主框架将 InfoWnd
感知。这可能是解决方案的一部分。
这是 window 的代码。
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();
}
原来答案很简单。没有钩子或黑客。 CMyApp::OnIdle(..)
在有模式对话框时不参与。因此,销毁信息的安全场所 window。 info_window.h
已包含在此翻译单元中,因为这是创建 window 的地方。
在上面的例子中,删除在InfoWnd::OnTimer(..)
DestroyWindow()
在声明中添加一个成员(注意,有一个,但现在只是 returns 指针):
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 是否为空来检查信息 window 是否有效。
CMainFrame::OnMoving(){
if (*pInfoWnd){
//Move the window
}
}