Automatically Disappearing Dialog, Used to Provide VERIFY and ASSERT on ReactOS





5.00/5 (1 vote)
Automatically Disappearing Dialog
Introduction
ReactOS is an open source alternative to the Windows operation system. Even if the first version of ReactOS dates back to 1998, there is still no 'stable' version of ReactOS. May be, the most important reason is a lack of attention.
The motivation for this tip comes from the fact that:
- I want to use the
ASSERT
andVERIFY
macros on ReactOS in the same way as on Windows - but a captured mouse cannot be released and thus the
MessageBox
cannot be used either.
For details about the ASSERT
and VERIFY
macros, see the CODE PROJECT article Useful Debugging Macros by William E. Kempf.
Background
The ASSERT
and VERIFY
macros start a dialog that reports the failed assertion or verification. Probably this dialog is a MessageBox
. This could be done for ReactOS as well, but there is a problem in the particular case of mouse capture. There are essentially two situations in which the mouse is captured:
- Between
WM_INITMENUPOPUP
andWM_UNINITMENUPOPUP
and - during a drag and drop operation (see the Code Project article Capturing the Mouse by Chris Becke).
Unfortunately both solutions, which are presented in the CODE PROJECT article, How to bring window to top with SetForegroundWindow() by Siarhei Boika, do not work on ReactOS.
After trying for some time to find any combination of SetCapture()
, ReleaseCapture()
and EnableWindow()
with other API calls (that are available on ReactOS - e.g., AllowSetForegroundWindow()
is not available on ReactOS) to release the mouse, I gave up and thought about: Which solutions are possible when the mouse is still captured? It seemed to me to be the best solution, in case the mouse is not available at all, to have a dialog that disappears automatically. Therefore, I developed the C++ class AutoDisappearDlg
, which is universally applicable and can also be used under Windows. The implementation is based on Win32
, not on MFC or WTL.
Using the Code
All source code presented here is part of my Ogww
library and is fully included in the attachment to this article. I have introduced the Ogww
library with my CODE PROJECT article, More about OpenGL with C/C++ and C# on ReactOS (or Windows) and enhanced with my Code Project article, A basic icon editor running on ReactOS.
I will start with my macro declarations (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()
and OgwwMainFrame_ASSERT_MSGBOX()
are function prototypes, that are implemented within OgwwMainFrame.cpp and used by my ASSERT
and VERIFY
macros (forward declaration).
The assert
macro is already defined by C++, so I only need to add the verify
macro. To become compatible to the Windows programming standard, I finally defined the ASSERT
and VERIFY
macros.
Now I can choose whether to use the assert
und verify
macros (without dialog prompt) or the ASSERT
and VERIFY
macros (with dialog prompt) in the source code of my application.
The implementation of OgwwMainFrame_VERIFY_MSGBOX()
and OgwwMainFrame_ASSERT_MSGBOX()
are looking like that (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. ####
// ###########################################################################################
The implementation of the class OgwwAutoDisappearDlg
is located within the files AutoDisappearDlg.hpp and AutoDisappearDlg.cpp. I assumed that the OgwwAutoDisappearDlg
dialog can never call itself - means there can only be one instance of the dialog at a time. Thus, for simplicity, many member fields are declared 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__
These static
member fields are directly accessible by the DialogProcedure
, that must be static
to meet the prototype and calling conditions (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 ####
// ###########################################################################################
The creation of the controls, used to display icon, message and progress within the dialog, is done during WM_CREATE
. The auto disappearing feature is implemented by a timer, that decrements the remaining time during WM_TIMER
.
The class OgwwAutoDisappearDlg
is part of my Ogww class hierarchy:
Destructable
-> Object
-> HandledObject
-> OgwwGenericWindow
-> OgwwAutoDisappearDlg
- The
Destructable
class is designed to act as a common root of reference types (types, that provide a constructor/destructor). - The
Object
class is designed to act as a common root of the class hierarchy. It uses the binding confirmation of a constructor/destructor derived from its base classDestructable
and adds run time type information. - The
HandledObject
class is designed to act as a common root of the classes, that are associated to a handle. It uses the binding confirmation of a constructor/destructor and the run time type information derived from its base classObject
and it adds a handle associated to its data structure. - The
OgwwGenericWindow
class is designed to act as a common root of the classes, that are independent windows. It uses the binding confirmation of a constructor/destructor, the run time type information and the handle associated to it's data structure derived from it's base classHandledObject
and adds a handle to the application instance and a handle to it's parent window.
That's why my OgwwAutoDisappearDlg
class overrides some inherited methods and uses some inherited fields (e.g., _hHandle
and _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 ####
// ###########################################################################################
And now the last two methods of my OgwwAutoDisappearDlg
class:
// ###########################################################################################
// ### 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 ####
// ###########################################################################################
That's it! I hope that the OgwwAutoDisappearDlg
dialog is a good inspiration to solve similar problems. To follow the progress of the Ogww library, I recommend a look at my Code Project article, A basic icon editor running on ReactOS.
History
- 8th January, 2020: Initial version