![]() |
|
[C] Complexity: Introduction to the Win32 API - Printable Version +- Hack Forums (https://hackforums.net) +-- Forum: Tech (https://hackforums.net/forumdisplay.php?fid=444) +--- Forum: Coding (https://hackforums.net/forumdisplay.php?fid=151) +---- Forum: Software Development (https://hackforums.net/forumdisplay.php?fid=118) +---- Thread: [C] Complexity: Introduction to the Win32 API (/showthread.php?tid=3541303) |
Complexity: Introduction to the Win32 API - Inso - Jun 10, 2013 Introduction to the Win32 API
![]() Hello and welcome to my tutorial on the Windows Application Programming Interface for C++. So what is it? The Windows API is a way for programmers to create graphical user interfaces (GUI) on the Windows operating system (OS). It allows the programmer to have a simpler way to do this rather than have to handle compatibility with different hardware and various other aspects of development between machines. So why use the WinAPI? Aren't there easier packages to do this? This is true, various frameworks such as MFC and .NET exist for rapid development of windows forms. Most of these frameworks work as wrappers for the Win32 API, where all of the code used would be wrapped within premade classes that cuts down on the amount of code that must be wrote. The Win32 API should still be learned though as it can be helpful in the event of an error with one of these frameworks. With no understanding of how these frameworks really work, it will be difficult or more time consuming to fix any problems. As this is my first tutorial on the Win32 API, I'm only going to show the Windows version of the popular Hello World program and explain it step by step. Because a bit more work would go into using the WinAPI, the Hello World program will be much bigger than expected. This is a simple "Hello World" program using regular C++: PHP Code This is a nice short program. In contrast, the Win32 requires much more code just for a simple window. Charles Petzold (who has wrote many books on programming with the Windows API) has been quoted as saying "The original hello world program in the Windows 1.0 SDK was a bit of a scandal. HELLO.C was about 150 lines long, and the HELLO.RC resource script had another 20 or so more lines. (...) Veteran C programmers often curled up in horror or laughter when encountering the Windows hello-world program". Okay, so now onto our program. The first thing to do before anything else is done, is include the Windows header file, as this is the file where we get all the Windows functions, data structures, etc. This is done using something called a "preprocessor directive". This is a command that the compiler reads and responds to, in this case it bundles the windows header file along with your source into the program. Preprocessor directives are preceeded by a hash (#) symbol. Unlike other statements in C++, it is not ended by a semicolon (;). PHP Code The next thing to do is create our main function. Console based programs in C++ will use an actual main() function, but when programming with Win32 you use either WinMain() or wWinMain() as Windows.h will provide its own version of the main function. The only difference between the two is wWinMain uses unicode and thus wide characters, whereas WinMain used ASCII character codes. In this tutorial I will use the wWinMain() function. It has the following prototype: PHP Code hInstance is the handle to the windows instance. prevInstance is also handle to a windows instance, but it should always be 0 as it is a parameter only used on 16 bit Windows. cmdLine is the arguments given to the program as a unicode string if the program was run from the command prompt. cmdShow is a flag that tells Windows if the program should start maximised, minimised or normal. Here is our empty wWinMain function: PHP Code Because our program won't actually be using prevInstance or cmdLine, two additional functions can be called inside wWinMain which stops the compiler throwing errors at you for not using arguments. PHP Code Okay, now the next step is to create a windows class. This is done using WNDCLASSEX, a type defintion of the data structure tagWNDCLASSEX. The MSDN documentation defines it as follows: PHP Code To create your new window class, put the following code in your wWinMain function, after the UNREFERENCED_PARAMETER() function and before the return statement. PHP Code In this snippet of code, we have created an instance of WNDCLASSEX and called it wndClass, then we initiate all of its values to 0 (0 is equivalent to null in the Windows API, somewhere in Windows.h there is a line reading #define NULL 0). The size of the structure is set to the size of the WNDCLASSEX structure. Next it is given several style flags, in this case CS_HREDRAW and CS_VREDRAW. Flags are always separated by the bitwise OR symbol. CS_HREDRAW and CS_VREDRAW both redraw the entire window if a change is made to the horizontal or vertical client area respectively. A list of style flags can be found here. Next lpfnWndProc was set to WndProc, a function that we will be creating later. The hInstance is set to the application instance containing the window procedure for the class. hCursor is set using the LoadCursor function, which takes an HINSTANCE (optional) and LPCTSTR argument which is the name for the cursor to be used. To get a list of all cursor types available, use this link. this function will return a variable of type HCURSOR, which is the handle to the cursor. If the function fails it returns a NULL (0) value. hbrBackground is the handle to the background brush for this class. It may be a colour value or a handle to a brush. If it is a colour value, it must use one of the following colour values:
Each of these values is equivalent to an integer. The reason we must use +1 is because the first value is equivalent to 0, so we add 1 so windows doesn't think we are supplying a NULL brush. We have set the menu name to null as we don't have one and we have called our class "MyTutorialWindow". Remember this name, as it will be used again later. Next we must register this class. Add the following snippet of code after before the return statement. PHP Code This if conditional check uses the RegisterClassEx function as it's condition. If RegisterClassEx successfully registers the class it will return true. If it fails it will return false. This statement uses the NOT operator (!), so it checks if RegisterClassEx returns false. If it does, the wWinMain function returns -1 and will terminate. The next step is to create our window and the handle to it. Add the following code before the return statement. PHP Code We have created a handle to a window called hwnd, which has been initialised using the CreateWindow() function. This takes 11 parameters. Although this is a function call, you may have noticed I have separated the arguments on different lines. C++ allows the use of whitespace like this, so it should be taken advantage of to improve readability of your code. In the MSDN document, CreateWindow is defined as follows: PHP Code It has a return value of HWND (Window Handle). It takes the argument of lpClassName (the class name we created earlier), lpWindowName (the text that will appear in the title label), the style flags (a full list can be found here), the initial x and y coordinates (in this case we used the default, CW_USEDEFAULT), the dimensions of the window, the handle of the parent window (ours has no parent so it is NULL), the handle to any menu associated with the window, an instance to the module associated with the window, and a pointer to a value passed through CREATESTRUCT (this isn't necessary for our window so it has also been set to NULL). After creating a window it is good practice to check if CreateWindow() failed. To do this, add the following snippet. PHP Code If this function is successful it will return an HWND for hwnd, but if it fails hwnd will be NULL which is equivalent to 0. This conditional check sees if hwnd is NULL, if it is the wWinMain function returns -1 and the program terminates. To display our window we now called the ShowWindow() function. This takes 2 arguments, the window handle of the window we want to display, and what state the window should open in. PHP Code cmdShow will be given by the wWinMain function, it is one of the arguments we gave when writing it. After the first time the window is called and you decide to call ShowWindow again to change the state, the following values can be used instead of cmdShow:
To see these calls in action, you could change your ShowWindow call to the following: PHP Code This will wait for 2 seconds, then maximise the window. Don't try this until the entire window is made though, as there are still some parts to add before the window will even work. While following the rest of this tutorial, keep only the original ShowWindow() call, you can try out the second one and the Sleep() afterwards. Next I'm going to introduce the MSG structure. It is defined in the MSDN documentation as follows: PHP Code hwnd is the handle to the window that has a windows procedure that receives messages (you will learn more about this later), message is the message identifier, wParam and lParam contains additional information about this message, time is the time the message was sent, and pt is the posiiton of the cursor at the time of the message being sent. Now it's time to populate our MSG structure, add the following: PHP Code We first create an instance of the data structure, called msg and we set all of its member variables to 0. We have created a while loop that continues as long as msg.message is not WM_QUIT. WM_QUIT is the message used to quit a program. For each iteration of the loop we check to see if PeekMessage returns true. If it returns NULL there are no messages in the "message queue" (the queue where message are kept until they can be processed). PeekMessage() takes arguments of the address of our MSG structure, the handle to a window where messages should be received, the first and last in the range of messages to be examined, and a flag specifiying how messages are handled. In this case it is PM_REMOVE (Removed from queue after being processed by PeekMessage). Other flags that could be used (may be read about in more detail here) instead are:
All messages are automatically processed, but if you only want one or more types of messages to be processed you can also use the following flags:
These flags (like the window style flags) are separated by the bitwise OR operator (|). TranslateMessage() will translate messages into character messages which are put into the message queue. DispatchMessage() will take messages from the queue and sends it to the window procedure, which is the next function we are going to create. Just to recap, your current wWinMain function should be as follows: PHP Code Now onto our windows procedure function. First, we need to write the prototype for our function, before the wWinMain function definition and after the #include directive write the following: PHP Code What this does is create the prototype for our WndProc (Windows Procedure). It takes 4 arguments; the handle to the window, the message identifier being sent, the first message parameter, and the second message parameter. Now below the wWinMain function definition, create our WndProc function: PHP Code Now it's time to fill our WndProc function. First, we create a PAINTSTRUCT data structure that will hold information used to paint on the client window. After this we create a handle to the device context which can either be of type display, printer, memory, or information. The device context is used to support drawing or data retrieval on different types of devices. PHP Code Now we have to create a switch which uses cases defined by the message identifiers: PHP Code This switch takes the message identifier passed to the WndProc function and does different things depending on this message identifier. In our switch we have included the WM_PAINT identifier and the WM_DESTROY identifier, but we have also included a default case in the event of being passed a message identifier that doesn't have a specific set of instructions attributed to it. When populating this switch, each case should be in a block enclosed by braces, this prevents some errors which are caused when braces are left out (I'm not sure why though). For example: PHP Code So first, in the WM_PAINT case, add the following: PHP Code BeginPaint takes a handle to the window being repainted and the address of our paintStruct structure. It uses these values and returns a device context handle but if it fails it will return NULL. EndPaint takes the handle to the window that has been repainted, and the address of paintStruct which should hold data passed to it from BeginPaint. Now add the following to the WM_DESTROY case: PHP Code When the WM_DESTROY message identifer is sent, it will call PostQuitMessage. PostQuitMessage will take an argument that specifies the exit code (0 tends to mean everything executed correctly). The exit code is used as the wParam parameter of the WM_QUIT message. Because WM_QUIT is not associated with a window (it is a call for the entire application to quit) it does not need a case to be created in the window procedure. It is instead generated when PostQuitMessage is called. Now add this to the default case: PHP Code DefWindowProc takes 4 arguments, the handle to the window procedure that received the message, the message identifier, and two parameters for additional message information. It is used to handle any messages that haven't been handled by our switch cases in the WndProc function. If you haven't caught on yet, the basic flow for a Windows program will be as follows:
And now your window is complete! This is a basic window that will say "Hello, World!" in the title bar. The entire source code can be seen in the spoiler below: Spoiler (Click to View) * - Inso - Jun 10, 2013 (Jun 10, 2013 11:51 AM)Shihanus Wrote: Another user completely mistaking the idea of the WIN Api. I'm using it for forms creation in this thread and tutorial, if I was wanting to create a tutorial about networking or using the OS shell then I would have mentioned those reasons. As of now, I haven't looked at those uses of it so I'm not going to mention them. I'm sharing what I (think) I know so far. I like to try learn something and then post a tutorial and get feedback. I can then see if I've learned it correctly, and if I get constructive feedback about mistakes, I can fix it and I've learned something new. * - pr3fix - Jun 10, 2013 You should have linked the MSDN documentation, so that people will know where to look at when they need explanation on certain function or parameter to pass to a function. Eventually, they will remember these and it'll be much easier. * - Shihanus - Jun 10, 2013 (Jun 10, 2013 11:54 AM)Inso Wrote: I'm using it for forms creation in this thread and tutorial, if I was wanting to create a tutorial about networking or using the OS shell then I would have mentioned those reasons. As of now, I haven't looked at those uses of it so I'm not going to mention them. I'm sharing what I (think) I know so far. I like to try learn something and then post a tutorial and get feedback. I can then see if I've learned it correctly, and if I get constructive feedback about mistakes, I can fix it and I've learned something new. I didn't even necessarily mean networking or OS Shells. If you wanted to give a tutorial on the WIN APi. You could have started with something even more basic. Code #define WIN32_LEAN_AND_MEAN* - Inso - Jun 10, 2013 (Jun 10, 2013 11:58 AM)Shihanus Wrote: I didn't even necessarily mean networking or OS Shells. I did think of that but I thought it wouldn't be worth a tutorial on just the message box. I was trying to learn off several sites and one of them was the MSDN docs, two of them mentioned the message box at the start but it only seemed to take up a small space for the overall tutorial. I myself don't know too much about it (the entire WinAPI, just starting it basically), currently I'm just learning about adding controls but I don't know enough about it either which is why I left it out too. I'm a bit annoyed about that, one site I was looking at had some great tutorials that I was learning from but the next part of the series is being wrote and isn't up yet. :/ (Jun 10, 2013 11:58 AM)pr3fix Wrote: You should have linked the MSDN documentation, so that people will know where to look at when they need explanation on certain function or parameter to pass to a function. Eventually, they will remember these and it'll be much easier. I'll see if I can edit it in now, the thread was too big so it had to be done as two posts and once they merge it won't let you edit because of the character limit. If not, the MSDN is referenced for a few of the functions and structures in there, so hopefully they'll be able to navigate from those links. EDIT: Nope, sorry can't. Quote:There was an error performing the update. * - Krofnica - Jun 10, 2013 I stopped reading at Code Console based programs in C++ will use an actual main() function, but when programming with Win32 you use either WinMain() or wWinMain() as Windows.h will provide its own version of the main function.* - Inso - Jun 10, 2013 (Jun 10, 2013 01:32 PM)Krofnica Wrote: I stopped reading at I don't see what's wrong with that? It might not be as simple as I've wrote it, from what I've read it's the CRT that provides an implementation of main() but I thought it would just be simpler to write it as I did. * - Krofnica - Jun 10, 2013 (Jun 10, 2013 01:41 PM)Inso Wrote: I don't see what's wrong with that? It might not be as simple as I've wrote it, from what I've read it's the CRT that provides an implementation of main() but I thought it would just be simpler to write it as I did. CRT defines a function which does some initialization before user-defined main function begins with execution. That function is usually called maincrtstartup, and is entry point of all c(++) programs. It does not only call main with prototypes: int main(); int main(int, char**); But it can also call: int __stdcall WinMain(HINSTANCE, HINSTANCE, LPSTR, int); windows.h does not define its own startup function :P Its all done by crt. * - Inso - Jun 10, 2013 Oh right, guess I've learned something today. :P Thanks for the correction. * - Deesr - Jul 8, 2013 Thanks for this, I'm sure this will come in handy! Bookmarked. * - hansp - Jul 14, 2013 Thank you very much :) I learned a lot and improved my C++ Skills. And because I am a beginner at C++ I ported your code to C#. Pretty useless, but a nice challenge Spoiler (Click to View) * - rekcahcave - Aug 3, 2013 Nice, thanks for the tutorial! I will reference it later! * - Vexane - Aug 3, 2013 Book. Marked. 35 Characters Still 35 Characters * - beastmode16 - Aug 4, 2013 Apparently, very informative tutorial.. got bookmarked it now! Thanks a lot for this man! * - Makaveli The Don - Aug 11, 2013 Great guide. Was good to read. Thanks. 🤝 |