简体   繁体   English

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

[英]When defining a Windows API interface in C#, do I have to define all members? Can I only define the methods I'm going to use?

For example, this is the full definition of the IFileOpenDialog interface, a Windows Shell Interface, taken from the Pinvoke site: 例如,这是从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 );
}

If I'm only going to use two methods from this interface, can I define it like: 如果我只使用此接口中的两个方法,我可以将它定义为:

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

Is it going to work? 它会起作用吗? Or do I have to define the full interface with all methods? 或者我是否必须使用所有方法定义完整的界面?

No, this won't work. 不,这不行。 The CLR builds a dispatch table for the COM interface based on the declaration. CLR根据声明为COM接口构建一个调度表。 The order of the function pointers in that table is set by the order of the method definitions in your declaration. 该表中函数指针的顺序由声明中方法定义的顺序设置。 The Show() method will occupy the first slot in both cases, no trouble there. Show()方法将占用两种情况下的第一个插槽,没有麻烦。 SetOptions() however is going to end up calling the 2nd one, which is actually SetFileTypes(). 然而,SetOptions()最终将调用第二个,实际上是SetFileTypes()。 They have different arguments, that's going to go kaboom in a nasty way when the implementation gets garbage arguments and the stack becomes unbalanced. 它们有不同的参数,当实现获得垃圾参数并且堆栈变得不平衡时,它将以令人讨厌的方式进入kaboom。

You can omit any declarations from the tail end. 可以省略尾部的任何声明。 Also note that the actual declaration of the method doesn't matter when you don't call it. 另请注意,当您不调用该方法时,该方法的实际声明无关紧要。 Which allows you to lie and avoid having to declare their argument types. 这允许你撒谎并避免必须声明他们的参数类型。 Do make sure it is obvious that the method won't actually work, name it something like void DontCallMe2() . 确保显然该方法实际上不起作用,将其命名为void DontCallMe2()

Note that these interfaces are already wrapped in the Windows API Code Pack as well as .NET 4.0's version of Microsoft.Win32.OpenFileDialog and .NET 3.5's version of System.Windows.Forms.OpenFileDialog 请注意,这些接口已包含在Windows API代码包以及.NET 4.0版本的Microsoft.Win32.OpenFileDialog和.NET 3.5的System.Windows.Forms.OpenFileDialog版本中。

If you want to define a type that implements an interface you're obliged to implement all elements of the interface. 如果要定义实现接口的类型,则必须实现接口的所有元素。 If you don't then the best case is that you'll get "erratic" behaviour from your code, the worst case is that your application will crash as soon as you try to do anything with your implementation of the interface. 如果不这样做,那么最好的情况是你的代码会出现“不稳定”的行为,最糟糕的情况是你的应用程序会在你尝试对你的接口实现做任何事情时崩溃。

Bear in mind that these two interfaces are not the same: 请记住,这两个接口一样:

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

Even though they have the same name, the same Guid and both implement Show , the fact that only one implements SetOptions makes them different. 尽管它们具有相同的名称,相同的Guid并且都实现了Show ,但只有一个实现SetOptions的事实使它们不同。

By all means, throw a NotImplemented exception from any methods of the interface you don't implement, but to say that you *implement interface ISomeInterface , you actually have to do so. 无论如何,从你没有实现的接口的任何方法抛出一个NotImplemented异常,但是说你实现接口ISomeInterface ,你实际上必须这样做。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 我在哪里可以看到在使用界面时要定义哪些界面成员? - Where can i see which interface members to define when i'm using the interface? 我可以定义一个 IDictionary 在 C# 中实现的接口吗? - Can I define an Interface that IDictionary implements in C#? 我可以使用属性来限制C#中接口方法的使用吗? - Can I use attributes to restrict usage of interface methods in C#? C#如何在mutliple文件中为同一个类定义方法? - C# How do i define methods for the same class in mutliple files? C# 可选参数:为什么我可以在接口和派生类上定义默认不同? - C# optional parameters: Why can I define the default different on interface and derived class? 为什么不能在 C# 9 中定义顶级扩展方法? - Why can't I define top-level extension methods in C# 9? 我可以用来定义具有相同名称的方法的最佳设计是什么? - What is the best design I can use to define methods with same name? 我可以在C#7中定义与C#8 beta兼容的索引器吗? - Can I define C# 8 beta compatible indexers in C# 7? 当我在c#中定义一个类中的所有函数时,我在进行函数式编程吗? - Am I doing functional programming when I define all of my functions in a class in c#? 如何定义IList <T> 在界面中 - How do I define IList<T> in Interface
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM