P /调用枚举

时间:2012-12-13 11:13:58

标签: alignment pinvoke packing

我想从C#调用DISM API以找出安装了哪些可选的Windows功能。 DISM功能没有出现在知名的pinvoke.net网站上,所以我开始使用P / Invoke Interop Assistant工具来解决它。

问题涉及一个如下所示的结构:

[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工作时发现并且想知道发生了什么。)

1 个答案:

答案 0 :(得分:2)

这似乎是对齐而不是大小的不匹配。 DisplayName指针有8字节对齐。但根据您对布局的分析,它被放置在4字节边界上。你问题中的C#代码会给DisplayName一个16的偏移量。但你的分析表明它的偏移量为12.这使我怀疑本机struct实际上是打包的。

[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct DismFeatureInfo
....

应该解决你的问题。

<强>更新

我可以确认我的预感是正确的。以下内容可以在dismapi.h中找到包装结构声明:

#pragma pack(push, 1)