stringtranslate.com

Servicios de invocación de plataforma

Los servicios de invocación de plataforma , comúnmente conocidos como P/Invoke , son una característica de las implementaciones de Common Language Infrastructure , como Common Language Runtime de Microsoft , que permite que el código administrado llame a código nativo .

El código administrado, como C# o VB.NET, proporciona acceso nativo a clases, métodos y tipos definidos dentro de las bibliotecas que conforman .NET Framework. Si bien .NET Framework proporciona un amplio conjunto de funciones, puede carecer de acceso a muchas bibliotecas de nivel inferior del sistema operativo que normalmente se escriben en código no administrado o bibliotecas de terceros también escritas en código no administrado. P/Invoke es la técnica que un programador puede utilizar para acceder a las funciones de estas bibliotecas. Las llamadas a funciones dentro de estas bibliotecas se producen declarando la firma de la función no administrada dentro del código administrado, que sirve como la función real que se puede llamar como cualquier otro método administrado. La declaración hace referencia a la ruta del archivo de la biblioteca y define los parámetros de la función y el retorno en tipos administrados que es más probable que se envíen implícitamente hacia y desde los tipos no administrados por el tiempo de ejecución del lenguaje común (CLR). Cuando los tipos de datos no administrados se vuelven demasiado complejos para una conversión implícita simple desde y hacia tipos administrados, el marco permite al usuario definir atributos en la función, el retorno y/o los parámetros para refinar explícitamente cómo se deben ordenar los datos para no generar excepciones al intentar hacerlo implícitamente.

Hay muchas abstracciones de conceptos de programación de nivel inferior disponibles para los programadores de código administrado en comparación con la programación en lenguajes no administrados. Como resultado, un programador con solo experiencia en código administrado necesitará repasar conceptos de programación como punteros, estructuras y paso por referencia para superar algunos de los obstáculos en el uso de P/Invoke.

Arquitectura

Descripción general

Actualmente se utilizan dos variantes de P/Invoke:

Explícito

Implícito

Detalles

Al utilizar P/Invoke, el CLR maneja la carga de DLL y la conversión de los tipos anteriores no administrados a tipos CTS (también conocido como ordenación de parámetros ). [1] [ cita requerida ] Para realizar esto, el CLR :

P/Invoke es útil para usar DLL de C o C++ estándar (no administradas) . Se puede usar cuando un programador necesita tener acceso a la extensa API de Windows , ya que muchas funciones proporcionadas por las bibliotecas de Windows carecen de contenedores disponibles . Cuando .NET Framework no expone una API de Win32, el contenedor de esta API debe escribirse manualmente.

Trampas

Escribir wrappers P/Invoke puede ser difícil y propenso a errores. El uso de DLL nativas significa que el programador ya no puede beneficiarse de la seguridad de tipos y la recolección de basura como se proporciona habitualmente en el entorno .NET. Cuando se utilizan de forma incorrecta, esto puede causar problemas como errores de segmentación o fugas de memoria . Obtener las firmas exactas de las funciones heredadas para su uso en el entorno .NET puede ser difícil, lo que puede dar lugar a este tipo de problemas. Para este propósito, existen herramientas y sitios web para obtener dichas firmas, lo que ayuda a prevenir problemas de firmas. [1]

Otros peligros incluyen:

Al utilizar C++/CLI, el CIL emitido puede interactuar libremente con objetos ubicados en el montón administrado y, simultáneamente, con cualquier ubicación de memoria nativa direccionable. Se puede llamar, modificar o construir un objeto residente en el montón administrado utilizando la simple notación "object->field;" para asignar valores o especificar llamadas a métodos. Se obtienen importantes mejoras de rendimiento al eliminar cualquier cambio de contexto innecesario y se reducen los requisitos de memoria (pilas más cortas).

Esto trae consigo nuevos desafíos:

Estas referencias especifican soluciones para cada uno de estos problemas, si se presentan. Un beneficio principal es la eliminación de la declaración de la estructura; el orden de declaración de los campos y los problemas de alineación no están presentes en el contexto de la interoperabilidad de C++.

Ejemplos

Ejemplos básicos

Este primer ejemplo sencillo muestra cómo obtener la versión de una DLL particular :

Firma de la función DllGetVersion en la API de Windows :

HRESULT DllGetVersion ( DLLVERSIONINFO * pdvi )   

P/Invoque el código C# para invocar la función DllGetVersion :

[StructLayout(LayoutKind.Sequential)] estructura privada DLLVERSIONINFO { entero público cbSize ; entero público dwMajorVersion ; entero público dwMinorVersion ; entero público dwBuildNumber ; entero público dwPlatformID ; } [DllImport("shell32.dll")] entero externo estático DllGetVersion ( ref DLLVERSIONINFO pdvi ) ;                       

El segundo ejemplo muestra cómo extraer un icono en un archivo:

Firma de la función ExtractIcon en la API de Windows:

HICON ExtractIcon ( HINSTANCE hInst , LPCTSTR lpszExeFileName , UINT nIconIndex );        

P/Invoque código C# para invocar la función ExtractIcon :

[DllImport("shell32.dll")] estático externo IntPtr ExtractIcon ( IntPtr hInst , [MarshalAs(UnmanagedType.LPStr)] cadena lpszExeFileName , uint nIconIndex );            

El siguiente ejemplo complejo muestra cómo compartir un evento entre dos procesos en la plataforma Windows :

Firma de la función CreateEvent :

 MANEJAR CreateEvent ( LPSECURITY_ATTRIBUTES lpEventAttributes , BOOL bManualReset , BOOL bInitialState , LPCTSTR lpName );          

Código C# P/Invoke para invocar la función CreateEvent :

[DllImport("kernel32.dll", SetLastError=true)] estático externo IntPtr CreateEvent ( IntPtr lpEventAttributes , bool bManualReset , bool bInitialState , [MarshalAs(UnmanagedType.LPStr)] cadena lpName );              

Un ejemplo más complejo

// declaración nativa typedef struct _PAIR { DWORD Val1 ; DWORD Val2 ; } PAIR , * PPAIR ;          
// Compilado con /clr; el uso de #pragma manage/unmanaged puede generar doble procesamiento; // evítelo utilizando un .cpp independiente con inclusiones .h. // Este se ubicaría en un archivo .h.plantilla <> inline CLR_PAIR ^ marshal_as < CLR_PAIR ^ , PAIR > ( const PAIR & Src ) { // Tenga en cuenta el uso de des/referenciación. Debe coincidir con su uso. CLR_PAIR ^ Dest = gcnew CLR_PAIR ; Dest -> Val1 = Src . Val1 ; Dest -> Val2 = Src . Val2 ; return Dest ; };                
CLR_PAIR ^ mgd_pair1 ; CLR_PAIR ^ mgd_pair2 ; PAR nativo0 , * nativo1 =& nativo0 ;   nativo0 = NativeCallGetRefToMemory ();  // Uso de marshal_as. Tiene sentido para tipos grandes o de uso frecuente. mgd_pair1 = marshal_as < CLR_PAIR ^> ( * native1 );  // Uso directo del campo mgd_pair2 -> Val1 = native0 . Val1 ; mgd_pair2 -> val2 = native0 . val2 ;    return ( mgd_pair1 ); // Regresar a C# 

Herramientas

Hay una serie de herramientas diseñadas para ayudar en la producción de firmas P/Invoke.

Escribir una aplicación de utilidad que importe archivos de encabezado de C++ y archivos DLL nativos y produzca un ensamblaje de interfaz automáticamente resulta bastante difícil. El principal problema con la producción de un importador/exportador de este tipo para las firmas P/Invoke es la ambigüedad de algunos tipos de parámetros de llamada a funciones de C++.

Brad Abrams tiene esto que decir sobre el tema: [4]

El problema radica en funciones de C++ como las siguientes:

__declspec ( dllexport ) void MiFuncion ( char * params );   

¿Qué tipo deberíamos usar para el parámetro params en nuestra firma P/Invoke? Podría ser una cadena terminada en null de C++, una matriz de caracteres o un parámetro de salida de caracteres . Entonces, ¿deberíamos usar string , StringBuilder , char [] o ref char  ?

Independientemente de este problema, hay algunas herramientas disponibles para simplificar la producción de firmas P/Invoke.

Una de las herramientas enumeradas a continuación, xInterop C++ .NET Bridge, ha resuelto este problema implementando múltiples anulaciones del mismo método C++ en el mundo .NET; los desarrolladores pueden luego elegir el correcto para realizar la llamada.

PInvoke.net

PInvoke.net es una wiki que contiene firmas P/Invoke para una gran cantidad de API estándar de Windows. Es propiedad de Redgate Software y tiene alrededor de 50 000 visitas por mes.

Las firmas son creadas manualmente por los usuarios de la wiki. Se pueden buscar mediante un complemento gratuito de Microsoft Visual Studio .

Invocador de pin

PInvoker es una aplicación que importa archivos DLL nativos y .h de C++ y exporta archivos DLL de interoperabilidad P/Invoke completamente compilados y formados . Supera el problema de la ambigüedad al encapsular los parámetros de la función de puntero nativos en clases de interfaz .NET específicas de PInvoker. En lugar de utilizar tipos de parámetros .NET estándar en las definiciones de métodos P/Invoke ( char[] , string , etc.), utiliza estas clases de interfaz en las llamadas a funciones P/Invoke.

Por ejemplo, si consideramos el código de ejemplo anterior, PInvoker produciría una función P/Invoke .NET que acepta una clase de interfaz .NET que envuelve el puntero char * nativo . La construcción de esta clase podría ser a partir de una cadena o de una matriz char [] . La estructura de memoria nativa real para ambos es la misma, pero los constructores de clase de interfaz respectivos para cada tipo llenarán la memoria de diferentes maneras. Por lo tanto, la responsabilidad de decidir qué tipo .NET se debe pasar a la función recae en el desarrollador.

Asistente de interoperabilidad de Microsoft

Microsoft Interop Assistant es una herramienta gratuita disponible con archivos binarios y código fuente disponibles para descargar en CodePlex . Tiene licencia pública limitada de Microsoft (Ms-LPL).

Consta de dos partes:

Dado que esta herramienta produce código fuente de C# en lugar de una DLL compilada, el usuario puede realizar los cambios necesarios en el código antes de usarlo. Por lo tanto, el problema de ambigüedad se resuelve cuando la aplicación elige un tipo .NET particular para usar en la firma del método P/Invoke y, si es necesario, el usuario puede cambiarlo por el tipo requerido.

Asistente de invocación P

El Asistente P/Invoke utiliza un método similar al Asistente de interoperabilidad de Microsoft, ya que acepta código de archivo .h nativo de C++ y produce código C# (o VB.NET) para que usted lo pegue en el código de su aplicación .NET.

También tiene opciones para el marco al que desea apuntar: .NET Framework para el escritorio o .NET Compact Framework para dispositivos inteligentes Windows Mobile (y Windows CE).

Puente C++ .NET de xInterop

xInterop C++ .NET Bridge es una aplicación de Windows para crear un contenedor C# para DLL nativas de C++ y un puente C++ para acceder a ensamblajes .NET. Viene con una biblioteca C#/.NET que envuelve las clases C++ estándar, como string, iostream, etc. Se puede acceder a las clases y objetos C++ desde .NET.

Esta herramienta genera archivos DLL de contenedor de C# con código fuente de archivos DLL nativos de C++ existentes y los archivos de encabezado asociados que la herramienta necesita para crear un archivo DLL de contenedor de C#. La aplicación genera las firmas P/Invoke y la serialización de datos. El contenedor de C# resultante tiene una interfaz similar a la de su contraparte de C++ con el tipo de parámetro convertido al código .NET.

Esta herramienta reconoce las clases de plantilla que no se exportan desde la DLL de C++, crea una instancia de la clase de plantilla y la exporta en una DLL complementaria, y la interfaz de C++ correspondiente se puede utilizar en .NET.

Véase también

Referencias

  1. ^ El serializado de parámetros no debe confundirse con el término general serialización , que significa serialización . Los parámetros serializados se copian en la pila CLR después de su conversión a tipos CTS , pero no se serializan.
  2. ^ "Doble procesamiento (C++)".
  3. ^ "Inicializacion de Conjuntos Mixtos".
  4. ^ "El problema de PInvoke". learn.microsoft.com . 6 de febrero de 2004.

Enlaces externos