2015-12-31 156 views
2

正在讀取結構化存儲文件。並試圖獲取根結構的所有子元素。但是我這樣做時會出現訪問衝突異常。調用IStorage的EnumElements時發生訪問衝突

這裏是本地方法,

[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標誌的不同組合,當我打開文件,但它不工作。

回答

2

正如你可能會懷疑的那樣,你的接口聲明是完全錯誤的。方法的名稱是不相關的,就像所有名稱都在COM中一樣,它是關鍵方法的訂單。他們必須與界面的v-表共同參與。這意味着你不能只是忽略方法。

這是一個與.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

+0

OpenStream是接口中的第二種方法。我實際上刪除了短代碼的其他方法, –

+0

剛剛簽出。接口中缺少一種方法。現在工作正常。謝謝, –