Tutorial 1: Win32 - Our First Baby Window...
by bkenwright@xbdev.net
Our Very First Window (A Window Is Born)
I always like nice simple code....and this always makes me smile...its can't
get any simpler can it?...So if you run this up and compile it you should see a
wonderful window pop up to your eyes amazment...WOW...
#include
<windows.h>
int
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
MessageBox(NULL, "Hello World!", "Message", MB_OK);
return 0;
} |
|
Now for the explanation of what all that code does... Firstly WinMain
is our entry point, and is used in all windows programs to declare that this is
where our code will start exectution from.....so matter how big our code grows
to, this is where it starts at. The parameters that come with this
function starting point we'll come to later.
The WINAPI, is the calling convention, its so the compiler/linker knows how
parameters are being passed... as if you look deep deep within the windows
declaration files you will find:
#define
WINAPI __stdcall
#define
CALLBACK __stdcall
Which shows that WINAPI is just replaced with __stdcall at compile
time.....and yup, you could replace the WINAPI with __stdcall in the code above
and it will compile okay. The calling convention is how parameters are
passes on the stack...e.g. the first parameter on top or at the bottom etc.
Next MessageBox(..)...now this is our API...or Application Programming
Interface, which means that we'll be using pre-written functions, that windows
has for us. These API functions are in the various dll's in the system32
directory. The parameters for this MessageBox API?...let me explain them.
Paramter -1- is the window handle...if we specify NULL as we have done above,
this implies the desktop.
Paramter -2- is our window text
Paramter -3- Is our Caption Box Text
Finallly Paramter -4- is the type of caption box buttons to have......MK_OK
is defined as '0'...and MB_OKCANCEL, which gives us an okay and a cancel button,
there's a few different types which you can look up in the windows API help on
the message box.
Createing A Bigger And Better Window
Now didn't that nice messagebox code look nice and easy....well it gets a
little harder for our next coding adventure. We'll have to add a bit more
code.... You can't deny that when your learning windows this is a scary piece of
code.... and you really do wonder if you need it all, but the answer is yes!
As you'll soon learn.
ARrrggg....yup....look at all that code!.
And we only wanted to create a simple window. Whats it all for? Do
we really need it all? Well you'll soon get familiar with it.
Beleive it or not, its usually the same for all your win32 applications, once
you write 3 or 4 applications, you'll soon end up putting it all in a seperate
file so you can copy and paste it, as its more or less the same for every window
you'll create.
Now the 'Three' main parts to this code below
are the CreateWindow(..) function, the GetMessage(..) while loop and the
WndProc(..)
procedure which gets called over and over again and does our work for us.
CreateWindow, is the API that creates our
window, and it returns a window handle, in this case hWnd...and is basically a
handle as the name says to our window. Every window in windows has a
window handle, and its used to pass to all your other API's to reference your
windows. Like drawing a line, you need to know your window handle, so you
can draw on it. If you use the window handle of null, this implies the
desktop!
Lets take a moment to define the basic
parameters for CreateWindow(..) so you know what they mean etc...
hWnd = CreateWindow(
CLASSNAME, // A
string which describes our window
WINNAME,
// Our windows caption name which appears in the top of
the window
WS_OVERLAPPEDWINDOW, //
Type of window... you can look in the documentation for the differetn types
CW_USEDEFAULT, CW_USEDEFAULT, // Starting top left
corner of the window...you can put 0, 0 if you want
to define the top left corner of
your screen, but CW_USEDEFAULT lets windows decide (its defined as -1)
300, 200, // Your windows
width and height in pixels
NULL, // Our parent window, in this case NULL as
there is no parent, so the desktop is assumed to be our parent!
NULL, // Are there any pop
up menus?...nop...so we say NULL
hInstance, // This
parameter is passes to us in our WinMain(...) function entry point...now its
really just and unsigned integer at the heart of it...but it reapresents our
.exe, so that when you pass it to applications they can access resouces
etc.....no need to really worry about it...just something that you keep in a
safe place and pass when needed.
NULL); // Applicaton
data? ...nop...where not passing anything to our window...so again we set
it to NULL.
Before creating our window we call RegisterClass(..)
which tells windows to send us any messages...for example if the user clicks on
our window we want to know about it!...if the user closes are application...we
want windows to send us a message informing us of this.
To RegisterClass(..) we pass a single structure,
which we first fill in. We set things like our background colour of our
window, the icon if any, and our windows 'CALLBACK' function. This
function in the below example is called 'WndProc'....but feel free to call it
what ever you want. Now windows knows what our function will be called,
and it will call it when ever something happens in our window.
And in this simple case, windows calls WndProc(..)
and tells us when the user has selected to close our application. And
we'll give a message and if so exit nice and politely.
#include
<windows.h>
// These are definitions which we'll define once here.
const
char *CLASSNAME = "Tutorial Win32", *WINNAME
= "A Simple Window";
LRESULT
CALLBACK WndProc(HWND hWnd, unsigned
int iMessage, WPARAM wParam, LPARAM lParam);
// This function is our program entry point, but this time its got a lot
// of Windows initilisation code :-/
int
WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR,
int nCmdShow)
{
HWND hWnd;
MSG
Message;
WNDCLASS WndClass;
ZeroMemory(&WndClass, sizeof(WndClass));
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
// Casting, e.g. (HBRUSH), is for the
compilers benefit, so that
// it doesn't get unhappy, and to tell it that
where passing a paramter
// but we want it passes as a HBRUSH or what ever
we've cast it to.
WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hIcon = LoadIcon(hInstance, NULL);
WndClass.hInstance = hInstance;
WndClass.lpfnWndProc = WndProc;
WndClass.lpszClassName = CLASSNAME;
WndClass.style = CS_HREDRAW | CS_VREDRAW;
if(!RegisterClass(&WndClass))
return 0;
hWnd = CreateWindow(CLASSNAME, WINNAME, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 300, 200,
NULL, NULL, hInstance, NULL);
ShowWindow(hWnd, nCmdShow);
// This is our windows messaging loop!
while(GetMessage(&Message, hWnd, 0, 0))
{
TranslateMessage(&Message);
DispatchMessage(&Message);
}
return Message.wParam;
}
// This is our Call Back procedure, where you would put your code...this
gets
// called repeatedly from our windows messaging loop.
LRESULT
CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
switch(iMessage)
{
case WM_CLOSE:
case WM_DESTROY:
if(MessageBox(hWnd,
"Leave? Quit the our mini window?", "Message", MB_YESNO) == IDYES)
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd,
iMessage, wParam, lParam);
}
return 0;
} |
So if we run up this code what do we get? Well its a simple windows....
hmmm.... its not much.. but when you run up Office, or Corel, or even Notepad,
this is what it is....its a window!...
Wahooo....your own drawing program!
Now with a basic window, you can modify your code to do some amazing things,
...and for our next amazing trick we'll create a doodle pad!....how cool is
that!
Holy Cow...look at all that code!... wait wait...come back...don't run away.
Before getting scared, take a look at it carefully, its the same as before, our
basic window remember, I've added a few extra lines which turns our basic
window into our 'Doodle' window.
I've commented the new lines with '**
NEW **' comments so you can see the major changes.
How to draw a line...Hmmmm...well you use to important function calls, the
MoveToEx, which takes a Handle to a DC (HDC) and some screen coordinates.
Next we use LineTo(..) which takes a HDC again and some more coordinates....that
will draw a line for us.
But a Handle to a DC?...whats this? Its another one of windows handles
that you can get for your window using GetDC(hWnd)...which will return your
window DC handle so you can use MoveToEx and LineTo to draw on your window.
Now when we draw a LineTo, that new point becomes our starting point for our
next LineTo....so we dont have to keep calling MoveToEx(..).
But be warned, always 'Release' a DC after you've finished with it....as
windows allocates memory for each new DC that you call....so it would lead to a
memory leak if you didn't release them after you've finished with them, and
eventually to your application crashing (in an extreme case)
[NOTE] - Just for fun, you can even draw on
the desktop, if you do this: GetDC(NULL), then you are getting a dc for the
whole screen, which will allow you to draw all over your desktop. Just
press F5 when you've finished and it will clean your desktop...and remove your
doodle lines.
When you press the left mouse button, windows calls your callback function, 'WndProc'
in this case and the iMessage parameter is set to 'WM_LBUTTONDOWN', assuming you
pressed the left mouse button.... we test this in our switch statement...and we
respond appropriately...by setting our starting position for our line.
Now whenever we move our mouse, the WM_MOUSEMOVE case switch statement is
processed, so we just move our line bit by bit...createing our drawing.
#include
<windows.h>
// These are definitions which we'll define once here.
const
char *CLASSNAME = "Tutorial Win32", *WINNAME
= "Doodle pad";
LRESULT
CALLBACK WndProc(HWND hWnd, unsigned
int iMessage, WPARAM wParam, LPARAM lParam);
// NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW **
// Global declaration of your handle to a DC!..used so we can draw on our
// window.
HDC hDC;
// This function is our program entry point, but this time its got a lot
// of Windows initilisation code :-/
int
WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR,
int nCmdShow)
{
HWND
hWnd;
MSG
Message;
WNDCLASS WndClass;
ZeroMemory(&WndClass, sizeof(WndClass));
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
// Casting, e.g. (HBRUSH), is for the compilers
benefit, so that
// it doesn't get unhappy, and to tell it that
where passing a paramter
// but we want it passes as a HBRUSH or what ever
we've cast it to.
WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hIcon = LoadIcon(hInstance, NULL);
WndClass.hInstance = hInstance;
WndClass.lpfnWndProc = WndProc;
WndClass.lpszClassName = CLASSNAME;
WndClass.style = CS_HREDRAW | CS_VREDRAW;
if(!RegisterClass(&WndClass))
return 0;
hWnd = CreateWindow(CLASSNAME, WINNAME, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 300, 200,
NULL, NULL, hInstance, NULL);
ShowWindow(hWnd, nCmdShow);
// This is our windows messaging loop!
while(GetMessage(&Message, hWnd, 0, 0))
{
TranslateMessage(&Message);
DispatchMessage(&Message);
}
return Message.wParam;
}
// NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW **
// The only changes to the basic window compared to before is in here,
// we've added a few new lines to our switch statement.
// This is our Call Back procedure, where you would put your code...this
gets
// called repeatedly from our windows messaging loop.
LRESULT
CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
switch(iMessage)
{
// NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW
** NEW ** NEW **
case WM_LBUTTONDOWN:
hDC = GetDC(hWnd);
if(hDC)
{
int xp = LOWORD(lParam);
int yp = HIWORD(lParam);
MoveToEx(hDC, xp, yp, NULL);
}
break;
// NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW
** NEW ** NEW **
case WM_LBUTTONUP:
if(hDC)
{
ReleaseDC(hWnd, hDC);
hDC=NULL;
}
break;
// NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW
** NEW ** NEW **
case WM_MOUSEMOVE:
if(hDC)
{
int xp = LOWORD(lParam);
int yp = HIWORD(lParam);
LineTo(hDC, xp, yp);
}
break;
case WM_CLOSE:
if(MessageBox(hWnd, "Leave? Quit
the our mini window?", "Message", MB_YESNO) == IDYES)
{
// NEW ** NEW ** NEW ** NEW ** NEW **
NEW ** NEW ** NEW **
if(hDC) ReleaseDC(hWnd,
hDC);
PostQuitMessage(0);
}
break;
default:
return DefWindowProc(hWnd,
iMessage, wParam, lParam);
}
return 0;
} |
|
OOooo...doesn't that look cool...well open up your windows compiler and give
it a try. See how easy it is to create windows applications.
Now if you want to clean it, just put another window in front of your window
then move it away. And your wonderful drawing has gone :( Well that
would be the next stage, you would need to save all your drawings in memory and
evertime your appliation is asked to refresh your window, you would need to
re-draw them. Its amazing how your application can grow...first doodle
..then doodle with colour....then before you know it you'll soon have a full
paint program!
Who said programming windows isn't fun :)
|