我想从C#调用DISM API以了解安装了哪些可选的Windows功能。DISM函数没有出现在著名的pinvoke.net网站上,所以我开始使用P/Invoke互操作助手工具来解决它。
问题涉及这样一个开始的结构:
[StructLayout(LayoutKind.Sequential)]
public struct DismFeatureInfo
{
/// PCWSTR->WCHAR*
[MarshalAs(UnmanagedType.LPWStr)]
public string FeatureName;
/// DismPackageFeatureState
[MarshalAs(UnmanagedType.I4)]
public DismPackageFeatureState FeatureState;
/// PCWSTR->WCHAR*
[MarshalAs(UnmanagedType.LPWStr)]
public string DisplayName;
我有一个指向非托管DismFeatureInfo
的指针。当我使用Marshal.PtrToStructure
时,我会得到一个FatalExecutionEngineError
异常。
如果我注释掉DisplayName
以后的所有字段(只保留前两个),那么它就起作用了,并且我在FeatureName
中得到了一个有效的字符串。
如果我使用调试器"内存"窗口来检查结构地址处的内存,则会按预期进行布局。我用64位运行,它开始:
[ 8 bytes ] // pointer to a string containing the feature name
[ 4 bytes ] // enum value
[ 8 bytes ] // pointer to a string containing the display name
更重要的是,如果我手动进行偏移以读取指针,然后从中获取字符串:
var featureName = Marshal.PtrToStringUni(Marshal.ReadIntPtr(featureInfoPtr));
var displayName = Marshal.PtrToStringUni(Marshal.ReadIntPtr(featureInfoPtr, 8 + 4));
这很好——displayName
包含显示名称。
这一切都表明enum DismPackageFeatureState
被编组为错误的大小。从检查原始内存来看,它肯定应该是4个字节。我放了一个属性,说它应该是I4
。
如果我将FeatureState
字段改为:
public UInt32 FeatureState;
那么我仍然得到了例外。那到底发生了什么?
(注意。我对p/Invoke方面特别感兴趣,而不是我最初的问题。从那以后,我通过new ManagementClass("Win32_OptionalFeature")
更容易地找到了我需要的信息,但我仍然对我在尝试使p/Invoke工作时发现的内容感到困惑,并想知道发生了什么。)
这似乎是对齐不匹配,而不是大小不匹配。DisplayName
指针具有8字节对齐。但根据您对布局的分析,它被放置在4字节的边界上。问题中的C#代码将使DisplayName
偏移16。但你的分析表明,它的偏移量为12。这让我怀疑本机struct
实际上是打包的。
[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct DismFeatureInfo
....
应该解决你的问题。
更新
我可以确认我的预感是正确的。在包装结构声明的dismapi.h中可以找到以下内容:
#pragma pack(push, 1)