Page 1 of 1

Intro to the Windows API Rate Topic: ****- 1 Votes

#1 Aphex19  Icon User is offline

  • Born again Pastafarian.
  • member icon

Reputation: 615
  • View blog
  • Posts: 1,873
  • Joined: 02-August 09

Posted 30 December 2010 - 01:29 AM

*
POPULAR

Welcome to my first tutorial on the Windows API, I hope it is in some way helpful.

The Windows API (aplication programming interface) is a very powerful framework when it is used correctly, however it it often considered by beginners as confusing and difficult to learn. This tutorial will go over the basics of setting up a window and displaying it to the user in the hopes that the Windows API will seem less intimidating to people who want to learn but have little or no knowledge of it.

What you will need for this tutorial
The code I write for this tutorial is designed for compilation in Visual C++ 2010, of which the Express edition is free to download and register, I'd recommend using that.

How to create a Windows API project in Visual C++ 2010
1) Go to file/new/project
2) When the "new project" dialog box shows, select a "Win32 project", choose a name and location for the project and then click the "Ok" button.
3) Click "next" in the next form.
4) Now, under "Additional options:", check the box that says, "Empty project", as we will be doing this from scratch.
5) Click finish.
6) Under the "Solution explorer" tab, right click "source files" directory and in the menu click add/new item. If you can't find the solution explorer, go to "view/solution explorer".
7) Select a "C++ file (.cpp)" and name it "WinMain.cpp", click add.
8) Now, finally we are going to tell Visual C++ to use the "Multi-byte character set". Go to project/<your project name> properties/configuration properties/general/character set/Use Multi-Byte Character Set. We use this character set so that we don't need to use macros when dealing with string literals.
9) Now we're read to code, yay!

A basic Windows API program
As you probably know, C++ console applications use an entry symbol called "main", which takes optional arguments and should return an integer. In contrast to this, Windows API programs use an entry symbol called "WinMain". Like "main", it is the first function to execute in a program, and also returns an integer, but "WinMain" has some arguments which are required for any Windows API program to compile.

The following code is about the most basic Windows API program you will ever see, it simply displays a message box to the user. All the code in this tutorial will be heavily commented to make it more clear.

#include <Windows.h>	/* The standard "windows.h" inclusion */

int WINAPI WinMain( HINSTANCE hInstance,		/* A handle to the current instance of the application. */
					HINSTANCE hPrevInstance,	/* A handle to the previous instance of the application. */
					LPSTR lpCmdLine,			/* The command line for the application, excluding the program name. */
					int nCmdShow)				/* Controls how the window is to be shown. */
{	
	/* Call to the MessageBox function */
	MessageBox(NULL, "Hello, Windows API!", "Hello", MB_OK | MB_ICONINFORMATION);

	/* WinMain returns 0 if we exit before we enter message loop, more on that to come */
	return 0;	
}


You can compile this by going to the build menu and clicking on "build" (or press F7), then you can run it by clicking the green arrow in Visual C++.

Most of the arguments that "WinMain" takes will likely never be used by you, probably the most important argument is called "hInstance", but you will see its use when we set up a window. Note that "WinMain" uses the "__stdcall" calling convention in Visual C++, which is what WINAPI is #defined as.

Creating a window
Creating, displaying and running a window requires alot more code, but a basic window isn't too hard to set up. I'll get straight to the code, here is the code that will display a window.

#include <Windows.h>	

/* Create nice looking controls */
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")

/* Name of our class and the title */
const char *clsName = "WinAPI";
char *title = "Windows API";

/* Global flag for our message loop */
bool running = true;

/* Handle to the window */
HWND hWnd = NULL;

/* A windows callback procedure. */
LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)									
	{
		/* Message created when the user tries to close the window */
		case WM_CLOSE:	
			DestroyWindow(hWnd);
			return 0;

        case WM_DESTROY:
            PostQuitMessage(0);
			running = false;
			return 0;

		default:
			return DefWindowProc(hWnd,uMsg,wParam,lParam);
	}
}

/* Entry point of our application */
int WINAPI WinMain( HINSTANCE hInstance,		/* A handle to the current instance of the application. */
					HINSTANCE hPrevInstance,	/* A handle to the previous instance of the application. */
					LPSTR lpCmdLine,			/* The command line for the application, excluding the program name. */
					int nCmdShow)				/* Controls how the window is to be shown. */
{			
	WNDCLASSEX	WndEx;								
	MSG			msg;

	WndEx.cbSize			= sizeof(WNDCLASSEX);					/* The size, in bytes, of this structure. */
	WndEx.style				= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;	/* The class style(s) */				
	WndEx.lpfnWndProc		= (WNDPROC)WndProc;						/* A pointer to the window procedure. */			
	WndEx.cbClsExtra		= 0;									/* The number of extra bytes to allocate following the window-class structure. */ 
	WndEx.cbWndExtra		= 0;									/* The number of extra bytes to allocate following the window instance. */
	WndEx.hIcon				= LoadIcon(NULL, IDI_APPLICATION);		/* A handle to the class icon. */
	WndEx.hCursor			= LoadCursor(NULL, IDC_ARROW);			/* A handle to the class cursor. */
	WndEx.hbrBackground		= NULL;									/* A handle to the class background brush. */
	WndEx.lpszMenuName		= NULL;									/* We're not using a menu here */
	WndEx.lpszClassName		= clsName;								/* A pointer to a string that contains the class name */	
	WndEx.hInstance			= hInstance;							/* A handle to the instance that contains the window procedure for the class. */ 			
	WndEx.hIconSm			= LoadIcon(NULL, IDI_APPLICATION);		/* A handle to a small icon that is associated with the window class */

	/* Register the windows class */
	if (!RegisterClassEx(&WndEx))
	{
		MessageBox(NULL, "Failed to register class", "ERROR", MB_OK | MB_IConerror);
		return 0;
	}

	/* Create the window */
	if (!(hWnd = CreateWindowEx(WS_EX_APPWINDOW | WS_EX_WINDOWEDGE, /* The extended window style */
								clsName,							/* A pointer to a string that contains the class name */
								title,								/* A pointer to a string that contains the title of the window */
								WS_OVERLAPPEDWINDOW |				/* The style of the window being created */
								WS_CLIPSIBLINGS | WS_CLIPCHILDREN,				
								CW_USEDEFAULT, CW_USEDEFAULT,		/* initial x,y position of the window */
								460, 340,							/* initial x,y size of the window */
								NULL,								/* A handle to the parent or owner window */
								NULL,								/* A handle to a menu */
								hInstance,							/* A handle to the instance of the window */
								NULL)))								/* lParam */
	{	
		MessageBox(NULL, "Failed to create the window", "ERROR", MB_OK | MB_IConerror);
		return 0;
	}

	/* The window is initially hidden, we need to show it */
	ShowWindow(hWnd, SW_SHOW);	

	/* The main message loop of our program */
	while(running)								
	{
		/* Are there any messages in the message queue? */
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))	
		{
			/* translate and dispatch the message */
			TranslateMessage(&msg);			
			DispatchMessage(&msg);			
		}
	}

	return msg.wParam;
}




Let's look at the steps taken to create that window.

Step 1: Create a set up a "WNDCLASSEX" structure.

	WndEx.cbSize			= sizeof(WNDCLASSEX);					
	WndEx.style				= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;				
	WndEx.lpfnWndProc		= (WNDPROC)WndProc;								
	WndEx.cbClsExtra		= 0;									
	WndEx.cbWndExtra		= 0;									
	WndEx.hIcon				= LoadIcon(NULL, IDI_APPLICATION);		
	WndEx.hCursor			= LoadCursor(NULL, IDC_ARROW);			
	WndEx.hbrBackground		= NULL;									
	WndEx.lpszMenuName		= NULL;									
	WndEx.lpszClassName		= clsName;									
	WndEx.hInstance			= hInstance;										
	WndEx.hIconSm			= LoadIcon(NULL, IDI_APPLICATION);	


This structure contains information about the windows class. It is used with the RegisterClassEx and describes various aspects of the window. It is heavily commented so i won't go too far into that.

Step 2: Register the class

	/* Register the windows class */
	if (!RegisterClassEx(&WndEx))
	{
		MessageBox(NULL, "Failed to register class", "ERROR", MB_OK | MB_IConerror);
		return 0;
	}


Pretty self explanitory, just registers the previously set up WNDCLASSEX structure with the class.

Step 3: Create the window

	/* Create the window */
	if (!(hWnd = CreateWindowEx(WS_EX_APPWINDOW | WS_EX_WINDOWEDGE, 
								clsName,							
								title,								
								WS_OVERLAPPEDWINDOW |				
								WS_CLIPSIBLINGS | WS_CLIPCHILDREN,				
								CW_USEDEFAULT, CW_USEDEFAULT,		
								460, 340,							
								NULL,							
								NULL,								
								hInstance,							
								NULL)))								
	{	
		MessageBox(NULL, "Failed to create the window", "ERROR", MB_OK | MB_IConerror);
		return 0;
	}


This creates the window and sets up some details about it, such as its position, size, style and it's parent (if any).

Step 4: Show the window

	/* The window is initially hidden, we need to show it */
	ShowWindow(hWnd, SW_SHOW);	


Displays the window

Step 5: The message loop

	/* The main message loop of our program */
	while(running)								
	{
		/* Are there any messages in the message queue? */
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))	
		{
			/* translate and dispatch the message */
			TranslateMessage(&msg);			
			DispatchMessage(&msg);			
		}
	}


This loop keeps running as long as the global "running" flag is set.

Now would be a good time to talk about the message loop. Whenever anything happens to your window, a message is sent. This might be a resize, a create message, a close message etc etc. This message needs to be caught and translated in order to be of any use, this is the job of our message loop, along with our callback procedure. The message loop catches the message and our callback procedure defines how we should act. Here is our callback procedure again.

/* A windows callback procedure. */
LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)									
	{
		/* Message created when the user tries to close the window */
		case WM_CLOSE:	
			DestroyWindow(hWnd);
			return 0;

        case WM_DESTROY:
            PostQuitMessage(0);
			running = false;
			return 0;

		default:
			return DefWindowProc(hWnd,uMsg,wParam,lParam);
	}
}


Notice that the message is passed to the function and it is decoded by by the switch statement. the "WM_<insert message name here>" defines are just values that reperesent a message.

Back to our message loop
The "PeekMessage" function checks whether there are any messages that need to be processed, if there are, then the message is translated and dispatched. In this process, the callback procedure is called so that any messages sent can be caught arbitrarily as described above.

That's about it for a very basic window, i hope this was informative.
Thanks

Is This A Good Question/Topic? 6
  • +

Replies To: Intro to the Windows API

#2 anonymous26  Icon User is offline

  • D.I.C Lover

Reputation: 0
  • View blog
  • Posts: 3,638
  • Joined: 26-November 10

Posted 20 January 2011 - 02:54 PM

Great job in keeping the code tidy as well! :)
Was This Post Helpful? 0
  • +
  • -

#3 Aphex19  Icon User is offline

  • Born again Pastafarian.
  • member icon

Reputation: 615
  • View blog
  • Posts: 1,873
  • Joined: 02-August 09

Posted 21 January 2011 - 05:07 AM

Thanks ButchDean, but in hindsight I probably went over the top with the comments, which makes the formatting on dreamincode's syntax highlighter look terrible.
Was This Post Helpful? 0
  • +
  • -

#4 anonymous26  Icon User is offline

  • D.I.C Lover

Reputation: 0
  • View blog
  • Posts: 3,638
  • Joined: 26-November 10

Posted 21 January 2011 - 12:54 PM

Or maybe put the comment above the line of code rather than next to. :)
Was This Post Helpful? 0
  • +
  • -

#5 mkb1995  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 4
  • Joined: 11-December 10

Posted 23 January 2011 - 09:46 PM

Interesting tutorial, Good work! :tup:
Was This Post Helpful? 0
  • +
  • -

#6 Aphex19  Icon User is offline

  • Born again Pastafarian.
  • member icon

Reputation: 615
  • View blog
  • Posts: 1,873
  • Joined: 02-August 09

Posted 04 March 2011 - 08:14 AM

My next tutorial can be found here, which details various methods of creating a menu.
Was This Post Helpful? 0
  • +
  • -

#7 ogadit  Icon User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 22
  • Joined: 31-July 12

Posted 14 December 2012 - 03:35 AM

Very Nice tutorial however there was one error that you wrote the error icon as IConerror you should've written it as IConerror only thats the problem other than that good tutorial! :rockon:
Was This Post Helpful? 0
  • +
  • -

#8 Offroad  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 31-January 13

Posted 31 January 2013 - 08:27 AM

View Postogadit, on 14 December 2012 - 03:35 AM, said:

Very Nice tutorial however there was one error that you wrote the error icon as IConerror you should've written it as IConerror only thats the problem other than that good tutorial! :rockon:/>


The dreamincode editor is autocorrecting iconerror for some reason. For us newbies, it should be in ALL CAPS: MB_I C O N E R R O R (without the spaces)

Great tutorial!
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1