I'm trying to make a simple C++ wrapper around the Windows UI library but I'm getting some weird crashes that I can't seem to debug.
I have a base class Widget that does all the RegisterClassEx()/CreateWindowEx()/window subclassing in the class constructor.
Window is derived from Widget which sets up a top level window,
allows you to add a child, etc.
Now the library code is in a DLL, and my test app is linking against it.
So in the test app I add a button to the Window. The button is displayed, sized correctly,
etc, BUT when I close the window the test app crashes. I've traced it to deleting the child widget in the Window destructor, but I have no idea WHY this is causing the test app to crash...
Second thing, since my library lives in a DLL, I can't use GetModuleHandle(0), for when I need a HINSTANCE correct? Because that would return the exe's address space??
Widget.cpp
Code:
// I think this is the correct way of doing this...
HMODULE GetInstance()
{
HMODULE hModule;
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
(LPCTSTR)&GetInstance,
&hModule);
return hModule;
}
Widget::Widget()
{
CreateWidget("MyClassName");
}
Widget::Widget(LPCTSTR className)
{
CreateWidget(className);
}
Widget::~Widget()
{
}
void Widget::CreateWidget(LPCTSTR className)
{
static bool registered = false;
if (!registered)
{
WNDCLASSEX wcx;
wcx.cbSize = sizeof(wcx);
wcx.style = 0;
wcx.lpfnWndProc = DefWindowProc;
wcx.cbClsExtra = 0;
wcx.cbWndExtra = 0;
wcx.hInstance = GetInstance();
wcx.hIcon = NULL;
wcx.hCursor = static_cast<HCURSOR>(LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE));
wcx.hbrBackground = NULL;
wcx.lpszMenuName = NULL;
wcx.lpszClassName = "MyClassName";
wcx.hIconSm = NULL;
if (!RegisterClassEx(&wcx))
throw Exception("The window class could not be registered.");
registered = true;
}
m_handle = CreateWindowEx(0, // extended styles
className, // class name
"", // window name
0, // window styles
0, // x
0, // y
CW_USEDEFAULT, // width
CW_USEDEFAULT, // height
NULL, // parent window
NULL, // menu handle
GetInstance(), // instance handle
NULL); // create params
if (m_handle == NULL)
throw Exception("The widget could not be created.");
SetWindowLongPtr(m_handle, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
// subclass the window
m_oldWndProc = reinterpret_cast<WNDPROC>(GetWindowLongPtr(m_handle, GWLP_WNDPROC));
SetWindowLongPtr(m_handle, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(WindowProc));
}
// ... other code snipped
LRESULT CALLBACK Widget::ProcessMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return CallWindowProc(m_oldWndProc, hwnd, uMsg, wParam, lParam);
}
LRESULT CALLBACK Widget::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
Widget* widget = reinterpret_cast<Widget* >(GetWindowLongPtr(hwnd, GWLP_USERDATA));
if (widget)
return widget->ProcessMessage(hwnd, uMsg, wParam, lParam);
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Window.cpp
Code:
Window::Window()
{
// change the window style to a top level window
SetWindowLongPtr(GetHandle(), GWL_STYLE, WS_OVERLAPPEDWINDOW);
m_child = nullptr;
}
Window::Window(LPCTSTR className)
: Widget(className)
{
// change the window style to a top level window
SetWindowLongPtr(GetHandle(), GWL_STYLE, WS_OVERLAPPEDWINDOW);
m_child = nullptr;
}
Window::~Window()
{
// with this in it's a guaranteed crash...
//delete m_child;
}
void Window::SetChild(Widget* child)
{
if (child == nullptr)
return;
delete m_child;
m_child = nullptr;
m_child = child;
m_child->SetParent(this);
// layout the window
Layout();
}
LRESULT CALLBACK Window::ProcessMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_SIZE:
Layout();
break;
default:
return Widget::ProcessMessage(hwnd, uMsg, wParam, lParam);
}
return 0;
}
Test.cpp
Code:
int main(int argc, char* argv[])
{
Window win;
Button* btn = new Button();
btn->Show();
win.SetChild(btn);
win.Show();
// Init common controls
// Run windows event loop...
Application::Run();
return 0;
}
So once I click the close button on the window everything blows up.
Also if I derive a C++ class from the Window object in my test app I get a crash on
startup...
I'm probably doing something insanely stupid somewhere, but I can't seem to find it....
EDIT:
I forgot to mention that my test app *still* crashes even if I don't do "delete m_child;". It just crashes less consistently.