2010-11-11 11 views
5

Por ejemplo, esta es la definición completa de la interfaz IFileOpenDialog, una interfaz de Windows Shell, tomado del sitio de Pinvoke:Al definir una interfaz API de Windows en C#, ¿tengo que definir todos los miembros? ¿Puedo solo definir los métodos que voy a usar?

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

Si sólo voy a utilizar dos métodos de esta interfaz, puedo definir es como:

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

¿Va a funcionar? ¿O tengo que definir la interfaz completa con todos los métodos?

+1

¿Lo intentaste? –

+0

Sí, si solo llamo al método Show, funciona, si intento llamar a SetOptions, aparece una excepción: "Intenté leer o escribir en la memoria protegida. Esto a menudo indica que otra memoria está dañada". – AnAurelian

Respuesta

9

No, esto no funcionará. El CLR crea una tabla de envío para la interfaz COM basada en la declaración. El orden de los punteros de función en esa tabla se establece por el orden de las definiciones de método en su declaración. El método Show() ocupará el primer espacio en ambos casos, sin problemas. SetOptions() sin embargo, terminará llamando al segundo, que en realidad es SetFileTypes(). Tienen argumentos diferentes, eso va a ir de forma desagradable cuando la implementación obtiene argumentos basura y la pila se desequilibra.

puede omitir cualquier declaración desde el extremo posterior. También tenga en cuenta que la declaración real del método no importa cuando no la llame. Lo que le permite mentir y evitar tener que declarar sus tipos de argumentos. Asegúrese de que sea obvio que el método no funcionará, nómbrelo como void DontCallMe2().

Tenga en cuenta que estas interfaces ya están envueltos en el Código API Pack de Windows, así como la versión de .NET 4.0 de Microsoft.Win32.OpenFileDialog y la versión de .NET 3.5 de System.Windows.Forms.OpenFileDialog

+0

Eso es exactamente lo que sucede en mi código: si solo llamo al método Show, funciona, si intento llamar a SetOptions, obtengo una excepción: "Intenté leer o escribir en la memoria protegida".Esto es a menudo una indicación de que otra memoria está dañada. " ¿Así que esto es? ¿No hay forma de hacer trampa de alguna manera? ¿Tengo que definir todos los métodos hasta SetOptions? Algunos de ellos pueden requerir definiciones de tipos de WinAPI adicionales, por lo que el código crecer y crecer rápidamente ... – AnAurelian

+0

Sí, esa es la 'manera desagradable' –

+0

Tenga en cuenta que he editado mi publicación para señalar que esto ya está hecho para usted. –

3

Si quiere definir un tipo que implementa una interfaz, está obligado a implementar todos los elementos de la interfaz. Si no lo hace, entonces el mejor caso es que obtendrá un comportamiento "errático" de su código, el peor caso es que la aplicación se bloqueará tan pronto como intente hacer cualquier cosa con su implementación de la interfaz.

tener en cuenta que estas dos interfaces son no la misma:

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

pesar de que tienen el mismo nombre, el mismo GUID y el implemento Show, el hecho de que sólo uno implementa SetOptions los hace diferente.

Por supuesto, una excepción de NotImplemented de cualquiera de los métodos de la interfaz no se implementa, pero decir que usted * Implementar interfaz ISomeInterface, que realmente tiene que hacerlo.

+0

Esto no es realmente cierto. Llamar al método Show() con cualquier declaración funciona bien. Las interfaces COM se identifican por su Guid, no por su tipo de declaración. Esto es lo que hizo posible la característica de Equivalencia de Tipo de .NET 4.0, visible a través de la nueva propiedad 'Insertar Tipos de Interoperabilidad' de una referencia de ensamblado. –

Cuestiones relacionadas