Win32Hello.zip
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. 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 htis is useful for Windows Store (Windows 8) programming.
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.
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". Deleteing 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 or even an OOP 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;
// Main message loop:
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 to the Program class:
static string Hello = "Hello";
private static string m_AppName = "DrawHello Program";
private static string m_ClassName = "DrawHelloClass";
private IntPtr m_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 to the CWindow class, 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.