www.xbdev.net xbdev - software development
Friday December 14, 2018
home | about | contact | Donations

     
 

Win32..

Using our Windows API's...

 

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

 

 

 

 

 
 Visitor: 9534626  { 209.237.238.175 } Copyright (c) 2002-2017 xbdev.net - All rights reserved.
Designated tutorial and software are the property of their respective owners.