pInvoke和COM Interop有什么区别?

时间:2010-06-29 05:06:48

标签: .net com pinvoke

让我们说我正在访问第三方库,其文档声明我可以使用pInvoke或创建互操作库并使用COM。这两种技术之间有什么区别,为什么我可以选择其中一种?

3 个答案:

答案 0 :(得分:13)

P / Invoke用于调用plain-C API(与大多数Win32 API一样)。 COM interop用于调用COM对象。

如果API调用的数量相对较高,您可以围绕C API创建一个C ++ COM包装器,然后使用COM interop来调用您的包装器(并且您可以使用COM包装器将它们封装到一个或两个调用中) 。这是因为托管本机互操作可能相对昂贵,并且最小化转换次数是有益的。虽然实际上我会说使用C ++ / CLI创建包装器可能对C#方面更友好(例如SlimDX,这是围绕COM API的C ++ / CLI包装器(的DirectX))。

话虽如此,除非你有一个特定的性能问题,否则我会使用你试图调用的API更自然的方法:如果它是一个C API(如Win32 API那样)那么使用P /调用。如果它是基于COM的,那么使用COM互操作。

答案 1 :(得分:2)

PInvoke使用动态链接机制将外部代码引入执行进程。动态链接库(DLL)必须与调用应用程序具有相同的目标体系结构,因此无法进行从64位到32位的交叉调用,反之亦然。而是将DLL映射到调用者的地址空间并在进程中执行。

COM,DCOM,COM +和ActiveX都基于进程间通信库,但有时可以转换为简单的DLL加载。 COM链接对象是相关的,但与CORBA对象不完全相同,但是当CORBA进化出自己的对象定位器时,COM实现仍然基于Sun Microsystems RPC和XDR库,它们具有COM面向对象功能的扩展。 COM对象不是由DLL引用,而是由GUID引用,GUID用于查找对象类并查询其接口。目标代码通常在单独的进程中运行,并且可能在单独的服务器上运行。

答案 2 :(得分:-1)

互操作性使您可以保留和利用对非托管代码的现有投资。在公共语言运行库(CLR)的控制下运行的代码称为托管代码,在CLR外部运行的代码称为非托管代码。 COM,COM +,C ++组件,ActiveX组件和Microsoft Win32 API是非托管代码的示例。

.NET Framework通过平台调用(P / Invoke)服务,System.Runtime.InteropServices命名空间,C ++互操作性和COM互操作性(COM互操作)来实现与非托管代码的互操作性。

PInvoke使用动态链接机制将外部代码带入执行过程。动态链接库(DLL)必须具有与调用应用程序相同的目标体系结构,因此无法进行从64位到32位的交叉调用,反之亦然。而是将DLL映射到调用者的地址空间并在进程中执行。

COM,DCOM,COM +和ActiveX都基于进程间通信库,但有时会演变为简单的DLL加载。 COM链接的对象是相关的,但与CORBA对象并不相同,但是,尽管CORBA演化了自己的对象定位器,但COM实现仍然宽松地基于Sun Microsystems RPC和XDR库,并带有COM面向对象功能的扩展。 COM对象不是由DLL引用的,而是由一个GUID引用的,该GUID用于查找对象类并查询其接口。目标代码通常在单独的进程中运行,或者可以在单独的服务器上。

对于.NET语言(例如Visual Basic和C#),与本机组件进行互操作的规定方法是P / Invoke。因为.NET Framework支持P / Invoke,所以Visual C ++也支持它,但是Visual C ++还提供了自己的互操作性支持,这称为C ++ Interop。 C ++ Interop优于P / Invoke,因为P / Invoke不是类型安全的。因此,错误主要是在运行时报告的,但是C ++ Interop的性能优于P / Invoke。

由C ++ Interop执行的数据封送处理是最简单的形式:将参数以位方式简单地复制到托管/非托管边界上。根本不执行任何转换。对于P / Invoke,仅当所有参数均为简单的可变位类型时,才为真。否则,P / Invoke将执行非常健壮的步骤,将每个托管参数转换为适当的本机类型,如果将参数标记为“ out”或“ in,out”,则反之亦然。

换句话说,C ++ Interop使用最快的数据封送方法,而P / Invoke使用最可靠的方法。这意味着默认情况下,C ++ Interop(以C ++的典型方式)提供最佳性能,并且程序员负责解决这种行为不安全或不适当的情况。

因此,

C ++ Interop要求必须明确提供数据封送处理,但是优点是,程序员可以自由地决定什么是适当的(在给定数据的性质以及如何使用的情况下)。此外,尽管可以在一定程度上自定义P / Invoke数据封送的行为,但是C ++ Interop允许在逐个呼叫的基础上自定义数据封送。使用P / Invoke无法做到这一点。

下面的P /调用示例:

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互操作示例(在使用C#代码的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;  
}