在尝试将现有的32位应用程序转换为64位应用程序时,我遇到了让一些COM Interop代码正常工作的问题。该代码正在使用我从各种Windows SDK头文件/IDL文件翻译的托管代码访问结构化存储API。
当我尝试使用STG_E_INVALIDPARAMETER调用IPropertyStorage.ReadMultiple()时,代码失败。前面对StgOpenStorageEx和IPropertySetStorage.Open的互操作调用似乎工作得很好。MSDN声称这个错误意味着我的PROPSPEC参数有问题,但相同的参数值在编译为32位应用程序时工作得很好,并且我返回的值是指定属性的正确字符串值。
以下是我认为的相关部分:
// PropertySpecKind enumeration.
public enum PropertySpecKind : uint
{
Lpwstr = 0,
PropId = 1
}
// PropertySpec structure:
[StructLayout(LayoutKind.Explicit)]
public struct PropertySpec
{
[FieldOffset(0)] public PropertySpecKind kind;
[FieldOffset(4)] public uint propertyId;
[FieldOffset(4)] public IntPtr name;
}
// PropertyVariant Structure:
[StructLayout(LayoutKind.Explicit)]
public struct PropertyVariant
{
[FieldOffset(0)] public Vartype vt;
[FieldOffset(8)] public IntPtr pointerValue;
}
// IPropertyStorage interface
[ComImport]
[Guid("00000138-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IPropertyStorage
{
int ReadMultiple(
uint count,
[MarshalAs(UnmanagedType.LPArray, SizeConst = 0)] PropertySpec[] properties,
[Out, MarshalAs(UnmanagedType.LPArray, SizeConst = 0)] PropertyVariant[] values);
void WriteMultiple(
uint count,
[MarshalAs(UnmanagedType.LPArray, SizeConst = 0)] PropertySpec[] properties,
[MarshalAs(UnmanagedType.LPArray, SizeConst = 0)] PropertyVariant[] values,
uint miniumumPropertyId);
}
var properties = new PropertySpec[1];
properties[0].kind = PropertySpecKind.PropId;
properties[0].propertyId = 2;
var propertyValues = new PropertyVariant[1];
// This helper method just calls StgOpenStorageEx with appropriate parameters.
var propertySetStorage = StorageHelper.GetPropertySetStorageReadOnly(fileName);
var propertyStorage = propertySetStorage.Open(StoragePropertySets.PSGUID_SummaryInformation, StorageMode.Read | StorageMode.ShareExclusive);
propertyStorage.ReadMultiple(1, properties, propertyValues); // Exception is here.发布于 2013-06-04 02:54:02
[StructLayout(LayoutKind.Sequential)]
public struct PropertySpec
{
public PropertySpecKind kind;
public PropertySpecData data;
}是的,这是声明该结构的一个好方法。现在,将计算data.name字段的偏移量的任务留给pinvoke interop封送处理程序,它会得到正确的结果。
name字段是一个IntPtr,它在32位模式下占用4个字节,在64位模式下占用8个字节。结构的字段与字段大小的整数倍的偏移量对齐。默认包装是8,这意味着任何8个字节或更少的字段都将对齐。这使得该字段在32位模式下对齐要求为4,在64位模式下为8。以前,您通过使用FieldOffset(4)属性在偏移量4处强制它。对于32位代码可以,但对于64位代码是错误的偏移量。
在这个MSDN Library article中有一些关于结构打包的背景知识。
发布于 2013-06-04 01:47:13
在尝试了互操作定义的多次迭代之后,我终于找到了答案。我不能确切地确定这为什么会有所不同,但我所做的更改是将单个PROPSPEC和PROPVARIANT结构定义替换为嵌套的结构定义;基本上,我将匿名联合拆分成它们自己的类型。我假设在我这样做的时候会有某种对齐问题得到解决。
具体地说,PROPSPEC的工作32位定义如下:
[StructLayout(LayoutKind.Explicit)]
public struct PropertySpec
{
[FieldOffset(0)]
public PropertySpecKind kind;
[FieldOffset(4)]
public uint propertyId;
[FieldOffset(4)]
public IntPtr name;
}我把它改成这样,现在它在两个架构上都有效:
[StructLayout(LayoutKind.Sequential)]
public struct PropertySpec
{
public PropertySpecKind kind;
public PropertySpecData data;
}
[StructLayout(LayoutKind.Explicit)]
public struct PropertySpecData
{
[FieldOffset(0)]
public uint propertyId;
[FieldOffset(0)]
public IntPtr name;
}发布于 2013-06-02 18:28:30
您应该像这样定义接口:
[ComImport]
[Guid("00000138-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IPropertyStorage
{
[PreserveSig]
uint ReadMultiple(
uint count,
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] PropertySpec[] properties,
[Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] PropertyVariant[] values);
[PreserveSig]
uint WriteMultiple(
uint count,
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] PropertySpec[] properties,
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] PropertySpec[] values,
uint miniumumPropertyId);
// other methods left as an exercise to the reader...
}注意PreserveSig attribute的用法。好的,这意味着你现在必须测试返回值:-)
注意:如果你需要更多的复合存储p/invoke声明,你可以看看这个100%免费的Nuget工具:CodeFluent Runtime Client。它包含一个CompoundStorage类实用程序,您可以使用它,或者只需使用.NET反射器或ILSpy检查它,并获取它包含的p/invoke定义。它应该支持32位和64位世界。
https://stackoverflow.com/questions/16798407
复制相似问题