tags:

views:

670

answers:

10

edit

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?

+7  A: 

Speaking as an ex-instructor in C Windows programming, I can safely say that there is no easy way to build a GUI using C - the language is not well suited to the task. Almost anything else would be a better choice - my own favourite for GUI development is Delphi.

anon
Maybe not in Windows. In the unix world, it's not that difficult (especially when using UIM/X)
ammoQ
+2  A: 

There is a better way. Use Qt. It's for C++, but C++ is better suited for GUIs, because its object oriented model fits the domain very well. Qt itself is a great library of amazing depth and breadth, well-supported and designed, licensed with LGPL and fun to use. It comes with a complete GUI designer that can help you build dialogs in a graphical manner very quickly.

If you insist on C and native Win32, there's also a way. Buy Petzold's Programming Windows book - it's very good. After reading the first few chapters you'll be able to build interesting GUIs in C relatively easily. Much more code and effort will be required than with Qt, but the positive side is that you'll get tiny and fast executables without external dependencies.

Eli Bendersky
Thanks for the recommendations--I'll probably have a look at that book, and I'll keep Qt in mind for when I move to C++.
Carson Myers
+4  A: 

Although you've mentioned you want to use C, you can still write C-like code in C++. I'd recommend giving Win32++ a try. Its basically an easier to use Win32 API.

John T
+3  A: 

I've never really seen a problem writing Win32 stuff in pure C, I actually think it's a lot cleaner than the alternatives I've tried (MFC, WTL, QT and a few others). A few simple pointers:

  • Regarding high- and low-word parameters, there's nice macros to handle that for you -- the names elude me right now though.

  • You can work in a rather nice object-oriented manner by allocating an "application" struct and storing its address with SetWindowLong(), that solves you problem of having global handles.

  • You can create a rather simple mapping from a WM_MESSAGE -> function pointer to ease the redability of the long switch in your window processes, or just refactor it into simpler functions to get a better overview.

So, in all, it's possible to write nice and clean stuff, but it's hard. The same goes for every other method though, the only thing that differs are the values of "possible" and "hard" :)

Christoffer
+1  A: 

Just to give you an idea how C compares to other languages when in comes to Win UI apps: Some time ago a simple MDI application with a few windows and a few classes to visualise medical data took me 17k lines of code in C. I rewrote this in Delphi a couple of years later, and it took only 5k LOCs.

Andy
+6  A: 

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.

Jason Owen
Ah, yes, the windowsx header -- why is that not more prominently documented? :) That's where you get a lot of convenience macros for checking and setting component features (such as checkbox states) too, right?
Christoffer
Thanks, good answer. As for the dialog boxes, that sounds a little more painless... Although I'm not compiling with Visual C++ (I'm waiting for it to download--though I'm just piggybacking slow wireless internet so it's taking days (the MSDN is in there)), so is there another way to do this?
Carson Myers
@christoffer: I don't know, it really should be... I wasn't even able to find a good link for it.@Carson Myers: The dialog boxes are put in a resource script, which your compiler should be able to handle and for which there are any number of editors, not just Visual C++. And, come to think of it, the Express Edition doesn't include the resource editor... I'll update my answer.
Jason Owen
+3  A: 

GUIs are ideally handled by more abstract languages than C, otherwise there's lots of redundant boilerplate code, in this sense I'd advise you to consider embedding a scripting language interpreter into your C application, so that the GUI is largely driven by scripts, while the core of your application is coded in C.

This would also have the advantage of making your application more configurable and more flexible without necessarily requiring recompilation.

You will want to look into lightweight scripting engines that are written in ANSI C and that provide bindings to GUI libraries such as QT, GTK, wxWindows etc.

You might want to check out Lua or nasal both of which are fairly lightweight and do provide bindings for GUI libs.

For some example uses, you might want to check out the following screenshots of two applications that feature GUIs that are largely based on nasal and its GTK bindings:

none
+5  A: 

I'll toss in my traditional outside-the-box suggestion: Add Lua to your application, and build the GUI with a combination of Lua and a suitable toolkit module. One good choice is IUP, and another is wxWidgets. Both have decent quality Lua bindings. Even better, Lua itself along with both wxLua and IUP can be had in a single installation along with a bunch of other useful Lua modules from the Lua for Windows project.

Lua is easy to integrate with C code, since it was designed for that purpose from the outset.

It is also easy to use Lua as the glue that hold the application together, with core functionality implemented in C or as interfaces to native API calls.

Lua is also supported by SWIG in case you have an extensive library to adapt.

Of course, you could just use either wxWidgets or IUP directly from C as they both have C callable APIs, but event-driven GUIs are often full of nit-picky little house-keeping callback functions that are easy to deal with in a scripting language.

Another toolkit to consider is Tk, which is most often seen in combination with the Tcl scripting language. Tcl was also developed with embedding in a larger application in mind, although it has evolved away from that as its primary use case. Tk has been bound to a variety of other languages, including Perl.

RBerteig
+2  A: 

If you want to do it in C, you definitely want Charles Petzold's Programming Windows.

JustJeff
huh, two recommendations for that book, I'll definitely have to read it now
Carson Myers
A: 

Using GTK+, you could use Glade to build the UI.

Bastien Léonard
Is that compatible with Windows? If not, I'll still have a look, since I'll need to learn Linux programming one time or another (note: "Need" here means, eventually feel like it).
Carson Myers
@Carson: GTK+ works on Windows. There's a Windows port of Glade as well: http://sourceforge.net/projects/gladewin32/. Compared to Win32, GTK+ will add an external dependency, though.
Bastien Léonard