假设我正在访问一个第三方库,文档中说明我可以使用pInvoke或创建Interop库并使用COM。这两种技术有什么区别,我为什么选择其中之一?
pInvoke是通过调用原生代码来访问非托管DLL的技术,而创建Interop库并使用COM是使用.NET对非托管组件进行包装的技术。
选择哪种技术取决于许多因素,例如库的类型和可用性、开发人员的经验和技能以及团队内部的约定。
P/Invoke用于调用纯C API(如大多数Win32 API)。COM interop用于调用COM对象。
如果API调用次数相对较高(并且您可以使用COM包装器将它们封装为仅需一两个调用),则可能会创建一个C ++ COM包装器来包装C API,然后使用COM interop调用您的包装器。这是因为托管本机互操作性可能相对昂贵,并且最好尽量减少转换次数。虽然实际上,我会说使用C ++ / CLI创建包装器可能对C#方面更加友好(例如,看看SlimDX,它是一个C ++ / CLI包装器,围绕着一个COM API(DirectX)。
话虽如此,除非您有特定的性能问题,否则我只会使用更自然的方法来调用所需的API:如果它是C API(如Win32 API),则使用P/Invoke。如果基于COM,则使用COM interop。
根据C++互操作性能考虑中的P/Invoke vs. C++ Interop一节,
对于.NET语言,例如Visual Basic和C#,与本地组件互操作的指定方法是P/Invoke。由于P/Invoke受.NET Framework支持,因此Visual C++也支持它,但是Visual C++还提供了自己的互操作支持,称为C++ Interop。 C++ Interop优于P / Invoke,因为P / Invoke不是类型安全的。因此,在运行时主要报告错误,但是C++ Interop也比P / Invoke具有性能优势。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);
}
}
// 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;
}
PInvoke使用动态链接机制将外部代码引入执行进程。动态链接库(DLL)必须具有与调用应用程序相同的目标架构,因此无法从64位调用32位或反之的跨平台调用。相反,DLL被映射到调用程序的地址空间中并在进程中执行。
COM、DCOM、COM+和ActiveX都基于进程间通信库,但有时会退化为简单的DLL加载。COM链接的对象与CORBA对象相关,但并不完全相同,虽然CORBA发展了自己的对象定位器,但COM实现仍松散地基于Sun Microsystems RPC和XDR库,并扩展了COM的面向对象特性。COM对象不是通过DLL引用,而是通过GUID引用,该GUID用于查找对象类并查询其接口。对象代码通常在单独的进程中运行,可能在单独的服务器上。