繁体   English   中英

pInvoke 和 COM Interop 有什么区别?

[英]What is the difference between pInvoke and COM Interop?

假设我正在访问第三方库,其文档说明我可以使用 pInvoke 或创建互操作库并使用 COM。 这两种技术有什么区别,为什么我可以选择一种而不是另一种?

P/Invoke 用于调用纯 C API(如大多数 Win32 API)。 COM互操作用于调用COM对象。

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

话虽如此,除非您有特定的性能问题,否则我只会使用对您尝试调用的 API 更自然的方法:如果它是 C API(如 Win32 API),则使用 P/Invoke。 如果它是基于 COM 的,则使用 COM 互操作。

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

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

互操作性使您能够保留和利用对非托管代码的现有投资。 在公共语言运行时 (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 引用。 目标代码通常在单独的进程中运行,或者可能在单独的服务器上。

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

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

换句话说,C++ Interop 使用最快的数据封送方法,而 P/Invoke 使用最健壮的方法。 这意味着 C++ 互操作(以 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;  
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM