65.9K
CodeProject 正在变化。 阅读更多。
Home

适用于 .NET 的 Win32 库

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.81/5 (86投票s)

2003 年 7 月 1 日

6分钟阅读

viewsIcon

270947

downloadIcon

1868

提供了一个包含 Win32 API 函数调用、常量和结构的类库。

引言

J# 最近被推出,并且 MSDN 杂志上有一篇关于它的文章。作者指出,J# 库可能对其他 .NET 语言有用。一个原因是,它包含许多很好的类,例如 Compression 和一个包含 Win32 API 函数调用的库。

本文主要关注 Win32 API。事实证明,如果关心性能,访问任何 J# 库都会有问题。J# 没有我们期望从 C# 中获得许多功能的概念,例如 structref 参数。Win32 数据结构必须声明为 class;J# 还使用另一个辅助类来复制数据并将其传递给 Win32 API。因此,由于复制和内存分配的开销,每次调用 Win32 函数的成本都很高。在 J# 中,ref 参数通过传递单个元素的数组来模拟。此外还有许多其他低效之处。

其他 .NET 语言也以某种形式包含 Win32 API 声明。VB 有 Win32API.txt 文件,托管 C++ 当然也有头文件,但 C# 没有相应的库。

Win32 API

我创建了一个新的类库 Win32,默认命名空间为 Win32。是的,我征用了 Win32 这个名字,因为该类封装了 Win32 函数调用、常量和结构。

这是我对 API 类库的首次尝试。新版本将在几天内发布;我现在发布 Win32 API 以供反馈。

处理结构

Win32 命名空间中实现了各种 Win32 结构(大写命名),例如 RECTPOINT 等等。例如,LOGFONT 结构的外观如下。它是 Win32 命名空间下的一个顶级类。

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 默认是顺序布局的,而类是自动布局的,但我应该也包含 SequentialLayout 属性,因为在标准中没有指定这种行为。有些 structs 由于包含联合(unions)实际上应该显式布局;但我还没有时间这样做。

方法调用

有一些特殊的类,例如 KernelUserGDIAdvApiNetApiWinMMShell 等。这些类包含了来自同名系统 DLL 的所有 Win32 API 调用。我省略了 Kernel32User32GDI32Shell32 的 32 后缀,因为我认为它们是多余且不吸引人的。

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;
}

常量和结构是稳定的。下周的大部分更改将发生在 Win32 函数签名上;但是,即使它们是通过自动化过程更改的,现在大部分都是正确的。

一些注意事项:我手动搜索了应该使用 StringBuilder 而不是 String 的函数调用。任何返回或修改字符串的函数都应该传入一个 StringBuilder。可能还有其他我没有正确地在原始类型参数上使用 ref 关键字的情况。一个接受非 POINT struct 的函数被假定为 ref;我实际上还没有找到一个不是这种情况的例子。(我会再次检查,因为有一些结构,如 COORD,可以放入 shortint 中)。

未来的修改将更加细致,区分 outref 参数;目前,使用 ref 参数代替 out 参数不会产生负面影响;但是,编译器将要求在将结构传递给 API 调用之前对其进行正确初始化。

处理常量

在不同的 DLL 类(UserKernelGDI 等)下也包含了所有自然地归类于该 DLL 的常量,该 DLL 的名称与父类相同。

我最初将常量分组在各种枚举中,其名称由前缀组成。因此,WM_PAINTWM.PAINT;但是,我认为这实际上可能会使查找所需常量变得更加困难,而且对我来说也更麻烦。还有关于子常量的问题,例如 WM_DDE_ANY,它们应该是一个 WM.DDE_ANY 常量、一个 WM.DDE.ANY 常量,还是一个 WM.DDE_ANY 常量。我们拭目以待,我已经完全决定了这一点。

处理错误

错误代码作为常量放置在抽象类 ERROR 中。我可以使用 enum 来存储值,但这需要我识别任何返回错误代码的调用,但好处是编译器和调试器已经知道如何显示 enum 字符串。

另一种方法是让返回错误代码的 Win32 方法抛出异常。我计划对此进行研究。对于大多数 API 调用,我计划仅仅返回错误代码值,但对于一些精选的 API 调用,我会抛出异常。这将取决于调用的性能要求和错误的严重程度。对于那些返回错误值并具有单个 out 参数的函数,我将包含一个抛出异常并返回 out 参数的第二个变体;这些调用将 PreservedSig 设置为 false

未来的添加

在接下来的几周内,我将做以下事情:

  1. 我计划通过一些自动化脚本将该库与 VB 的 Win32API.txt、C++ 头文件、J# 库和 MSDN 进行交叉检查。
  2. 我还计划引入新的类型来匹配 Win32 使用的句柄类型;目前,句柄被接受为 IntPtr。我的添加将提供 HWNDHDC 以及其他支持转换、相等性和标准常量(如 HWND_DESKTOP)的值类型。
  3. 我将重载某些 API 调用以支持 Win32 调用可接受的各种数据类型。例如,SendMessage 可以接受各种调用。
  4. 我还会为 Win32 支持的少数 CALLBACK 处理程序定义委托。
  5. 我计划将常见的 COM 接口也包含到类库中。

性能

人们自然会想知道在一个库中声明数千个 Win32 API 方法的性能影响。我怀疑影响不会太大,但在库最终经过检查后,我将进行一项性能分析,以评估引入一个使用少量函数的庞大库的影响。由于我已按 DLL 分割了库,因此很容易删除您未使用的 DLL。

结论

我认为这对 Code Project 和所有 C# 开发者来说都是一项有价值的补充。由于这是一个类库,它也将适用于其他 .NET 语言。我不在 Win32 API 方法签名中包含指针或其他非 CLSCompliant 类型。尽管对于像 CopyMemory 这样的少数调用,我可能会添加一个额外的重载(带有 (CLSCompliant(false) 属性))来接受指针。

我希望收到反馈来纠正或重构该库。如果您进行了值得注意的大改动,我会进行 diff 并将更改重新应用到我自己的副本中,然后重新提交。

感谢您的投票。

版本历史

  • 2003 年 7 月 1 日 - Win32 原始文章
© . All rights reserved.