При определении интерфейса Windows API в C # нужно ли определять всех участников?Могу ли я определить только методы, которые я собираюсь использовать? - PullRequest
5 голосов
/ 11 ноября 2010

Например, это полное определение IFileOpenDialog интерфейса Windows Shell, взятого с сайта Pinvoke :

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

Это сработает? Или я должен определить полный интерфейс со всеми методами?

Ответы [ 2 ]

9 голосов
/ 11 ноября 2010

Нет, это не сработает.CLR создает таблицу диспетчеризации для интерфейса COM на основе объявления.Порядок указателей на функции в этой таблице определяется порядком определений методов в вашем объявлении.Метод Show () будет занимать первый слот в обоих случаях, никаких проблем там нет.SetOptions (), однако, в конечном итоге вызовет второй, который на самом деле SetFileTypes ().У них разные аргументы, и это приведет к kaboom противным образом, когда реализация получит аргументы мусора и стек станет несбалансированным.

Вы можете пропустить любые объявления из хвостовой части.Также обратите внимание, что фактическое объявление метода не имеет значения, когда вы его не вызываете.Что позволяет вам лгать и избегать необходимости объявлять их типы аргументов.Удостоверьтесь, что очевидно, что метод на самом деле не будет работать, назовите его примерно так: void DontCallMe2 () .

Обратите внимание, что эти интерфейсы уже включены в пакет кода API Windows кака также версия .NET 4.0 Microsoft.Win32.OpenFileDialog и версия .NET 3.5 System.Windows.Forms.OpenFileDialog

3 голосов
/ 11 ноября 2010

Если вы хотите определить тип, который реализует интерфейс, вы обязаны реализовать все элементы интерфейса.Если вы этого не сделаете, то в лучшем случае вы получите «ошибочное» поведение из своего кода, в худшем случае ваше приложение будет аварийно завершено, как только вы попытаетесь выполнить что-нибудь с вашей реализациейинтерфейс.

Имейте в виду, что эти два интерфейса не одинаковы:

[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, вам действительно нужно это сделать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...