在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?

查看:167
本文介绍了在C#中定义Windows API接口时,是否必须定义所有成员?我可以只定义我要使用的方法吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

例如,这是的完整定义IFileOpenDialog 界面,一个Windows Shell界面,取自 Pinvoke site:

For example, this is the full definition of the IFileOpenDialog interface, a Windows Shell Interface, taken from the Pinvoke site:

[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?

推荐答案

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

No, this won't work. The CLR builds a dispatch table for the COM interface based on the declaration. 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. SetOptions() however is going to end up calling the 2nd one, which is actually 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.

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

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().

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

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

这篇关于在C#中定义Windows API接口时,是否必须定义所有成员?我可以只定义我要使用的方法吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆