Classes for Win32 UI and how to hide details
-
Hello everyone, as an academic exercise and because, apparently, I'm founding myself with some free time to spend, I'm trying to create a set of C++ classes to abstract away the Win32 APIs related to user interface creation (I'm talking about CreateWindowEx, Translate/DispatchMessage, and so on). Yes I know I'm reinventing the wheel, and no, I don't want to use Qt nor wxWidgets; as I said, this is an academic exercise. So, I have created a window class, which basically wraps many HWND-related functions, and adds support for children containment (as in Windows Forms Form.Controls) and event handling (using boost::signals2). I also have created derived classes (i.e. button, textbox, etc.) which I have subclassed from existing WNDCLASSes ("BUTTON", "EDIT", etc.) and have their window procedure redirected to each derived class internal methods, thus I'm able to catch any WM_ message and launch the corresponding boost::signal2 signal. Now, in order to achieve this, I had to keep, at the very least, a HWND as a private member of my window class. What I would like to do is to abstract away this HWND, because if it appears in the header then the consumers will have windows.h #included and I'm trying to avoid this. So I turned to the pimpl idiom and did something like the following:
// in window.h
class window : public container
{
public:
...
private:
struct windata;
std::unique_ptr _data;
};// window.cpp
struct window::winadata
{
HWND me;
};window::window()
{
...
_data->me = CreateWindowEx(...);
}So this worked for window class, but then, for the derived classes, I need access to windata structure (for there is the needed HWND). Having struct windata declared as protected won't work, because in button.cpp windata is an incomplete type, and thus it won't compile. So I'm looking for alternatives here, and wanted to ask CPians opinions on how to best achieve this abstraction. By digging on wxWidgets and other's source code, I have come up with a few alternatives: * Have struct windata defined in its own header file and have it included in the cpps as needed. * Create a window_base class and then have a window_win32 class inheriting window_base, and put all the Win32 specific code in the derived class (this is what wxWidgets do, I think). * Follow the abstract factory pattern instead, which (I think) is what John Torjo does with his Win32GUI library. * Some other options involving global variables and template voodoo.
-
Hello everyone, as an academic exercise and because, apparently, I'm founding myself with some free time to spend, I'm trying to create a set of C++ classes to abstract away the Win32 APIs related to user interface creation (I'm talking about CreateWindowEx, Translate/DispatchMessage, and so on). Yes I know I'm reinventing the wheel, and no, I don't want to use Qt nor wxWidgets; as I said, this is an academic exercise. So, I have created a window class, which basically wraps many HWND-related functions, and adds support for children containment (as in Windows Forms Form.Controls) and event handling (using boost::signals2). I also have created derived classes (i.e. button, textbox, etc.) which I have subclassed from existing WNDCLASSes ("BUTTON", "EDIT", etc.) and have their window procedure redirected to each derived class internal methods, thus I'm able to catch any WM_ message and launch the corresponding boost::signal2 signal. Now, in order to achieve this, I had to keep, at the very least, a HWND as a private member of my window class. What I would like to do is to abstract away this HWND, because if it appears in the header then the consumers will have windows.h #included and I'm trying to avoid this. So I turned to the pimpl idiom and did something like the following:
// in window.h
class window : public container
{
public:
...
private:
struct windata;
std::unique_ptr _data;
};// window.cpp
struct window::winadata
{
HWND me;
};window::window()
{
...
_data->me = CreateWindowEx(...);
}So this worked for window class, but then, for the derived classes, I need access to windata structure (for there is the needed HWND). Having struct windata declared as protected won't work, because in button.cpp windata is an incomplete type, and thus it won't compile. So I'm looking for alternatives here, and wanted to ask CPians opinions on how to best achieve this abstraction. By digging on wxWidgets and other's source code, I have come up with a few alternatives: * Have struct windata defined in its own header file and have it included in the cpps as needed. * Create a window_base class and then have a window_win32 class inheriting window_base, and put all the Win32 specific code in the derived class (this is what wxWidgets do, I think). * Follow the abstract factory pattern instead, which (I think) is what John Torjo does with his Win32GUI library. * Some other options involving global variables and template voodoo.
I would recommend your first alternative:
Have struct windata defined in its own header file and have it included in the cpps as needed.
There is a simpler solution, which is practical but not academically pleasing. WinDefs.h has:
typedef HANDLE HWND;
WinNT.h has:
typedef void *PVOID;
typedef PVOID HANDLE;So you could define a protected typedef in your base class:
typedef void* WindowHandle;
And use that throughout. In a sense, this is cheating, because it depends on looking behind the facade, and it could, at some point stop being valid, since Microsoft could, in theory, change the definitions in a later version of Windows. If you use their definitions, you'd be safe. However, given how much code would break, it's exceedingly unlikely they'll ever change the definitions of those types.
-
I would recommend your first alternative:
Have struct windata defined in its own header file and have it included in the cpps as needed.
There is a simpler solution, which is practical but not academically pleasing. WinDefs.h has:
typedef HANDLE HWND;
WinNT.h has:
typedef void *PVOID;
typedef PVOID HANDLE;So you could define a protected typedef in your base class:
typedef void* WindowHandle;
And use that throughout. In a sense, this is cheating, because it depends on looking behind the facade, and it could, at some point stop being valid, since Microsoft could, in theory, change the definitions in a later version of Windows. If you use their definitions, you'd be safe. However, given how much code would break, it's exceedingly unlikely they'll ever change the definitions of those types.
Thank you Orjan, yeh I was lurking the windef file and actually got this working:
struct opaque
{
int* data;
};
typedef opaque* opaque_ptr;And then this works:
HWND wnd = GetSomeWindowHandle();
opaque_ptr h = reinterpret_cast(wnd);
// and then...
wnd = reinterpret_cast(h);But as you said, it feels like cheating. I'll give it further thought, and if I can't find anything else, I think I'll be using this approach. Thanks again for the help! Best regards.
-
Thank you Orjan, yeh I was lurking the windef file and actually got this working:
struct opaque
{
int* data;
};
typedef opaque* opaque_ptr;And then this works:
HWND wnd = GetSomeWindowHandle();
opaque_ptr h = reinterpret_cast(wnd);
// and then...
wnd = reinterpret_cast(h);But as you said, it feels like cheating. I'll give it further thought, and if I can't find anything else, I think I'll be using this approach. Thanks again for the help! Best regards.
If you use a typedef of void* you will not need any reinterpret_cast calls, as it's all the same type. The problem of using a pointer to a struct like this is that someone looking at the code would be curious about what's in
h->data
so it is misleading to have it if it's never used.
-
Hello everyone, as an academic exercise and because, apparently, I'm founding myself with some free time to spend, I'm trying to create a set of C++ classes to abstract away the Win32 APIs related to user interface creation (I'm talking about CreateWindowEx, Translate/DispatchMessage, and so on). Yes I know I'm reinventing the wheel, and no, I don't want to use Qt nor wxWidgets; as I said, this is an academic exercise. So, I have created a window class, which basically wraps many HWND-related functions, and adds support for children containment (as in Windows Forms Form.Controls) and event handling (using boost::signals2). I also have created derived classes (i.e. button, textbox, etc.) which I have subclassed from existing WNDCLASSes ("BUTTON", "EDIT", etc.) and have their window procedure redirected to each derived class internal methods, thus I'm able to catch any WM_ message and launch the corresponding boost::signal2 signal. Now, in order to achieve this, I had to keep, at the very least, a HWND as a private member of my window class. What I would like to do is to abstract away this HWND, because if it appears in the header then the consumers will have windows.h #included and I'm trying to avoid this. So I turned to the pimpl idiom and did something like the following:
// in window.h
class window : public container
{
public:
...
private:
struct windata;
std::unique_ptr _data;
};// window.cpp
struct window::winadata
{
HWND me;
};window::window()
{
...
_data->me = CreateWindowEx(...);
}So this worked for window class, but then, for the derived classes, I need access to windata structure (for there is the needed HWND). Having struct windata declared as protected won't work, because in button.cpp windata is an incomplete type, and thus it won't compile. So I'm looking for alternatives here, and wanted to ask CPians opinions on how to best achieve this abstraction. By digging on wxWidgets and other's source code, I have come up with a few alternatives: * Have struct windata defined in its own header file and have it included in the cpps as needed. * Create a window_base class and then have a window_win32 class inheriting window_base, and put all the Win32 specific code in the derived class (this is what wxWidgets do, I think). * Follow the abstract factory pattern instead, which (I think) is what John Torjo does with his Win32GUI library. * Some other options involving global variables and template voodoo.
I've used a couple of way of getting around this: - use the void pointer hack - wrap every WIN32 operation in another class called window_handle then implement window classes as needed using that. I also replaced the concrete base class with an interface class as virtually all the code that would have been saved by implementation inheritance ended up in the window_handle. This is similar to your shared PIMPL but the PIMPL is actually a full blown object in its own right and does all the WIN32 stuff and doesn't just hide an HWND. Another way I want to experiment with further decouples the windows guff from the logic of what the window actually does. The idea is to have a window class that has a message handler interface pointer but I haven't tried that yet. You'll be getting a similar effect using boost::signal2 to route messages.
-
I've used a couple of way of getting around this: - use the void pointer hack - wrap every WIN32 operation in another class called window_handle then implement window classes as needed using that. I also replaced the concrete base class with an interface class as virtually all the code that would have been saved by implementation inheritance ended up in the window_handle. This is similar to your shared PIMPL but the PIMPL is actually a full blown object in its own right and does all the WIN32 stuff and doesn't just hide an HWND. Another way I want to experiment with further decouples the windows guff from the logic of what the window actually does. The idea is to have a window class that has a message handler interface pointer but I haven't tried that yet. You'll be getting a similar effect using boost::signal2 to route messages.
Thank you Aescleal, I think I'll use the void pointer.
Aescleal wrote:
The idea is to have a window class that has a message handler interface pointer but I haven't tried that yet.
I'm trying that just yet, and I have already subclassed some existing common controls to get their WndProc and route the messages accordingly through boost::singal2, but on my own terms. I think this is what Windows.Forms do. Thanks for the help!