调用IStorage的EnumElements时发生访问冲突

时间:2015-12-31 11:42:21

标签: c# access-violation ole istorage compound-file

我正在阅读结构化存储文件。并尝试获取根结构的所有子元素。但是这样做会导致访问冲突异常。

这是本机方法,

[ComImport][Guid("0000000d-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IEnumSTATSTG
{
    [PreserveSig] uint Next(uint celt, [MarshalAs(UnmanagedType.LPArray), Out] System.Runtime.InteropServices.ComTypes.STATSTG[] rgelt, out uint pceltFetched);
}

[ComImport][Guid("0000000b-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IStorage
{
    [return: MarshalAs(UnmanagedType.Interface)]
    IStream OpenStream([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, IntPtr reserved1, [In, MarshalAs(UnmanagedType.U4)] int grfMode, [In, MarshalAs(UnmanagedType.U4)] int reserved2);

    void EnumElements(
        /* [in] */ uint reserved1,
        /* [size_is][unique][in] */ IntPtr reserved2,
        /* [in] */ uint reserved3,
        /* [out] */ out IEnumSTATSTG ppenum);
}

    [DllImport("ole32.dll", CharSet = CharSet.Unicode)]
    internal static extern uint StgOpenStorageEx
    (
        [MarshalAs(UnmanagedType.LPWStr)] string name, uint accessMode,
        uint storageFileFormat, uint fileBuffering, IntPtr options,
             IntPtr reserved, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] ref IStorage stg
    );

这是我的主叫代码。

IStorage _storageObject;
// Opening file,
NativeMethods.StgOpenStorageEx(path, (uint)STM.Read | STM.ShareDenyWrite, (uint)storageFileFormat, (uint)fileBuffering,
                options, IntPtr.Zero, ref _iidIStorage, ref _storageObject);

// Here I am calling EnumElements, I get exception here.
IEnumSTATSTG pIEnumStatStg;
_storageObject.EnumElements(0, IntPtr.Zero, 0, out pIEnumStatStg);

请注意,如果我调用另一个方法,如果IStorage(如OpenStream)可以正常工作,

_storageObject.OpenStream(streamName, IntPtr.Zero, (int)accessMode, 0);

我打开文件时尝试了不同的STM标志组合,但它无法正常工作。

1 个答案:

答案 0 :(得分:2)

您可能怀疑,您的界面声明完全错误。这些方法的名称是无关紧要的,就像所有名称都在COM中一样,它是方法的 order 至关重要。它们必须与界面的 v-table 共同使用。这意味着你不能随便省略方法。

这是一个与.NET接口不同的细节,CLR计算出如何将接口方法绑定到它们的实现,它可以这样做,因为它可以访问声明和实现。但它没有对COM方法实现的这种访问,它们被隐藏在视野之外,通常是在用C ++或Delphi编写的DLL中。

您的OpenStream()测试实际上并不起作用。您将其声明为第一种方法,但它实际上是第二种方法。您实际上正在调用CreateStream()。它没有爆炸是一个意外,CreateStream碰巧也采取了5个参数,它们是相似的。这种运气在EnumElements()上运行了,它实际上是界面的第9种方法。您正在调用第二个方法OpenStream。这次有完全错误的论点,kaboom。

可以在接口声明上获取快捷方式,但您必须使用占位符来表示您要跳过的方法。像:

[ComImport][Guid("0000000b-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IStorage
{
    void Dummy1();
    [return: MarshalAs(UnmanagedType.Interface)]
    IStream OpenStream([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, IntPtr reserved1, [In, MarshalAs(UnmanagedType.U4)] int grfMode, [In, MarshalAs(UnmanagedType.U4)] int reserved2);
    void Dummy3();
    void Dummy4();
    void Dummy5();
    void Dummy6();
    void Dummy7();
    void Dummy8();
    void EnumElements(
        /* [in] */ uint reserved1,
        /* [size_is][unique][in] */ IntPtr reserved2,
        /* [in] */ uint reserved3,
        /* [out] */ out IEnumSTATSTG ppenum);
}

可以省略尾随方法。您必须以相同的方式修复IEnumSTATSTG声明。这不是这种情况,但如果接口继承自IUnknown或IDispatch之外的基接口,那么您还必须声明继承的方法。

或者只是复制/粘贴参考源中的声明(如果可用),这通常是最好的。它们是herehere

相关问题