EWin Tutorial - Introduction

Conventions of this document

Abbreviation Meaning
GUI Graphical User Interface
Win95 Windows 95
Win98 Windows 98
WinNT Windows NT
Win2K Windows 2000
WinME Windows Millenium Edition
WinCE Windows CE
Win9X Windows 95, Windows 98, or Windows ME
WinNT+ Windows NT 3.51, NT 4.0, Windows 2000, Windows XP
API "Application Programmer's Interface"
API Function A "low-level" Windows function such as GetTextExtent() or MoveWindow()
[MORE] This paragraph is for people who weren't confused by the preceding paragraph and want more details.
Windows Microsoft Windows
windows blocks of screen real-estate. Note the capitalization.
Msg a Windows Message
Dlg a Windows Dialog
Win a window


How is this document supposed to be used?

Well, it is supposed to be a tutorial that will teach you the basics of EWin. Hopefully it has been broken up in such a way that it can be used as a reference later on, also. But this is not a stand-alone tutorial. You're expected to also have EWIN.CHM, the compressed-HTML-help file that documents the various EWin objects and member functions. When you see something that doesn't make sense, or see a function and you can't guess what it does, please look it up in the online help file and see if it becomes clearer.


What is EWin?

EWin is a set of C++ wrappers for writing GUI applications for 32-bit Windows platforms (Win95, Win98, WinNT, Win2K, WinME). It wrappers such things as windows, dialogs, bitmaps and other resources, the mouse, the keyboard, etc.


What operating systems does EWin support?

EWin supports all 32-bit Windows platforms, including Win32s, Win9X, WinNT+, and WinCE. To run in WinCE, you must rebuild the EWin libraries with Unicode support. This is very easy (you open the library project and add UNICODE to the list of defined values). If you aren't building for WinCE, I recommend using the stock, non-Unicode libraries, as Win9X doesn't support Unicode.


Would you break EWin into basic parts?

I think we're getting a bit ahead of things. But sure, why not? There is the TWin, which is the most basic kind of Window. All other windows, including dialogs, are derived from TWin. And every EWin application has a TModule object, which is poorly named, but is basically the "brains" for the program. The TModule houses the program's Message Loop. And there is TGdiObject, which is the base class for all GDI objects such as pens, brushes, bitmaps, and fonts. And then there is TFauxGdiObject, which is the base class for things that are kinda like GDI objects: icons, image lists, etc. But as I said, this is jumping the gun a bit. How about we start with What is Windows Programming Like, and then get around to this. Skip the parts you already know, of course.


What is Windows programming like?

What kind of a question is that? Read "Programming Windows" by Charles Petzold. This is THE book. Buy it, get it, steal it, have it. You NEED this book in order to understand how Windows works at the API level.

You don't need to read all of it, if you're going to use EWin. But read chapters 1 - 3, at least. This will teach you how painful Windows programming is without an object-oriented class library like EWin.


Damn you, tell me useful things about Windows programming!

Okay, okay. I will try to summarize. First off, I assume you know how Windows works, from a user's point of view. That is, you understand about windows and the mouse and stuff. If not, go use Windows for a couple days and come back.

Okay. Pretty much everything on the screen is a "window". This is a structure that is used internally to keep track of what is on the screen.
Even fairly advanced things have at their core the same "window"ness. A dialog is a fancy kind of window. A tooltip is a fancy window. The start button? A window. Checkboxes? A window. Edit boxes, where you type in text? A window. List box? A window. Mouse cursor? Not a window. Just seeing if you were paying attention. Basically (and there are a few exceptions), everything on the screen is a window, except the mouse cursor.

Now what does that get us? Well, it means that everything on the screen has some fundamental properties. That brings us to the next topic...


What are the fundamental properties that every window has?

Every window has an x,y position and width and height. In Windows, coordinates are top-down. That is, the upper-left of the screen is (0,0) and the bottom-right of the screen is (640,480) or whatever your screen resolution is. (Yes, yes, this is backwards from how mathematicians do things, and how OpenGL coordinates work, and so on. But it's Windows, and you'll get used to it. Most people find it pretty intuitive right off the bat.)

So all windows have an x, a y, a width, and a height. They also have a "text" attribute. For most windows, this is the text on their title bar. But if the window doesn't have a title bar, say it is an editbox, then the "text" field is the text that is currently in the editbox. Some controls, like listboxes, don't display this "text" anywhere at all. You can still get and set the text field, but it will never be shown to the user.

All windows also have a bunch of "style" and "extended style" flags. Some windows have even more, "extended extended style" flags and so on. But all windows at least have these two sets of flags, and there are a bunch of pre-designed flags. These flags all have the WS_ prefix, for style flags, and WS_EX_ prefix for extended flags. Sample styles are WS_VISIBLE. If a window has this style, it is visible; otherwise it is invisible. Another is WS_DISABLED, which, if a window has this, causes the window to not accept user input. Many kinds of windows draw themselves "grayed out" if they have this style. For more styles, see WS_XXXX and WS_EX_XXXX in the EWin help.

Oh, oh, another important thing that the styles tell you is what kind of window it is. There are three basic "kinds" of windows. These are Cascading Windows, Popup Windows, and Child Windows. "Cascading Window" is the default, so there isn't a style for that. A Popup Window has the WS_POPUP style flag. A pop-up window is pretty much the same as a cascading window, except that it isn't contained within it's parent, but rather, floats free like a bird, able to be moved anywhere on the screen. The last kind of window is a Child Window, which has the WS_CHILD style flag. A child window is one that is contained inside another window. It doesn't have a menu. It is generally a "widget", that is, a GUI control that the user interacts with, like a button or an editbox or a listbox.

Oh, and never make a window that has both the WS_CHILD and the WS_POPUP style at the same time. You'll go insane and it will behave unpredictably.

[MORE] There is actually a WS_OVERLAPPED style, but it is just made for completeness' sake. It's value is zero, so it doesn't matter if you try to bitwise-OR it into a window's styles or not -- it won't do anything.[/MORE]

Another thing to note is that there are a few flags in the "styles" area that don't have a pre-determined meaning. They are used by different window types for different things. For instance, the BS_DEFPUSHBUTTON style for button windows has a value of "1", and so does the LBS_NOTIFY style for listboxes. They both have the same value, but those styles only have meaning for that particular kind of control, so this is not ambiguous. You simply never give a list box the BS_DEFPUSHBUTTON style. (If you did, you would be inadvertently giving it the LBS_NOTIFY style instead.)

(Many controls need more flags than are available in the "styles" flags, and so they create new "extended extended" styles and so on. But don't worry about that right now.)

All windows have a menu. Well, that isn't exactly true. All windows have an internal field where they *could* have a menu ID. But only certain kinds of windows, Cascading Windows and Popup Windows, can have menus. The third kind of window, Child Windows, can't have menus. So, to save internal space, that menu field is used for a different purpose: an ID number. Thus, child windows have an ID number which you can use to easily reference them. And non-child windows have a place where you can associate a menu with them. You can never have both a menu and an ID number, since they use the same 16-bit field internally. Got it? [Actually, it appears to be a 32-bit field under Windows NT, but you had probably better assume it is still a 16-bit field.]

Every window has a Parent Window field. It may be NULL, meaning that this window has no parent. (Child Windows must have a parent, of course.) What does Parent Window mean? Well, for child windows, it indicates who owns the 'widget'. For other kinds of windows, its meaning is more subtle and we'll get to that later.

If a window has a parent, and that window is NOT a Popup window, then its x,y coordinate is relative to its parent's client space rather than the screen. That is, if a button is at (0,0), it will appear at the upper-left corner of its parent. But if a popup window is at (0,0), it will appear at the upper-left corner of the *screen*. Confused yet?

There are several other attributes that EWin "fakes". In other words, these things aren't necessarily actual attributes of all windows, but EWin pretends that they are. If you are using EWin, then every window has a font, an icon, a cursor shape (the shape the mouse cursor takes when it is over the window), and a background brush (to determine the color your window is painted).

So, there you have it. Those are the things that every window has. Now, every TWin represents a window, and so a TWin provides functions for getting and setting all this data. It uses the [controversial] system of providing two functions with the same name. For backwards compatibility, it also provides Get and Set versions of most, BUT NOT ALL, of these functions. Thus, if you have a TWin and want to set its text, you would call

MyWin.Text("Texty Texty");

and to get the text out, you would use

STRING s = MyWin.Text();

Similarly, all TWin's provide functions named Style(), ExStyle(), Font(), Icon(), BkBrush() (the background brush), Cursor(), X(), Y(), Width(), Height(), etc.

You could also use GetText() and SetText() to set the text, or GetFont() and SetFont() to set the font. But not all of these attributes have get/set versions, so for the sake of orthogonality I recommend you use the other versions. Or just hop into win.h and add the Get/Set versions for the attributes that are missing them. <shrug> Actually, I prefer the Get/Set versions myself. It is a hard decision to make, and different people like different things, so both versions are provided. If you want to use them, go ahead. If I missed any Get/Set versions, let me know and I will add them.

Every window also has a window class, but you don't need to worry about that too much...


What's a window class? And why do I care?

You probably don't care. But I will explain a bit anyway.

Internally, deep down inside Windows, every window has a Window Class. Don't confuse this with a C++ class -- a Window Class is represented by a WNDCLASS structure. This structure describes some of the more "fundamental" attributes of a window. There may be 100 of a particular kind of window, and yet all of them will share the same class data. A window's class is an "old-school" part of Windows, and has been nearly 100% hidden by EWin. You very rarely need to care about the window's class. Just know that it is there. There are two exceptions. A window's "class name" is often very useful for finding a window. And a window's Class Styles are sometimes used to configure some of the very low-level aspects of a window. But you don't need to fiddle with a class to set these things now. If you have a particular set of CS_ class styles you want to use, just overload TWin::GetClassStyle() and have it return the styles you want. Also overload TWin::GetClassName() and have it return a unique name. Whenever you overload GetClassStyle() you should also overload GetClassName() and provide a new, unique name for you class. (Otherwise your GetClassStyle() function may not be called, because Windows will go, "Oh, hey, this window has the same name as another kind of window I already know about; no need to ask it for its info -- I already know it!").

[MORE] Sometimes you may want to just overload TWin::GetClassName() without overloading TWin::GetClassStyle(). This is useful if you want to later find your window by calling the FindWindow() API function.

[MORE] If you really want to set the class information yourself, you can overload TWin::GetWinClass(). You shouldn't need to do this, except in some cases when you want fancy WinNT 3.51 support, and a few very arcane situations. Usually, the library's code fills this structure in with sufficient defaults, and takes care of all of the messy details. But you can certainly overload GetWinClass() to do whatever you want. I highly recommend that you call the base class version of GetWinClass() first (to get good initial values for the different fields) and then just change the fields that you need to change.


[Advanced Topic] I know something about Windows programming already. Where is the message dispatch loop, the WNDPROC window procedure, and the DLGPROC?

A: These things are done for you by EWin. The message-dispatch loop is done for you by the EWinMain() macro [via the call to Module.Run()]. EWin has a hidden WNDPROC and DLGPROC function. There is only one of each. They're in module.cpp. They are "generic": they don't do anything, really, except call the appropriate TWin's OnMsg() function. So instead of writing a WNDPROC for each different kind of window, you create a TWin for each different kind of window. All messages received by that window are sent to its OnMsg() function. So if you were 'porting Win32 C GUI code to EWin, you could just overload an OnMsg() and move all of your WNDPROC code there with only minor changes.

But don't do this for new programs. Instead, overload the functions you care about. See, the stock TWin version of OnMsg() figures out what kind of message it is, and then calls another OnXXX function appropriately. So instead of directly overloading OnMsg() to handle, say, a mouse event, overload OnMouseMsg() instead. This provides better encapsulation and makes it easier to alter your window's behavior in derived classes. It also makes the code cleaner.

So do you ever overload OnMsg()? Yes, for messages that don't already have OnXXX messages. Say you need to handle WM_IME messages, which haven't been wrapped by EWin; you would overload OnMsg(), and if it's an IME function, have it call some new OnImeMsg() virtual function that you create. (If it's not an IME message, have your OnMsg() function call the base class OnMsg() function so that the other kinds of messages still get dispatched.)

Note also that you don't need to do this for WM_USER+### messages or WM_APP+### messages -- all of these messages are sent to OnUserMsg() or OnAppMsg(), respectively.  


So, anyway, how do I get started? I want a minimal EWin application!

Okay, okay. A minimal EWin app is actually nice and small. HOWEVER, if you are using Visual C++, the code won't be your biggest enemy. Your enemy will be the environment, and setting up the 5,000,000 settings and toggles you need to make things work. For this reason, I recommend you use the EWin Application Wizard to whip up a basic application. You can then throw away the code that it generates, if you want, but it will have set up all the myriad switches, which is the hard part.

[MORE] If you are using Borland C++ 5.0, it's easy: just add Ewinlib.lib to your build, pretty much. (Or EWinlibd.lib for the debug version.) When you use the debug lib, #define DEBUG for your project.

But for purposes of explaining things, here's the smallest possible EWin app:

 
#include <common.h>
#include <win.h>

EWinMain( TWin(NULL, TWinAttr()) );

There you go. This will make a window that just sits there. When you close the window the program will end. You can even make this fancier:

 
#include <common.h>
#include <win.h>

EWinMain( TMyWin(NULL, TWinAttr().Text("Hello World!")) );

This will make a TWin with the titlebar text "Hello World!".

Now you are probably wondering what in the eight hot hells that TWinAttr() thing does. Don't sweat it just yet. It's a neat trick that lets you specify any of the TWin's "built-in" attributes like text, x, y, width, height, style, etc., without having to specify them ALL. In other words, it is a C++ technique that gives you variable-argument functions. I will go over it in detail soon. But for now just trust me.

Okay, so you got your cheesy one-line program. How to make it do more? You'll need to use a non-stock TWin. So:

 
#include <common.h>
#include <win.h>
  
class TMyWin : public TWin
{
  public:
  TMyWin(TWin *PParent, const TWinAttr &Changes)
    : TWin(PParent, Changes) { }
};

EWinMain( TMyWin(NULL, TWinAttr().Text("Hello World!")) );

This actually doesn't do anything different -- it still just says "Hello World" -- but now you are ready to change it's behavior by overloading things. Like so:

 
#include <common.h>
#include <win.h>


class TMyWin : public TWin
{
  public:
  TMyWin(TWin *PParent, const TWinAttr &Changes)
    : TWin(PParent, Changes) { }

  virtual bool OnMouseMsg(MouseMsg Msg, WORD KeyFlags,
                          short WheelDelta, TPoint XY)
  {
    if (Msg == WM_LBUTTONDOWN)
      MessageBeep(1);
    return true;
  }
};

EWinMain( TMyWin(NULL, TWinAttr().Text("Hello World!")) );

This new version catches mouse events. When the user presses down on the left mouse button inside your window, your window will now beep. Exciting, no?

This simple example seems to present several more questions...


What is EWinMain, exactly?

A: EWinMain is a macro. It is defined in emacros.h. It is one of the very few macros that EWin uses. Macros are generally considered icky and wrong. But EWinMain hides the stupid stuff you have to do for every Windows GUI program. What it does is this: it creates a TModule object, which every EWin program needs. Then it creates an instance of the TWin-derived class you passed it, and makes this the main window of your program. Then it calls TModule->Run(), which causes your program to do its message loop, dispatching events to the windows that need them. Maybe it would be easier if I show you what this tranforms into.

 
EWinMain( TWin(NULL, TWinAttr()) );

Yields code very similar to (but not exactly like) this:

 
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow)
{
  TModule Module(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
  TWin *pMainWindow = new TWin(NULL, TWinAttr());
  Module.SetMainWin(pMainWindow);
  if (!pMainWindow->Create())
    Module.Quit();
  int n = Module.Run();
  delete pMainWindow;
  return n;
}

So you see it creates the program's "main" function, or, as it is called for Win32 GUI apps, the "WinMain" function.

Sometimes, though, you need to do something before or after the main window has been created. There is a different version of EWinMain for this: EWinSuperMain. It takes three parameters. The first is the same as for EWinMain(). The second is the name of a function to run before it does anything else, and the third is the name of a function to run AFTER everything else is done (right before the "return n;" line). For instance:

 
void PreFunc()
{
  //do pre-setup-processing here
}

void PostFunc()
{
  //do post-run processing here, if any; otherwise just leave it blank
}

EWinSuperMain( TWin(NULL, TWinAttr()), PreFunc(), PostFunc() );

What could be easier?

Don't answer that.

There are two more versions of EWinMain, used when you want a different message loop so that you can detect when nothing is happening. These are EWinMainWithIdleChecking() and EWinSuperMainWithIdleChecking(). Don't worry too much about all this right now, though. Just use EWinMain().


What the HELL is TWinAttr()??? And isn't that a syntax error, there?

To explain what TWinAttr is and how it is used, it is probably easiest to explain why we came up with it. See, we used to have a whole bunch of different TWin constructors. If we wanted to set the width and height, we'd use  

 
p = new TWin(PParent, width, height);

and if we wanted to set the text and the width and the height we'd go

 
p = new TWin(PParent, text, width, height);

and so on and so forth. Well, there are a LOT of different parameters that we like to set on our TWins. This meant we had LOTS of constructors for TWin, and LOTS of constructors for each thing that derived from a TWin. It hurt, and it was unusably sucky. So we got rid of them all. Then, if we wanted to set the width, we would have to go

 
p = new TWin(PParent);
p->Width(width);
p->Height(height);
p->Text(text);

And just set the things manually. This was a good solution and we used it for a while. But programmers are lazy and we soon longed for those constructors back. It saved us a few lines. What we needed was a way to specify some of the different fields, but not all of them all the time. So I read some books by Bjarne Stroustrup (the designer of C++) and found out how to do this. It's a neat trick.

What we do is we have a structure, called a TWinAttr, that has all the different things that are common to all windows (Text, X, Y, Width, Height, Font, etc., etc.). And all of our TWin-derived objects take one of these TWinAttr() objects. They all just pass it to the base TWin constructor. The base TWin constructor gets all the data out of the TWinAttr and puts it into itself. So it's pretty straightforward. I could write:

 
TWinAttr Attributes;
Attributes.Text("This is the window's default text");
p = new TWin(PParent, Attributes);

and the window's default text would be set to the string above.

But yes, yes, you saw the TWinAttr being used for more evil magic than that.Well, in order to be able to stick all this stuff on one line, we create a temporary TWinAttr. That's what the () are for. So

 
p = new TWin(PParent, TWinAttr());

Creates a temporary TWinAttr and then throws it away when the function is done.

But if we want, we can call member functions of this TWinAttr. We can even chain them together, because all of TWinAttr's functions return a const reference to themself! Thus, we can say

 
p = new TWin(PParent, TWinAttr().Text("blah").X(6).Y(5));

Hmm, I think this is advanced material. Well, never mind how it works. It just works. If you are scared of it or it confuses you, just don't use it. It is definitely not required. You never *need* to use it to do anything. It is just a kind of shorthand.


What is the first parameter that you pass to the TWin's constructor, and why has it been NULL in the examples so far?

That's the pointer to the window's parent window (or NULL to indicate that it doesn't have a parent window).

The first parameter of the TWin that you use in EWinMain() is always NULL, because that is supposed to be your main window, and it shouldn't have a parent. But all the TWins that your main window owns will get your main window as their parent. Thus, if you needed to make a dialog in your main window somewhere, you might go:

 
TMyDlg Dlg(this);
Dlg.Create();

You passed it a pointer to its parent, which is you, the main window. ("this" is a C++ keyword that gives a pointer to the current object.)


Why isn't that first parameter, the Parent pointer, smushed into the TWinAttr like all the other attributes?

Well, it... I... hm. I didn't think of that. But now that I have thought about it for a moment, it doesn't sound like a good idea, because you set the parent pointer a LOT, and the other attributes get set much less often. I like it this way. Pbth.

(go on to the next page in the tutorial...)