В чем разница между pInvoke и COM Interop?

Скажем, что я обращаюсь к сторонней библиотеке, для которой в документации указано, что я могу использовать pInvoke или создать библиотеку interop и использовать COM. В чем разница между этими двумя методами и почему я могу выбрать один из них?

Ответы

Ответ 1

P/Invoke используется для вызова API-интерфейсов plain-C (как и большинство Win32 API). COM-вызов используется для вызова COM-объектов.

Вы можете создать обертку С++ COM вокруг API C, а затем использовать COM-взаимодействие для вызова вашей оболочки, если количество вызовов API относительно велико (и вы можете использовать COM-оболочку, чтобы инкапсулировать их в один или два вызова), Это связано с тем, что интерфейс с управляемым интерфейсом может быть относительно дорогостоящим, и хорошо свести к минимуму количество переходов. Хотя на самом деле я бы сказал, что использование С++/CLI для создания обертки, вероятно, будет немного более дружественным для стороны С# (смотря на SlimDX, например, который является оболочкой С++/CLI вокруг COM API (DirectX)).

Сказав это, если у вас нет конкретной проблемы с производительностью, я бы просто использовал любой метод, более естественный для API, который вы пытаетесь вызвать: если это API C (например, Win32 API), то используйте P/Invoke. Если это COM-based, то используйте COM-взаимодействие.

Ответ 2

PInvoke использует механизм динамической компоновки для приведения внешнего кода в исполняемый процесс. Библиотеки динамической компоновки библиотек (DLL) должны обладать той же целевой архитектурой, что и вызывающее приложение, поэтому нет возможности перекрестных вызовов с 64-разрядной до 32-разрядной или наоборот. Вместо этого DLL отображается в адресное пространство вызывающего абонента и выполняется в процессе.

COM, DCOM, COM + и ActiveX основаны на межпроцессных коммуникационных библиотеках, но иногда могут превращаться в простую загрузку DLL. Связанные с COM объекты связаны, но не идентичны объектам CORBA, но в то время как CORBA развивал свой собственный локатор объектов, реализация COM по-прежнему свободно основана на библиотеках Sun Microsystems RPC и XDR с расширениями для объектно-ориентированных функций COM. Объекты COM ссылаются не на DLL, а на GUID, который используется для поиска класса объекта и запроса его интерфейсов. Объектный код обычно запускается в отдельном процессе и, возможно, на отдельном сервере.

Ответ 3

Функциональная совместимость позволяет вам сохранять и использовать существующие вложения в неуправляемый код. Код, который выполняется под управлением общеязыковой среды выполнения (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 все еще слабо основана на библиотеках RPC и XDR Sun Microsystems с расширениями для объектно-ориентированных функций COM. На COM-объекты ссылаются не DLL, а GUID, который используется для поиска класса объекта и запроса его интерфейсов. Объектный код обычно выполняется в отдельном процессе или может быть на отдельном сервере.

Для языков .NET, таких как Visual Basic и С#, предписанный метод взаимодействия с нативными компонентами - P/Invoke. Поскольку P/Invoke поддерживается .NET Framework, Visual C++ также поддерживает его, но Visual C++ также предоставляет свою собственную поддержку взаимодействия, которая называется C++ Interop. C++ Взаимодействие предпочтительнее P/Invoke, потому что P/Invoke не является безопасным для типов. В результате ошибки в основном сообщаются во время выполнения, но C++ Interop также имеет преимущества в производительности по сравнению с P/Invoke.

Маршалинг данных, выполняемый Interop C++, является самой простой из возможных форм: параметры просто копируются через управляемую/неуправляемую границу побитовым образом; преобразование не выполняется вообще. Для P/Invoke это верно только в том случае, если все параметры являются простыми, blittable типами. В противном случае P/Invoke выполняет очень надежные шаги для преобразования каждого управляемого параметра в соответствующий собственный тип, и наоборот, если аргументы помечены как "out" или "in, out".

Другими словами, C++ Interop использует самый быстрый из возможных методов маршалинга данных, тогда как P/Invoke использует самый надежный метод. Это означает, что C++ Interop (в типичной для C++ манере) обеспечивает оптимальную производительность по умолчанию, и программист отвечает за рассмотрение случаев, когда это поведение небезопасно или нецелесообразно.

C++ Поэтому взаимодействие требует, чтобы маршалинг данных был предоставлен явно, но преимущество в том, что программист свободен решать, что уместно, учитывая природу данных и как их использовать. Кроме того, хотя поведение маршалинга данных P/Invoke может быть изменено до определенной степени, взаимодействие C++ позволяет настраивать маршалинг данных на индивидуальной основе. Это невозможно с P/Invoke.

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);
    }
}

Пример Com Interop (в 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;  
}