簡體   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