C# 使用 CLI 包装器调用非托管 C++

Posted

技术标签:

【中文标题】C# 使用 CLI 包装器调用非托管 C++【英文标题】:C# calling unmanaged C++ using CLI wrapper 【发布时间】:2020-11-18 15:50:17 【问题描述】:

我一直在阅读此处的多个示例以及有关如何完成此操作的其他来源。最近,我关注了这个在多个其他类似问题中已链接到的具体示例。

https://dorodnic.com/blog/2014/12/10/calling-cpp-by-example/

但是,即使直接从 github 导入此项目,它仍然会在 C# 项目中出现错误,无法引用 C++ CLI 代码。 “找不到类型或命名空间(您是否缺少 using 指令或程序集引用)”。这是我在自己遵循其他示例时发现的相同错误。谁能向我解释为什么这会失败和/或建议修复它的步骤?

编辑:在此处添加代码以节省时间。

//Logic.h
#pragma once

namespace MeaningOfLife

    namespace Cpp
    
        // This is our native implementation
        // It's marked with __declspec(dllexport) 
        // to be visible from outside the DLL boundaries
        class __declspec(dllexport) Logic
        
        public:
            int Get() const; // That's where our code goes
        ;
    

//Logic.cpp
#include "Logic.h"

int MeaningOfLife::Cpp::Logic::Get() const

    return 42; // Really, what else did you expect?

//Logic.h CLI
#pragma once

namespace MeaningOfLife

    namespace Cpp
    
        // First a Forward Declaration to Cpp::Logic class:
        class Logic; // This allows us to mention it in this header file
        // without actually including the native version of Logic.h

        namespace CLI
        
            // Next is the managed wrapper of Logic:
            public ref class Logic
            
            public:
                // Managed wrappers are generally less concerned 
                // with copy constructors and operators, since .NET will
                // not call them most of the time.
                // The methods that do actually matter are:
                // The constructor, the "destructor" and the finalizer
                Logic();
                ~Logic();
                !Logic();

                int Get();

                void Destroy();

                static void InitializeLibrary(System::String^ path);
            private:
                // Pointer to our implementation
                Cpp::Logic* _impl;
            ;
        
       

//Logic.cpp CLI
#include "Logic.h"
#include "..\MeaningOfLife.Cpp\Logic.h"
#include <string>
#include <Windows.h>

using namespace std;

MeaningOfLife::Cpp::CLI::Logic::Logic()
    : _impl(new Cpp::Logic()) 
    // Allocate some memory for the native implementation



int MeaningOfLife::Cpp::CLI::Logic::Get()

    return _impl->Get(); // Call native Get


void MeaningOfLife::Cpp::CLI::Logic::Destroy()

    if (_impl != nullptr)
    
        delete _impl;
        _impl = nullptr;
    


MeaningOfLife::Cpp::CLI::Logic::~Logic()

    // C++ CLI compiler will automaticly make all ref classes implement IDisposable.
    // The default implementation will invoke this method + call GC.SuspendFinalize.
    Destroy(); // Clean-up any native resources 


MeaningOfLife::Cpp::CLI::Logic::!Logic()

    // This is the finalizer
    // It's essentially a fail-safe, and will get called
    // in case Logic was not used inside a using block.
    Destroy(); // Clean-up any native resources 


string ManagedStringToStdString(System::String^ str)

    cli::array<unsigned char>^ bytes = System::Text::Encoding::ASCII->GetBytes(str);
    pin_ptr<unsigned char> pinned = &bytes[0];
    std::string nativeString((char*)pinned, bytes->Length);
    return nativeString;


void MeaningOfLife::Cpp::CLI::Logic::InitializeLibrary(System::String^ path)

    string nativePath = ManagedStringToStdString(path);
    LoadLibrary(nativePath.c_str()); // Actually load the delayed library from specific location

//C#
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace MeaningOfLife.WPF

    using Cpp.CLI;
    using Microsoft.Win32;

    public partial class MainWindow : Window
    
        public MainWindow()
        
            InitializeComponent();
        

        private void Button_Click(object sender, RoutedEventArgs e)
        
            var fileOpenDialog = new OpenFileDialog
            
                CheckFileExists = true,
                Filter = "Native Library|MeaningOfLife.Cpp.dll",
                InitialDirectory = Environment.CurrentDirectory
            ;

            var result = fileOpenDialog.ShowDialog(this);
            if (result.HasValue && result.Value)
            
                Logic.InitializeLibrary(fileOpenDialog.FileName);

                using (var wrapper = new Logic())
                
                    MessageBox.Show("The answer is " + wrapper.Get());
                
            
        
    


错误发生在使用 Cpp.CLI 时,然后在调用 Logic 时发生。

作为跟进,我也尝试了这个示例,并在代码的可比较位置收到了相同的错误。

https://www.red-gate.com/simple-talk/dotnet/net-development/creating-ccli-wrapper/

【问题讨论】:

您的 CLI 包装类将输出您需要在托管 C# 类中引用的程序集。根据错误,您似乎没有引用程序集。 对,我认为我做得对。我会用一些代码更新它以节省时间。谢谢。 您缺少 using 指令或程序集引用。前者是该网页最明显的原因,MainWindow.cs 缺少让编译器识别 Logic 类所需的 using 指令。它需要using MeaningOfLife.Cpp.CLI; 才能正确编译。或者其他方式,拼写全名:var wrapper = new MeaningOfLife.Cpp.CLI.Logic() MeaningOfLife 只给了我MeaningOfLife.WPF 的选项。 MeaningOfLife.Cpp 无法识别。 C# 有 CLI 作为参考,我认为这会更正,但情况似乎并非如此。 您可能会更轻松地使用#pragma unmanaged,而不是为 cpp/cli 类使用两个单独的程序集:docs.microsoft.com/en-us/cpp/preprocessor/… 【参考方案1】:

这已通过以下方式解决: A) 删除并添加回应用程序的 WPF 部分 B) 将 CLI 引用的“复制本地”属性设置为 true。

我之所以遵循这些示例,是因为缺乏相关知识,因此想进一步了解这个过程。我不认为这是真正的答案,但它确实解决了我的直接问题。

【讨论】:

以上是关于C# 使用 CLI 包装器调用非托管 C++的主要内容,如果未能解决你的问题,请参考以下文章

从非托管 c++ 调用 C# 函数(通过托管包装器)

尝试从 C++/CLI 调用非托管 C++ 时解决错误

非托管C++通过C++/CLI包装调用C# DLL

通过 CLI 包装器在非托管 C++ 中使用 C#.NET Winform - 需要线程?

如何为非托管 c dll 创建 c++\cli 包装器

用 C++/CLI 包装非托管 C++ - 一种正确的方法