Нарушение доступа при вызове 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, вы также должны объявить унаследованные методы.
Или просто скопируйте / вставьте объявления из справочного источника, если это возможно, обычно это лучше. Они здесь и здесь