First | Next | Previous | Last | Glossary | About |
In all of your work so we have followed a fairly simple approach to programming. Our programs have been strictly sequential in nature, in effect the program was in the driving seat. The user of the program was directed by the program to take the next step - whatever it might be.
Windowed systems present a different approach - the user of the program is in the driving seat and the program presents itself and then waits for some action from the user.
Windowed systems are event-driven. You shouldn't think that Windows ala Microsoft introduced this concept of being event-driven, it is a computer fact of life that has been around for many years and you might have met it in other environments.
Events are triggered by such things as keystrokes, mouse movements and mouse clicks. The OS is in control of all the different events, ie it captures all events packages them as messages and places them in a queue for the applications to examine.
Each application has its own message queue and contains code to extract messages from the queue. Each message that the application extracts is dispatched to a procedure which will then process it.
From the perspective of Win95, 98 and NT, Windows presents an API - Applications Programming Interface - which is a mechanism programmers can use to write applications. The API consists of a collection of many thousands of functions and data structures, and seems to grow with each new Microsoft venture. There was a time when an experienced programmer could get to know most of the details of an API on small systems but that was before the days of GUI's (graphic user interfaces). The best approach to living with the Windows API is to use Microsoft's own resources available through the Microsoft Developer Network. There are also many very good texts which cover aspects of the Win API.
In this introduction to Windows programming I will start with the Win API but I warn you that it is dominated by C code, not C++. We will look at a few simple programs which use the API directly. After that introduction I plan to use class libraries which greatly simplify the task of programming in Windows.
There are many Windows class libraries around and some very good ones are free in both the FSF sense and the financial sense. For example:
The first three are notable because they are multi-platform which means that we can build programs for Microsoft Windows and Linux for instance. The last is a Microsoft Windows only library but is notable because there is an excellent book written by Paul DiLascia which describes how the library evolves. We will use wxWindows mainly and also look at FLTK.
If you have the programming resource CD you have access to a large (24 MBytes) help file - Microsoft® Win32® Programmer's Reference -. Although it is a bit dated now you will find it very useful. The link is to a zipped file which is quite large (about 6 MBytes).
Our first task is to examine "Hello World", Win API style. The complete program can be viewed here. If you look at it you will see that it contains some 70+ lines of code. Don't be dismayed by the size. Even though the program doesn't do any really useful work it packs a lot of windows functionality into those 70 lines.
The fundamental steps in creating a simple windows program are:
There will be other things but these represent the bare bones.
This step consists of completing a WNDCLASS structure. The window class is not a class in the OOP sense. It is more of a specification for the characteristics of a given type of window.
WNDCLASS wc; wc.lpszClassName = "HelloClass"; wc.lpfnWndProc = MainWndProc; wc.style = CS_VREDRAW | CS_HREDRAW; wc.hInstance = hInstance; wc.hIcon = LoadIcon( NULL, IDI_APPLICATION ); wc.hCursor = LoadCursor( NULL, IDC_ARROW ); wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); wc.lpszMenuName = NULL; wc.cbClsExtra = 0; wc.cbWndExtra = 0;
The sample shows that we use a WNDCLASS data structure. Each of the fields has some value assigned to it.
Since this might be your first view API code you might be musing over the odd variable names. A naming-convention has developed which is quite useful. Each variable begins with some letters which indicate the data type. For instance the lpsz in lpszClassName indicates that this is a long pointer to a zero-terminated string. lpfn is a long pointer to a function, h is a handle ie, a pointer to an object (again not necessarily in the OOP sense). So hInstance is a handle or pointer to an instance, perhaps an instance of a window class. I think cb refers to a char or byte but it is misnamed since cbClsExtra and cbWndExtra are actually ints.
You can read more about WNDCLASS here.
This is a simple thing and is required so that when we create in the next step Windows is aware that the class created in the previous step exists.
RegisterClass( &wc );
The first two steps were concerned with priming the Windows operating system for this step where we actually create the window. The CreateWindow function creates an overlapped, pop-up, or child window. It specifies the window class, window title, window style, and (optionally) the initial position and size of the window. The function also specifies the window's parent or owner, if any, and the window's menu.
hWnd = CreateWindow ( "HelloClass", "Hello World", WS_OVERLAPPEDWINDOW|WS_HSCROLL|WS_VSCROLL, 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL ); ShowWindow( hWnd, nCmdShow );
The CreateWindow function has many arguments which tie the window to the WNDCLASS instance that was registered before. After the window has been created we show it with the ShowWindow function.
Now we have a window and we are ready to start responding to events generated by the user. In order to do we need to retrieve messages from a queue and do so message management.
I wrote earlier that:
"Each application has its own message queue and contains code to extract messages from the queue. Each message that the application extracts is dispatched to a procedure which will then process it."
It's in the message loop that this extracting and dispatching of messages occurs.
while( GetMessage( &msg, NULL, 0, 0 ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } return msg.wParam;
Here is a very simple message loop. It Gets Messages, Translates Keyboard Messages and Dispatches Messages to a window procedure.
Translate message receives a message and sends it back to Windows which takes care of key translation. Dispatch also sends a message back to windows but this time Windows makes a call to a window procedure which is our last step.
It is primarily through the callback procedures that Windows (the operating system) communicates with the application. A user generates some event, Windows captures the event, a message is placed in the application queue, the application gets the message and then requests Windows send the message to the appropriate procedure. Hence the term callback.
LRESULT CALLBACK MainWndProc ( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) { PAINTSTRUCT ps; HDC hDC; switch( msg ) { case WM_PAINT: hDC = BeginPaint( hWnd, &ps ); TextOut( hDC, 10, 10, MESG1, strlen(MESG1) ); TextOut( hDC, 10, 25, MESG2, strlen(MESG2) ); EndPaint( hWnd, &ps ); break; case WM_DESTROY: PostQuitMessage( 0 ); break; default: return( DefWindowProc( hWnd, msg, wParam, lParam )); } return 0; }
Every window that has to respond to messages must have a callback procedure.
It is in the particular callback procedure that the application determines what should be done with a message.
You can see one way of dealing with messages in this example, a switch statement is used. The MainWndProc procedure is coded to handle two messages a WM_PAINT message and a WM_DESTROY message. If MainWndProc receives a WM_PAINT message from Windows then it will display some text in its window. If MainWndProc receives a WM_DESTROY message then it will call PostQuitMessage and the application will terminate.
You will probably agree that there is a great deal of detail "behind the scenes". Most of the detail tho' concerns how Windows works and you need to have some understanding of this in order to use the Windows API.
There are other ways to do "Hello World", here is a much shorter version.
#include <windows.h> int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR szCmd, int nCmd) { MessageBox( NULL, "Hello World!", "Message Box", 0); return 0; }
You will probably agree that we can't get much mileage out of this as a vehicle for looking at the Win API.
The - Microsoft® Win32® Programmer's Reference - has a very complete discussion on "Messages and Message Queues" and you should definitely read it.
To build a simple Windows program with Mingw32:
g++ -o hello hello.c -mwindows
The command line just requires the addition of -mwindows to make sure the correct libraries are incorporated.
#include <wpp.h> MyApp myApp; class HelloWin : public WPMainWin { public: HelloWin() { createWin("Hello"); } void paint(WPPaintDC &pdc) { WPRect clientArea = this; pdc.drawText(clientArea, "Hello World!", DT_SINGLELINE | DT_CENTER | DT_VCENTER); } }; void MyApp::main() { mainWin = new HelloWin(); run(); }
Here is "Hello World" written using Paul DiLascia's W++ Windows class library.
There's no mention of WNDCLASS, creating windows, message loops and callback functions.
We will make much use of libraries like this but I stress that you should get used to reading the Win32 API documentation and understanding what the various parts of the API do. If you can understand the API (you don't have to know it in detail) then you will understand the different class libraries.
Build both of the sample "Hello World" programs and run them.
For the following exercises you may need to use the - Microsoft® Win32® Programmer's Reference -.
Exercise 1.2TextOut( hDC, 10, 25, MESG2, strlen(MESG1) );What happens?
First | Next | Previous | Last | Glossary | About |
Copyright © 1999 - 2001
David Beech