适用于 .NET 的 Win32 库






4.81/5 (86投票s)
2003 年 7 月 1 日
6分钟阅读

270947

1868
提供了一个包含 Win32 API 函数调用、常量和结构的类库。
引言
J# 最近被推出,并且 MSDN 杂志上有一篇关于它的文章。作者指出,J# 库可能对其他 .NET 语言有用。一个原因是,它包含许多很好的类,例如 Compression
和一个包含 Win32 API 函数调用的库。
本文主要关注 Win32 API。事实证明,如果关心性能,访问任何 J# 库都会有问题。J# 没有我们期望从 C# 中获得许多功能的概念,例如 struct
和 ref
参数。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 结构(大写命名),例如 RECT
、POINT
等等。例如,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)实际上应该显式布局;但我还没有时间这样做。
方法调用
有一些特殊的类,例如 Kernel
、User
、GDI
、AdvApi
、NetApi
、WinMM
、Shell
等。这些类包含了来自同名系统 DLL 的所有 Win32 API 调用。我省略了 Kernel32
、User32
、GDI32
和 Shell32
的 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
,可以放入 short
或 int
中)。
未来的修改将更加细致,区分 out
和 ref
参数;目前,使用 ref
参数代替 out
参数不会产生负面影响;但是,编译器将要求在将结构传递给 API 调用之前对其进行正确初始化。
处理常量
在不同的 DLL 类(User
、Kernel
、GDI
等)下也包含了所有自然地归类于该 DLL 的常量,该 DLL 的名称与父类相同。
我最初将常量分组在各种枚举中,其名称由前缀组成。因此,WM_PAINT
是 WM.PAINT
;但是,我认为这实际上可能会使查找所需常量变得更加困难,而且对我来说也更麻烦。还有关于子常量的问题,例如 WM_DDE_ANY
,它们应该是一个 WM.DDE_ANY
常量、一个 WM.DDE.ANY
常量,还是一个 WM.DDE_ANY
常量。我们拭目以待,我已经完全决定了这一点。
处理错误
错误代码作为常量放置在抽象类 ERROR
中。我可以使用 enum
来存储值,但这需要我识别任何返回错误代码的调用,但好处是编译器和调试器已经知道如何显示 enum
字符串。
另一种方法是让返回错误代码的 Win32 方法抛出异常。我计划对此进行研究。对于大多数 API 调用,我计划仅仅返回错误代码值,但对于一些精选的 API 调用,我会抛出异常。这将取决于调用的性能要求和错误的严重程度。对于那些返回错误值并具有单个 out
参数的函数,我将包含一个抛出异常并返回 out
参数的第二个变体;这些调用将 PreservedSig
设置为 false
。
未来的添加
在接下来的几周内,我将做以下事情:
- 我计划通过一些自动化脚本将该库与 VB 的 Win32API.txt、C++ 头文件、J# 库和 MSDN 进行交叉检查。
- 我还计划引入新的类型来匹配 Win32 使用的句柄类型;目前,句柄被接受为
IntPtr
。我的添加将提供HWND
、HDC
以及其他支持转换、相等性和标准常量(如HWND_DESKTOP
)的值类型。 - 我将重载某些 API 调用以支持 Win32 调用可接受的各种数据类型。例如,
SendMessage
可以接受各种调用。 - 我还会为 Win32 支持的少数
CALLBACK
处理程序定义委托。 - 我计划将常见的 COM 接口也包含到类库中。
性能
人们自然会想知道在一个库中声明数千个 Win32 API 方法的性能影响。我怀疑影响不会太大,但在库最终经过检查后,我将进行一项性能分析,以评估引入一个使用少量函数的庞大库的影响。由于我已按 DLL 分割了库,因此很容易删除您未使用的 DLL。
结论
我认为这对 Code Project 和所有 C# 开发者来说都是一项有价值的补充。由于这是一个类库,它也将适用于其他 .NET 语言。我不在 Win32 API 方法签名中包含指针或其他非 CLSCompliant
类型。尽管对于像 CopyMemory
这样的少数调用,我可能会添加一个额外的重载(带有 (CLSCompliant
(false
) 属性))来接受指针。
我希望收到反馈来纠正或重构该库。如果您进行了值得注意的大改动,我会进行 diff 并将更改重新应用到我自己的副本中,然后重新提交。
感谢您的投票。
版本历史
- 2003 年 7 月 1 日 - Win32 原始文章