R – painless way to build a Windows UI in C



alright, I guess C is painful in nature–Just, this part, is particularly painful.
Also, there isn't a real reason I'm only writing in C, other than, that's the language I want to write in, and be proficient in, for now. I know moving from C to C++ is bad, but whatever.

And does anyone know a solution, to my problem with making more than one window in a program?

Well, I was interfacing a C DLL with a VB6 front-end, but — trying to pass strings back and forth got ugly, and it seemed like every time I added a new function, everything else would break. So, I thought, Why would I put myself through all this pain just to interface with a language that isn't even supported anymore? Why not just put that pain to better use and learn to build a front-end in C?

Well I started, but I have to ask: is there an easy way to do this? The huge switch statement that is WndProc is hurting my eyes, and going against everything I learned about clean code (much like the 12 parameter CreateWindowEx(), or the 14 parameter CreateFont()).

I realize I could refactor all this — to an extent — and if I was using C++ I could put windows and their components into classes and access them in a more natural way I suppose…

Anyways, I managed to construct a big form titled "Main Window!", with a "Popup Message!" button, which works alright — and it wasn't complicated at all. Then there's a "Change that text!" button which changes the text in an edit control. For this to be possible, the WndProc has to know about the edit control while it's receiving a WM_COMMAND message from the button. I can't pass the hWnd for the edit control into it, since I'm not the one calling it and you can't just add arguments to WndProc. So I had to put the edit control in global scope.

I wanted to change the font of the buttons to 13pt Tahoma — easy, right? Of course — but I couldn't get any farther than the WM_PARENTNOTIFY message… After some careful reading of MSDN, I discovered that some information is in the high word of wParam, and some in the low word. So, I had to write this: if(wParam==(WM_CREATE | (POPUP_COMMAND<<16))){

Making the controls global? huge switch statements? 12 and 14 parameter function calls? Bit shifts for simple notifications? I still can't even figure out how to make more that one window (window window, not control window) appear–this is painful…

Please tell me there's a better way!

How do I make more than one window by the way? If I just register two classes, or CreateWindowEx on the same class twice (for two handles), only the first will appear. I tried playing with the dwStyle parameter a little, but to no avail. Anyone?

Best Solution

Win32 + C is a very low-level approach, which has its advantages and disadvantages. That said, there are ways to make things easier.

First, regarding bit-shifts, there are several macros to work with WPARAMs and LPARAMs. You probably shouldn't be writing bit-shifts, in fact, because the size of WPARAM and LPARAM has changed over the years, and you might be creating a future bug.

Secondly, regarding the giant switch statement in your WindowProc function, while that will be there, there's a way to make it a bit more manageable. #include <windowsx.h> to get a whole bunch of useful macros, most notably HANDLE_MSG. Instead of needing to write

LRESULT CALLBACK MyWindowProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)    {
    switch(Message) {
    case WM_COMMAND:

You can instead write:

void MyOnCommand(HWND hwnd, int controlID, HWND hwndCtl, UINT codeNotify);
LRESULT CALLBACK MyWindowProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)    {
    switch(Message) {
    HANDLE_MSG(hwnd, WM_COMMAND, MyOnCommand);

and the macro automatically casts and separates all the different parameters for that message type into the appropriately named & typed variables. Browse windowsx.h to see the messages that are handled, and the function prototypes it requires. Not all the different messages are handled, but quite a few are.

As for having multiple windows at once, there shouldn't be anything stopping you from calling CreateWindow or its relatives multiple times. While you only have one HINSTANCE (given to you in your WinMain), you can have as many HWNDs as you want.

You may find Raymond Chen's scratch program useful, as it should give you a decent skeleton to start from. You might also consider making your application use a dialog box instead of just a window; you get a bunch of stuff for free, and you can use a resource editor to design the window and all its controls. Visual C++ Standard and above include a resource editor, and there are also several free ones: XN Resource Editor seems to be a popular open-source editor, and I'm sure you can find more on Google. All of these produce a resource script, and you should have a resource compiler in your toolchain: rc.exe for Visual Studio (including the Express Editions, or available as part of the SDK), or windres as part of the GNU BinUtils in cygwin or mingw.

Related Question