0
Contents
- 1. Basics
- 1.1 Introduction
- 1.2 Simple Message
- 1.3 Simple Window
- 1.4 Text
- 1.5 Menus
- 1.5.1 Resources
- 1.5.2 On The Spot
- 1.6 Dialogs
- 1.7 Controls
- 2. Intermediate
- 2.1 Bitmaps
- 2.1.1 Resource
- 2.1.2 From a File
- 2.2 System Tray
- 2.3 Toolbars
- 2.3.1 Custom Buttons
- 2.3.2 Common Buttons
- 2.4 Statusbars
- Appendix
- A The Compiler
- B Tools
- C Links
- D Shoutouts
1. Basics
1.1 Introduction
First off if you are reading this tutorial, I am going to assume a few things. Because windows programming is "more advanced" than console programming I am going to have to assume you have a good grasp on C/C++. You need to know what #include's are and how to use them, you need to know how to use arrays and pointers, you need to know how to use switch() and case's, and you need to know what a typedef is. These are things this tutorial will not cover. I will format my code to my style, this to make it easier for me and you to read.
You are also going to need a C/C++ Compiler that supports Windows API. I used Borlands Free C/C++ 5.5 Compiler to compile all of these examples, basically because I am to cheap to get Microsoft Visual C++ and Borland is free alternative. For information on getting a free compiler see the Tools Appendix. For information on how to compile windows programs with the compiler see the Compiler Section.
I like to look at the code and compiled examples before I go over what it all means so after each example I will go over the code to clarify what it means.
1.2 Simple Message
Source - Screen Shot
This first example is here just to make sure that your compiler does support windows.
#include int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MessageBox (NULL, "Hello World" , "Hello", 0); return 0; } |
Put that code in a test file and compile it, any errors you get consult your compilers help files. Now lets go through the code.
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
WinMain() is the windows equivalent of main() in DOS and UNIX. This is where the program starts. WinMain() takes the following parameters:
- hInstance
- Identifies the programs current instance (the programs place in the memory)
- hPrevInstance
- Identifies the previous instance of the application. For a Win32-based application, this parameter is always NULL.
- lpCmdLine
- All of the command-line arguments in a string (does not include the program name)
- nCmdShow
- A value that can be passed to ShowWindow()
Many of the keywords and types have windows specific definitions, like UINT is unsigned int and LPSTR is char *, this is intended to increase portability. If you are more comfortable using char * instead of LPSTR, feel free to do so.
MessageBox() is the function that pops up a message box, hence the name. MessageBox() takes the following parameters:
- hWnd
- lpText
- lpCaption
- uType
- Identifies the owner window of the message box to be created. If this parameter is NULL, the message box has no owner window.
- This is the text contained in the window.
- This is the text contained in the titlebar of the window.
- This is the style of the message box (like if its an error, warning, etc. and what buttons should appear. Setting this to 0 will add no icon and just a Ok button)
Source - Screen Shot
On of the first questions that plagues a new windows programmer is "How Do I make A Window?" Well I am afraid that the answer is not entirely simple. Here is the source for a simple window.
#include LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); static char gszClassName[] = "MyWindowClass"; static HINSTANCE ghInstance = NULL; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX WndClass; HWND hwnd; MSG Msg; ghInstance = hInstance; WndClass.cbSize = sizeof(WNDCLASSEX); WndClass.style = NULL; WndClass.lpfnWndProc = WndProc; WndClass.cbClsExtra = 0; WndClass.cbWndExtra = 0; WndClass.hInstance = ghInstance; WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); WndClass.hCursor = LoadCursor(NULL, IDC_ARROW); WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); WndClass.lpszMenuName = NULL; WndClass.lpszClassName = gszClassName; WndClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if(!RegisterClassEx(&WndClass)) { MessageBox(0, "Window Registration Failed!", "Error!", MB_ICONSTOP | MB_OK); return 0; } hwnd = CreateWindowEx( WS_EX_STATICEDGE, gszClassName, "Windows Title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 320, 240, NULL, NULL, ghInstance, NULL); if(hwnd == NULL) { MessageBox(0, "Window Creation Failed!", "Error!", MB_ICONSTOP | MB_OK); return 0; } ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); while(GetMessage(&Msg, NULL, 0, 0)) { TranslateMessage(&Msg); DispatchMessage(&Msg); } return Msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) { switch(Message) { case WM_CLOSE: DestroyWindow(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, Message, wParam, lParam); } return 0; } |
As you can see its a good 70 or so lines to make a simple window.
Windows Programs unlike DOS or UNIX programs are event driven. Windows programs generaly stay idle untill they recive a message, they act on that message, and then wait for the next one. When a windows program does respond to a message its called handling the message.
As you see I decalred my WiinProc function so that it can be used later on. I decalerd my variables (I prfer to use Hungarian Notation, but you can use what you want).
Inside the function WiNmain() we see some new variables. These are WndClass, hwnd, and msg. The WndClass is the structure that holds all of the widnows class information which is later passed to RegisterClassEx(). The hwnd structure identifies the window. The msg structure contains message information from a thread's message queue. The next thing we come to is when the hInstance is made global in the variable ghInstance. After that we come to the defining of each of the WndClass settings:
- cbSize
- Is the size (in bytes) of this structure. Always set this to sizeof(WNDCLASSEX).
- style
- The Class style sof the window. Usually net to NULL.
- lpfnWndProc
- This points to the windows callback procedure.
- cbClsExtra
- Amount of extra data allocated for this class in memory. Usually 0.
- cbWndExtra
- Amount of extra data allocated in memory per window. Usually 0.
- hInstance
- This is the handle for the window instance.
- hIcon
- The icon shown when the user presses Alt-Tab. We will worry about this more when we get into resources.
- hCursor
- The cursor that will be displayed when the mouse is over our window. We will worry about this more when we get into resources.
- hbrBackground
- The brush to set the color of our window.
- lpszMenuName
- Name of the menu resource to use. We will worry about this more when we get into resources.
- lpszClassName
- Name to identify the class with.
- hIconSm
- The small icon shown in the taskbar and in the top-left corner. We will worry about this more when we get into resources.
- dwExStyle
- This tells it what style of window we want (In the example I used a plain static window)
- lpClassName
- This points to our class name we came up with earlier.
- lpWindowName
- This is the text that will apear in the title bar of out window.
- dwStyle
- This tells tells what style of window we are creating.
- x
- The inital horizontal starting position of the window (Set this to CW_USEDEFAULT if you want windows to pick a place)
- y
- The inital verticle starting position of the window (Set this to CW_USEDEFAULT if you want windows to pick a place)
- nWidth
- The width of the window (In pixels)
- nHeight
- The heighth of the window (In pixels)
- hWndParent
- The handle of the parent window (If one does not exist this is NULL)
- hMenu
- I dentifies a menu for the window (Only appleys if its a child window)
- hInstance
- Points to the hInstance of the window.
- lpParam
- Points to a value passed to the window through the CREATESTRUCT structure (I have never found a use for this, but I am sure one exists)
Now we get to probably the most important part of the program. The CallBack Procedure. What this is is a function with a giant switch() statment to switch off what each message should do (Sometimes this is a bunch of if's). Our callback takes the following parameters:
- hwnd
- Identifies the window.
- uMsg
- Specifies the message.
- wParam
- Specifies additional message information. The contents of this parameter depend on the value of the uMsg parameter.
- lParam
- Specifies additional message information. The contents of this parameter depend on the value of the uMsg parameter.
- hWnd
- Identifies the window to be destroyed.
- nExitCode
- Specifies an application exit code. This value is used as the wParam parameter of the WM_QUIT message.
Source - Screen Shot
Now most windows programmers (including myself) never memorize much of that. We usually take a simple window application like that and use it as a skelaton for all of our new applications, adding what we need and changning things. This is what I am going to do throughout the entire tutorial. I will take this code form the last section and I will reuse it in all outher applications that have a window. Now next up how do we write text to our window? This to is not as simple as it may seem.
#include LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); static char gszClassName[] = "MyWindowClass"; static HINSTANCE ghInstance = NULL; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX WndClass; HWND hwnd; MSG Msg; ghInstance = hInstance; WndClass.cbSize = sizeof(WNDCLASSEX); WndClass.style = NULL; WndClass.lpfnWndProc = WndProc; WndClass.cbClsExtra = 0; WndClass.cbWndExtra = 0; WndClass.hInstance = ghInstance; WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); WndClass.hCursor = LoadCursor(NULL, IDC_ARROW); WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); WndClass.lpszMenuName = NULL; WndClass.lpszClassName = gszClassName; WndClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if(!RegisterClassEx(&WndClass)) { MessageBox(0, "Window Registration Failed!", "Error!", MB_ICONSTOP | MB_OK); return 0; } hwnd = CreateWindowEx( WS_EX_STATICEDGE, gszClassName, "Windows Title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 320, 240, NULL, NULL, ghInstance, NULL); if(hwnd == NULL) { MessageBox(0, "Window Creation Failed!", "Error!", MB_ICONSTOP | MB_OK); return 0; } ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); while(GetMessage(&Msg, NULL, 0, 0)) { TranslateMessage(&Msg); DispatchMessage(&Msg); } return Msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; LPSTR szMessage = "Hello World"; switch(Message) { case WM_PAINT: hdc = BeginPaint(hwnd, &ps); TextOut(hdc, 70, 50, szMessage, strlen(szMessage)); EndPaint(hwnd, &ps); break; case WM_CLOSE: DestroyWindow(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, Message, wParam, lParam); } return 0; } |
This prints Hello World out on the screen, not very interesting I know, but we are learning. First to exapling what I added. At the top of the WinProc I added a few variables. hdc Identifies the display DC to be used for painting. ps is our paint structure the PAINTSTRUCT structure contains information for an application. This information can be used to paint the client area of a window owned by that application. message is just a simple character array that hold our message.
After that we can see i also added another case. Remember when I said above in Section 1.3 how the UpdateWidnow() send the message WM_PAINT, well here we can use it. First we tell hdc to recive painting information from BeginPaint(). BeginPaint() takes the following parameters:
- hwnd
- Identifies the window to be painted.
- pPaint
- Pointer to the PAINTSTRUCT structure that will receive painting information.
- hdc
- Identifies the device context.
- nXStart
- Specifies the logical x-coordinate of the reference point that Windows uses to align the string.
- nYStart
- Specifies the logical y-coordinate of the reference point that Windows uses to align the string.
- lpString
- Points to the string to be printe dout (The string does not have to be \0 terminated, because cbString is the length)
- cbString
- Specifies the number of characters in the string.
- hwnd
- Identifies the window that was painted.
- lpPaint
- Points to a PAINTSTRUCT structure that contains the painting information retrieved by BeginPaint.
When programming there are usually a few ways to do things, making a windows menu is no different. I am going to go over the two main ways you can make a menu. I will have each one make the same menu but it will show you different ways of doing things.
1.5.1 Resources
Source - Screen Shot
Resources is probably the simplest way of making a menu. The menu is predefined in a resource file (always something.rc). The resource files are compiled by the resource compiler and linked to the program when the linker runs. This is the first program where I am also going to throw other files besides the source files in. There will be a resource file (*.rc) and a header file (*.h) in the zip along with a make file. Type make when you are in the directory of the source and Borlands should compile and link it all in the right order.
#define ID_FILE_NEW 1000 #define ID_FILE_OPEN 1001 #define ID_FILE_SAVE 1002 #define ID_FILE_EXIT 1003 #define ID_DO_SOMETHING 1004 #define ID_DO_SOMETHING_ELSE 1005 #define ID_HELP_ABOUT 1006 |
That is our header file. Here we just define all of the id's we are going to use so that we can just include this in both the resource file and the source file.
#include "section_1_5_1.h" ID_MENU MENU DISCARDABLE BEGIN POPUP "&File" BEGIN MENUITEM "&New", ID_FILE_NEW MENUITEM "&Open", ID_FILE_OPEN MENUITEM "&Save", ID_FILE_SAVE MENUITEM SEPARATOR MENUITEM "E&xit", ID_FILE_EXIT END POPUP "&Do" BEGIN MENUITEM "&Something", ID_DO_SOMETHING MENUITEM "Something &Else", ID_DO_SOMETHING_ELSE END POPUP "&Help" BEGIN MENUITEM "&About", ID_HELP_ABOUT END END |
This is our resource file. Here we are derfining the layout of the menus and giving them message id's. These message id's have to be defined in both the resource file and the source file. This is why we use a header file to define them and then we just include the header file.
#include #include "section_1_5_1.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); static char gszClassName[] = "MyWindowClass"; static HINSTANCE ghInstance = NULL; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX WndClass; HWND hwnd; MSG Msg; ghInstance = hInstance; WndClass.cbSize = sizeof(WNDCLASSEX); WndClass.style = NULL; WndClass.lpfnWndProc = WndProc; WndClass.cbClsExtra = 0; WndClass.cbWndExtra = 0; WndClass.hInstance = ghInstance; WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); WndClass.hCursor = LoadCursor(NULL, IDC_ARROW); WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); WndClass.lpszMenuName = "ID_MENU"; WndClass.lpszClassName = gszClassName; WndClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if(!RegisterClassEx(&WndClass)) { MessageBox(0, "Window Registration Failed!", "Error!", MB_ICONSTOP | MB_OK); return 0; } hwnd = CreateWindowEx( WS_EX_STATICEDGE, gszClassName, "Windows Title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 320, 240, NULL, NULL, ghInstance, NULL); if(hwnd == NULL) { MessageBox(0, "Window Creation Failed!", "Error!", MB_ICONSTOP | MB_OK); return 0; } ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); while(GetMessage(&Msg, NULL, 0, 0)) { TranslateMessage(&Msg); DispatchMessage(&Msg); } return Msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) { switch(Message) { case WM_CLOSE: DestroyWindow(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; case WM_COMMAND: switch(LOWORD(wParam)) { case ID_FILE_NEW: MessageBox(hwnd, "New File", "Menu", 0); break; case ID_FILE_OPEN: MessageBox(hwnd, "Open File", "Menu", 0); break; case ID_FILE_SAVE: MessageBox(hwnd, "Save File", "Menu", 0); break; case ID_FILE_EXIT: PostQuitMessage(0); case ID_DO_SOMETHING: MessageBox(hwnd, "Do Something", "Menu", 0); break; case ID_DO_SOMETHING_ELSE: MessageBox(hwnd, "Do Something Else", "Menu", 0); break; case ID_HELP_ABOUT: MessageBox(hwnd, "Written By AZTEK", "About", 0); break; } break; default: return DefWindowProc(hwnd, Message, wParam, lParam); } return 0; } |
You see we changed the WndClass.lpszMenuName to what the name for our menu is. Then we added WM_COMMAND. This is the message that is posted when the user clicks a menu item. The ID of the item is sent with the message. We use LOWORD() to get the message from the lower owrd of the 32-bit. LOWORD() takes the following parameter:
- dwValue
- The value to get the low word from.
Source - Screen Shot
Using resource menus are great if you have a menu that won't change. Some applications although require menus that can be made on the spot. This might be used to add or delete items from the menu or to make some items gray.
#define ID_FILE_NEW 1000 #define ID_FILE_OPEN 1001 #define ID_FILE_SAVE 1002 #define ID_FILE_EXIT 1003 #define ID_DO_SOMETHING 1004 #define ID_DO_SOMETHING_ELSE 1005 #define ID_HELP_ABOUT 1006 |
Since we are making an identicle program we will use the same header file as above.
#include #include "section_1_5_2.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); static char gszClassName[] = "MyWindowClass"; static HINSTANCE ghInstance = NULL; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX WndClass; HWND hwnd; MSG Msg; ghInstance = hInstance; WndClass.cbSize = sizeof(WNDCLASSEX); WndClass.style = NULL; WndClass.lpfnWndProc = WndProc; WndClass.cbClsExtra = 0; WndClass.cbWndExtra = 0; WndClass.hInstance = ghInstance; WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); WndClass.hCursor = LoadCursor(NULL, IDC_ARROW); WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); WndClass.lpszMenuName = NULL; WndClass.lpszClassName = gszClassName; WndClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if(!RegisterClassEx(&WndClass)) { MessageBox(0, "Window Registration Failed!", "Error!", MB_ICONSTOP | MB_OK); return 0; } hwnd = CreateWindowEx( WS_EX_STATICEDGE, gszClassName, "Windows Title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 320, 240, NULL, NULL, ghInstance, NULL); if(hwnd == NULL) { MessageBox(0, "Window Creation Failed!", "Error!", MB_ICONSTOP | MB_OK); return 0; } ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); while(GetMessage(&Msg, NULL, 0, 0)) { TranslateMessage(&Msg); DispatchMessage(&Msg); } return Msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) { HMENU hMenu, hSubMenu; switch(Message) { case WM_CLOSE: DestroyWindow(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; case WM_CREATE: hMenu = CreateMenu(); hSubMenu = CreatePopupMenu(); AppendMenu(hSubMenu, MF_STRING, ID_FILE_NEW, "&New"); AppendMenu(hSubMenu, MF_STRING, ID_FILE_OPEN, "&Open"); AppendMenu(hSubMenu, MF_STRING, ID_FILE_SAVE, "&Save"); AppendMenu(hSubMenu, MF_SEPARATOR, 0, 0); AppendMenu(hSubMenu, MF_STRING, ID_FILE_EXIT, "E&xit"); AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&File"); hSubMenu = CreatePopupMenu(); AppendMenu(hSubMenu, MF_STRING, ID_DO_SOMETHING, "&Something"); AppendMenu(hSubMenu, MF_STRING, ID_DO_SOMETHING_ELSE, "Something &Else"); AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&Do"); hSubMenu = CreatePopupMenu(); AppendMenu(hSubMenu, MF_STRING, ID_HELP_ABOUT, "&About"); AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&Help"); SetMenu(hwnd, hMenu); break; case WM_COMMAND: switch(LOWORD(wParam)) { case ID_FILE_NEW: MessageBox(hwnd, "New File", "Menu", 0); break; case ID_FILE_OPEN: MessageBox(hwnd, "Open File", "Menu", 0); break; case ID_FILE_SAVE: MessageBox(hwnd, "Save File", "Menu", 0); break; case ID_FILE_EXIT: PostQuitMessage(0); case ID_DO_SOMETHING: MessageBox(hwnd, "Do Something", "Menu", 0); break; case ID_DO_SOMETHING_ELSE: MessageBox(hwnd, "Do Something Else", "Menu", 0); break; case ID_HELP_ABOUT: MessageBox(hwnd, "Written By AZTEK", "About", 0); break; } break; default: return DefWindowProc(hwnd, Message, wParam, lParam); } return 0; } |
At the top you may notice I set WndClass.lpszMenuName back to NULL. Thats because we are making the menu on the spot and don't need the resource file. First you might notice we added WM_CREATE. This is the message sent when the window is first created. We also declared two varibales of the type HMENU. You see that we set hMenu to CreateMenu(). This will start the whole menu. Then you see we set hSubMenu to CreatePopupMenu(). This will make a blank individual sub menu. We need to fill it. You see we call AppendMenu() to add items to our menus. AppendMenu() takes the following parameters:
- hMenu
- Identifies the menu bar.
- uFlags
- Specifies flags to control the appearance and behavior of the new menu item. This parameter can be a combination of values.
- uIDNewItem
- Specifies either the identifier of the new menu item or, if the uFlags parameter is set to MF_POPUP, the handle to the drop-down menu or submenu.
- lpNewItem
- Specifies the content of the new menu item.
- hWnd
- Identifies the window to which the menu is to be assigned.
- hMenu
- Identifies the new menu. If this parameter is NULL, the window's current menu is removed.
Source - Screen Shot
Dialogs are a special kind of windows message box where you have a lot more control than just a title and some text. In dialogs you can have edit boxes, check boxes, icons, bitmaps, change colors, etc. It is easiest to design dialog boxes using a resource editor, you can find some in the Tools Section.
#define ID_FILE_EXIT 1000 #define ID_HELP_ABOUT 1001 #define IDOK 2000 #define IDEMAIL 2001 #define IDAZTEK 2003 #define IDBSRF 2004 |
This is our header file.
#include "section_1_6.h" ABOUTDLG DIALOG 19, 17, 182, 71 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "About" FONT 8, "MS Sans Serif" BEGIN CTEXT "Written by AZTEK", 101, 17, 30, 81, 11 GROUPBOX "About", 102, 11, 11, 95, 48, WS_TABSTOP DEFPUSHBUTTON "&Ok", IDOK, 112, 6, 64, 14 PUSHBUTTON "&E-mail AZTEK", IDEMAIL, 112, 21, 64, 14 PUSHBUTTON "Visit &AZTEK", IDAZTEK, 112, 36, 64, 14 PUSHBUTTON "Visit &Blacksun", IDBSRF, 112, 51, 64, 14 END ID_MENU MENU BEGIN POPUP "&File" BEGIN MENUITEM "E&xit", ID_FILE_EXIT END POPUP "&Help" BEGIN MENUITEM "&About", ID_HELP_ABOUT END END |
This is our resource file. Next the ABOUTDLG DIALOG there are the diameters of our popup dialog box. The next line decalres some styles for the dialog. The third line is the caption or what will appear in the title bar of our window. The FONT 8, "MS Sans Serif" just states 8 point type in the font MS Sans Serif. CTEXT starts some standard text for our dialog. Group box just adds the frame aorund the text. Then DEFPUSHBUTTON decalres the default push button. The rest of the PUSHBUTTON's decalre normal pushable buttons. Now for our source file.
#include #include "section_1_6.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); BOOL CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM); static char gszClassName[] = "MyWindowClass"; static HINSTANCE ghInstance = NULL; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX WndClass; HWND hwnd; MSG Msg; ghInstance = hInstance; WndClass.cbSize = sizeof(WNDCLASSEX); WndClass.style = NULL; WndClass.lpfnWndProc = WndProc; WndClass.cbClsExtra = 0; WndClass.cbWndExtra = 0; WndClass.hInstance = ghInstance; WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); WndClass.hCursor = LoadCursor(NULL, IDC_ARROW); WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); WndClass.lpszMenuName = NULL; WndClass.lpszClassName = gszClassName; WndClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if(!RegisterClassEx(&WndClass)) { MessageBox(0, "Window Registration Failed!", "Error!", MB_ICONSTOP | MB_OK); return 0; } hwnd = CreateWindowEx( WS_EX_STATICEDGE, gszClassName, "Windows Title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 320, 240, NULL, NULL, ghInstance, NULL); if(hwnd == NULL) { MessageBox(0, "Window Creation Failed!", "Error!", MB_ICONSTOP | MB_OK); return 0; } ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); while(GetMessage(&Msg, NULL, 0, 0)) { TranslateMessage(&Msg); DispatchMessage(&Msg); } return Msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) { switch(Message) { case WM_COMMAND: switch(LOWORD(wParam)) { case ID_FILE_EXIT: PostMessage(hwnd, WM_CLOSE, 0, 0); break; case ID_HELP_ABOUT: DialogBox(ghInstance, "ABOUTDLG", hwnd, DlgProc); break; } break; case WM_CLOSE: DestroyWindow(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, Message, wParam, lParam); } return 0; } BOOL CALLBACK DlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) { switch(Message) { case WM_INITDIALOG: return TRUE; case WM_COMMAND: switch(LOWORD(wParam)) { case IDOK: EndDialog(hwnd, IDOK); return TRUE; case IDEMAIL: ShellExecute(hwnd, "open", "mailto:aztek@faction7.com", 0, 0, 0); EndDialog(hwnd, IDEMAIL); return TRUE; case IDAZTEK: ShellExecute(hwnd, "open", "http://aztek.faction7.com", 0, 0, 0); EndDialog(hwnd, IDAZTEK); return TRUE; case IDBSRF: ShellExecute(hwnd, "open", "http://blacksun.box.sk", 0, 0, 0); EndDialog(hwnd, IDBSRF); return TRUE; } break; } return FALSE; } |
You will notice I added a new function, DlgProc(). This is known as the Dailog Procedure. This function although it returns the Boolen type takes the same parameters as WndProc(). This function is alot like the WndProc() function, it recives and acts on the messages for the dialog. We call DialogBox() to create the dialog. DialogBox() takes the following parameters:
- hInstance
- Handle to the windows instance.
- lpTemplate
- Identifies the dialog box template. Usually a null-terminated string.
- hWndParent
- Identifies the parent window of the dialog box.
- pDialogFunc
- Points to the window procedure for the Dialog Box
- hDlg
- Identifies the dialog box to be destroyed.
- nResult
- Specifies the value to be returned to the application.
- hwnd
- Specifies a parent window. This window receives any message boxes that an application produces.
- lpOperation
- Pointer to a null-terminated string that specifies the operation to perform.
- lpFile
- Pointer to a null-terminated string that specifies the file to open or print or the folder to open or explore.
- lpParameters
- If lpFile specifies an executable file, lpParameters is a pointer to a null-terminated string that specifies parameters to be passed to the application.
- lpDirectory
- Pointer to a null-terminated string that specifies the default directory.
- nShowCmd
- If lpFile specifies an executable file, nShowCmd specifies how the application is to be shown when it is opened.
Source - Screen Shot
Controls are just child windows like buttons, checkboxess, edit boxes, etc. For each control we make a new window using CreateWindowEx() but these are childs of the first main window and not new windows.
#include LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); static char gszClassName[] = "MyWindowClass"; static HINSTANCE ghInstance = NULL; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX WndClass; HWND hwnd; MSG Msg; ghInstance = hInstance; WndClass.cbSize = sizeof(WNDCLASSEX); WndClass.style = NULL; WndClass.lpfnWndProc = WndProc; WndClass.cbClsExtra = 0; WndClass.cbWndExtra = 0; WndClass.hInstance = ghInstance; WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); WndClass.hCursor = LoadCursor(NULL, IDC_ARROW); WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); WndClass.lpszMenuName = NULL; WndClass.lpszClassName = gszClassName; WndClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if(!RegisterClassEx(&WndClass)) { MessageBox(0, "Window Registration Failed!", "Error!", MB_ICONSTOP | MB_OK); return 0; } hwnd = CreateWindowEx( WS_EX_STATICEDGE, gszClassName, "Windows Title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 320, 240, NULL, NULL, ghInstance, NULL); if(hwnd == NULL) { MessageBox(0, "Window Creation Failed!", "Error!", MB_ICONSTOP | MB_OK); return 0; } ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); while(GetMessage(&Msg, NULL, 0, 0)) { TranslateMessage(&Msg); DispatchMessage(&Msg); } return Msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) { HWND hButton, hCombo, hEdit, hList, hScroll, hStatic; switch(Message) { case WM_CREATE: hButton = CreateWindowEx( NULL, "Button", "Button", WS_BORDER | WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 0, 0, 100, 30, hwnd, NULL, ghInstance, NULL); hCombo = CreateWindowEx( NULL, "ComboBox", "", WS_BORDER | WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST, 0, 30, 100, 100, hwnd, NULL, ghInstance, NULL); hEdit = CreateWindowEx( NULL, "Edit", "Edit", WS_BORDER | WS_CHILD | WS_VISIBLE, 0, 60, 100, 30, hwnd, NULL, ghInstance, NULL); hList = CreateWindowEx( NULL, "ListBox", "", WS_BORDER | WS_CHILD | WS_VISIBLE, 100, 0, 100, 200, hwnd, NULL, ghInstance, NULL); hScroll = CreateWindowEx( NULL, "ScrollBar", "", WS_BORDER | WS_CHILD | WS_VISIBLE | SBS_VERT, 210, 0, 100, 200, hwnd, NULL, ghInstance, NULL); hStatic = CreateWindowEx( NULL, "Static", "", WS_BORDER | WS_CHILD | WS_VISIBLE | SS_BLACKRECT, 0, 90, 100, 30, hwnd, NULL, ghInstance, NULL); break; case WM_CLOSE: DestroyWindow(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, Message, wParam, lParam); } return 0; } |
In this example add some very simple controls to this window. There are many other controls you can use but these are the most simple and easy ones to add. Lokk in your windows programming help files for how to recive and send messages to these controls we added.
2. Intermediate
2.1 Bitmaps
In this section I will discus how to display a bitmap file in your program. The first way I will discus is the static way using a resource. The second way is to do it dynamicly using a actual bitmap file. Throughout our examples here I will use this bitmap file.
2.1.1 Resource
Source - Screen Shot
Using a bitmap from a resource file is probbaly the best way to do it unless you need the expandibility of a dynamic bitmap file.
#define IDB_BITMAP1 1000 |
This is are header file.
#include "section_2_1_1.h" IDB_BITMAP1 BITMAP "bitmap.bmp" |
Now for our source code.
#include #include "section_2_1_1.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); static char gszClassName[] = "MyWindowClass"; static HINSTANCE ghInstance = NULL; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX WndClass; HWND hwnd; MSG Msg; ghInstance = hInstance; WndClass.cbSize = sizeof(WNDCLASSEX); WndClass.style = NULL; WndClass.lpfnWndProc = WndProc; WndClass.cbClsExtra = 0; WndClass.cbWndExtra = 0; WndClass.hInstance = ghInstance; WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); WndClass.hCursor = LoadCursor(NULL, IDC_ARROW); WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); WndClass.lpszMenuName = NULL; WndClass.lpszClassName = gszClassName; WndClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if(!RegisterClassEx(&WndClass)) { MessageBox(0, "Window Registration Failed!", "Error!", MB_ICONSTOP | MB_OK); return 0; } hwnd = CreateWindowEx( WS_EX_STATICEDGE, gszClassName, "Windows Title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 320, 240, NULL, NULL, ghInstance, NULL); if(hwnd == NULL) { MessageBox(0, "Window Creation Failed!", "Error!", MB_ICONSTOP | MB_OK); return 0; } ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); while(GetMessage(&Msg, NULL, 0, 0)) { TranslateMessage(&Msg); DispatchMessage(&Msg); } return Msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) { HBITMAP hBitmap; HWND hBmpStat; switch(Message) { case WM_CREATE: hBitmap = LoadBitmap(ghInstance, MAKEINTRESOURCE(IDB_BITMAP1)); hBmpStat = CreateWindowEx( NULL, "Static", "", WS_VISIBLE | WS_CHILD | SS_BITMAP, 0, 0, 0, 0, hwnd, NULL, ghInstance, NULL); SendMessage(hBmpStat, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM) hBitmap); break; case WM_CLOSE: DestroyWindow(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, Message, wParam, lParam); } return 0; } |
We use a new resource type BITMAP in our resource file. We use the type HBITMAP to define a variable to hold all of the information for loading the bitmap. We give hBitmap the value returned by LoadBitmap(). The function LoadBitmap() takes the following parameters:
- hInstance
- The instance of the current window.
- lpszBitmapName
- Points to a null-terminated string that contains the name of the bitmap resource to be loaded. We will use the macro MAKEINTRESOURCE()
- wInteger
- Specifies the integer value to be converted.
- hWnd
- Identifies the window whose window procedure will receive the message.
- Msg
- Specifies the message to be sent.
- wParam
- Specifies additional message-specific information.
- lParam
- Specifies additional message-specific information.
Source - Screen Shot
Loading a bitmap from a file is alot like loading one from a resource, in fact there is actually only 1 line change.
#include LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); static char gszClassName[] = "MyWindowClass"; static HINSTANCE ghInstance = NULL; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX WndClass; HWND hwnd; MSG Msg; ghInstance = hInstance; WndClass.cbSize = sizeof(WNDCLASSEX); WndClass.style = NULL; WndClass.lpfnWndProc = WndProc; WndClass.cbClsExtra = 0; WndClass.cbWndExtra = 0; WndClass.hInstance = ghInstance; WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); WndClass.hCursor = LoadCursor(NULL, IDC_ARROW); WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); WndClass.lpszMenuName = NULL; WndClass.lpszClassName = gszClassName; WndClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if(!RegisterClassEx(&WndClass)) { MessageBox(0, "Window Registration Failed!", "Error!", MB_ICONSTOP | MB_OK); return 0; } hwnd = CreateWindowEx( WS_EX_STATICEDGE, gszClassName, "Windows Title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 320, 240, NULL, NULL, ghInstance, NULL); if(hwnd == NULL) { MessageBox(0, "Window Creation Failed!", "Error!", MB_ICONSTOP | MB_OK); return 0; } ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); while(GetMessage(&Msg, NULL, 0, 0)) { TranslateMessage(&Msg); DispatchMessage(&Msg); } return Msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) { HBITMAP hBitmap; HWND hBmpStat; switch(Message) { case WM_CREATE: hBitmap = (HBITMAP) LoadImage(NULL, "bitmap.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); hBmpStat = CreateWindowEx( NULL, "Static", "", WS_VISIBLE | WS_CHILD | SS_BITMAP, 0, 0, 0, 0, hwnd, NULL, ghInstance, NULL); SendMessage(hBmpStat, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM) hBitmap); break; case WM_CLOSE: DestroyWindow(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, Message, wParam, lParam); } return 0; } |
You can see we only changed 1 line from LoadBitmap() to LoadImage(). The function LoadImage() takes the following parameters:
- hInstance
- The instance of the current procedure.
- lpszName
- Identifies the image to load.
- uType
- Specifies the type of image to be loaded.
- cxDesired
- Specifies the width, if this parameter is zero and LR_DEFAULTSIZE is not used, the function uses the actual image width.
- cyDesired
- Specifies the height, if this parameter is zero and LR_DEFAULTSIZE is not used, the function uses the actual image width.
- fuLoad
- Specifies additional flags for the image.
Source - Screen Shot
The "Big Thing" for windows programs right now seems to be to put a icon in the system tray. In this next example we will go over putting a icon in in the system tray and having a menu pop up when the user right clicks on the icon.
#include "section_2_2.h" IDI_ICON1 ICON "icon.ico" |
Here is our resource file.
#define WM_TRAYNOTIFY (WM_USER + 1000) #define IDI_ICON1 1000 #define IDC_TRAYICON 1001 #define IDM_EXIT 1002 #define IDM_ABOUT 1003 |
Our header file.
#include #include "section_2_2.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); static char gszClassName[] = "MyWindowClass"; static HINSTANCE ghInstance = NULL; HMENU hMenu; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX WndClass; HWND hwnd; MSG Msg; ghInstance = hInstance; WndClass.cbSize = sizeof(WNDCLASSEX); WndClass.style = NULL; WndClass.lpfnWndProc = WndProc; WndClass.cbClsExtra = 0; WndClass.cbWndExtra = 0; WndClass.hInstance = ghInstance; WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); WndClass.hCursor = LoadCursor(NULL, IDC_ARROW); WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); WndClass.lpszMenuName = NULL; WndClass.lpszClassName = gszClassName; WndClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if(!RegisterClassEx(&WndClass)) { MessageBox(0, "Window Registration Failed!", "Error!", MB_ICONSTOP | MB_OK); return 0; } hwnd = CreateWindowEx( WS_EX_STATICEDGE, gszClassName, "Windows Title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 320, 240, NULL, NULL, ghInstance, NULL); if(hwnd == NULL) { MessageBox(0, "Window Creation Failed!", "Error!", MB_ICONSTOP | MB_OK); return 0; } ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); while(GetMessage(&Msg, NULL, 0, 0)) { TranslateMessage(&Msg); DispatchMessage(&Msg); } return Msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) { NOTIFYICONDATA notifyIconData; switch(Message) { case WM_CREATE: notifyIconData.cbSize = sizeof(NOTIFYICONDATA); notifyIconData.hWnd = hwnd; notifyIconData.uID = IDC_TRAYICON; notifyIconData.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; notifyIconData.uCallbackMessage = WM_TRAYNOTIFY; notifyIconData.hIcon = (HICON) LoadImage( ghInstance, MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, 16, 16, NULL); lstrcpyn(notifyIconData.szTip, "Tray Icon", sizeof(notifyIconData.szTip)); Shell_NotifyIcon(NIM_ADD, ¬ifyIconData); hMenu = CreatePopupMenu(); AppendMenu(hMenu, MF_STRING, IDM_EXIT, "E&xit"); AppendMenu(hMenu, MF_SEPARATOR, NULL, NULL); AppendMenu(hMenu, MF_STRING, IDM_ABOUT, "&About"); break; case WM_TRAYNOTIFY: switch(wParam) { case IDC_TRAYICON: switch(lParam) { case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: POINT point; GetCursorPos(&point); SetForegroundWindow(hwnd); TrackPopupMenu(hMenu, TPM_RIGHTALIGN, point.x, point.y, NULL, hwnd, NULL); SendMessage(hwnd, WM_NULL, NULL, NULL); break; } break; } break; case WM_COMMAND: switch(wParam) { case IDM_EXIT: SendMessage(hwnd, WM_CLOSE, NULL, NULL); break; case IDM_ABOUT: MessageBox( hwnd, "Example of a windows system tray program\r\nWritten by AZTEK", "About", NULL); break; break; } break; case WM_CLOSE: DestroyWindow(hwnd); break; case WM_DESTROY: notifyIconData.cbSize = sizeof(NOTIFYICONDATA); notifyIconData.hWnd = hwnd; notifyIconData.uID = IDC_TRAYICON; Shell_NotifyIcon(NIM_DELETE, ¬ifyIconData); DestroyMenu(hMenu); PostQuitMessage(0); break; default: return DefWindowProc(hwnd, Message, wParam, lParam); } return 0; } |
You see we use a new resource type ICON. In our header file we define our messages. We use the NOTIFYICONDATA structure to hold the data that initilize the tray icon. The structure NOTIFYICONDATA has the following settings:
- cbSize
- Size of the NOTIFYICONDATA structure.
- hWnd
- Handle of the window that receives notification messages associated with an icon in the taskbar status area.
- uID
- Application-defined identifier of the taskbar icon.
- uFlags
- Array of flags that indicate which of the other members contain valid data.
- uCallbackMessage
- Application-defined message identifier. The system uses the specified identifier for notification messages that it sends to the window identified by hWnd whenever a mouse event occurs in the bounding rectangle of the icon.
- hIcon
- Handle of the icon to add, modify, or delete.
- szTip
- Tooltip text to display for the icon.
- dwMessage
- Identifier of the message to send.
- pnid
- Pointer to a NOTIFYICONDATA structure. The content of the structure depends on the value of dwMessage.
- x
- y
- Specifies the x-coordinate of the point.
- Specifies the y-coordinate of the point.
- lpPoint
- Points to a POINT structure that receives the screen coordinates of the cursor.
- hWnd
- Identifies the window that should be activated and brought to the foreground.
- hMenu
- Identifies the shortcut menu to be displayed.
- uFlags
- A set of bit flags that specify function options.
- x
- Specifies the horizontal location of the shortcut menu, in screen coordinates.
- y
- Specifies the vertical location of the shortcut menu, in screen coordinates.
- nReserved
- Reserved; must be zero.
- hWnd
- Identifies the window that owns the shortcut menu. This window receives all messages from the menu. The window does not receive a WM_COMMAND message from the menu until the function returns.
- prcRect
- Points to a RECT structure that specifies the portion of the screen in which the user can select without dismissing the shortcut menu. If this parameter is NULL, the shortcut menu is dismissed if the user clicks outside the shortcut menu.
In this section I will cover how to add a toolbar to you window. I will first go over adding custom buttons from a bitmap file. The I will go over common buttons that are predefined.
2.3.1 Custom Buttons
Source - Screen Shot
Custom buttons are just a bitmap image that is devided up in 16x16 squares. In this next example, I use this bitmap file.
#include "section_2_3.h" IDB_TOOLBAR BITMAP "toolbar.bmp" |
Here is our resource file.
#define IDC_TOOLBAR 1000 #define IDB_TOOLBAR 1001 #define IDM_BUTTON0 1002 #define IDM_BUTTON1 1003 #define IDM_BUTTON2 1004 #define IDM_BUTTON3 1005 #define IDM_BUTTON4 1006 #define IDM_BUTTON5 1007 #define IDM_BUTTON6 1008 #define IDM_BUTTON7 1009 |
This is our header file.
#include #include #include "section_2_3_1.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); static char gszClassName[] = "MyWindowClass"; static HINSTANCE ghInstance = NULL; HWND ghToolBar; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX WndClass; HWND hwnd; MSG Msg; ghInstance = hInstance; WndClass.cbSize = sizeof(WNDCLASSEX); WndClass.style = NULL; WndClass.lpfnWndProc = WndProc; WndClass.cbClsExtra = 0; WndClass.cbWndExtra = 0; WndClass.hInstance = ghInstance; WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); WndClass.hCursor = LoadCursor(NULL, IDC_ARROW); WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); WndClass.lpszMenuName = NULL; WndClass.lpszClassName = gszClassName; WndClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if(!RegisterClassEx(&WndClass)) { MessageBox(0, "Window Registration Failed!", "Error!", MB_ICONSTOP | MB_OK); return 0; } hwnd = CreateWindowEx( WS_EX_STATICEDGE, gszClassName, "Windows Title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 320, 240, NULL, NULL, ghInstance, NULL); if(hwnd == NULL) { MessageBox(0, "Window Creation Failed!", "Error!", MB_ICONSTOP | MB_OK); return 0; } ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); while(GetMessage(&Msg, NULL, 0, 0)) { TranslateMessage(&Msg); DispatchMessage(&Msg); } return Msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) { switch(Message) { case WM_CREATE: TBADDBITMAP tbAddBitmap; TBBUTTON tbButton[8]; InitCommonControls(); ghToolBar = CreateWindowEx( NULL, TOOLBARCLASSNAME, NULL, WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, hwnd, (HMENU)IDC_TOOLBAR, ghInstance, NULL); SendMessage(ghToolBar, TB_BUTTONSTRUCTSIZE, (WPARAM) sizeof(TBBUTTON), NULL); tbAddBitmap.hInst = ghInstance; tbAddBitmap.nID = IDB_TOOLBAR; SendMessage(ghToolBar, TB_ADDBITMAP, 0, (LPARAM) &tbAddBitmap); ZeroMemory(tbButton, sizeof(tbButton)); tbButton[0].iBitmap = 0; tbButton[0].fsState = TBSTATE_ENABLED; tbButton[0].fsStyle = TBSTYLE_BUTTON; tbButton[0].idCommand = IDM_BUTTON0; tbButton[1].iBitmap = 1; tbButton[1].fsState = TBSTATE_ENABLED; tbButton[1].fsStyle = TBSTYLE_BUTTON; tbButton[1].idCommand = IDM_BUTTON1; tbButton[2].fsStyle = TBSTYLE_SEP; tbButton[3].iBitmap = 2; tbButton[3].fsState = TBSTATE_ENABLED; tbButton[3].fsStyle = TBSTYLE_BUTTON; tbButton[3].idCommand = IDM_BUTTON2; tbButton[4].iBitmap = 3; tbButton[4].fsState = TBSTATE_ENABLED; tbButton[4].fsStyle = TBSTYLE_BUTTON; tbButton[4].idCommand = IDM_BUTTON3; tbButton[5].fsStyle = TBSTYLE_SEP; tbButton[6].iBitmap = 4; tbButton[6].fsState = TBSTATE_ENABLED; tbButton[6].fsStyle = TBSTYLE_BUTTON; tbButton[6].idCommand = IDM_BUTTON4; tbButton[7].iBitmap = 5; tbButton[7].fsState = TBSTATE_ENABLED; tbButton[7].fsStyle = TBSTYLE_BUTTON; tbButton[7].idCommand = IDM_BUTTON5; SendMessage(ghToolBar, TB_ADDBUTTONS, 8, (LPARAM) &tbButton); break; case WM_COMMAND: switch(LOWORD(wParam)) { case IDM_BUTTON0: case IDM_BUTTON1: case IDM_BUTTON2: case IDM_BUTTON3: case IDM_BUTTON4: case IDM_BUTTON5: MessageBox(hwnd, "A Button was clicked.", "Toolbar Message", MB_ICONINFORMATION | MB_OK); break; } break; case WM_SIZE: SendMessage(ghToolBar, TB_AUTOSIZE, 0, 0); break; case WM_CLOSE: DestroyWindow(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, Message, wParam, lParam); } return 0; } |
You will notice that we added a new header file, commctrl.h, this is the header file use for common controls. Common controls are dialogs like Open, Save, Print, etc that many program use it is also a set of controls to make toolbars and statusbars we will get into common controls more in the next tutorial. We declare a TBADDBITMAP structure. The TBADDBITMAP structure adds a bitmap that contains button images to a toolbar. It has the following parameters:
- hInst
- Handle to the module instance with the executable file that contains a bitmap resource.
- nID
- Resource identifier of the bitmap resource that contains the button images.
Source - Screen Shot
It would be a real pain if you had to make new New, Open, Save, etc. buttons for every application you write, so they came up with common buttons. Common buttons are buttons used by alot of apps. Below I have a common button program example, I will explain the new parts but its pretty much the same as the custom button example above.
#define IDC_TOOLBAR 1000 #define IDM_BUTTON0 1001 #define IDM_BUTTON1 1002 #define IDM_BUTTON2 1003 #define IDM_BUTTON3 1004 #define IDM_BUTTON4 1005 #define IDM_BUTTON5 1006 #define IDM_BUTTON6 1007 #define IDM_BUTTON7 1008 |
Our header file.
#include #include #include "section_2_3_2.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); static char gszClassName[] = "MyWindowClass"; static HINSTANCE ghInstance = NULL; HWND ghToolBar; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX WndClass; HWND hwnd; MSG Msg; ghInstance = hInstance; WndClass.cbSize = sizeof(WNDCLASSEX); WndClass.style = NULL; WndClass.lpfnWndProc = WndProc; WndClass.cbClsExtra = 0; WndClass.cbWndExtra = 0; WndClass.hInstance = ghInstance; WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); WndClass.hCursor = LoadCursor(NULL, IDC_ARROW); WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); WndClass.lpszMenuName = NULL; WndClass.lpszClassName = gszClassName; WndClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if(!RegisterClassEx(&WndClass)) { MessageBox(0, "Window Registration Failed!", "Error!", MB_ICONSTOP | MB_OK); return 0; } hwnd = CreateWindowEx( WS_EX_STATICEDGE, gszClassName, "Windows Title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 320, 240, NULL, NULL, ghInstance, NULL); if(hwnd == NULL) { MessageBox(0, "Window Creation Failed!", "Error!", MB_ICONSTOP | MB_OK); return 0; } ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); while(GetMessage(&Msg, NULL, 0, 0)) { TranslateMessage(&Msg); DispatchMessage(&Msg); } return Msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) { switch(Message) { case WM_CREATE: TBADDBITMAP tbAddBitmap; TBBUTTON tbButton[7]; InitCommonControls(); ghToolBar = CreateWindowEx( NULL, TOOLBARCLASSNAME, NULL, WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, hwnd, (HMENU)IDC_TOOLBAR, ghInstance, NULL); SendMessage(ghToolBar, TB_BUTTONSTRUCTSIZE, (WPARAM) sizeof(TBBUTTON), NULL); tbAddBitmap.hInst = HINST_COMMCTRL; tbAddBitmap.nID = IDB_STD_SMALL_COLOR; SendMessage(ghToolBar, TB_ADDBITMAP, 0, (LPARAM) &tbAddBitmap); ZeroMemory(tbButton, sizeof(tbButton)); tbButton[0].iBitmap = STD_FILENEW; tbButton[0].fsState = TBSTATE_ENABLED; tbButton[0].fsStyle = TBSTYLE_BUTTON; tbButton[0].idCommand = IDM_BUTTON0; tbButton[1].iBitmap = STD_FILEOPEN; tbButton[1].fsState = TBSTATE_ENABLED; tbButton[1].fsStyle = TBSTYLE_BUTTON; tbButton[1].idCommand = IDM_BUTTON1; tbButton[3].iBitmap = STD_FILESAVE; tbButton[3].fsState = TBSTATE_ENABLED; tbButton[3].fsStyle = TBSTYLE_BUTTON; tbButton[3].idCommand = IDM_BUTTON2; tbButton[5].fsStyle = TBSTYLE_SEP; tbButton[4].iBitmap = STD_CUT; tbButton[4].fsState = TBSTATE_ENABLED; tbButton[4].fsStyle = TBSTYLE_BUTTON; tbButton[4].idCommand = IDM_BUTTON3; tbButton[6].iBitmap = STD_COPY; tbButton[6].fsState = TBSTATE_ENABLED; tbButton[6].fsStyle = TBSTYLE_BUTTON; tbButton[6].idCommand = IDM_BUTTON4; tbButton[7].iBitmap = STD_PASTE; tbButton[7].fsState = TBSTATE_ENABLED; tbButton[7].fsStyle = TBSTYLE_BUTTON; tbButton[7].idCommand = IDM_BUTTON5; SendMessage(ghToolBar, TB_ADDBUTTONS, 7, (LPARAM) &tbButton); break; case WM_COMMAND: switch(LOWORD(wParam)) { case IDM_BUTTON0: case IDM_BUTTON1: case IDM_BUTTON2: case IDM_BUTTON3: case IDM_BUTTON4: case IDM_BUTTON5: MessageBox(hwnd, "A Button was clicked.", "Toolbar Message", MB_ICONINFORMATION | MB_OK); break; } break; case WM_SIZE: SendMessage(ghToolBar, TB_AUTOSIZE, 0, 0); break; case WM_CLOSE: DestroyWindow(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, Message, wParam, lParam); } return 0; } |
As you can see not much overall has changed but the first thing you might notice is that tbAddBitmap.hInst is now set to HINST_COMMCTRL and that tbAddBitmap.nID is set to IDB_STD_SMALL_COLOR this just tell it to use the standard buttons and not a bitmap. And now our iBitmap is a STD_ and not a number. The STD_ is what our button will be like STD_COPY is the copy button. There is not much difference besides that.
2.4 Status Bars
Source - Screen Shot
We have all seen status bars. Most text editing programs have a statusbar. They can be very useful, but they can also be one of the most abused thing in windows programming.
#define IDC_STATBAR 1000 |
Our header file.
#include #include #include "section_2_4.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); static char gszClassName[] = "MyWindowClass"; static HINSTANCE ghInstance = NULL; HWND ghStatBar; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX WndClass; HWND hwnd; MSG Msg; ghInstance = hInstance; WndClass.cbSize = sizeof(WNDCLASSEX); WndClass.style = NULL; WndClass.lpfnWndProc = WndProc; WndClass.cbClsExtra = 0; WndClass.cbWndExtra = 0; WndClass.hInstance = ghInstance; WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); WndClass.hCursor = LoadCursor(NULL, IDC_ARROW); WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); WndClass.lpszMenuName = NULL; WndClass.lpszClassName = gszClassName; WndClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if(!RegisterClassEx(&WndClass)) { MessageBox(0, "Window Registration Failed!", "Error!", MB_ICONSTOP | MB_OK); return 0; } hwnd = CreateWindowEx( WS_EX_STATICEDGE, gszClassName, "Windows Title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 320, 240, NULL, NULL, ghInstance, NULL); if(hwnd == NULL) { MessageBox(0, "Window Creation Failed!", "Error!", MB_ICONSTOP | MB_OK); return 0; } ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); while(GetMessage(&Msg, NULL, 0, 0)) { TranslateMessage(&Msg); DispatchMessage(&Msg); } return Msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) { switch(Message) { case WM_CREATE: int iBarWidths[] = {100, 200, 300, -1}; InitCommonControls(); ghStatBar = CreateWindowEx( NULL, TOOLBARCLASSNAME, NULL, WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, hwnd, (HMENU)IDC_TOOLBAR, ghInstance, NULL); SendMessage(ghStatBar, SB_SETPARTS, 4, (LPARAM) iBarWidths); SendMessage(ghStatBar, SB_SETTEXT, 0, (LPARAM) "Part 1"); SendMessage(ghStatBar, SB_SETTEXT, 1, (LPARAM) "Part 2"); SendMessage(ghStatBar, SB_SETTEXT, 2, (LPARAM) "Part 3"); SendMessage(ghStatBar, SB_SETTEXT, 3, (LPARAM) "Part 4"); break; case WM_SIZE: SendMessage(ghStatBar, WM_SIZE, 0, 0); break; case WM_CLOSE: DestroyWindow(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, Message, wParam, lParam); } return 0; } |
You will see I defined a array of integers at the top of WM_CREATE these are the widths of the sections we will break the statusbar into. A value of -1 will take up the remaining space. Then we create our statusbar with CreateWindowEx(). We then send our statusbar the widths with the message SB_SETPARTS the WPARAM is how many parts we will brak it into. The we set some text fo the parts with the message SB_SETTEXT. The WPARAM of SB_SETTEXT is the zero-based index of the part. When we received the windows you will see in the WM_SIZE trap that we send the statusbar the WM_SIZE message that we just received so it can adjust properly.
Appendix
A. The Compiler
I like most of you are pretty low on cash most of the time. So I use a free compiler from Borland. I also use the old Borland 5.02 the last one with an IDE. I also got my hands on a copy of Visual C++ 6 ( not going to say how) which I didn't find all that great and pretty intrusive. All of the examples in this tutorial were compiled with the Borland Free Compiler v5.5 check the tools section for a link to where you can download it. After you get the compiler set up correctly just open up a command line and browse to where you unziped the tutorials. The compile using:
C:\Cpp\tutorial\examples\section_1_2> bcc32 -tW section_1_2.cpp |
The -tW parameter tells the compiler to link to the windows library and make a windows application and not a dos application. Some of the more complicated examples have a makefile.mak file, this is a make file. Just browse to that directory and type "make" (without quotes) to compile that example. I do not support people who e-mail me about Dev-C++. Personally I do not like that program. The IDE is great but the compiler is probably the worst I have ever used. So just use the program for an IDE and the use Borland for a compiler its what I did for a while until I found a better IDE (its listed in the tools section).
B. Tools
Every great programmer has to have a few good tools he uses. The first being his compiler and IDE. Here I have listed a few tools I find that might be useful.
- Borland Free 5.5 Compiler - A great overall windows compiler, and its FREE!
- MultiEdit - A good overall IDE that I use all the time
- Komodo - Another really good IDE that I find myself using alot
- Win API Reference - You cannot consider yourself a windows programmer unless you have this, it is a must have!
- LCC-Win32 - A fairly nice compiler with a user interface and a good resource editor
These are a few sights I find invaluable to learning programming of any type.
- Microsoft Developers Network - Still one of the best resource for Windows API Programming
- Planet Source Code - Tons of free example code
- Code Box - A great place no matter what language you learn
- Programmers Heaven - Great Place the name says it all
- #winprog - A great site I found when I first started programming
- CProgramming.com - A really nice site if you are just starting programming or need a refresher course
- Windows API Reference - This is the Windows API help fle that comes with the old Borland 5.2 Compiler, this is a must for ever programmer
0Awesome Comments!