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 :) 
  
  
  
                                   
 |