【问题标题】:Managed C++ to form a bridge between c# and C++托管 C++ 以在 C# 和 C++ 之间架起一座桥梁
【发布时间】:2010-12-22 01:50:28
【问题描述】:

我有点生疏了,实际上我的 C++ 真的生疏了。自从大学一年级以来就没有碰过它,所以已经有一段时间了。

无论如何,我正在做大多数人所做的相反的事情。从 C++ 调用 C# 代码。我在网上做了一些研究,似乎我需要创建一些托管 C++ 来形成一座桥梁。使用 __declspec(dllexport) 然后从中创建一个 dll 并将整个东西用作包装器。

但我的问题是 - 我真的很难找到例子。我发现了一些基本的东西,有人想使用 C# 版本的 String.ToUpper() 但那是非常基本的,只是一个小代码 sn-p。

有人知道我可以在哪里寻找更具体的东西吗?注意,我不想使用 COM。目标是完全不接触 C# 代码。

【问题讨论】:

标签: c# clr c++-cli unmanaged


【解决方案1】:

在 Visual Studio 中创建一个新的 C++/CLI 项目并添加对 C# dll 的引用。假设我们有一个名为 DotNetLib.dll 的 C# dll,该类位于:

namespace DotNetLib
{
    public class Calc
    {
        public int Add(int a, int b)
        {
            return a + b;
        }
    }
}

现在将 CLR C++ 类添加到您的 C++/CLI 项目中:

// TestCPlusPlus.h

#pragma once

using namespace System;
using namespace DotNetLib;

namespace TestCPlusPlus {

    public ref class ManagedCPlusPlus
    {
    public:
        int Add(int a, int b)
        {
            Calc^ c = gcnew Calc();
            int result = c->Add(a, b);
            return result;
        }
    };
}

这将从 C++ 调用 C#。

现在,如果需要,您可以将本机 C++ 类添加到您的 C++/CLI 项目中,它可以与 CLR C++ 类对话:

// Native.h
#pragma once

class Native
{
public:
    Native(void);
    int Add(int a, int b);
    ~Native(void);
};

和:

// Native.cpp
#include "StdAfx.h"
#include "Native.h"
#include "TestCPlusPlus.h"

Native::Native(void)
{
}

Native::~Native(void)
{
}

int Native::Add(int a, int b)
{
    TestCPlusPlus::ManagedCPlusPlus^ c = gcnew TestCPlusPlus::ManagedCPlusPlus();
    return c->Add(a, b);
}

您应该能够像往常一样从任何其他本机 C++ dll 调用本机类。

另请注意,托管 C++ 与 C++/CLI 不同,并已被 C++/CLI 取代。维基百科解释得最好:

http://en.wikipedia.org/wiki/C%2B%2B/CLI

【讨论】:

    【解决方案2】:

    虽然懒得我写了一个例子,但我还是会发布它以防万一......

    编写包装器以访问您自己的库的过程与访问标准 .Net 库之一相同。

    一个名为 CsharpProject 的项目中的示例 C# 类代码:

    using System;
    
    namespace CsharpProject {
        public class CsharpClass {
            public string Name { get; set; }
            public int Value { get; set; }
    
            public string GetDisplayString() {
                return string.Format("{0}: {1}", this.Name, this.Value);
            }
        }
    }
    

    您将创建一个托管 C++ 类库项目(例如 CsharpWrapper)并将您的 C# 项目添加为对其的引用。为了在内部使用和引用项目中使用相同的头文件,您需要一种使用正确 declspec 的方法。这可以通过定义预处理器指令(在这种情况下为CSHARPWRAPPER_EXPORTS)并使用#ifdef 在头文件中的C/C++ 接口中设置导出宏来完成。非托管接口头文件必须包含非托管内容(或已被预处理器过滤掉)。

    非托管C++接口头文件(CppInterface.h):

    #pragma once
    
    #include <string>
    
    // Sets the interface function's decoration as export or import
    #ifdef CSHARPWRAPPER_EXPORTS 
    #define EXPORT_SPEC __declspec( dllexport )
    #else
    #define EXPORT_SPEC __declspec( dllimport )
    #endif
    
    // Unmanaged interface functions must use all unmanaged types
    EXPORT_SPEC std::string GetDisplayString(const char * pName, int iValue);
    

    然后您可以创建一个内部头文件,以便能够包含在您的托管库文件中。这将添加 using namespace 语句,并且可以包含您需要的辅助函数。

    托管 C++ 接口头文件 (CsharpInterface.h):

    #pragma once
    
    #include <string>
    
    // .Net System Namespaces
    using namespace System;
    using namespace System::Runtime::InteropServices;
    
    // C# Projects
    using namespace CsharpProject;
    
    
    //////////////////////////////////////////////////
    // String Conversion Functions
    
    inline
    String ^ ToManagedString(const char * pString) {
     return Marshal::PtrToStringAnsi(IntPtr((char *) pString));
    }
    
    inline
    const std::string ToStdString(String ^ strString) {
     IntPtr ptrString = IntPtr::Zero;
     std::string strStdString;
     try {
      ptrString = Marshal::StringToHGlobalAnsi(strString);
      strStdString = (char *) ptrString.ToPointer();
     }
     finally {
      if (ptrString != IntPtr::Zero) {
       Marshal::FreeHGlobal(ptrString);
      }
     }
     return strStdString;
    }
    

    然后您只需编写执行包装的接口代码。

    托管 C++ 接口源文件 (CppInterface.cpp):

    #include "CppInterface.h"
    #include "CsharpInterface.h"
    
    std::string GetDisplayString(const char * pName, int iValue) {
     CsharpClass ^ oCsharpObject = gcnew CsharpClass();
    
     oCsharpObject->Name = ToManagedString(pName);
     oCsharpObject->Value = iValue;
    
     return ToStdString(oCsharpObject->GetDisplayString());
    }
    

    然后只需在非托管项目中包含非托管标头,告诉链接器在链接时使用生成的 .lib 文件,并确保 .Net 和包装 DLL 与非托管应用程序位于同一文件夹中。

    #include <stdlib.h>
    
    // Include the wrapper header
    #include "CppInterface.h"
    
    void main() {
     // Call the unmanaged wrapper function
     std::string strDisplayString = GetDisplayString("Test", 123);
    
     // Do something with it
     printf("%s\n", strDisplayString.c_str());
    }
    

    【讨论】:

    • 你解释得更多了——希望我能同时选择这两个答案。谢谢!
    • 字符串是一个很好的例子,因为当您需要将字符串从本机编组到托管时,就需要转换函数(这在大多数互操作中通常很快!)
    • 对于“没有这样的文件或目录”,您需要:1)将CppInterface.h添加到非托管应用程序。如果所有项目都位于同一个位置/存储库中,我不会这样做。因为你有重复的文件要维护。 2) 在非托管应用的编译包含文件夹下添加包装项目文件的路径。对于 Visual Studio,右键单击项目并选择“属性”。然后在“配置属性”->“C/C++”->“常规”->“附加包含目录”下添加CppInterface.h位置的路径。
    • 一件小事:当你找不到 .lib 文件并且链接器正在哭泣时,可能是因为你没有在你的类中添加“#define EXPORT_SPEC __declspec(dllexport)”。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-12
    • 2011-08-26
    • 1970-01-01
    • 1970-01-01
    • 2022-11-13
    • 1970-01-01
    相关资源
    最近更新 更多