Windows API Window Using C#

The code in this article is intended for educational purposes only. This program will create a very simple window that just says "Hello" but it does using just the Windows GUI API in C#. It does not use Windows Presentation Foundation or even Windows Forms; it only uses the "System" library of classes that is the default library for and required by C# . The program is a very simple C# program that introduces the basics of what a Windows programmer would do to write a Windows API program without .Net.

This article assumes you understand Platform Invoke (DllImport) and how to use it including how to convert a C header #include Windows API function definition to a DllImport. I provide the complete source code but you need to understand DllImport. I intend to use this sample program for future articles about the Windows API. Understanding the Windows API might help you if you are doing things like creating a User Control for WPF or Windows Forms. I assume that very little or none of this is useful for Windows Store (Windows 8) programming.

For the purpose of creating a project to start with, we will create a Windows Forms application however we will remove the form and everything related to it. I will assume you know how to create a Windows Forms application. Create the project then delete the form.

Next, in the references, you can delete all the references except the one for "system.dll". Deleting the references is not a requirement but it shows that we are not using anything except the system.dll part of .Net. Similarly, in the "Program.cs" file you can remove the "using" statements at the beginning of the file, except you can keep the "using System;".

Then add a using for InteropServices, as in "using System.Runtime.InteropServices;". Then delete the three lines in the "Main" function. We will replace the code in the "Main" function with code that does the following:

  • Registers a window class (this "class" is not the same thing as a C# class)
  • Uses the Windows API to create the window
  • Goes into a "message loop"
  • Use the following code for the "Main" function:

    1. Win32.MSG Msg = new Win32.MSG();  
    2. int rv;  
    3. if (Program.RegisterClass() == 0)  
    4.     return;  
    5. if (Program.Create() == 0)  
    6.     return;  
    7. // Main message loop:  
    8. while ((rv = Win32.GetMessage(out Msg, IntPtr.Zero, 0, 0)) > 0)  
    9. {  
    10.     Win32.TranslateMessage(ref Msg);  
    11.     Win32.DispatchMessage(ref Msg);  
    12. }  
    The while loop in that code is the "message loop"; it is what makes Windows programs event-driven. Windows sends events to Windows applications but Windows does not make applications event-driven; Windows applications make themselves event-driven, and the Message Loop is what does it.  When the message loop finishes the window will do nothing more. The GetMessage function usually returns a value greater than 0. It returns 0 when a WM_QUIT message is received. In the Windows API, a window is normally destroyed before a WM_QUIT message is sent. Note that in the window procedure below, the PostQuitMessage function is used to send a WM_QUIT message (from the window to itself) after the window is destroyed.
     
    Add the following members to the Program class:
    1. static string Hello = "Hello";      // Text to be drawn  
    2. static string AppName = "DrawHello Program";  
    3. static string ClassName = "DrawHelloClass";  
    4. static IntPtr hWnd;         // window handle  

    Then we need to add three methods to the Program class. First add the following method:

    1. public int RegisterClass()  
    2. {  
    3.     Win32.WNDCLASSEX wcex = new Win32.WNDCLASSEX();  
    4.     wcex.style = Win32.ClassStyles.DoubleClicks;  
    5.     wcex.cbSize = (uint)Marshal.SizeOf(wcex);  
    6.     wcex.lpfnWndProc = WndProc;  
    7.     wcex.cbClsExtra = 0;  
    8.     wcex.cbWndExtra = 0;  
    9.     wcex.hIcon = Win32.LoadIcon(IntPtr.Zero, (IntPtr)Win32.IDI_APPLICATION);  
    10.     wcex.hCursor = Win32.LoadCursor(IntPtr.Zero, (int)Win32.IDC_ARROW);  
    11.     wcex.hIconSm = IntPtr.Zero;  
    12.     wcex.hbrBackground = (IntPtr)(Win32.COLOR_WINDOW + 1);  
    13.     wcex.lpszMenuName = null;  
    14.     wcex.lpszClassName = m_ClassName;  
    15.     if (Win32.RegisterClassEx(ref wcex) == 0)  
    16.     {  
    17.         Win32.MessageBox(IntPtr.Zero, "RegisterClassEx failed", m_AppName,  
    18.             (int)(Win32.MB_OK | Win32.MB_ICONEXCLAMATION | Win32.MB_SETFOREGROUND));  
    19.         return (0);  
    20.     }  
    21.     return (1);  
    22. }  
    Before .Net and C# there was C++. Before C++ was C. The Windows API was first developed before C++ existed. The Windows API uses window classes, but they have nothing to do with C# or C++ classes. Windows API classes are totally different. The preceding method RegisterClass registers a Windows class. That tells Windows various things about the window, including:
    • Window Styles
    • Window Procedure (extremely important)
    • Icons
    • Cursor
    • Background Brush
    • Menu
    • Class name

    All that is put into a WNDCLASSEX structure and then the Windows API function RegisterClassEx is called with that. The Window Styles, Window Procedure, Menu, Class Name and Background Brush will be explained in future articles.

    Then add the following method:

     
    1. public int Create()  
    2. {  
    3.     int usedefault = 250;  
    4.     m_hWnd = Win32.CreateWindowEx(0, m_ClassName, m_AppName, Win32.WS_OVERLAPPEDWINDOW | Win32.WS_VISIBLE,  
    5.         usedefault, usedefault, usedefault, usedefault, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);  
    6.     if (m_hWnd != IntPtr.Zero)  
    7.         return (1);  
    8.     Win32.MessageBox(IntPtr.Zero, "CreateWindow failed", m_AppName,  
    9.         (int)(Win32.MB_OK | Win32.MB_ICONEXCLAMATION | Win32.MB_SETFOREGROUND));  
    10.     return (0);  
    11. }  

    That function calls the Windows API function CreateWindowEx to create the window. Then add the following function, which is the last function to be added:

    1. private static IntPtr WndProc(IntPtr hWnd, uint message, IntPtr wParam, IntPtr lParam)  
    2. {  
    3.     switch (message)  
    4.     {  
    5.         case Win32.WM_PAINT:  
    6.             {  
    7.                 IntPtr hDC;  
    8.                 Win32.PAINTSTRUCT ps = new Win32.PAINTSTRUCT();  
    9.                 hDC = Win32.BeginPaint(hWnd, out ps);  
    10.                 Win32.TextOut(hDC, 0, 0, Program.Hello, Program.Hello.Length);  
    11.                 Win32.EndPaint(hWnd, ref ps);  
    12.                 return IntPtr.Zero;  
    13.             }  
    14.         case Win32.WM_DESTROY:  
    15.             Win32.PostQuitMessage(0);  
    16.             return IntPtr.Zero;  
    17.         default:  
    18.             return (Win32.DefWindowProc(hWnd, message, wParam, lParam));  
    19.     }  
    20. }  

    The WndProc method is very important, it is what is commonly called the Wndows Procedure. It processes the messages sent to the window. In this Wndows Procedure, only two messages are processsed; a paint message and a destroy message. For all other messages, the procedure just calls the Windows API function DefWindowProc to allow Windows to do default processing of the message. When a paint message is received, the Windows API function TextOut is called to write the text in Program.Hello to the window. The Windows API functions BeginPaint and EndPaint are called first and last, respectively, to process the paint message. All of this will be further explained in future articles.

    The attached Zip file has a project created using Visual Studio 2010. It has a Win32.cs file with all the Windows API stuff, such as DllImports for the Windows API functions.