P/调用枚举



我想从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)

最新更新