This tutorial is part of a Collection: 01. DirectX 9 - Braynzar Soft Tutorials
3483
views
02. Creating a Win32 Window
Learn how to set up and display a window with a message loop to keep the window displayed.
Before we start displaying the content of our application, we need to create a window to put it on. Theres quite a bit more code than the last lesson, so lets get on it. Our application starts out again by including the windows.h header file. When you make a window, you need to make a windows class for that window, so thats what the next line is for. Below that is just making a handle to our window #include <windows.h> LPCTSTR WndClassName = "firstwindow"; HWND hwnd = NULL; We'll define the width and height of our window with these constants const int Width = 800; const int Height = 600; InitializeWindow() function will do what it says, then depending on if the window was made or not, return either true or false. Then we initialize the message loop function, which is the function that keeps our program running. We'll get into these in a bit. bool InitializeWindow(HINSTANCE hInstance, int ShowWnd, int width, int height, bool windowed); int messageloop(); We initialize the windows callback procedure. Windows API is an "event-driven" programming model, so in this function, we can "capture" windows messages, like a key press (also called events) and manipulate the flow of our program. LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); We need a main function for our application to start in. In windows programming, this function is the WinMain() function. int WINAPI WinMain(HINSTANCE hInstance, //Main windows function HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { The first thing we do in our WinMain() function is create and register our window by calling our InitializeWindow() funtion. The function will return true or false based on if the window was registered and created or not. After the initialization of the window, we go on to the interactive part of our app. the message loop. After that, we jump into the message loop. When the message loop is done, we end our program by returning 0, meaning that it was a success and there were no errors. if(!InitializeWindow(hInstance, nShowCmd, Width, Height, true)) { MessageBox(0, "Window Initialization - Failed", "Error", MB_OK); return 0; } messageloop(); return 0; } Our next funtion is the InitializeWindow(hInstance, nShowCmd) funtion. This will create and register the class. HINSTANCE hInstance - This is the handle to our current application int ShowWnd - How the window should be displayed. Some common commands are SW_SHOWMAXIMIZED, SW_SHOW, SW_SHOWMINIMIZED. int width - Width of the window in pixels int height - Height of the window in pixels bool windowed - False if the window is fullscreen and true if the window is windowed bool InitializeWindow(HINSTANCE hInstance, int ShowWnd, int width, int height, bool windowed) { We create an extended styles windows class called wc, then fill out all the properties. the WNDCLASSESEX structure looks like this: typedef struct _WNDCLASS { UINT cbSize UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HANDLE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; } WNDCLASS; WNDCLASSEX wc; First one is cbSize. It should be set as the size of our windowclass, sizeof(WNDCLASSEX) wc.cbSize = sizeof(WNDCLASSEX); style is the style of a windows class. They all start with CS_. Here are some examples. **CS_CLASSDC **1 OK Button **CS_DBLCLKS **One device context is shared between all windows created with this class. **CS_HREDRAW **Enable double mouse clicks made on the window. **CS_HREDRAW **Window is refreshed if there is a change in the window's width or if the window is moved horizontally. **CS_NOCLOSE **Disables the close option on the window menu. **CS_OWNDC **A unique device context is created for each window created. This is the opposite to CS_CLASSDC. **CS_PARENTDC **This sets the clipping rectangle of the child window to that of the parent window. This allows the child window to be able to draw on the parent window. **CS_VREDRAW **The window is redrawn if there is a change in the window's height or if the window is moved vertically. For more class styles, go here We'll just set ours to be redrawn when the window is moved or changed size wc.style = CS_HREDRAW | CS_VREDRAW; lpfnWndProc is a pointer to the function we want to process the windows messages. It's set to WndProc because thats what the name of our windows processing function. wc.lpfnWndProc = WndProc; cbClsExtra is the number of extra bytes allocated after WNDCLASSEX. wc.cbClsExtra = NULL; cbWndExtra specifies the number of bytes allocated after the windows instance. wc.cbWndExtra = NULL; This is a handle to our current windows application. The GetModuleHandle function can be used to get the current window application by passing NUll to its 1 parameter. wc.hInstance = hInstance; hIcon is used to specify the icon at the top left corner of the window in the title bar. Here are some standard icons: **IDI_APPLICATION **Default application icon **IDI_HAND **Hand-shaped icon **IDI_EXCLAMATION **Exclamation icon **IDI_INFORMATION **Asterix icon **IDI_QUESTION **Question mark icon **IDI_WINLOGO **Default application icon if using XP, otherwise windows logo. For more information on icons, go here wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); This one defines the icon your cursor will be. Here are some: **IDC_APPSTARTING **Standard arrow and small hourglass cursor. **IDC_ARROW **Standard arrow cursor. **IDC_CROSS **Crosshair cursor. **IDC_HAND **Hand cursor. **IDC_NO **Slashed circle cursor. **IDC_WAIT **Hourglass cursor. For more cursors, go here wc.hCursor = LoadCursor(NULL, IDC_ARROW); hbrBackground is a handle to a brush. This will make the background black. play with it. wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 2); This is the name to the menu that is attached to our window. we don't have one so we put NULL. wc.lpszMenuName = NULL; We name our class here. wc.lpszClassName = WndClassName; This specifies the icon in your taskbar. It uses the same IDI_ icons as the one above. wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); Now we register the class. If it fails we will get an error. if it passes, we move on to create the window. if (!RegisterClassEx(&wc)) { MessageBox(NULL, "Error registering class", "Error", MB_OK | MB_ICONERROR); return 1; } This is the part where we create our window! HWND hwnd = CreateWindowEx( This is the extended styles. You can add in more than one by putting a | between them. Here a couple: **WS_EX_ACCEPTFILES **A window created with this can accept drag-drop files. **WS_EX_APPWINDOW **Forces a top-level window onto the taskbar when the window is visible. **WS_EX_CONTEXTHELP **The help button at the top right of the window. cannot be used with WS_MAXIMIZEBOX or WS_MINIMIZEBOX styles. **WS_EX_TOOLWINDOW **Creates a window intended to be a floating toolbar. For a complete list of extended styles, click here NULL, This is the name of the class our window will use. This is the class we registered earlier. WndClassName, This is the text that will appear in the title bar. "Window Title", This is the styles of the old CreateWindow function. The CreateWindowEX was made to allow the extended styles to be applied to a window. Some styles are below. **WS_BORDER **Creates a window that has a thin-line border. **WS_CAPTION **Creates a window that has a title bar (includes the WS_BORDER style). **WS_DISABLED **Creates a window that is initially disabled. A disabled window cannot receive input from the user. **WS_HSCROLL **Creates a window that has a horizontal scroll bar. **WS_MAXIMIZE **Creates a window that is initially maximized. **WS_MAXIMIZEBOX** Creates a window that has a maximize button. **WS_MINIMIZE **Creates a window that is initially minimized. **WS_MINIMIZEBOX **Creates a window that has a minimize button. **WS_POPUP **Creates a pop-up window. **WS_SIZEBOX **Creates a window that has a sizing border. Same as the WS_THICKFRAME style. **WS_SYSMENU **Creates a window that has a window menu on its title bar. **WS_VSCROLL **Creates a window that has a vertical scroll bar. **WS_OVERLAPPEDWINDOW **Creates an overlapped window with the WS_OVERLAPPED, WS_CAPTION, WS_SYSMENU, WS_THICKFRAME, WS_MINIMIZEBOX, and WS_MAXIMIZEBOX styles. **WS_OVERLAPPED **Creates a window that has a window menu on its title bar. **WS_THICKFRAME **Creates a window that has a sizing border. Same as the WS_SIZEBOX style. Click here for more window styles WS_OVERLAPPEDWINDOW, Starting X and Y positions. The top left corner of the window. 0,0 is the top left of the screen. CW_USEDEFAULT, CW_USEDEFAULT, Height and Width of the window in pixels width, height, Handle to Parent window. We have no parent so it is set to NULL. NULL, Handle to menu that is attached to window. We don't have one so again it is set to NULL. NULL, Instance of the current program. hInstance, This would be used if our window was an MDI client. Ours isn't so we set it to NULL. After that we check to see if our window was created. NULL ); if (!hwnd) { MessageBox(NULL, "Error creating window", "Error", MB_OK | MB_ICONERROR); return 1; } We need to show the window, so that is what the ShowWindow() function does. This is the syntax for ShowWindow(). BOOL ShowWindow( HWND hWnd, int nCmdShow ); The first parameter is the handle to the window we want shown, and the second is how we want it shown, like maximized or minimized. After we show the window we should refresh it, with UpdateWindow(). BOOL UpdateWindow( HWND hWnd ); The only parameter for this is the handle to the window we showed. What UpdateWindow() does is send a WM_PAINT message directly to the windows procedure. If there isn't anything in the client area of the window, then UpdateWindow() does not send a message. We then return true so our WinMain() function can get on to the message loop. ShowWindow(hwnd, ShowWnd); UpdateWindow(hwnd); return true; } After getting through initializing the window, we can move on to the main part of the program, the message loop. It's what keeps the program going. int messageloop(){ First thing we do is make an instance to the MSG structure which is the structure of the windows message. This will hold the message's information. MSG msg; ZeroMemory() clears a structure and sets it to NULL. It takes two parameters. First one is a pointer to the structure you want cleared, and the second one is the size of the structure you want to clear. ZeroMemory(&msg, sizeof(MSG)); while there is a message... while(true) { We use PeekMessage() to see if there was a message. There are five parameters: BOOL PeekMessage( LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg ); LPMSG lpMsg - This is the pointer to a our message structure HWND hWnd - This is the handle to the window that is sending the message. If this is set to null, it will get messages from any window in the current program. UINT wMsgFilterMin - Specifies the value of the first message in the range of messages to be examined. If wMsgFilterMin and wMsgFilterMax are both set to zero, PeekMessage will examine all messages. UINT wMsgFilterMax - Specifies the value of the last message in the range of messages to be examined. UINT wRemoveMsg - This specifies how the message will be handled. We set to PM_REMOVE so the message will be deleted after we read it. For more info on PeekMessage(), go here if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { If the message was a quit message, we close our program. if (msg.message == WM_QUIT) break; If our message was a windows message, we translate then dispatch. TranslateMessage() will have windows do some translating like the keyboard's virtual keys to characters. The DispatchMessage() sends the message to our windows procedure, WndProc. After that we return the message. TranslateMessage(&msg); DispatchMessage(&msg); } If there was not a windows message, we run our game. else{ // run game code } } return msg.wParam; } Were finally towards the end. We are now at the windows message processing function. HWND hwnd is the handle to the window that got the message. UINT msg is the contents of the message. Here's a couple. **WM_ACTIVATE **Message sent when window is activated. **WM_CLOSE **Message sent when the window is closed. **WM_CREATE **Message sent when window is created. **WM_DESTROY **Message sent when window is destroyed. Here are a couple links to more windows messages. Window Messages Keyboard Messages wParam and lParam are extra information about the message. we will be using wParam to detect keyboard input. LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { This is where we check the message for events. If the escape key was press, we display a message boxe asking If you really wanna quit. If yes is clicked, the program closes. If no was hit, the messagebox closes. If the message contained WM_DESTROY, meaning the window is being destroyed, we return 0 and the application closes. switch( msg ) { case WM_KEYDOWN: if( wParam == VK_ESCAPE ){ if(MessageBox(0, "Are you sure you want to exit?", "Really?", MB_YESNO | MB_ICONQUESTION) == IDYES) DestroyWindow(hwnd); } return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } At the end we call the DefWindowProc function. This is the default windows procedure function. We call this at the end to take care of all the messages we recieve but don't take care of ourselves. return DefWindowProc(hwnd, msg, wParam, lParam); } Now we finally conclude this lesson. If you got it all and there were no errors, this program should create a window with a black background. We are now ready for The fun stuff, directX and opengl! Here's the final code: main.cpp //Include necessary Headers// #include <windows.h> //Define variables/constants// LPCTSTR WndClassName = "firstwindow"; //Define our window class name HWND hwnd = NULL; //Sets our windows handle to NULL const int Width = 800; //window width const int Height = 600; //window height //Functions// bool InitializeWindow(HINSTANCE hInstance, //Initialize our window int ShowWnd, int width, int height, bool windowed); int messageloop(); //Main part of the program LRESULT CALLBACK WndProc(HWND hWnd, //Windows callback procedure UINT msg, WPARAM wParam, LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance, //Main windows function HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { //Initialize Window// if(!InitializeWindow(hInstance, nShowCmd, Width, Height, true)) { //If initialization failed, display an error message MessageBox(0, "Window Initialization - Failed", "Error", MB_OK); return 0; } messageloop(); //Jump into the heart of our program return 0; } bool InitializeWindow(HINSTANCE hInstance, //Initialize our window int ShowWnd, int width, int height, bool windowed) { //Start creating the window// WNDCLASSEX wc; //Create a new extended windows class wc.cbSize = sizeof(WNDCLASSEX); //Size of our windows class wc.style = CS_HREDRAW | CS_VREDRAW; //class styles wc.lpfnWndProc = WndProc; //Default windows procedure function wc.cbClsExtra = NULL; //Extra bytes after our wc structure wc.cbWndExtra = NULL; //Extra bytes after our windows instance wc.hInstance = hInstance; //Instance to current application wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); //Title bar Icon wc.hCursor = LoadCursor(NULL, IDC_ARROW); //Default mouse Icon wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 2); //Window bg color wc.lpszMenuName = NULL; //Name of the menu attached to our window wc.lpszClassName = WndClassName; //Name of our windows class wc.hIconSm = LoadIcon(NULL, IDI_WINLOGO); //Icon in your taskbar if (!RegisterClassEx(&wc)) //Register our windows class { //if registration failed, display error MessageBox(NULL, "Error registering class", "Error", MB_OK | MB_ICONERROR); return 1; } HWND hwnd = CreateWindowEx( //Create our Extended Window NULL, //Extended style WndClassName, //Name of our windows class "Window Title", //Name in the title bar of our window WS_OVERLAPPEDWINDOW, //style of our window CW_USEDEFAULT, CW_USEDEFAULT, //Top left corner of window width, //Width of our window height, //Height of our window NULL, //Handle to parent window NULL, //Handle to a Menu hInstance, //Specifies instance of current program NULL //used for an MDI client window ); if (!hwnd) //Make sure our window has been created { //If not, display error MessageBox(NULL, "Error creating window", "Error", MB_OK | MB_ICONERROR); return 1; } ShowWindow(hwnd, ShowWnd); //Shows our window UpdateWindow(hwnd); //Its good to update our window return true; //if there were no errors, return true } int messageloop(){ //The message loop MSG msg; //Create a new message structure ZeroMemory(&msg, sizeof(MSG)); //clear message structure to NULL while(true) //while there is a message { //if there was a windows message if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) //if the message was WM_QUIT break; //Exit the message loop TranslateMessage(&msg); //Translate the message //Send the message to default windows procedure DispatchMessage(&msg); } else{ //Otherewise, keep the flow going } } return (int)msg.wParam; //return the message } LRESULT CALLBACK WndProc(HWND hwnd, //Default windows procedure UINT msg, WPARAM wParam, LPARAM lParam) { switch( msg ) //Check message { case WM_KEYDOWN: //For a key down //if escape key was pressed, display popup box if( wParam == VK_ESCAPE ){ if(MessageBox(0, "Are you sure you want to exit?", "Really?", MB_YESNO | MB_ICONQUESTION) == IDYES) //Release the windows allocated memory DestroyWindow(hwnd); } return 0; case WM_DESTROY: //if x button in top right was pressed PostQuitMessage(0); return 0; } //return the message for windows to handle it return DefWindowProc(hwnd, msg, wParam, lParam); }
Sign in to comment