.net - ¿Cuál es la diferencia entre pInvoke y COM Interop?
(3)
Digamos que estoy accediendo a una biblioteca de terceros, para lo cual la documentación indica que puedo usar pInvoke o crear una biblioteca de interoperabilidad y usar COM. ¿Cuál es la diferencia entre estas dos técnicas y por qué puedo elegir una sobre la otra?
La interoperabilidad le permite conservar y aprovechar las inversiones existentes en código no administrado. El código que se ejecuta bajo el control del Common Language Runtime (CLR) se llama código administrado, y el código que se ejecuta fuera del CLR se llama código no administrado. Los componentes COM, COM +, C ++, ActiveX y Microsoft Win32 API son ejemplos de código no administrado.
.NET Framework permite la interoperabilidad con código no administrado a través de los servicios de invocación de plataforma (P / Invoke), el espacio de nombres System.Runtime.InteropServices, la interoperabilidad C ++ y la interoperabilidad COM (interoperabilidad COM).
PInvoke utiliza el mecanismo de enlace dinámico para llevar código externo al proceso de ejecución. Las bibliotecas de enlace dinámico (DLL) deben tener la misma arquitectura de destino que la aplicación de llamada, por lo que no hay capacidad para realizar llamadas cruzadas de 64 bits a 32 bits o viceversa. En su lugar, la DLL se asigna al espacio de direcciones de la persona que llama y se ejecuta en el proceso.
COM, DCOM, COM + y ActiveX se basan en bibliotecas de comunicaciones entre procesos, pero a veces se convierten en una simple carga de DLL. Los objetos vinculados con COM están relacionados, pero no son idénticos a los objetos CORBA, pero mientras CORBA desarrolló su propio localizador de objetos, la implementación de COM aún se basa en las bibliotecas RPC y XDR de Sun Microsystems con extensiones para las características orientadas a objetos de COM. Los objetos COM son referenciados no por DLL, sino por un GUID que se usa para buscar la clase de objeto y consultar sus interfaces. El código de objeto generalmente se ejecuta en un proceso separado, o puede estar en un servidor separado.
Para lenguajes .NET, como Visual Basic y C #, el método prescrito para interoperar con componentes nativos es P / Invoke. Debido a que P / Invoke es compatible con .NET Framework, Visual C ++ también lo admite, pero Visual C ++ también proporciona su propio soporte de interoperabilidad, que se conoce como interoperabilidad de C ++. Se prefiere la interoperabilidad de C ++ en lugar de P / Invoke porque P / Invoke no es de tipo seguro. Como resultado, los errores se informan principalmente en el tiempo de ejecución, pero C ++ Interop también tiene ventajas de rendimiento sobre P / Invoke.
El cálculo de datos realizado por C ++ Interop es la forma más simple posible: los parámetros simplemente se copian a través de los límites administrados / no administrados en forma bit a bit; No se realiza ninguna transformación en absoluto. Para P / Invoke, esto solo es cierto si todos los parámetros son tipos simples y aptos para jugar. De lo contrario, P / Invoke realiza pasos muy sólidos para convertir cada parámetro administrado a un tipo nativo apropiado, y viceversa si los argumentos están marcados como "out" o "in, out".
En otras palabras, C ++ Interop usa el método más rápido posible de cálculo de datos, mientras que P / Invoke usa el método más robusto. Esto significa que C ++ Interop (de una manera típica para C ++) proporciona un rendimiento óptimo de forma predeterminada, y el programador es responsable de abordar los casos en que este comportamiento no es seguro o apropiado.
Por lo tanto, C ++ Interop requiere que el cálculo de datos se proporcione explícitamente, pero la ventaja es que el programador es libre de decidir qué es apropiado, dada la naturaleza de los datos y cómo se debe usar. Además, aunque el comportamiento del cálculo de datos de P / Invoke se puede modificar en cierto grado, C ++ Interop permite personalizar el cálculo de datos en una base llamada por llamada. Esto no es posible con P / Invoke.
P / Ejemplo de invocación abajo:
using System;
using System.Runtime.InteropServices;
public class Win32 {
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr MessageBox(int hWnd, String text,
String caption, uint type);
}
public class HelloWorld {
public static void Main() {
Win32.MessageBox(0, "Hello World", "Platform Invoke Sample", 0);
}
}
Com ejemplo de interoperabilidad (en C ++ que consume código c #)
// ConLoan.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#import "../LoanLib/LoanLib.tlb" raw_interfaces_only
using namespace LoanLib;
int main(int argc, char* argv[])
{
HRESULT hr = CoInitialize(NULL);
ILoanPtr pILoan(__uuidof(Loan));
if (argc < 5)
{
printf("Usage: ConLoan Balance Rate Term Payment/n");
printf(" Either Balance, Rate, Term, or Payment must be 0/n");
return -1;
}
double openingBalance = atof(argv[1]);
double rate = atof(argv[2])/100.0;
short term = atoi(argv[3]);
double payment = atof(argv[4]);
pILoan->put_OpeningBalance(openingBalance);
pILoan->put_Rate(rate);
pILoan->put_Term(term);
pILoan->put_Payment(payment);
if (openingBalance == 0.00)
pILoan->ComputeOpeningBalance(&openingBalance);
if (rate == 0.00) pILoan->ComputeRate(&rate);
if (term == 0) pILoan->ComputeTerm(&term);
if (payment == 0.00) pILoan->ComputePayment(&payment);
printf("Balance = %.2f/n", openingBalance);
printf("Rate = %.1f%%/n", rate*100);
printf("Term = %.2i/n", term);
printf("Payment = %.2f/n", payment);
VARIANT_BOOL MorePmts;
double Balance = 0.0;
double Principal = 0.0;
double Interest = 0.0;
printf("%4s%10s%12s%10s%12s/n", "Nbr", "Payment", "Principal", "Interest", "Balance");
printf("%4s%10s%12s%10s%12s/n", "---", "-------", "---------",
"--------", "-------");
pILoan->GetFirstPmtDistribution(payment, &Balance, &Principal, &Interest, &MorePmts);
for (short PmtNbr = 1; MorePmts; PmtNbr++)
{
printf("%4i%10.2f%12.2f%10.2f%12.2f/n",
PmtNbr, payment, Principal, Interest, Balance);
pILoan->GetNextPmtDistribution(payment, &Balance, &Principal, &Interest, &MorePmts);
}
CoUninitialize();
return 0;
}
P / Invoke se usa para llamar a API sin formato (como la mayoría de las API de Win32). La interoperabilidad COM se utiliza para llamar a objetos COM.
Puede crear una envoltura COM de C ++ alrededor de una API de C y luego usar la interoperabilidad COM para llamar a su envoltura si el número de llamadas a la API es relativamente alto (y puede usar la envoltura de COM para encapsularlas en solo una o dos llamadas). Esto se debe a que la interoperabilidad nativa administrada puede ser relativamente costosa y es bueno minimizar el número de transiciones. Aunque en realidad diría que usar C ++ / CLI para crear el contenedor probablemente sería un poco más amigable para el lado C # (por ejemplo, mirando SlimDX , que es un contenedor de C ++ / CLI alrededor de una API COM (DirectX)).
Dicho esto, a menos que tenga un problema de rendimiento específico, solo usaría el método que sea más natural para la API a la que intenta llamar: si es una API de C (como la API de Win32), use P / Invoke. Si está basado en COM, entonces use interoperabilidad COM.
PInvoke utiliza el mecanismo de enlace dinámico para llevar código externo al proceso de ejecución. Las bibliotecas de enlace dinámico (DLL) deben tener la misma arquitectura de destino que la aplicación de llamada, por lo que no hay capacidad para realizar llamadas cruzadas de 64 bits a 32 bits o viceversa. En su lugar, la DLL se asigna al espacio de direcciones de la persona que llama y se ejecuta en el proceso.
COM, DCOM, COM + y ActiveX se basan en bibliotecas de comunicaciones entre procesos, pero a veces pueden convertirse en una simple carga de DLL. Los objetos vinculados con COM están relacionados, pero no son idénticos a los objetos CORBA, pero mientras CORBA desarrolló su propio localizador de objetos, la implementación de COM aún se basa en las bibliotecas RPC y XDR de Sun Microsystems con extensiones para las características orientadas a objetos de COM. Los objetos COM son referenciados no por DLL, sino por un GUID que se usa para buscar la clase de objeto y consultar sus interfaces. El código objeto normalmente se ejecuta en un proceso separado, y posiblemente en un servidor separado.