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.)