pInvoke 和 COM Interop 之间有什么区别?

发布于 2024-09-07 05:47:24 字数 83 浏览 2 评论 0原文

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

Let us say that I am accessing a third-party library, for which the documentation states that I can use pInvoke or create an interop library and use COM. What is the difference between these two techniques, and why might I choose one over the other?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

久而酒知 2024-09-14 05:49:31

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

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

PInvoke uses the dynamic linking mechanism for bringing external code into the executing process. Dynamic linking libraries (DLLs) must have the same target architecture as the calling application, so there is no ability to make cross calls from 64-bit to 32-bit or vice versa. Instead the DLL is mapped into the caller's address space and executed in process.

COM, DCOM, COM+, and ActiveX are all based on interprocess communications libraries, but can sometimes devolve into a simple DLL load. COM linked objects are related, but not identical to CORBA objects, but while CORBA evolved its own object locator, the COM implementation is still loosely based on Sun Microsystems RPC and XDR libraries with extensions for the object oriented features of COM. COM objects are referenced not by DLL, but by a GUID which is used to look up the object class and query its interfaces. The object code usually runs in a separate process, and possibly on a separate server.

晒暮凉 2024-09-14 05:49:27

根据互操作性(C# 编程指南)

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

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

正如 Kevin Smathers 所说的在这个线程的其他地方

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

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

(强调我的。)

根据 互操作性的性能注意事项(C++),部分 P/Invoke 与 C++ 互操作

对于 .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,只有当所有参数都是简单的、blittable 类型时,这才是正确的。否则,P/Invoke 会执行非常稳健的步骤,将每个托管参数转换为适当的本机类型,如果参数标记为“out”或“in,out”,则反之亦然。

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

因此,C++ 互操作要求必须显式提供数据封送处理,但优点是程序员可以根据数据的性质以及如何使用数据自由决定什么是合适的。此外,虽然 P/Invoke 数据封送的行为可以在一定程度上进行自定义修改,但 C++ Interop 允许在逐个调用的基础上自定义数据封送。这对于 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 互操作示例< /a> (在使用 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;  
}

According to Interoperability (C# Programming Guide),

Interoperability enables you to preserve and take advantage of existing investments in unmanaged code. Code that runs under the control of the common language runtime (CLR) is called managed code, and code that runs outside the CLR is called unmanaged code. COM, COM+, C++ components, ActiveX components, and Microsoft Windows API are examples of unmanaged code.

.NET enables interoperability with unmanaged code through platform invoke services, the System.Runtime.InteropServices namespace, C++ interoperability, and COM interoperability (COM interop).

As Kevin Smathers has stated elsewhere in this thread,

PInvoke uses the dynamic linking mechanism for bringing external code into the executing process. Dynamic linking libraries (DLLs) must have the same target architecture as the calling application, so there is no ability to make cross calls from 64-bit to 32-bit or vice versa. Instead the DLL is mapped into the caller's address space and executed in process.

COM, DCOM, COM+, and ActiveX are all based on interprocess communications libraries, but can sometimes devolve into a simple DLL load. COM linked objects are related, but not identical to CORBA objects, but while CORBA evolved its own object locator, the COM implementation is still loosely based on Sun Microsystems RPC and XDR libraries with extensions for the object oriented features of COM. COM objects are referenced not by DLL, but by a GUID which is used to look up the object class and query its interfaces. The object code usually runs in a separate process, and possibly on a separate server.

(Emphasis mine.)

Per Performance Considerations for Interop (C++), section P/Invoke vs. C++ Interop,

For .NET languages, such as Visual Basic and C#, the prescribed method for interoperating with native components is P/Invoke. Because P/Invoke is supported by the .NET Framework, Visual C++ supports it as well, but Visual C++ also provides its own interoperability support, which is referred to as C++ Interop. C++ Interop is preferred over P/Invoke because P/Invoke is not type-safe. As a result, errors are primarily reported at run time, but C++ Interop also has performance advantages over P/Invoke.

...

The data marshaling performed by C++ Interop is the simplest possible form: the parameters are simply copied across the managed/unmanaged boundary in a bitwise fashion; no transformation is performed at all. For P/Invoke, this is only true if all parameters are simple, blittable types. Otherwise, P/Invoke performs very robust steps to convert each managed parameter to an appropriate native type, and vice versa if the arguments are marked as "out", or "in,out".

In other words, C++ Interop uses the fastest possible method of data marshaling, whereas P/Invoke uses the most robust method. This means that C++ Interop (in a fashion typical for C++) provides optimal performance by default, and the programmer is responsible for addressing cases where this behavior is not safe or appropriate.

C++ Interop therefore requires that data marshaling must be provided explicitly, but the advantage is that the programmer is free to decide what is appropriate, given the nature of the data, and how it is to be used. Furthermore, although the behavior of P/Invoke data marshaling can be modified at customized to a degree, C++ Interop allows data marshaling to be customized on a call-by-call basis. This is not possible with P/Invoke.

P/Invoke example below:

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 example (in C++ that is consuming C# code):

// 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;  
}
别低头,皇冠会掉 2024-09-14 05:49:16

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 互操作。

P/Invoke is used to call plain-C APIs (like most of the Win32 API). COM interop is used to call COM objects.

You might create a C++ COM wrapper around a C API and then use COM interop to call your wrapper if the number of API calls is relatively high (and you can use the COM wrapper to encapsulate them into just one or two calls). This is because managed-native interop can be relatively expensive and it's good to minimise the number of transitions. Though actually I would say using C++/CLI to create the wrapper would probably be a little more friendly for the C# side of thing (looking at SlimDX, for example, which is a C++/CLI wrapper around a COM API (DirectX)).

Having said that, unless you have a specific performance problem, I would just use whichever method is more natural for the API you're trying to call: if it's a C API (like the Win32 API is) then use P/Invoke. If it's COM-based, then use COM interop.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文