Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A Win32 Library for .NET

0.00/5 (No votes)
30 Jun 2003 2  
Provides a class library containing Win32 API function calls, constants, and structures.

Introduction

J# was recently introduced and an article was included on it in MSDN Magazine. The author pointed out that, the J# libraries could be useful for other .NET languages. One reason was that, it included a number of fine classes such as Compression and a library containing Win32 API function calls.

This article is primarily interested in the Win32 API. It turns out that accessing any of the J# libraries is flawed, if you care about performance. J# has no notions of many of the features we come to expect from C#, such as structs and ref parameters. Win32 data structures must be declared as class; J# also use another helper class for each class to copy and pass the data to the Win32 API. Thus each call to a Win32 function is expensive because of the copying and memory allocation involved. In J#, ref parameters are simulated by passing in a single element array. There are also a host of other inefficiencies.

Other .NET languages also include Win32 APIs declarations in some form. VB has the Win32API.txt file and Managed C++, of course, has the header files, but the C# does not have a corresponding library.

Win32 API

I created a new class library Win32, with a default namespace of Win32. That's right, I appropriated the name Win32, because the class encapsulates Win32 function calls, constants and structures.

This is my first pass at the API class library. A new version will be arriving in days; I have released the Win32 API now for feedback.

Handling structures

Implemented in the Win32 namespace are all the various Win32 structures (named in uppercase) such as RECT, POINT, and so on. For example, this is what the LOGFONT structure looks like. It is a top-level class underneath the Win32 namespace.

public struct LOGFONT 
{
   public int lfHeight;
   public int lfWidth;
   public int lfEscapement;
   public int lfOrientation;
   public int lfWeight;
   public byte lfItalic;
   public byte lfUnderline;
   public byte lfStrikeOut;
   public byte lfCharSet;
   public byte lfOutPrecision;
   public byte lfClipPrecision;
   public byte lfQuality;
   public byte lfPitchAndFamily;
   [MarshalAs(UnmanagedType.ByValArray, SizeConst=GDI.LF_FACESIZE)]
   public byte[] lfFaceName; 
}

structs are, by default, sequentially laid out, while classes are auto-laid out, but I should include the SequentialLayout attribute as well, because the behavior is not specified in the standard. Some structs should actually be explicitly laid out because of the presence of unions in them; but I haven't had time to do that yet.

Method calls

There are few special classes such as Kernel, User, GDI, AdvApi, NetApi, WinMM, Shell and others. These classes contain all the Win32 API calls stemming from the system DLL of the same name. I have omitted the 32 suffix from Kernel32, User32, GDI32, and Shell32, because I felt they were redundant and unattractive.

public abstract class GDI
{
   [DllImport("gdi32")] 
    public static extern int AbortDoc(HDC hdc);
   [DllImport("gdi32")] 
    public static extern int AbortPath(HDC hdc);
   [DllImport("gdi32")] 
    public static extern int AddFontResource(string lpFileName);   
   [DllImport("gdi32")] 
    public static extern int AngleArc(HDC hdc, int x, int y, int dwRadius, 
           double eStartAngle, double eSweepAngle);
   [DllImport("gdi32")] 
    public static extern int AnimatePalette(HANDLE hPalette, 
           int wStartIndex, int wNumEntries, 
           PALETTEENTRY[] lpPaletteColors);
   [DllImport("gdi32")] 
    public static extern int Arc(HDC hdc, int X1, int Y1, int X2, int Y2, 
           int X3, int Y3, int X4, int Y4);
   [DllImport("gdi32")] 
    public static extern int ArcTo(HDC hdc, int X1, int Y1, int X2, int Y2, 
           int X3, int Y3, int X4, int Y4);
   [DllImport("gdi32")] 
    public static extern int BeginPath(HDC hdc);
   [DllImport("gdi32")] 
    public static extern int BitBlt(HDC hDestDC, int x, int y, int nWidth, 
           int nHeight, HDC hSrcDC, int xSrc, int ySrc, int dwRop);
 
   .....

    public const int ABSOLUTE = 1;
    public const int AD_CLOCKWISE = 2;
    public const int AD_COUNTERCLOCKWISE = 1;
    public const int ALTERNATE = 1;
    public const int ANSI_CHARSET = 0;
    public const int ANSI_FIXED_FONT = 11;
    public const int ANSI_VAR_FONT = 12;
    public const int ARABIC_CHARSET = 178;
    public const int ASPECTX = 40; 
    public const int ASPECTXY = 44; 
    public const int ASPECTY = 42; 
    public const int ASPECT_FILTERING = 0x1;
}

The constants and structures are stable. Most of the changes overt the next week will occur to Win32 function signatures; however, most of them are correct right now even though they were changed through an automated process.

Some caution: I manually searched for function calls that should take a StringBuilder in place of a String. Any function that returns or modifies a string should be passing in a StringBuilder. There may be other instances where I did not correctly use ref keyword on a parameter of a primitive type. A function taking a non-POINT struct was assumed to be a ref; I haven't really found a case where that was not true. (I will check again, since there are a few structures such as COORD that can fit into a short or int).

A future modification will be more nuanced and distinguish between out and ref parameters; for now, using a ref parameter instead of an out parameter will not produce negative consequences; however, the compiler will require a proper initialization of the structure before passing it into an API call.

Handling constants

Also included under the different DLL class (User, Kernel, GDI, ...) are all the constants that would naturally be categorized under that DLL whose name is that of the parent class.

I originally grouped constants in various enums whose name consisted of the prefix. So, WM_PAINT was WM.PAINT, however, I felt that it might actually make it harder to find the desired constant, and it was also more work on my part. There were also issues about sub-constants, like WM_DDE_ANY, should they be a WM.DDE_ANY constant, a WM.DDE.ANY constant, or a WM.DDE_ANY constant. Well see, I have fully decided on this point.

Handling errors

Error codes are placed as constants within the abstract class ERROR. I could use an enum to store the values but this would require me to identify any call that returns an error code, but the benefit is that the compiler and debugger already knows how to display an enum string.

An alternative is to have Win32 methods that return error codes throw an exception. I plan on investigating this. For most API calls, I plan to merely return the error code value, but, some select API calls, I will throw an exception. This will depend on the performance requirements of call and severity of the error. For those functions that return error values and have a single out parameter, I'll include a second variant that throws an exception and returns the out parameter; those calls would have PreservedSig set to false.

Future additions

Over the next week, I will do the following:

  1. I plan to cross-check the library with VB's Win32API.txt, the C++ header files, the J# library and MSDN through some automated scripts.
  2. I also plan to introduce new types to match the handle types that Win32 uses; currently, handles are accepted as IntPtr. My additions will provide a HWND, HDC, and other value types that support conversions, equality and standard constants like HWND_DESKTOP.
  3. I will overload certain API calls multiple types to allow for the range of possible data types accepted by Win32 calls. For example, SendMessage can accept a wide variety of calls.
  4. I will also define delegates for the few CALLBACK handlers that Win32 supports.
  5. I plan to place to include common COM interfaces as well into class library.

Performance

It's natural to wonder what the performance implications of having thousands of Win32 APIs methods declared in a library. I doubt there will be very much, but when the library is finally checked, I will conduct a performance analysis of introducing a large library where few functions are used. Since I have divided the library based on the DLL, it would be easy to remove those DLLs that you do utilize.

Conclusion

I believe this a worthy addition for Code Project and C# developers everywhere. Since this is a class library, it will work for other .NET languages. I do not include pointers or other non CLSCompliant types in the Win32 API method signatures. Although for a few calls like CopyMemory, I might actually add an additional overload with the (CLSCompliant(false) attribute) that does accept pointers.

I would like to receive feedback to correct or restructure the library. If you had made noteworthy editions, I will do a diff and reapply to changes to my own copy, which I resubmit.

I would appreciate your vote.

Version history

  • July 1, 2003 - Original article on Win32

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here