65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2020年1月8日

CPOL

4分钟阅读

viewsIcon

3722

downloadIcon

53

自动消失对话框

引言

ReactOS 是 Windows 操作系统的开源替代品。即使 ReactOS 的第一个版本可以追溯到 1998 年,但仍然没有“稳定”版本的 ReactOS。也许最重要的原因是缺乏关注。

这个技巧的灵感来自于一个事实:

  • 我想在 ReactOS 上以与 Windows 相同的方式使用 ASSERTVERIFY
  • 但是被捕获的鼠标无法释放,因此也无法使用 MessageBox

有关 ASSERTVERIFY 宏的更多详细信息,请参阅 CODE PROJECT 文章 William E. Kempf 撰写的“有用的调试宏”

背景

ASSERTVERIFY 宏会启动一个对话框,报告失败的断言或验证。这个对话框可能是一个 MessageBox。这也可以在 ReactOS 上实现,但在鼠标捕获的特定情况下存在问题。基本上有两种鼠标被捕获的情况:

不幸的是,CODE PROJECT 文章中提出的两种解决方案,Siarhei Boika 撰写的“如何使用 SetForegroundWindow() 将窗口带到顶部”,在 ReactOS 上不起作用。

在花费了一些时间尝试找到任何可以释放鼠标的 SetCapture()ReleaseCapture()EnableWindow() 与其他 API 调用(在 ReactOS 上可用 - 例如,AllowSetForegroundWindow() 在 ReactOS 上不可用)的组合后,我放弃了,并思考:当鼠标仍被捕获时,有哪些解决方案是可能的?对我来说,在鼠标根本不可用的情况下,有一个自动消失的对话框似乎是最好的解决方案。因此,我开发了 AutoDisappearDlg C++ 类,该类是通用的,也可以在 Windows 下使用。该实现基于 Win32,而不是 MFCWTL

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 中实现,并被我的 ASSERTVERIFY 宏使用(前向声明)。

assert 宏已由 C++ 定义,因此我只需要添加 verify 宏。为了与 Windows 编程标准兼容,我最终定义了 ASSERTVERIFY 宏。

现在我可以选择在我的应用程序的源代码中使用 assertverify 宏(无对话框提示)还是 ASSERTVERIFY 宏(带对话框提示)。

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.hppAutoDisappearDlg.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 日:初始版本
© . All rights reserved.