如何从c#调用c++ /CLI ?

时间:2022-09-01 11:28:03

I have a class implemented in C++ that's responsible for the arithmetic computation of the program, and an interface using WPF. I process the input with C# but then how can I use my C++ class?

我有一个用c++实现的类,负责程序的算术计算,还有一个使用WPF的接口。我使用c#处理输入,但是如何使用c++类呢?

I've seen some comments about making a managed C++ wrapper class to interact with it, but I don't know where to start. Nor do I know how I'd go to compile it along with all the other code. I can't really find a tutorial on this, and the stuff google shows on managed C++ doesn't really seem helpful.

我看到一些关于创建托管c++包装器类以与之交互的评论,但是我不知道从哪里开始。我也不知道如何将它与所有其他代码一起编译。我找不到关于这方面的教程,谷歌在托管c++上显示的内容似乎也没什么帮助。

Anything out there to help me out? This doesn't seem unreasonable to me.

有什么可以帮我的吗?这在我看来并不是不合理的。

EDIT Tried m3rLinEz solution but it's giving me a BadImageFormatException, I think it's because the DLL isn't generated. I did everything as told, dunno what happened. Any ideas?

编辑尝试过的m3rLinEz解决方案,但是它给了我一个BadImageFormatException,我认为这是因为DLL没有生成。我照办了,不知道发生了什么事。什么好主意吗?

4 个解决方案

#1


56  

Have you take a look at C++/CLI?

你看过c++ /CLI吗?

Let me give a very short example. Here is the source file from a Visual C++ -> CLR -> Class Library project. It basically get Windows username and return it.

我举一个很简单的例子。这是来自Visual c++ -> CLR ->类库项目的源文件。它获取Windows用户名并返回它。

Please note that, in order to get this compiled, you have to go into project settings and mark "Additional Dependencies" as "Inherit from parent" because we are using those Windows libs (kernel32.lib, user32.lib, ..)

请注意,为了编译这个程序,您必须进入项目设置并将“附加依赖项”标记为“继承自父”,因为我们正在使用这些Windows libs (kernel32)。*,user32。*,. .)

// CSCPP.h

#pragma once

#include "windows.h"

using namespace System;

namespace CSCPP {

    public ref class Class1
    {
        // TODO: Add your methods for this class here.
    public:
        String^ GetText(){
            WCHAR acUserName[100];
            DWORD nUserName = sizeof(acUserName);
            if (GetUserName(acUserName, &nUserName)) {
                String^ name = gcnew String(acUserName);
                return String::Format("Hello {0} !", name);
            }else{
                return gcnew String("Error!");
            }
        }
    };
}

Now created a new C# project and add reference to our first C++/CLI Class Library project. And then call the instance method.

现在创建了一个新的c#项目,并添加了对第一个c++ /CLI类库项目的引用。然后调用实例方法。

namespace CSTester
{
    class Program
    {
        static void Main(string[] args)
        {
            CSCPP.Class1 instance = new CSCPP.Class1();
            Console.WriteLine(instance.GetText());
        }
    }
}

This gave the following result on my machine:

这在我的机器上得到了如下结果:

Hello m3rlinez !

你好m3rlinez !

C++/CLI is basically a managed extension over C++ standard. It allows you to utilize CLR classes and data types in your C++/CLI project and also expose this to managed language. You can created a managed wrapper for your old C++ library using this. There are some weird syntaxes such as String^ to define reference type to CLR String. I find "Quick C++/CLI - Learn C++/CLI in less than 10 minutes" to be useful here.

c++ /CLI基本上是超越c++标准的托管扩展。它允许您在c++ /CLI项目中使用CLR类和数据类型,并将其公开给托管语言。您可以使用它为您的旧c++库创建一个托管包装器。有一些奇怪的语法比如字符串^定义引用类型CLR字符串。我发现“快速c++ /CLI -在不到10分钟内学习c++ /CLI”在这里是有用的。

#2


8  

There are at least three ways to call unmanaged code from managed in the same process:

在同一过程中,至少有三种方法从托管中调用非托管代码:

  1. C++/CLI
  2. c++ / CLI
  3. Platform Invoke
  4. 平台调用
  5. Wrap your C++ in a COM object
  6. 在COM对象中包装c++

At work we use C++/CLI for this, it seems to work.

在工作中,我们使用c++ /CLI来实现这一点,它似乎可以工作。

#3


4  

I would create a standard (non COM/Managed) Dynamic Link Library as described here and then use the DllImport attribute (platform invoke) in the c# code to access the exported functions.

我将创建这里描述的标准(非COM/Managed)动态链接库,然后使用c#代码中的DllImport属性(平台调用)来访问导出的函数。

The key point from that article:

这篇文章的要点是:

Note the __declspec(dllexport) modifier in the method declarations in this code. These modifiers enable the method to be exported by the DLL so that it can be used by other applications. For more information, see dllexport, dllimport.

注意此代码中的方法声明中的__declspec(dllexport)修饰符。这些修饰符使DLL导出的方法能够被其他应用程序使用。有关更多信息,请参见dllexport, dllimport。

This is a lighter weight alternative to an actual COM interop wrapper and avoids issues such as registration etc (the DLL can simply be placed in the application directory) .

这是一个比实际的COM互操作包装器更轻量级的替代方案,避免了诸如注册等问题(DLL可以简单地放在应用程序目录中)。

Another alternative is It Just Works (IJW). This is probably a better choice if you have managed C++ code and need to access this from other .NET languages. But this is only an option if you are able/happy to convert your unmanaged C++ to managed C++ though.

另一种选择是它只起作用(IJW)。如果您已经管理过c++代码,并且需要从其他。net语言访问这些代码,那么这可能是一个更好的选择。但是,只有当您能够/高兴地将非托管c++转换为托管c++时,这才是一个选项。

#4


3  

I would stay away from P/Invoke as it's pretty slow compared to IJW (It Just Works). The latter allows you to seamlessly interweave managed and and unmanaged c++. All you have to do is to create a managed c++ assembly, write a managed class that is visible from c# and call the unmanaged code out of that.

我将远离P/Invoke,因为与IJW相比,它非常慢(它只是工作)。后者允许您无缝地交织托管和非托管c++。您所要做的就是创建一个托管c++程序集,编写一个从c#可见的托管类,并从中调用非托管代码。

Uhm... OK. I was under the impression that P/Invoke calls were slower which they are inherintly not. However, by having explicit control over marshalling, you can make your C++/CLI version to perform better in a lot of the cases.

嗯……好的。我的印象是,P/Invoke调用要慢一些,而它们本质上并不慢。但是,通过对编组进行显式控制,您可以使您的c++ /CLI版本在许多情况下表现得更好。

Here is Microsoft's article about both mechanisms:

下面是微软关于两种机制的文章:

http://msdn.microsoft.com/en-us/library/ms235282.aspx

http://msdn.microsoft.com/en-us/library/ms235282.aspx

Advantages of IJW

IJW的优点

  • There is no need to write DLLImport attribute declarations for the unmanaged APIs the program uses. Just include the header file and link with the import library.
  • 不需要为程序使用的非托管api编写DLLImport属性声明。只需包含头文件并与导入库链接。
  • The IJW mechanism is slightly faster (for example, the IJW stubs do not need to check for the need to pin or copy data items because that is done explicitly by the developer).
  • IJW机制稍微快一点(例如,IJW存根不需要检查是否需要对数据项进行pin或复制,因为这是由开发人员显式完成的)。
  • It clearly illustrates performance issues. In this case, the fact that you are translating from a Unicode string to an ANSI string and that you have an attendant memory allocation and deallocation. In this case, a developer writing the code using IJW would realize that calling _putws and using PtrToStringChars would be better for performance.
  • 它清楚地说明了性能问题。在本例中,事实是您正在将Unicode字符串转换为ANSI字符串,并且您有相应的内存分配和释放。在这种情况下,使用IJW编写代码的开发人员会意识到,调用_putws和使用PtrToStringChars会更好地提高性能。
  • If you call many unmanaged APIs using the same data, marshaling it once and passing the marshaled copy is much more efficient than re-marshaling every time.
  • 如果您使用相同的数据调用许多非托管的api,那么一次封送并传递封送的副本要比每次重新封送要有效得多。

There are aesthetic advantages as well:

也有美学上的优势:

  • C# code looks like C# code without any interop'y weirdness.
  • c#代码看起来像c#代码,没有任何奇怪的交互。
  • You don't have to define DLLImport attribute, you don't have to define any of the data structures (also with p/invoke specific attributes) which could look like this:

    您不必定义DLLImport属性,也不必定义任何数据结构(也可以使用p/invoke特定属性),这些数据结构可以如下所示:

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct DevMode { [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string dmDeviceName; }

    [StructLayout LayoutKind。顺序的,CharSet = CharSet. ansi)]公共结构DevMode {[MarshalAs(UnmanagedType)。ByValTStr, SizeConst = 32)公共字符串dmDeviceName;}

  • You don't have to convert all parameter primitive types into their .NET counterparts (there is table on this page that lists how managed types map to unmanaged types).
  • 您不必将所有参数原语类型转换为它们的。net对应类型(在这个页面上有一个表列出了托管类型如何映射到非托管类型)。
  • You get to work with C++/CLI which is really fun to learn and is really polished. It's come a long way since VS 2003 and is now a fully featured .NET language. Microsoft documentation for it is pretty good, as is all the IJW information.
  • 您可以使用c++ /CLI进行工作,这非常有趣,而且非常精练。自从2003年VS以来,它已经取得了长足的进步,现在它是一种功能齐全的。net语言。它的Microsoft文档非常好,所有IJW信息也是如此。
  • Doing C++ interop in C++/CLI feels very natural as opposed to C#. This is completely subjective, but I would much rather prefer to do string marshalling in C++ that do Marshal.PtrToString(ptr).
  • 与c#相比,在c++ /CLI中执行c++互操作感觉非常自然。这完全是主观的,但我更喜欢在c++中执行编组,并执行编组。ptrtostring (ptr)。
  • If exposing an API you would probably want to wrap up all P/Invoke stuff in another layer, so you don't have to deal with P/Invoke ugliness. This way you have the overhead of all the marshalling AND the C# layer around it. With C++/CLI the marshalling and the interop abstraction are in one place, and you can choose how much marshalling you need.
  • 如果公开一个API,您可能想要在另一个层中包装所有的P/Invoke内容,因此您不必处理P/Invoke丑陋性。这样,就有了所有编组和c#层的开销。使用c++ /CLI的编组和互操作抽象在一个地方,您可以选择需要多少编组。

IMHO if you are calling an odd function in Windows SDK, go with P/Invoke. If you are exposing a moderately complex C++ API to the managed world, definitely C++/CLI.

IMHO如果你在Windows SDK中调用一个奇怪的函数,可以使用P/Invoke。如果您正在向托管世界公开一个适度复杂的c++ API,那么一定要使用c++ /CLI。

#1


56  

Have you take a look at C++/CLI?

你看过c++ /CLI吗?

Let me give a very short example. Here is the source file from a Visual C++ -> CLR -> Class Library project. It basically get Windows username and return it.

我举一个很简单的例子。这是来自Visual c++ -> CLR ->类库项目的源文件。它获取Windows用户名并返回它。

Please note that, in order to get this compiled, you have to go into project settings and mark "Additional Dependencies" as "Inherit from parent" because we are using those Windows libs (kernel32.lib, user32.lib, ..)

请注意,为了编译这个程序,您必须进入项目设置并将“附加依赖项”标记为“继承自父”,因为我们正在使用这些Windows libs (kernel32)。*,user32。*,. .)

// CSCPP.h

#pragma once

#include "windows.h"

using namespace System;

namespace CSCPP {

    public ref class Class1
    {
        // TODO: Add your methods for this class here.
    public:
        String^ GetText(){
            WCHAR acUserName[100];
            DWORD nUserName = sizeof(acUserName);
            if (GetUserName(acUserName, &nUserName)) {
                String^ name = gcnew String(acUserName);
                return String::Format("Hello {0} !", name);
            }else{
                return gcnew String("Error!");
            }
        }
    };
}

Now created a new C# project and add reference to our first C++/CLI Class Library project. And then call the instance method.

现在创建了一个新的c#项目,并添加了对第一个c++ /CLI类库项目的引用。然后调用实例方法。

namespace CSTester
{
    class Program
    {
        static void Main(string[] args)
        {
            CSCPP.Class1 instance = new CSCPP.Class1();
            Console.WriteLine(instance.GetText());
        }
    }
}

This gave the following result on my machine:

这在我的机器上得到了如下结果:

Hello m3rlinez !

你好m3rlinez !

C++/CLI is basically a managed extension over C++ standard. It allows you to utilize CLR classes and data types in your C++/CLI project and also expose this to managed language. You can created a managed wrapper for your old C++ library using this. There are some weird syntaxes such as String^ to define reference type to CLR String. I find "Quick C++/CLI - Learn C++/CLI in less than 10 minutes" to be useful here.

c++ /CLI基本上是超越c++标准的托管扩展。它允许您在c++ /CLI项目中使用CLR类和数据类型,并将其公开给托管语言。您可以使用它为您的旧c++库创建一个托管包装器。有一些奇怪的语法比如字符串^定义引用类型CLR字符串。我发现“快速c++ /CLI -在不到10分钟内学习c++ /CLI”在这里是有用的。

#2


8  

There are at least three ways to call unmanaged code from managed in the same process:

在同一过程中,至少有三种方法从托管中调用非托管代码:

  1. C++/CLI
  2. c++ / CLI
  3. Platform Invoke
  4. 平台调用
  5. Wrap your C++ in a COM object
  6. 在COM对象中包装c++

At work we use C++/CLI for this, it seems to work.

在工作中,我们使用c++ /CLI来实现这一点,它似乎可以工作。

#3


4  

I would create a standard (non COM/Managed) Dynamic Link Library as described here and then use the DllImport attribute (platform invoke) in the c# code to access the exported functions.

我将创建这里描述的标准(非COM/Managed)动态链接库,然后使用c#代码中的DllImport属性(平台调用)来访问导出的函数。

The key point from that article:

这篇文章的要点是:

Note the __declspec(dllexport) modifier in the method declarations in this code. These modifiers enable the method to be exported by the DLL so that it can be used by other applications. For more information, see dllexport, dllimport.

注意此代码中的方法声明中的__declspec(dllexport)修饰符。这些修饰符使DLL导出的方法能够被其他应用程序使用。有关更多信息,请参见dllexport, dllimport。

This is a lighter weight alternative to an actual COM interop wrapper and avoids issues such as registration etc (the DLL can simply be placed in the application directory) .

这是一个比实际的COM互操作包装器更轻量级的替代方案,避免了诸如注册等问题(DLL可以简单地放在应用程序目录中)。

Another alternative is It Just Works (IJW). This is probably a better choice if you have managed C++ code and need to access this from other .NET languages. But this is only an option if you are able/happy to convert your unmanaged C++ to managed C++ though.

另一种选择是它只起作用(IJW)。如果您已经管理过c++代码,并且需要从其他。net语言访问这些代码,那么这可能是一个更好的选择。但是,只有当您能够/高兴地将非托管c++转换为托管c++时,这才是一个选项。

#4


3  

I would stay away from P/Invoke as it's pretty slow compared to IJW (It Just Works). The latter allows you to seamlessly interweave managed and and unmanaged c++. All you have to do is to create a managed c++ assembly, write a managed class that is visible from c# and call the unmanaged code out of that.

我将远离P/Invoke,因为与IJW相比,它非常慢(它只是工作)。后者允许您无缝地交织托管和非托管c++。您所要做的就是创建一个托管c++程序集,编写一个从c#可见的托管类,并从中调用非托管代码。

Uhm... OK. I was under the impression that P/Invoke calls were slower which they are inherintly not. However, by having explicit control over marshalling, you can make your C++/CLI version to perform better in a lot of the cases.

嗯……好的。我的印象是,P/Invoke调用要慢一些,而它们本质上并不慢。但是,通过对编组进行显式控制,您可以使您的c++ /CLI版本在许多情况下表现得更好。

Here is Microsoft's article about both mechanisms:

下面是微软关于两种机制的文章:

http://msdn.microsoft.com/en-us/library/ms235282.aspx

http://msdn.microsoft.com/en-us/library/ms235282.aspx

Advantages of IJW

IJW的优点

  • There is no need to write DLLImport attribute declarations for the unmanaged APIs the program uses. Just include the header file and link with the import library.
  • 不需要为程序使用的非托管api编写DLLImport属性声明。只需包含头文件并与导入库链接。
  • The IJW mechanism is slightly faster (for example, the IJW stubs do not need to check for the need to pin or copy data items because that is done explicitly by the developer).
  • IJW机制稍微快一点(例如,IJW存根不需要检查是否需要对数据项进行pin或复制,因为这是由开发人员显式完成的)。
  • It clearly illustrates performance issues. In this case, the fact that you are translating from a Unicode string to an ANSI string and that you have an attendant memory allocation and deallocation. In this case, a developer writing the code using IJW would realize that calling _putws and using PtrToStringChars would be better for performance.
  • 它清楚地说明了性能问题。在本例中,事实是您正在将Unicode字符串转换为ANSI字符串,并且您有相应的内存分配和释放。在这种情况下,使用IJW编写代码的开发人员会意识到,调用_putws和使用PtrToStringChars会更好地提高性能。
  • If you call many unmanaged APIs using the same data, marshaling it once and passing the marshaled copy is much more efficient than re-marshaling every time.
  • 如果您使用相同的数据调用许多非托管的api,那么一次封送并传递封送的副本要比每次重新封送要有效得多。

There are aesthetic advantages as well:

也有美学上的优势:

  • C# code looks like C# code without any interop'y weirdness.
  • c#代码看起来像c#代码,没有任何奇怪的交互。
  • You don't have to define DLLImport attribute, you don't have to define any of the data structures (also with p/invoke specific attributes) which could look like this:

    您不必定义DLLImport属性,也不必定义任何数据结构(也可以使用p/invoke特定属性),这些数据结构可以如下所示:

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct DevMode { [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string dmDeviceName; }

    [StructLayout LayoutKind。顺序的,CharSet = CharSet. ansi)]公共结构DevMode {[MarshalAs(UnmanagedType)。ByValTStr, SizeConst = 32)公共字符串dmDeviceName;}

  • You don't have to convert all parameter primitive types into their .NET counterparts (there is table on this page that lists how managed types map to unmanaged types).
  • 您不必将所有参数原语类型转换为它们的。net对应类型(在这个页面上有一个表列出了托管类型如何映射到非托管类型)。
  • You get to work with C++/CLI which is really fun to learn and is really polished. It's come a long way since VS 2003 and is now a fully featured .NET language. Microsoft documentation for it is pretty good, as is all the IJW information.
  • 您可以使用c++ /CLI进行工作,这非常有趣,而且非常精练。自从2003年VS以来,它已经取得了长足的进步,现在它是一种功能齐全的。net语言。它的Microsoft文档非常好,所有IJW信息也是如此。
  • Doing C++ interop in C++/CLI feels very natural as opposed to C#. This is completely subjective, but I would much rather prefer to do string marshalling in C++ that do Marshal.PtrToString(ptr).
  • 与c#相比,在c++ /CLI中执行c++互操作感觉非常自然。这完全是主观的,但我更喜欢在c++中执行编组,并执行编组。ptrtostring (ptr)。
  • If exposing an API you would probably want to wrap up all P/Invoke stuff in another layer, so you don't have to deal with P/Invoke ugliness. This way you have the overhead of all the marshalling AND the C# layer around it. With C++/CLI the marshalling and the interop abstraction are in one place, and you can choose how much marshalling you need.
  • 如果公开一个API,您可能想要在另一个层中包装所有的P/Invoke内容,因此您不必处理P/Invoke丑陋性。这样,就有了所有编组和c#层的开销。使用c++ /CLI的编组和互操作抽象在一个地方,您可以选择需要多少编组。

IMHO if you are calling an odd function in Windows SDK, go with P/Invoke. If you are exposing a moderately complex C++ API to the managed world, definitely C++/CLI.

IMHO如果你在Windows SDK中调用一个奇怪的函数,可以使用P/Invoke。如果您正在向托管世界公开一个适度复杂的c++ API,那么一定要使用c++ /CLI。