首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Windows结构化存储-32位vs 64位COM Interop

Windows结构化存储-32位vs 64位COM Interop
EN

Stack Overflow用户
提问于 2013-05-29 01:50:25
回答 3查看 1.5K关注 0票数 0

在尝试将现有的32位应用程序转换为64位应用程序时,我遇到了让一些COM Interop代码正常工作的问题。该代码正在使用我从各种Windows SDK头文件/IDL文件翻译的托管代码访问结构化存储API。

当我尝试使用STG_E_INVALIDPARAMETER调用IPropertyStorage.ReadMultiple()时,代码失败。前面对StgOpenStorageExIPropertySetStorage.Open的互操作调用似乎工作得很好。MSDN声称这个错误意味着我的PROPSPEC参数有问题,但相同的参数值在编译为32位应用程序时工作得很好,并且我返回的值是指定属性的正确字符串值。

以下是我认为的相关部分:

代码语言:javascript
复制
// 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.
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-06-04 02:54:02

代码语言:javascript
复制
[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中有一些关于结构打包的背景知识。

票数 5
EN

Stack Overflow用户

发布于 2013-06-04 01:47:13

在尝试了互操作定义的多次迭代之后,我终于找到了答案。我不能确切地确定这为什么会有所不同,但我所做的更改是将单个PROPSPECPROPVARIANT结构定义替换为嵌套的结构定义;基本上,我将匿名联合拆分成它们自己的类型。我假设在我这样做的时候会有某种对齐问题得到解决。

具体地说,PROPSPEC的工作32位定义如下:

代码语言:javascript
复制
[StructLayout(LayoutKind.Explicit)]
public struct PropertySpec
{
    [FieldOffset(0)]
    public PropertySpecKind kind;

    [FieldOffset(4)]
    public uint propertyId;
    [FieldOffset(4)]
    public IntPtr name;
}

我把它改成这样,现在它在两个架构上都有效:

代码语言:javascript
复制
[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;
}
票数 1
EN

Stack Overflow用户

发布于 2013-06-02 18:28:30

您应该像这样定义接口:

代码语言:javascript
复制
[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位世界。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16798407

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档