自动消失对话框,用于在 ReactOS 上提供 VERIFY 和 ASSERT





5.00/5 (1投票)
自动消失对话框
引言
ReactOS 是 Windows 操作系统的开源替代品。即使 ReactOS 的第一个版本可以追溯到 1998 年,但仍然没有“稳定”版本的 ReactOS。也许最重要的原因是缺乏关注。
这个技巧的灵感来自于一个事实:
- 我想在 ReactOS 上以与 Windows 相同的方式使用
ASSERT
和VERIFY
宏 - 但是被捕获的鼠标无法释放,因此也无法使用
MessageBox
。
有关 ASSERT
和 VERIFY
宏的更多详细信息,请参阅 CODE PROJECT 文章 William E. Kempf 撰写的“有用的调试宏”。
背景
ASSERT
和 VERIFY
宏会启动一个对话框,报告失败的断言或验证。这个对话框可能是一个 MessageBox
。这也可以在 ReactOS 上实现,但在鼠标捕获的特定情况下存在问题。基本上有两种鼠标被捕获的情况:
- 介于
WM_INITMENUPOPUP
和WM_UNINITMENUPOPUP
之间,以及 - 在拖放操作期间(参见 Code Project 文章 Chris Becke 撰写的“捕获鼠标”)。
不幸的是,CODE PROJECT 文章中提出的两种解决方案,Siarhei Boika 撰写的“如何使用 SetForegroundWindow() 将窗口带到顶部”,在 ReactOS 上不起作用。
在花费了一些时间尝试找到任何可以释放鼠标的 SetCapture()
、ReleaseCapture()
和 EnableWindow()
与其他 API 调用(在 ReactOS 上可用 - 例如,AllowSetForegroundWindow()
在 ReactOS 上不可用)的组合后,我放弃了,并思考:当鼠标仍被捕获时,有哪些解决方案是可能的?对我来说,在鼠标根本不可用的情况下,有一个自动消失的对话框似乎是最好的解决方案。因此,我开发了 AutoDisappearDlg
C++ 类,该类是通用的,也可以在 Windows 下使用。该实现基于 Win32
,而不是 MFC 或 WTL。
Using the Code
这里提供的所有源代码都是我 Ogww
库的一部分,并且完全包含在本文章的附件中。我在 CODE PROJECT 文章 “在 ReactOS (或 Windows) 上使用 C/C++ 和 C# 进行 OpenGL 的更多信息” 中介绍了 Ogww
库,并通过我的 Code Project 文章 “在 ReactOS 上运行的基本图标编辑器” 进行了增强。
我将从我的宏声明开始 (Ogww.hpp)
#ifndef __OGWW_H__
#define __OGWW_H__
#define LIB_VERSION 0.20
/// <summary>Displays the fail in a message box and writes the fail to standard error
/// but the program will not break.</summary>
/// <param name="szExpression">The expression, the assertion failed for.</param>
/// <param name="fLibVersion">The library version.</param>
/// <param name="szFileName">The source code file name.</param>
/// <param name="nFileLine">The source code file line.</param>
int __cdecl OgwwMainFrame_VERIFY_MSGBOX(const char* szExpression, float fLibVersion,
const char* szFileName, int nFileLine);
/// <summary>Displays the fail in a message box and writes the fail to standard error
/// but the program will not break.</summary>
/// <param name="szExpression">The expression, the verification failed for.</param>
/// <param name="fLibVersion">The library version.</param>
/// <param name="szFileName">The source code file name.</param>
/// <param name="nFileLine">The source code file line.</param>
int __cdecl OgwwMainFrame_ASSERT_MSGBOX(const char* szExpression, float fLibVersion,
const char* szFileName, int nFileLine);
/// <summary>Verifies a condition within code during debug and release builds. If the
/// verification fails then the fail is written to standard error but the program will
/// not break at the line the assertion failed.</summary>
#ifndef verify
#define verify(expression) ((expression) ? (void)0 : (void)fprintf(stderr,
"Failed to verify '%s' in library version %3.2f file '%s' in line %d.\n" ,
#expression, LIB_VERSION, __FILE__, __LINE__))
#endif // verify
/// <summary>Verifies a condition within code during debug and release builds. If the
/// verification fails then a message box displays the fail and the fail is written to
/// standard error but the program will not break at the line the assertion failed.</summary>
/// <remarks>Use the <c>verify</c> macro instead of the <c>verify</c> macro within message
/// loop processing to avoid a message box blocking.</remarks>
#define VERIFY(expression) ((expression) ? (void)0 :
(void)OgwwMainFrame_VERIFY_MSGBOX(#expression, LIB_VERSION, __FILE__, __LINE__))
/// <summary>Asserts a condition within code during debug builds (when _DEBUG is defined).
/// If the assertion fails then a message box displays the fail, the fail is written to
/// standard error and the program will break at the line the assertion failed.</summary>
/// <remarks>Use the <c>assert</c> macro instead of the <c>ASSERT</c> macro within message
/// loop processing to avoid a message box blocking.</remarks>
#define ASSERT(expression) ((expression) ? (void)0 :
(void)OgwwMainFrame_ASSERT_MSGBOX(#expression, LIB_VERSION, __FILE__, __LINE__))
#endif // __OGWW_H__
OgwwMainFrame_VERIFY_MSGBOX()
和 OgwwMainFrame_ASSERT_MSGBOX()
是函数原型,它们在 OgwwMainFrame.cpp 中实现,并被我的 ASSERT
和 VERIFY
宏使用(前向声明)。
assert
宏已由 C++ 定义,因此我只需要添加 verify
宏。为了与 Windows 编程标准兼容,我最终定义了 ASSERT
和 VERIFY
宏。
现在我可以选择在我的应用程序的源代码中使用 assert
和 verify
宏(无对话框提示)还是 ASSERT
和 VERIFY
宏(带对话框提示)。
OgwwMainFrame_VERIFY_MSGBOX()
和 OgwwMainFrame_ASSERT_MSGBOX()
的实现看起来像这样 (OgwwMainFrame.cpp)
// ###########################################################################################
// ### START Debugging helper - message dialog based. ####
// ###########################################################################################
const char* VERIFY_FORMAT =
"Failed to verify '%s'~in OGWW library version %3.2f~file '%s'~line %d.\n";
const char* ASSERT_FORMAT =
"Failed to assert '%s'~in OGWW library version %3.2f~file '%s'~line %d.\n";
/// <summary>Displays the fail in a message box and writes the fail to standard error but
/// the program will not break.</summary>
/// <param name="szExpression">The expression, the assertion failed for.</param>
/// <param name="fLibVersion">The library version.</param>
/// <param name="szFileName">The source code file name.</param>
/// <param name="nFileLine">The source code file line.</param>
int __cdecl OgwwMainFrame_VERIFY_MSGBOX(const char* szExpression, float fLibVersion,
const char* szFileName, int nFileLine)
{
size_t nLen = strlen(VERIFY_FORMAT)
+ (szExpression != NULL ? strlen(szExpression) : 0) + /* version */ 5
+ (szFileName != NULL ? strlen(szFileName ) : 0) + /* line */ 6;
char* szBuffer = new char[nLen];
memset(szBuffer, 0, nLen);
sprintf(szBuffer, VERIFY_FORMAT, szExpression, fLibVersion, szFileName, nFileLine);
OgwwHandledObjectList* inst = OgwwMainFrame::InstanceList();
OgwwMainFrame* pWnd = (OgwwMainFrame*)(inst != NULL && inst->FirstItem() != NULL ?
inst->FirstItem()->HandledObj : NULL);
if (GetCapture() == NULL)
{
for (size_t nPos = 0; nPos < nLen; nPos++)
if (szBuffer[nPos] == '~') szBuffer[nPos] = ' ';
HWND hWnd = (pWnd != NULL ? pWnd->HWnd() : ::GetDesktopWindow());
::MessageBoxA(hWnd, szBuffer, "OGWW debugging - VERIFY failed",
MB_OK | MB_ICONERROR | MB_SYSTEMMODAL);
}
else
{
WString strMessage;
strMessage.SetA(szBuffer);
strMessage.Replace((WCHAR)'\n', (WCHAR)' ');
strMessage.Replace((WCHAR)'~', (WCHAR)'\n');
for (size_t nPos = 0; nPos < nLen; nPos++)
if (szBuffer[nPos] == '~') szBuffer[nPos] = ' ';
// Unfortunately none of this approaches work:
// -------------------------------------------
// ::SetCapture(NULL); -> Doesn't help to release the capture.
// ::ReleaseCapture();
// ::EnableWindow(hWndCapture, FALSE);
// Unfortunately both tips don't work on ReactOS:
// ----------------------------------------------
// Siarhei Boika, "How to bring window to top with SetForegroundWindow()"
// Use an automatically disappearing dialog instead.
// -------------------------------------------
OgwwAutoDisappearDlg* pMsgDlg = new OgwwAutoDisappearDlg(pWnd->HInst(),
pWnd->HPrevInst());
pMsgDlg->Show(strMessage.Value(), L"OGWW debugging - VERIFY failed",
SET_SIZE(460, 120), MB_ICONHAND);
pMsgDlg->Run();
}
delete szBuffer;
return fprintf(stderr, VERIFY_FORMAT, szExpression,
fLibVersion, szFileName, nFileLine);
}
/// <summary>Displays the fail in a message box, writes the fail to standard error and
/// breaks the program.</summary>
/// <param name="szExpression">The expression, the verification failed for.</param>
/// <param name="fLibVersion">The library version.</param>
/// <param name="szFileName">The source code file name.</param>
/// <param name="nFileLine">The source code file line.</param>
int __cdecl OgwwMainFrame_ASSERT_MSGBOX(const char* szExpression, float fLibVersion,
const char* szFileName, int nFileLine)
{
size_t nLen = strlen(ASSERT_FORMAT)
+ (szExpression != NULL ? strlen(szExpression) : 0) + /* version */ 5
+ (szFileName != NULL ? strlen(szFileName ) : 0) + /* line */ 6;
char* szBuffer = new char[nLen];
memset(szBuffer, 0, nLen);
sprintf(szBuffer, ASSERT_FORMAT, szExpression, fLibVersion, szFileName, nFileLine);
OgwwHandledObjectList* inst = OgwwMainFrame::InstanceList();
OgwwMainFrame* pWnd = (OgwwMainFrame*)(inst != NULL && inst->FirstItem() != NULL ?
inst->FirstItem()->HandledObj : NULL);
if (GetCapture() == NULL)
{
for (size_t nPos = 0; nPos < nLen; nPos++)
if (szBuffer[nPos] == '~') szBuffer[nPos] = ' ';
HWND hWnd = (pWnd != NULL ? pWnd->HWnd() : ::GetDesktopWindow());
::MessageBoxA(hWnd, szBuffer, "OGWW debugging - ASSERT failed",
MB_OK | MB_ICONERROR | MB_SYSTEMMODAL);
}
else
{
WString strMessage;
strMessage.SetA(szBuffer);
strMessage.Replace((WCHAR)'\n', (WCHAR)' ');
strMessage.Replace((WCHAR)'~', (WCHAR)'\n');
for (size_t nPos = 0; nPos < nLen; nPos++)
if (szBuffer[nPos] == '~') szBuffer[nPos] = ' ';
// Unfortunately none of this approaches work:
// -------------------------------------------
// ::SetCapture(NULL); -> Doesn't help to release the capture.
// ::ReleaseCapture();
// ::EnableWindow(hWndCapture, FALSE);
// Unfortunately both tips don't work on ReactOS:
// ----------------------------------------------
// Siarhei Boika, "How to bring window to top with SetForegroundWindow()"
// Use an automatically disappearing dialog instead.
// -------------------------------------------
OgwwAutoDisappearDlg* pMsgDlg = new OgwwAutoDisappearDlg(pWnd->HInst(),
pWnd->HPrevInst());
pMsgDlg->Show(strMessage.Value(), L"OGWW debugging - ASSERT failed",
SET_SIZE(460, 120), MB_ICONHAND);
pMsgDlg->Run();
}
delete szBuffer;
int result = fprintf(stderr, ASSERT_FORMAT, szExpression, fLibVersion,
szFileName, nFileLine);
// This is NO alternative to the accurate exit - sending WM_QUIT.
// This does not clean up resources and might cause memory leaks.
ExitProcess(1);
return result;
}
// ###########################################################################################
// ### END Debugging helper - message dialog based. ####
// ###########################################################################################
OgwwAutoDisappearDlg
类的实现位于 AutoDisappearDlg.hpp 和 AutoDisappearDlg.cpp 文件中。我假设 OgwwAutoDisappearDlg
对话框永远不会调用自身——这意味着一次只能有一个对话框实例。因此,为了简单起见,许多成员字段被声明为 static
(AutoDisappearDlg.hpp)
#ifndef __AUTODISAPPEARDLG_H__
#define __AUTODISAPPEARDLG_H__
/// <summary>
/// The <see cref="OgwwMessageDlg"> class is designed to act as a message prompting window.
/// </summary>
class OgwwAutoDisappearDlg : public OgwwGenericWindow
{
private:
/// <summary> /// Determine whether the windows class for <see cref="OgwwMessageDlg">
/// has already been registered. </summary>
static bool _bMessageDlgClassRegistered;
/// <summary>Define the control ID of the single timer associated to this
/// <see cref="OgwwMessageDlg">. </summary>
static UINT _nTimerID;
/// <summary>Define the control ID of the static for the icon, included in this
/// <see cref="OgwwMessageDlg">. </summary>
static UINT _nIconID;
/// <summary>Define the window handle of the static for the icon, included in this
/// <see cref="OgwwMessageDlg">. </summary>
static HWND _hWndIcon;
/// <summary>Define the control ID of the static for the message, included in this
/// <see cref="OgwwMessageDlg">. </summary>
static UINT _nMessageID;
/// <summary>Define the window handle of the static for the message, included in this
/// <see cref="OgwwMessageDlg">. </summary>
static HWND _hWndMessage;
/// <summary>Define the control ID of the progress bar, included in this
/// <see cref="OgwwMessageDlg">. </summary>
static UINT _nProgressBarID;
/// <summary>Define the window handle of the progress bar, included in this
/// <see cref="OgwwMessageDlg">. </summary>
static HWND _hWndProgressBar;
/// <summary>Define the initial number of timer events until timer stops and dialog
/// disappears. </summary>
static UINT _nInitialTimerEvents;
/// <summary>Define the remaining number of timer events until timer stops and dialog
/// disappears. </summary>
static UINT _nRemainingTimerEvents;
private:
HINSTANCE _hPrevInst;
LPCWSTR _wszWindowClassName;
LPCSTR _szWindowClassName;
/// <summary>The windows event loop procedure, processing the actual windows message.
/// </summary>
/// <param name="hWnd">The handle of the window, the windows event loop procedure is
/// called for.</param>
/// <param name="uiMsg">The message, the <c>WindowProcedure</c> shall process.</param>
/// <param name="wParam">The <c>WPARAM</c> parameter of the message, the
/// <c>WindowProcedure</c> shall process.</param>
/// <param name="lParam">The <c>LPARAM</c> parameter of the message, the
/// <c>WindowProcedure</c> shall process.</param>
/// <returns>Returns <c>0</c> on success, or non-zero otherwise.</returns>
static LRESULT CALLBACK DialogProcedure(HWND hWnd, UINT uiMsg, WPARAM wParam,
LPARAM lParam);
public:
// ---------------------- Construction and destruction
/// <summary>Initializes a new instance of the <see cref="OgwwMessageDlg"/> class with
/// instance handle and previous instance handle. </summary>
/// <param name="hInst">The application/module/executable handle. Must not be <c>NULL</c>.
/// </param>
/// <param name="hPrevInst">The previous application/module/executable handle. Typically
/// <c>NULL</c>.</param>
OgwwAutoDisappearDlg(HINSTANCE hInst, HINSTANCE hPrevInst);
/// <summary>
/// Cleans up this instance of the <see cref="OgwwWindow"> class.
/// </summary>
/// <remark>Must be virtual to enable a successful down-cast for <c>delete</c> operator.
/// </remark>
virtual ~OgwwAutoDisappearDlg();
// ---------------------- Overridden virtual methods
/// <summary>
/// Gets the runtime type of this class.
/// </summary>
/// <returns>The runtime type of this class instance.</returns>
int GetType();
/// <summary>
/// Gets the registered previous application/module/executable handle.
/// </summary>
/// <returns>The registered previous application/module/executable handle. Typically
/// <c>NULL</c>.</returns>
HINSTANCE HPrevInst();
// ---------------------- Message loop methods
/// <summary>Creates and registers a new window class, creates a new dialog from this
/// class and shows the new dialog.</summary>
/// <param name="wszMessage">The message to display.</param>
/// <param name="wszWindowTitle">The title of the window to create and show.</param>
/// <param name="aSize">The size of this dialog.</param>
/// <param name="nIcon">The icon to display. Aupported are: MB_ICONASTERIC, MB_ICONERROR,
/// MB_ICONEXCLAMATION, MB_ICONHAND, MB_ICONINFORMATION, MB_ICONQUESTION, MB_ICONWARNING
/// </param>
/// <returns>The handle of the window, that has to be created and shown.</returns>
/// <remarks>This is one of the rare functions, that use ASCII string instead if UNICODE
/// string internally.</remarks>
HWND Show(LPCWSTR windowClassName, LPCWSTR wszWindowTitle, SIZE aSize,
int nIcon = MB_ICONINFORMATION);
/// <summary>Executes the windows event loop.</summary>
/// <returns>The <c>wParam</c> member of the last processed window message, that is
/// equal to the exit code of <c>PostQuitMessage</c>().</returns>
int Run();
};
#endif // __AUTODISAPPEARDLG_H__
这些 static
成员字段可以直接由 DialogProcedure
访问,后者必须是 static
才能满足原型和调用条件 (AutoDisappearDlg.cpp)
#if defined(UNICODE) && !defined(_UNICODE)
#define _UNICODE
#elif defined(_UNICODE) && !defined(UNICODE)
#define UNICODE
#endif
#include <tchar.h>
#include <windows.h>
#include <commctrl.h>
#include <assert.h>
// -- C++ std namespace -------------
#include <string>
// -- C++ std namespace -------------
#include "../Ogww.hpp"
#include "../WString.hpp"
#include "../Console.hpp"
#include "../Root.hpp"
#include "../HandledObjectList.hpp"
#include "GenericWindow.hpp"
#include "AutoDisappearDlg.hpp"
// ###########################################################################################
// ### START Static member ####
// ###########################################################################################
#ifdef __cplusplus
extern "C"
{
#endif
/// <summary>The windows event loop procedure, processing the actual windows message.
/// </summary>
/// <param name="hWnd">The handle of the window, the windows event loop procedure is called
/// for.</param>
/// <param name="uiMsg">The message, the <c>WindowProcedure</c> shall process.</param>
/// <param name="wParam">The <c>WPARAM</c> parameter of the message, the
/// <c>WindowProcedure</c> shall process.</param>
/// <param name="lParam">The <c>LPARAM</c> parameter of the message, the
/// <c>WindowProcedure</c> shall process.</param>
/// <returns>Returns <c>0</c> on success, or non-zero otherwise.</returns>
LRESULT CALLBACK OgwwAutoDisappearDlg::DialogProcedure(HWND hWnd, UINT uiMsg, WPARAM wParam,
LPARAM lParam)
{
int nPos = 0;
switch(uiMsg)
{
case WM_CREATE:
RECT rcDlgFrame;
::GetWindowRect(hWnd, &rcDlgFrame);
OgwwAutoDisappearDlg::_hWndIcon =
::CreateWindowExW(0, WC_STATIC, (LPCWSTR)NULL, SS_CENTERIMAGE | SS_ICON |
SS_CENTER | SS_REALSIZEIMAGE | WS_VISIBLE | WS_CHILD,
8, 8, 32, 32, hWnd,
(HMENU)OgwwAutoDisappearDlg::_nIconID, NULL, NULL);
OgwwAutoDisappearDlg::_hWndMessage =
::CreateWindowExW(0, WC_STATIC, (LPCWSTR)NULL, WS_VISIBLE | WS_CHILD,
48, 8, rcDlgFrame.right - rcDlgFrame.left - 64,
rcDlgFrame.bottom - rcDlgFrame.top - 66, hWnd,
(HMENU)OgwwAutoDisappearDlg::_nMessageID, NULL, NULL);
OgwwAutoDisappearDlg::_hWndProgressBar =
::CreateWindowExW(0, PROGRESS_CLASS, (LPCWSTR)NULL, WS_VISIBLE | WS_CHILD,
(rcDlgFrame.right - rcDlgFrame.left) / 2 - 100,
rcDlgFrame.bottom - rcDlgFrame.top - 50, 200, 15, hWnd,
(HMENU)OgwwAutoDisappearDlg::_nProgressBarID, NULL, NULL);
::SendMessage(OgwwAutoDisappearDlg::_hWndProgressBar, PBM_SETRANGE, 0,
MAKELPARAM(/* MIN range value*/ 0, /* MAX range value*/100));
::SendMessage(OgwwAutoDisappearDlg::_hWndProgressBar, PBM_SETSTEP, 1, (LPARAM)0);
nPos = (OgwwAutoDisappearDlg::_nInitialTimerEvents != 0 ?
(int)(100.0 * OgwwAutoDisappearDlg::_nRemainingTimerEvents /
OgwwAutoDisappearDlg::_nInitialTimerEvents) :
0);
::SendMessage(OgwwAutoDisappearDlg::_hWndProgressBar, PBM_SETPOS, nPos,
(LPARAM)0);
break;
case WM_COMMAND: // 273
DestroyWindow(hWnd);
break;
case WM_TIMER:
if (OgwwAutoDisappearDlg::_nRemainingTimerEvents > 0)
{
OgwwAutoDisappearDlg::_nRemainingTimerEvents--;
nPos = (OgwwAutoDisappearDlg::_nInitialTimerEvents != 0 ?
(int)(100.0 * OgwwAutoDisappearDlg::_nRemainingTimerEvents /
OgwwAutoDisappearDlg::_nInitialTimerEvents) :
0);
::SendMessage(OgwwAutoDisappearDlg::_hWndProgressBar, PBM_SETPOS, nPos,
(LPARAM)0);
}
else
{
::KillTimer(hWnd, _nTimerID);
::PostMessage(hWnd, WM_CLOSE, (WPARAM)0, (LPARAM)0);
}
return (LRESULT)0;
case WM_CLOSE: // 16
// By default, the DefWindowProc function calls the DestroyWindow function
// to destroy the window.
//::DestroyWindow(hWnd);
::PostQuitMessage(0);
break;
}
return (DefWindowProcW(hWnd, uiMsg, wParam, lParam));
}
#ifdef __cplusplus
}
#endif
/// <summary>Determine whether the windows class for "OgwwMessageDlg" has already been
/// registered. </summary>
bool OgwwAutoDisappearDlg::_bMessageDlgClassRegistered = false;
/// <summary>Define the control ID of the single timer associated to this
/// <see cref="OgwwMessageDlg">. </summary>
UINT OgwwAutoDisappearDlg::_nTimerID = 1;
/// <summary>Define the control ID of the static for the icon, included in this
/// <see cref="OgwwMessageDlg">. </summary>
UINT OgwwAutoDisappearDlg::_nIconID = 2;
/// <summary>Define the window handle of the static for the icon, included in this
/// <see cref="OgwwMessageDlg">. </summary>
HWND OgwwAutoDisappearDlg::_hWndIcon = NULL;
/// <summary>Define the control ID of the static for the message, included in this
/// <see cref="OgwwMessageDlg">. </summary>
UINT OgwwAutoDisappearDlg::_nMessageID = 3;
/// <summary>Define the window handle of the static for the message, included in this
/// <see cref="OgwwMessageDlg">. </summary>
HWND OgwwAutoDisappearDlg::_hWndMessage = NULL;
/// <summary>Define the control ID of the progress bar, included in this
/// <see cref="OgwwMessageDlg">. </summary>
UINT OgwwAutoDisappearDlg::_nProgressBarID = 4;
/// <summary>Define the window handle of the progress bar, included in this
/// <see cref="OgwwMessageDlg">. </summary>
HWND OgwwAutoDisappearDlg::_hWndProgressBar = NULL;
/// <summary>Define the initial number of timer events until timer stops and dialog
/// disappears. </summary>
UINT OgwwAutoDisappearDlg::_nInitialTimerEvents = 0;
/// <summary>Define the remaining number of timer events until timer stops and dialog
/// disappears. </summary>
UINT OgwwAutoDisappearDlg::_nRemainingTimerEvents = 0;
// ###########################################################################################
// ### END Static member ####
// ###########################################################################################
在 WM_CREATE
期间创建用于在对话框中显示图标、消息和进度控件。自动消失功能是通过一个定时器实现的,该定时器在 WM_TIMER
中递减剩余时间。
OgwwAutoDisappearDlg
类是我 Ogww 类层次结构的一部分
Destructable
-> Object
-> HandledObject
-> OgwwGenericWindow
-> OgwwAutoDisappearDlg
Destructable
类被设计用作引用类型的通用根(提供构造函数/析构函数的类型)。Object
类被设计用作类层次结构的通用根。它使用派生自其基类Destructable
的构造函数/析构函数的绑定确认,并添加了运行时类型信息。HandledObject
类被设计用作与句柄关联的类的通用根。它使用派生自其基类Object
的构造函数/析构函数的绑定确认和运行时类型信息,并添加了与其数据结构关联的句柄。OgwwGenericWindow
类被设计用作独立窗口类的通用根。它使用派生自其基类HandledObject
的构造函数/析构函数的绑定确认、运行时类型信息以及与其数据结构关联的句柄,并添加了应用程序实例的句柄及其父窗口的句柄。
这就是为什么我的 OgwwAutoDisappearDlg
类会覆盖一些继承的方法并使用一些继承的字段(例如,_hHandle
和 _hInst
)
// ###########################################################################################
// ### START Construction and destruction ####
// ###########################################################################################
/// <summary>Initializes a new instance of the <see cref="OgwwMessageDlg"/> class with
/// instance handle and previous instance handle. </summary>
/// <param name="hInst">The application/module/executable handle. Must not be <c>NULL</c>.
/// </param>
/// <param name="hPrevInst">The previous application/module/executable handle. Typically
/// <c>NULL</c>.</param>
OgwwAutoDisappearDlg::OgwwAutoDisappearDlg(HINSTANCE hInst, HINSTANCE hPrevInst)
{
if (hInst == NULL)
throw L"OgwwMessageDlg: Argument 'hInst' must not be NULL!";
_hHandle = NULL;
_hInst = hInst;
_hPrevInst = hPrevInst;
}
/// <summary>
/// Cleans up this instance of the <see cref="OgwwMessageDlg"/> class.
/// </summary>
OgwwAutoDisappearDlg::~OgwwAutoDisappearDlg()
{
if (_bMessageDlgClassRegistered && _hInst != NULL)
{
// All window classes that an application registers are unregistered when it
// terminates. But no window classes registered by a DLL are unregistered when
// the DLL is unloaded. To prevent class usage after DLL is unloaded we
// explicitly unregister the class here.
::UnregisterClassA(_szWindowClassName, _hInst);
_bMessageDlgClassRegistered = false;
if (LOG_LEVEL >= LOG_VERBOSE)
{
WString className; className.SetA(_szWindowClassName);
Console::WriteText(Console::__VERBOSE,
L"De-registration of WNDCLASSEX '%s' succeeded.\n", className.Value());
}
}
::GlobalFree((HGLOBAL)_wszWindowClassName);
_wszWindowClassName = NULL;
::GlobalFree((HGLOBAL)_szWindowClassName);
_szWindowClassName = NULL;
OgwwAutoDisappearDlg::_hWndIcon = NULL;
OgwwAutoDisappearDlg::_hWndMessage = NULL;
OgwwAutoDisappearDlg::_hWndProgressBar = NULL;
OgwwAutoDisappearDlg::_nInitialTimerEvents = 0;
OgwwAutoDisappearDlg::_nRemainingTimerEvents = 0;
if (LOG_LEVEL >= LOG_INFORMATION)
{
Console::WriteText(Console::__INFORMATION,
L"OgwwMessageDlg: Destruction of window with 'hWnd' %d succeeded.\n",
(int)_hHandle);
}
}
// ###########################################################################################
// ### END Construction and destruction ####
// ###########################################################################################
// ###########################################################################################
// ### START Overridden virtual methods ####
// ###########################################################################################
/// <summary>
/// Gets the runtime type of this class instance.
/// </summary>
/// <returns>The runtime type of this class instance.</returns>
int OgwwAutoDisappearDlg::GetType()
{
return Object::OgwwMessageDlgType;
}
/// <summary>Gets the registered previous application/module/executable handle.</summary>
/// <returns>The registered previous application/module/executable handle. Typically
/// <c>NULL</c>.</returns>
HINSTANCE OgwwAutoDisappearDlg::HPrevInst()
{
return _hPrevInst;
}
// ###########################################################################################
// ### END Overridden virtual methods ####
// ###########################################################################################
现在是我 OgwwAutoDisappearDlg
类的最后两个方法
// ###########################################################################################
// ### START Methods ####
// ###########################################################################################
/// <summary>Creates and registers a new window class, creates a new dialog from this class
/// and shows the new dialog. </summary>
/// <param name="wszMessage">The message to display.</param>
/// <param name="wszWindowTitle">The title of the window to create and show.</param>
/// <param name="aSize">The size of this dialog.</param>
/// <param name="nIcon">The icon to display. Aupported are: MB_ICONASTERIC, MB_ICONERROR,
/// MB_ICONEXCLAMATION, MB_ICONHAND, MB_ICONINFORMATION, MB_ICONQUESTION, MB_ICONWARNING
/// </param>
/// <returns>The handle of the window, that has to be created and shown.</returns>
/// <remarks>This is one of the rare functions, that use ASCII string instead if UNICODE
/// string internally.</remarks>
HWND OgwwAutoDisappearDlg::Show(LPCWSTR /* weak */ wszMessage,
LPCWSTR /* weak */ wszWindowTitle, SIZE aSize, int nIcon)
{
if (wszMessage == NULL || wcslen(wszMessage) == 0)
throw L"OgwwMessageDlg: Argument 'windowClassName' must not be NULL or empty!";
_wszWindowClassName = WString::Copy(L"AutoDisappearDlg");
_szWindowClassName = WString::CopyA(L"AutoDisappearDlg");
/* =================================== RESTRICTION ================================== */
/* While the wide character versions "WNDCLASSW" and "RegisterClassW" work correctly, */
/* the "CreateWindowW" doesn't process the windowName parameter correctly. */
/* =================================== RESTRICTION ================================== */
if (!_bMessageDlgClassRegistered)
{
/* register window class */
WNDCLASSEX wcex = {0};
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = _hInst;
wcex.hIcon = ::LoadIcon(NULL, IDI_APPLICATION);
wcex.hIconSm = ::LoadIcon(NULL, IDI_APPLICATION);
wcex.hCursor = ::LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)::GetSysColorBrush(COLOR_3DFACE);
wcex.lpszMenuName = NULL;
#ifdef UNICODE
wcex.lpszClassName = _wszWindowClassName;
#else
wcex.lpszClassName = _szWindowClassName;
#endif
wcex.lpfnWndProc = OgwwAutoDisappearDlg::DialogProcedure;
if (!::RegisterClassEx(&wcex))
{
Console::WriteText(Console::__ERROR,
L"OgwwMessageDlg::Show: Registration of WNDCLASSEX '%s' failed!\n",
_wszWindowClassName);
return 0;
}
else
{
if (LOG_LEVEL >= LOG_INFORMATION)
Console::WriteText(Console::__INFORMATION,
L"OgwwMessageDlg::Show: Registration of WNDCLASSEX '%s' succeeded.\n",
_wszWindowClassName);
}
_bMessageDlgClassRegistered = true;
}
/* create main window */
HWND hWnd = ::CreateWindowEx(WS_EX_DLGMODALFRAME | WS_EX_TOPMOST, // no extended styles
#ifdef UNICODE
_wszWindowClassName, // name of window class
WString::Copy (wszWindowTitle), // title of window
#else
_szWindowClassName, // name of window class
WString::CopyA(wszWindowTitle), // title of window
#endif
WS_VISIBLE | WS_SYSMENU | WS_CAPTION,
(GetSystemMetrics(SM_CXSCREEN) - aSize.cx) / 2,
(GetSystemMetrics(SM_CYSCREEN) - aSize.cy) / 2,
aSize.cx, aSize.cy, // the size
NULL,
NULL,
_hInst,
NULL);
if (_hHandle == NULL)
_hHandle = (HANDLE)hWnd;
else if (_hHandle != (HANDLE)hWnd)
Console::WriteText(Console::__ERROR,
L"OgwwMessageDlg::Show: Missmatching 'hWnd' %d!\n", (int)_hHandle);
if (_hHandle == NULL)
{
Console::WriteErrorMessageFromID(Console::__ERROR,
L"OgwwMessageDlg::Show: Window creation not successful! Error code is: %d\n",
::GetLastError());
return 0;
}
else
{
if (LOG_LEVEL >= LOG_INFORMATION)
{
Console::WriteText(Console::__INFORMATION,
L"OgwwMessageDlg::Show: Creation of window with 'hWnd' %d succeeded.\n",
(int)_hHandle);
}
}
// Shared icon - no destruction required!
HICON hIco = NULL;
if (nIcon == MB_ICONERROR) // MB_ICONHAND
hIco = ::LoadIcon(NULL, MAKEINTRESOURCE(32513));
else if (nIcon == MB_ICONWARNING) // MB_ICONEXCLAMATION
hIco = ::LoadIcon(NULL, MAKEINTRESOURCE(32515));
else if (nIcon == MB_ICONQUESTION)
hIco = ::LoadIcon(NULL, MAKEINTRESOURCE(32514));
else // MB_ICONINFORMATION // MB_ICONASTERIC
hIco = ::LoadIcon(NULL, MAKEINTRESOURCE(32516));
::SendMessageW(OgwwAutoDisappearDlg::_hWndIcon, STM_SETICON, (WPARAM)hIco, (LPARAM)0);
HFONT hFont = (HFONT)::GetStockObject(DEFAULT_GUI_FONT);
::SendMessage(OgwwAutoDisappearDlg::_hWndMessage, WM_SETFONT, (WPARAM)hFont,
(LPARAM)MAKELONG(TRUE, 0));
::SetWindowTextW(OgwwAutoDisappearDlg::_hWndMessage, wszMessage);
::ShowWindow((HWND)_hHandle, SW_SHOW);
return (HWND)_hHandle;
}
/// <summary>Executes the windows event loop.</summary>
/// <returns>The <c>wParam</c> member of the last processed window message, that is equal
/// to the exit code of <c>PostQuitMessage</c>().</returns>
int OgwwAutoDisappearDlg::Run()
{
OgwwAutoDisappearDlg::_nInitialTimerEvents = 50;
OgwwAutoDisappearDlg::_nRemainingTimerEvents = 50;
::SetTimer((HWND)_hHandle, (UINT_PTR)_nTimerID, 100, NULL);
BOOL bQuit = FALSE;
MSG msg = {0};
while (!bQuit)
{
/* check for messages */
if (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
/* handle or dispatch messages */
if (msg.message == WM_QUIT)
{
bQuit = TRUE;
}
else
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
}
}
return msg.wParam;
}
// ###########################################################################################
// ### END Methods ####
// ###########################################################################################
就是这样!我希望 OgwwAutoDisappearDlg
对话框能为解决类似问题提供好的启发。要了解 Ogww 库的进展,我建议查看我的 Code Project 文章 “在 ReactOS 上运行的基本图标编辑器”。
历史
- 2020 年 1 月 8 日:初始版本