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:
- Win32.MSG Msg = new Win32.MSG();
- int rv;
- if (Program.RegisterClass() == 0)
- return;
- if (Program.Create() == 0)
- return;
-
- while ((rv = Win32.GetMessage(out Msg, IntPtr.Zero, 0, 0)) > 0)
- {
- Win32.TranslateMessage(ref Msg);
- Win32.DispatchMessage(ref Msg);
- }
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:
- static string Hello = "Hello";
- static string AppName = "DrawHello Program";
- static string ClassName = "DrawHelloClass";
- static IntPtr hWnd;
Then we need to add three methods to the Program class. First add the following method:
- public int RegisterClass()
- {
- Win32.WNDCLASSEX wcex = new Win32.WNDCLASSEX();
- wcex.style = Win32.ClassStyles.DoubleClicks;
- wcex.cbSize = (uint)Marshal.SizeOf(wcex);
- wcex.lpfnWndProc = WndProc;
- wcex.cbClsExtra = 0;
- wcex.cbWndExtra = 0;
- wcex.hIcon = Win32.LoadIcon(IntPtr.Zero, (IntPtr)Win32.IDI_APPLICATION);
- wcex.hCursor = Win32.LoadCursor(IntPtr.Zero, (int)Win32.IDC_ARROW);
- wcex.hIconSm = IntPtr.Zero;
- wcex.hbrBackground = (IntPtr)(Win32.COLOR_WINDOW + 1);
- wcex.lpszMenuName = null;
- wcex.lpszClassName = m_ClassName;
- if (Win32.RegisterClassEx(ref wcex) == 0)
- {
- Win32.MessageBox(IntPtr.Zero, "RegisterClassEx failed", m_AppName,
- (int)(Win32.MB_OK | Win32.MB_ICONEXCLAMATION | Win32.MB_SETFOREGROUND));
- return (0);
- }
- return (1);
- }
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:
- public int Create()
- {
- int usedefault = 250;
- m_hWnd = Win32.CreateWindowEx(0, m_ClassName, m_AppName, Win32.WS_OVERLAPPEDWINDOW | Win32.WS_VISIBLE,
- usedefault, usedefault, usedefault, usedefault, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
- if (m_hWnd != IntPtr.Zero)
- return (1);
- Win32.MessageBox(IntPtr.Zero, "CreateWindow failed", m_AppName,
- (int)(Win32.MB_OK | Win32.MB_ICONEXCLAMATION | Win32.MB_SETFOREGROUND));
- return (0);
- }
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:
- private static IntPtr WndProc(IntPtr hWnd, uint message, IntPtr wParam, IntPtr lParam)
- {
- switch (message)
- {
- case Win32.WM_PAINT:
- {
- IntPtr hDC;
- Win32.PAINTSTRUCT ps = new Win32.PAINTSTRUCT();
- hDC = Win32.BeginPaint(hWnd, out ps);
- Win32.TextOut(hDC, 0, 0, Program.Hello, Program.Hello.Length);
- Win32.EndPaint(hWnd, ref ps);
- return IntPtr.Zero;
- }
- case Win32.WM_DESTROY:
- Win32.PostQuitMessage(0);
- return IntPtr.Zero;
- default:
- return (Win32.DefWindowProc(hWnd, message, wParam, lParam));
- }
- }
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.