在C#中定义Windows API接口时,是否必须定义所有成员? 我可以只定义我要使用的方法吗?

例如,这是从Pinvoke站点获取的IFileOpenDialog接口的完整定义,即Windows Shell接口:

[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 ); } 

它会起作用吗? 或者我是否必须使用所有方法定义完整的界面?

不,这不行。 CLR根据声明为COM接口构建一个调度表。 该表中函数指针的顺序由声明中方法定义的顺序设置。 Show()方法在两种情况下都会占用第一个插槽,没有麻烦。 然而,SetOptions()将最终调用第二个,实际上是SetFileTypes()。 它们有不同的参数,当实现获得垃圾参数并且堆栈变得不平衡时,它将以令人讨厌的方式进入kaboom。

可以省略尾部的任何声明。 另请注意,当您不调用时,方法的实际声明无关紧要。 这允许你撒谎并避免必须声明他们的参数类型。 确保显然该方法实际上不起作用,将其命名为void DontCallMe2()

请注意,这些接口已包含在Windows API代码包以及.NET 4.0版本的Microsoft.Win32.OpenFileDialog和.NET 3.5的System.Windows.Forms.OpenFileDialog版本中。

如果要定义实现接口的类型,则必须实现接口的所有元素。 如果你不这样做,那么最好的情况是你的代码会出现“不稳定”的行为,最糟糕的情况是你的应用程序会在你尝试对你的接口实现做任何事情时崩溃。

请记住,这两个接口是一样的:

 [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的事实使它们不同。

无论如何,从你没有实现的接口的任何方法抛出一个NotImplementedexception,但是说你实现接口ISomeInterface ,你实际上必须这样做。