With the .NET Framework, it is much easier and faster to build an application. However there are still times when you need to use Win32 API, which usually causes headaches to .NET programmers. That’s the intent I started this project – P/Invoke Library.
Due to limited spare time, I’ve added the Win32 functions to this library in an ad-hoc manner, so don’t be surprised at the code at the time of this writing. If you want to join in this project and improve it, I’d be very glad.
The P/Invoke Interop Assistant and the pinvoke.net wiki are great references for P/Invoke too.
Sharing Source Code between Platforms
This library has too builds – one for the .NET Framework, and the other for the .NET Compact Framework. As you know most of the API have the same interface between the two platforms, so sharing the source code is necessary to reduce the work involved. There are at least two means to share classes between desktop and mobile – sharing the binary, or sharing the source code files.
To share the binary, we need to build the assembly under the Windows Mobile platform, and then we can use the same binary on desktop too. The reason is that the .NET Compact Framework (NET CF) assemblies are “retargetable”, which means that when we use an assembly in NET CF (e.g. mscorlib) on desktop, the desktop CLR will instead load its counterpart built for desktop. So a NET CF assembly (binary) can be used on desktop directly, provided that it is not platform-dependent. Because the desktop assemblies are not retargetable, we cannot do the reverse.
In case we have to call platform-specific classes / API at run time, we can branch these calls based on whether or not:
Environment.OSVersion.Platform == PlatformID.WinCE |
To use the second option, i.e. sharing source code, there are again two options – using the #directives (similar to C++) or using linked files. The following steps show how to use linked files to share source code:
- Create a source code file which contains the common (or shared) part of a class. This class should be partial class. The code file is added in one project; in another project you add a link to this file.
- In the desktop project add a partial class that contains code specific to the desktop platform;
- In the smart devices project add a partial class that contains code specific to smart devices.
The P/Invoke project uses linked files to share code, as you can see in the source code. In this way we can avoid the lots of #derectives for the most part. But #derective is still helpful, as used here:
public static partial class User32 { #if PocketPC private const string User32Dll = "coredll.dll"; #else private const string User32Dll = "user32.dll"; #endif [DllImport(User32Dll, SetLastError = true)] public static extern int GetWindowLong(IntPtr hWnd, GWL nIndex); |
An interesting Demo – a Win32 Windows application in C#
Have you ever thought of building a classic-style Win32 Windows application using C#, rather than using C/C++? Here is a demo to show you how to do that with the P/Invoke library:
//This file demonstrates how to build a Win32 Windows program using C#. //Created by Warren Tang on 8/27/2008 using System; using System.ComponentModel; using System.Runtime.InteropServices; using Win32; namespace Win32Demo { /// <summary> /// Mimics a Win32 program using C#. /// </summary> class Program { //This is WinMain. static void Main(string[] args) { IntPtr hInstance = Marshal.GetHINSTANCE(typeof(Program).Module); string wndClassName = "Win32Demo"; string wndName = "Win32 in C#"; MyRegisterClass(hInstance, wndClassName); InitInstance(hInstance, wndClassName, wndName); //Message loop User32.MSG msg = new User32.MSG(); while (User32.GetMessage(out msg, IntPtr.Zero, 0, 0)) { User32.TranslateMessage(ref msg); User32.DispatchMessage(ref msg); } } //Register window class private static void MyRegisterClass(IntPtr hInstance, string wndClassName) { User32.WNDCLASSEX wcex = new User32.WNDCLASSEX() { cbSize = Marshal.SizeOf(typeof(User32.WNDCLASSEX)), style = User32.CS.CS_HREDRAW | User32.CS.CS_VREDRAW, lpfnWndProc = MyWndProc, cbClsExtra = 0, cbWndExtra = 0, hInstance = hInstance, hIcon = Properties.Resources.Icon1.Handle, hbrBackground = (IntPtr)(GDI32.COLOR.COLOR_WINDOW + 1), hCursor = User32.LoadCursor(IntPtr.Zero, User32.IDC.IDC_ARROW), lpszClassName = wndClassName, lpszMenuName = null, hIconSm = IntPtr.Zero }; short atom = User32.RegisterClassEx(ref wcex); if (atom == 0) throw new Win32Exception(Marshal.GetLastWin32Error()); } //Create window private static void InitInstance(IntPtr hInstance, string wndClassName, string wndName) { IntPtr hWnd = User32.CreateWindowEx( User32.WS_EX.WS_EX_NONE, wndClassName, wndName, User32.WS.WS_OVERLAPPEDWINDOW, User32.CW_USEDEFAULT, User32.CW_USEDEFAULT, User32.CW_USEDEFAULT, User32.CW_USEDEFAULT, IntPtr.Zero, IntPtr.Zero, hInstance, IntPtr.Zero); if (hWnd == IntPtr.Zero) throw new Win32Exception(Marshal.GetLastWin32Error()); //Cannot find window class??? User32.ShowWindow(hWnd, User32.SW.SW_SHOWNORMAL); User32.UpdateWindow(hWnd); } private static int MyWndProc(IntPtr hWnd, uint msg, uint wParam, uint lParam) { switch (msg) { case (int)User32.WM.WM_PAINT: GDI32.RECT rect = new GDI32.RECT(); User32.GetClientRect(hWnd, out rect); GDI32.PAINTSTRUCT ps = new GDI32.PAINTSTRUCT(); IntPtr hdc = User32.BeginPaint(hWnd, out ps); User32.DrawText(hdc, "Hello, World!", -1, ref rect, User32.DT.DT_CENTER | User32.DT.DT_VCENTER | User32.DT.DT_SINGLELINE); User32.EndPaint(hWnd, out ps); break; case (int)User32.WM.WM_DESTROY: User32.PostQuitMessage(0); break; default: return User32.DefWindowProc(hWnd, msg, wParam, lParam); } return 0; } } } |
Links
The P/Invoke Library project hosted on CodePlex http://www.codeplex.com/pinvoke

