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

时间:2010-02-06 03:15:05

标签: c# c++ c++-cli pinvoke managed-c++

我有一个用C ++实现的类,它负责程序的算术运算,以及一个使用WPF的接口。我使用C#处理输入但是我如何使用我的C ++类?

我已经看到一些关于使托管C ++包装类与它交互的注释,但我不知道从哪里开始。我也不知道如何将其与所有其他代码一起编译。我无法真正找到这方面的教程,并且在托管C ++上显示谷歌的东西看起来并没有什么帮助。

有什么东西可以帮助我吗?这对我来说似乎并不合理。

编辑尝试了m3rLinEz解决方案,但它给了我一个BadImageFormatException,我认为这是因为没有生成DLL。我按照所说的做了一切,不知道发生了什么。有什么想法吗?

4 个答案:

答案 0 :(得分:56)

你看过C ++ / CLI吗?

让我举一个很短的例子。这是Visual C ++中的源文件 - > CLR - >类库项目。它基本上获得Windows用户名并返回它。

请注意,为了进行编译,您必须进入项目设置并将“Additional Dependencies”标记为“从父级继承”,因为我们正在使用这些Windows库(kernel32.lib,user32.lib ,. 。)

// 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!");
            }
        }
    };
}

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

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

这在我的机器上给出了以下结果:

  

你好m3rlinez!

C ++ / CLI基本上是基于C ++标准的托管扩展。它允许您在C ++ / CLI项目中使用CLR类和数据类型,并将其公开给托管语言。您可以使用此方法为旧C ++库创建托管包装器。有一些奇怪的语法,例如String^来定义CLR String的引用类型。我发现"Quick C++/CLI - Learn C++/CLI in less than 10 minutes"在这里很有用。

答案 1 :(得分:9)

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

  1. C ++ / CLI
  2. 平台调用
  3. 将您的C ++包装在COM对象中
  4. 在工作中我们使用C ++ / CLI,它似乎有效。

答案 2 :(得分:4)

我将创建一个标准(非COM /托管)动态链接库作为described here,然后使用c#代码中的DllImport attribute(平台调用)来访问导出的函数。

该文章的关键点:

  

注意__declspec(dllexport)   方法声明中的修饰符   这段代码。这些修饰符使得   这样由DLL导出的方法   它可以被其他人使用   应用。欲获得更多信息,   见dllexport,dllimport。

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

另一种选择是It Just Works(IJW)。如果您有托管的C ++代码并且需要从其他.NET语言访问它,那么这可能是更好的选择。但是,如果您能够/很乐意将非托管C ++转换为托管C ++,那么这只是一个选项。

答案 3 :(得分:3)

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

嗯......好的。我的印象是P / Invoke调用速度较慢,而它们本身并不是。但是,通过明确控制编组,您可以使C ++ / CLI版本在许多情况下表现更好。

以下是Microsoft关于这两种机制的文章:

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

  

IJW的优势

     
      
  • 无需为此编写DLLImport属性声明   程序使用的非托管API。只是   包括头文件和链接   导入库。
  •   
  • IJW机制稍快(例如,IJW存根不会   需要检查是否需要销或   复制数据项,因为这样做   由开发商明确表示。
  •   
  • 它清楚地说明了性能问题。在这种情况下,事实是   您正在从Unicode进行翻译   字符串到ANSI字符串,你   有一个伴随的内存分配   和解除分配。在这种情况下,一个   开发人员使用IJW编写代码   会意识到调用_putws和   使用PtrToStringChars会更好   表现。
  •   
  • 如果使用相同的数据调用许多非托管API,请对其进行封送处理   曾经和通过编组的副本是   比重新编组更有效率   每一次。
  •   

还有美学上的优势:

  • C#代码看起来像C#代码,没有任何interop'y古怪。
  • 您不必定义DLLImport属性,您不必定义任何数据结构(也包含p / invoke特定属性),如下所示:

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

  • 您不必将所有参数基元类型转换为它们的.NET对应项(this page上的表格列出了托管类型如何映射到非托管类型)。
  • 您可以使用C ++ / CLI,这非常有趣,而且非常精致。自VS 2003以来它已经走了很长一段路,现在是一个功能齐全的.NET语言。与所有IJW信息一样,Microsoft的文档非常好。
  • 在C ++ / CLI中进行C ++互操作感觉非常自然而不是C#。这是完全主观的,但我更愿意在C ++中进行Marshal.PtrToString(ptr)的字符串编组。
  • 如果暴露API,您可能想要将所有P / Invoke内容包装在另一层中,因此您不必处理P / Invoke丑陋。这样你就可以获得所有编组和它周围的C#层的开销。使用C ++ / CLI,编组和互操作抽象在一个地方,您可以选择需要多少编组。

恕我直言,如果你在Windows SDK中调用一个奇怪的函数,请使用P / Invoke。如果要将一个中等复杂的C ++ API暴露给托管世界,那肯定是C ++ / CLI。