This tutorial is part of a Collection: 03. DirectX 11 - Braynzar Soft Tutorials
rate up
2
rate down
13612
views
bookmark
02. An Introduction to the Win32 API

Before we get started with DirectX, we first need to create the window which we will draw our graphics on.

1334 downloads
I'm just going to copy the creating a window lesson from DirectX 9 since they are the same. Theres a lot of code here to get started with, but once we're done here, we can start actually using DirectX. Our application starts out 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 .[http://msdn.microsoft.com/en-us/library/ms633574(VS.85).aspx#class_styles][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 false; } This is the part where we create our window! 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 false; } 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 grey background. We are now ready for The fun stuff, directX and opengl! Full Code: //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 false; } 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 false; } 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); } I almost forgot, if your getting an error something like this; error C2664: 'MessageBoxW' : cannot convert parameter 2 from 'const char [6]' to 'LPCWSTR' Then at the top, go to project, setting up properties...(or just ALT+F7), then on the left, expand configuration properties and click on General. On the right side, towards the bottom where it says Character Set, change it from Unicode to Multi-Byte. Another thing I forgot to mention, was if you wanted to use international characters, such as chinese, don't change the character set to multi-byte. Keep it the way it is, and before every string in your code, put a capital "L". An example is: "Are you sure you want to exit?" ---> L"Are you sure you want to exit?" You will have to remember to always put an "L" before every string. Also, you may have to change PeekMessage to PeekMessageL
Comments
Very good tutorial; easy to understand also for a beginner
on Feb 19 `16
Philipp77
LPCTSTR WndClassName = "firstwindow";//it says that "const char *" can't initialize LPCTSTR in VS2017
on Nov 25 `17
VictorWu
In WinMain " if(!InitializeWindow(hInstance, nShowCmd, Width, Height, true))" shouldn't delete ! that makes the error Initialize to end the whole process?
on Nov 28 `17
VictorWu
perfect,thank you very much!
on Dec 21 `17
601084453
Hey can anyone tell me how to get rid of this error - LNK1561 entry point must be defined
on Mar 04 `18
MrEj2019
in bool InitializeWindow(); return 1 means true.functions that check the failure may not works....
on May 16 `18
lynfield
your right lynfield, thank you, i'll update that
on May 16 `18
iedoc