سؤال

I wanted to call the DISM API from C# to find out which optional Windows features are installed. DISM functions don't appear on the well-known pinvoke.net site, so I started figuring it out with the P/Invoke Interop Assistant tool.

The problem involves a structure that starts like this:

[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;

I have a pointer to an unmanaged DismFeatureInfo. When I use Marshal.PtrToStructure I get a FatalExecutionEngineError exception.

If I comment out all the fields from DisplayName onward (only keeping the first two) then it works and I get a valid string in FeatureName.

If I use the debugger Memory window to inspect the memory at the address of the structure, it is laid out as expected. I'm running in 64-bit, and it starts:

[ 8 bytes ] // pointer to a string containing the feature name
[ 4 bytes ] // enum value 
[ 8 bytes ] // pointer to a string containing the display name

What's more, if I manually do the offsets to read the pointers and then get the strings from them:

var featureName = Marshal.PtrToStringUni(Marshal.ReadIntPtr(featureInfoPtr));
var displayName = Marshal.PtrToStringUni(Marshal.ReadIntPtr(featureInfoPtr, 8 + 4));

That works fine - displayName contains the display name.

Which all suggests that the enum DismPackageFeatureState is being marshalled as the wrong size. From inspecting the raw memory it is definitely supposed to be 4 bytes. And I've put an attribute saying it should be I4.

And if I change the FeatureState field to say:

public UInt32 FeatureState;

then I still get the exception. So what is going on?

(NB. I'm specifically interested in the P/Invoke aspects, not my original problem. I've since found the information I needed much more easily via new ManagementClass("Win32_OptionalFeature") but am still puzzled by what I found while trying to get P/Invoke working and would like to know what was going on.)

هل كانت مفيدة؟

المحلول

This appears to be a mismatch of alignment rather than size. The DisplayName pointer has 8 byte alignment. But according to your analysis of the layout, it is placed on a 4 byte boundary. The C# code in your question will give DisplayName an offset of 16. But your analysis says that it has an offset of 12. This leads me to suspect that the native struct is in fact packed.

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

should resolve your problems.

Update

I can confirm that my hunch was correct. The following can be found in dismapi.h wrapping the struct declarations:

#pragma pack(push, 1)
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top