2010-11-11 23 views
5

例如,這是IFileOpenDialog接口,Windows Shell界面,從Pinvoke現場拍攝的完整定義:在C#中定義Windows API接口時,是否必須定義所有成員?我只能定義我要使用的方法嗎?

[ComImport, Guid ("d57c7288-d4ad-4768-be02-9d969532d960"), InterfaceType (ComInterfaceType.InterfaceIsIUnknown)] 
interface IFileOpenDialog : IFileDialog 
{ 
// Defined on IModalWindow - repeated here due to requirements of COM interop layer 
// -------------------------------------------------------------------------------- 
[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), PreserveSig] 
int Show ([In] IntPtr parent); 

// Defined on IFileDialog - repeated here due to requirements of COM interop layer 
[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] 
void SetFileTypes ([In] uint cFileTypes, [In] COMDLG_FILTERSPEC[] rgFilterSpec); 

[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] 
void SetFileTypeIndex ([In] uint iFileType); 

[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] 
void GetFileTypeIndex (out uint piFileType); 

[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] 
void Advise ([In, MarshalAs (UnmanagedType.Interface)] IFileDialogEvents pfde, out uint pdwCookie); 

[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] 
void Unadvise ([In] uint dwCookie); 

[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] 
void SetOptions ([In] FOS fos); 

[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] 
void GetOptions (out FOS pfos); 

[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] 
void SetDefaultFolder ([In, MarshalAs (UnmanagedType.Interface)] IShellItem psi); 

[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] 
void SetFolder ([In, MarshalAs (UnmanagedType.Interface)] IShellItem psi); 

[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] 
void GetFolder ([MarshalAs (UnmanagedType.Interface)] out IShellItem ppsi); 

[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] 
void GetCurrentSelection ([MarshalAs (UnmanagedType.Interface)] out IShellItem ppsi); 

[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] 
void SetFileName ([In, MarshalAs (UnmanagedType.LPWStr)] string pszName); 

[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] 
void GetFileName ([MarshalAs (UnmanagedType.LPWStr)] out string pszName); 

[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] 
void SetTitle ([In, MarshalAs (UnmanagedType.LPWStr)] string pszTitle); 

[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] 
void SetOkButtonLabel ([In, MarshalAs (UnmanagedType.LPWStr)] string pszText); 

[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] 
void SetFileNameLabel ([In, MarshalAs (UnmanagedType.LPWStr)] string pszLabel); 

[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] 
void GetResult ([MarshalAs (UnmanagedType.Interface)] out IShellItem ppsi); 

[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] 
void AddPlace ([In, MarshalAs (UnmanagedType.Interface)] IShellItem psi, NativeMethods.FDAP fdap); 

[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] 
void SetDefaultExtension ([In, MarshalAs (UnmanagedType.LPWStr)] string pszDefaultExtension); 

[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] 
void Close ([MarshalAs (UnmanagedType.Error)] int hr); 

[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] 
void SetClientGuid ([In] ref Guid guid); 

[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] 
void ClearClientData (); 

// Not supported: IShellItemFilter is not defined, converting to IntPtr 
[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] 
void SetFilter ([MarshalAs (UnmanagedType.Interface)] IntPtr pFilter); 

// Defined by IFileOpenDialog 
// --------------------------------------------------------------------------------- 
[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] 
void GetResults ([MarshalAs (UnmanagedType.Interface)] out IShellItemArray ppenum); 

[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] 
void GetSelectedItems ([MarshalAs (UnmanagedType.Interface)] out IShellItemArray ppsai); 
} 

如果我只打算使用兩種方法從這個接口,可我定義它喜歡:

[ComImport, Guid ("d57c7288-d4ad-4768-be02-9d969532d960"), InterfaceType (ComInterfaceType.InterfaceIsIUnknown)] 
interface IFileOpenDialog : IFileDialog 
{ 
// Defined on IModalWindow - repeated here due to requirements of COM interop layer 
// -------------------------------------------------------------------------------- 
[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), PreserveSig] 
int Show ([In] IntPtr parent); 

[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] 
void SetOptions ([In] FOS fos); 
} 

它是否會工作?還是我必須用所有方法定義完整的界面?

+1

你試過了嗎? – 2010-11-11 13:11:08

+0

是的,如果我只調用Show方法,它會起作用,如果我嘗試調用SetOptions,則會得到一個異常:「嘗試讀取或寫入受保護的內存,這通常表示其他內存已損壞。 – AnAurelian 2010-11-11 14:01:22

回答

9

不,這不行。 CLR根據聲明爲COM接口構建一個調度表。該表中函數指針的順序由聲明中方法定義的順序設置。 Show()方法將在兩種情況下佔用第一個插槽,在那裏沒有問題。然而,SetOptions()將最終調用第二個,它實際上是SetFileTypes()。他們有不同的論點,當實現獲取垃圾參數並且堆棧變得不平衡時,這將會以一種討厭的方式進入kaboom。

可以忽略來自尾端的任何聲明。還要注意,當你不調用它時,方法的實際聲明並不重要。這可以讓你說謊並避免必須聲明它們的參數類型。確保很明顯該方法實際上不起作用,將其命名爲void DontCallMe2()

注意,這些接口已經被包裹在Windows API代碼包以及.NET 4.0的版本Microsoft.Win32.OpenFileDialog和.NET 3.5的版本System.Windows.Forms.OpenFileDialog的

+0

這正是我的代碼中發生的事情:如果我只調用Show方法,它會起作用,如果我嘗試調用SetOptions,則會得到一個異常:「試圖讀取或寫入受保護的內存。這通常表示其他內存已損壞。「 所以這就是它?無法以某種方式作弊?是否必須定義SetOptions之前的所有方法?其中一些可能需要額外的WinAPI類型定義,因此代碼將快速成長... – AnAurelian 2010-11-11 13:19:15

+0

是的,這是'討厭的方式' – 2010-11-11 13:20:55

+0

請注意,我編輯了我的帖子,指出這個東西已經爲你完成了 – 2010-11-11 13:22:45

3

如果您想要定義實現接口的類型,您必須實現接口的所有元素。如果你不這樣做,最好的情況是你的代碼中會出現「不穩定的」行爲,最糟糕的情況是你的應用程序會在你嘗試使用接口實現時立即崩潰。

記住,這兩個接口是一樣:

[ComImport, Guid ("d57c7288-d4ad-4768-be02-9d969532d960"), InterfaceType (ComInterfaceType.InterfaceIsIUnknown)] 
interface IFileOpenDialog : IFileDialog 
{ 
    [MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), PreserveSig] 
    int Show ([In] IntPtr parent); 
} 

[ComImport, Guid ("d57c7288-d4ad-4768-be02-9d969532d960"), InterfaceType (ComInterfaceType.InterfaceIsIUnknown)] 
interface IFileOpenDialog : IFileDialog 
{ 
    [MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), PreserveSig] 
    int Show ([In] IntPtr parent); 

    [MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] 
    void SetOptions ([In] FOS fos); 
} 

即使他們有相同的名字,相同的GUID,並都實現Show,只有一個實現這一事實SetOptions使他們不同。

通過一切手段,從扔你不實現接口的任何方法NotImplemented例外,而是說,你*實現接口ISomeInterface,你確實有這樣做。

+0

這實際上並非如此。用任何一個聲明調用Show()方法都可以。 COM接口由它們的Guid標識,而不是它們的聲明類型。這是.NET 4.0類型對等功能的可能性,通過程序集引用的新「嵌入互操作類型」屬性可以看到。 – 2010-11-11 14:03:45

相關問題