符数组的结构。例如,GetTimeZoneInformation() 函数接受指向以下结构的指针:
typedef struct _TIME_ZONE_INFORMATION {
LONG Bias;
WCHAR StandardName[ 32 ];
SYSTEMTIME StandardDate;
LONG StandardBias;
WCHAR DaylightName[ 32 ];
SYSTEMTIME DaylightDate;
LONG DaylightBias;
} TIME_ZONE_INFORMATION, *PTIME_ZONE_INFORMATION;
在 C# 中使用它需要有两种结构。一种是 SYSTEMTIME,它的设置很简单:
struct SystemTime
{
public short wYear;
public short wMonth;
public short wDayOfWeek;
public short wDay;
public short wHour;
public short wMinute;
public short wSecond;
public short wMilliseconds;
}
这里没有什么特别之处;另一种是 TimeZoneInformation,它的定义要复杂一些:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct TimeZoneInformation
{
public int bias;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string standardName;
SystemTime standardDate;
public int standardBias;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string daylightName;
SystemTime daylightDate;
public int daylightBias;
}
此定义有两个重要的细节。第一个是 MarshalAs 属性:
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
查看 ByValTStr 的文档,我们发现该属性用于内嵌的字符数组;另一个是 SizeConst,它用于设置数组的大小。
我在第一次编写这段代码时,遇到了执行引擎错误。通常这意味着部分互操作覆盖了某些内存,表明结构的大小存在错误。我使用 Marshal.SizeOf() 来获取所使用的封送拆收器的大小,结果是 108 字节。我进一步进行了调查,很快回忆起用于互操作的默认字符类型是 Ansi 或单字节。而函数定义中的字符类型为 WCHAR,是双字节,因此导致了这一问题。
我通过添加 StructLayout 属性进行了更正。结构在默认情况下按顺序布局,这意味着所有字段都将以它们列出的顺序排列。CharSet 的值被设置为 Unicode,以便始终使用正确的字符类型。
经过这样处理后,该函数一切正常。您可能想知道我为什么不在此函数中使用 CharSet.Auto。这是因为,它也没有 A 和 W 变体,而始终使用 Unicode 字符串,因此我采用了上述方法编码。
具有回调的函数
当 Win32 函数需要返回多项数据时,通常都是通过回调机制来实现的。开发人员将函数指针传递给函数,然后针对每一项调用开发人员的函数。
在 C# 中没有函数指针,而是使用“委托”,在调用 Win32 函数时使用委托来代替函数指针。
EnumDesktops() 函数就是这类函数的一个示例:
BOOL EnumDesktops(
HWINSTA hwinsta, // 窗口实例的句柄
DESKTOPENUMPROC lpEnumFunc, // 回调函数
LPARAM lParam // 用于回调函数的值
);
HWINSTA 类型由 IntPtr 代替,而 LPARAM 由 int 代替。DESKTOPENUMPROC 所需的工作要多一些。下面是 MSDN 中的定义:
BOOL CALLBACK EnumDesktopProc(
LPTSTR lpszDesktop, // 桌面名称
LPARAM lParam // 用户定义的值
);
我们可以将它转换为以下委托:
delegate bool EnumDesktopProc([MarshalAs(UnmanagedType.LPTStr)] string desktopName,int lParam);
完成该定义后,我们可以为 EnumDesktops() 编写以下定义:
[DllImport("user32.dll", CharSet = Cha