Нарушение доступа при вызове EnumElements из IStorage

Я читаю файл структурированного хранилища. И пытается получить все дочерние элементы корневой структуры. Но я получаю исключение нарушения доступа при этом.

Вот родные методы,

[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 ответ

Решение

Как вы можете подозревать, ваши объявления интерфейса абсолютно неверны. Имена методов не имеют значения, как и все имена в COM, порядок методов имеет решающее значение. Они должны совпадать с V-таблицей интерфейса. Это означает, что вы не можете просто опустить методы волей-неволей.

Эта деталь отличается от.NET-интерфейсов. CLR выясняет, как связать интерфейсные методы с их реализацией, и делает это потому, что имеет доступ как к объявлению, так и к реализации. Но у него нет такого доступа к реализациям COM-методов, они скрыты от глаз, обычно в DLL, написанной, скажем, на C++ или Delphi.

Ваш тест OpenStream() на самом деле не работает. Вы объявили это как 1-й метод, но на самом деле это 2-й метод. Вы на самом деле вызываете 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, вы также должны объявить унаследованные методы.

Или просто скопируйте / вставьте объявления из справочного источника, если это возможно, обычно это лучше. Они здесь и здесь

Другие вопросы по тегам