我已经在这个话题上挣扎了一周,但我try 的任何东西似乎都没有奏效. 我创建了一个非常简单的C#类:

namespace SimpleMathLib
{
    public class SimpleMath
    {
        public float SumFloat(float a, float b)
        {
            return a + b;
        }

        public int SumInt(int a, int b)
        {
            return a + b;
        }
    }
}

in a Visual Studio 2022 solution configured to build a DLL: VS C# project settings

我确实阅读了一些在线教程,但给我带来的问题较少的是关于创建使用CLR的C++包装器并将其编译为静态库(.lib)的教程. 下面是我在这个包装器中添加的脚本:

// SimpleMathLibWrapper.h
#pragma once

class SimpleMathLibWrapperPrivate;

class __declspec(dllexport) SimpleMathLibWrapper
{
    private:
        SimpleMathLibWrapperPrivate* _private;

    public:
        SimpleMathLibWrapper();
        ~SimpleMathLibWrapper();

        float SumFloat(const float a, const float b);
        int SumInt(const int a, const int b);
};

// SimpleMathLibWrapper.cpp
#include "..\public\SimpleMathLibWrapper.h"

#include <msclr\auto_gcroot.h>
#include <msclr\marshal_cppstd.h>

using namespace System::Runtime::InteropServices;
using namespace SimpleMathLib;

class SimpleMathLibWrapperPrivate
{
    public:
        msclr::auto_gcroot<SimpleMath^> simpleMathCSharp;
};

SimpleMathLibWrapper::SimpleMathLibWrapper()
{
    _private = new SimpleMathLibWrapperPrivate();
    _private->simpleMathCSharp = gcnew SimpleMath();
}

SimpleMathLibWrapper::~SimpleMathLibWrapper()
{
    delete _private;
}

float SimpleMathLibWrapper::SumFloat(const float a, const float b)
{
    return _private->simpleMathCSharp->SumFloat(a, b);
}

int SimpleMathLibWrapper::SumInt(const int a, const int b)
{
    return _private->simpleMathCSharp->SumInt(a, b);
}

and these are the settings of the Visual Studio CLR library project I had to change: C++ CLR Wrapper tab1 C++ CLR Wrapper tab2 moreover, I added the compiled C# DLL reference to the project C++ CLR Wrapper references

现在,对于C++可执行项目,它是一个带有以下脚本的Visual Studio C++控制台项目:

// SimpleMathLibUser.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

//#define LOAD_DLL_MANUALLY

#include <iostream>
#include <Windows.h>

#ifndef LOAD_DLL_MANUALLY
    #include "SimpleMathLibWrapper.h"
#endif // !LOAD_DLL_MANUALLY


#ifdef LOAD_DLL_MANUALLY
    void PrintExecutablePath()
    {
        TCHAR exePath[MAX_PATH];
        GetModuleFileName(NULL, exePath, MAX_PATH);
        char narrowExePath[MAX_PATH];

        // convert the string to a narrow character string
        if (WideCharToMultiByte(CP_ACP, 0, exePath, -1, narrowExePath, MAX_PATH, 0, 0) == 0)
        {
            std::cerr << "Failed to convert the path to a narrow character string." << std::endl;
            return;
        }

        char* lastSlash = strrchr(narrowExePath, '\\');
        if (lastSlash != NULL)
        {
            *lastSlash = '\0';
        }

        std::cout << "Current directory: " << narrowExePath << std::endl << std::endl;
    }
#endif // LOAD_DLL_MANUALLY

int main()
{
#ifdef LOAD_DLL_MANUALLY
    PrintExecutablePath();

    // load the DLL.
    HMODULE mathLib = LoadLibrary(TEXT("..\\Plugins\\SimpleMathLibWrapper.dll"));

    if (mathLib == NULL)
    {
        std::cerr << "Failed to load the DLL." << std::endl;
        return 1;
    }

    // get a pointer to functions from the DLL.
    float (*SumFloat)(float, float) = (float (*)(const float, const float))GetProcAddress(mathLib, "SumFloat");
    int (*SumInt)(int, int) = (int (*)(const int, const int))GetProcAddress(mathLib, "SumInt");

    if (SumFloat == NULL)
    {
        std::cerr << "Failed to find the 'SumFloat' function in the DLL." << std::endl;
        return 1;
    }

    if (SumInt == NULL)
    {
        std::cerr << "Failed to find the 'SumInt' function in the DLL." << std::endl;
        return 1;
    }

    // call the functions.
    float resultFloat = SumFloat(10.f, 5.f);
    std::cout << "Float Sum: " << resultFloat << std::endl;

    int resultInt = SumInt(2, 3);
    std::cout << "Int Sum: " << resultInt << std::endl;

    // unload the DLL.
    FreeLibrary(mathLib);
#else
    SimpleMathLibWrapper wrapper;

    std::cout << "Float Sum: " << wrapper.SumFloat(10.f, 5.f) << std::endl;
    std::cout << "Int Sum: " << wrapper.SumInt(2, 3) << std::endl;
#endif // LOAD_DLL_MANUALLY

    return 0;
}

如你所见,为此,我try 了两种方法:

  1. 手动加载包装器
  2. 使用链接器

These are the settings I added for the linker solution (which is currently the one that at least compile and seems to work until a certain point): C++ executable project tab1 C++ executable project tab2 C++ executable project tab3 C++ executable project tab4

C#DLL是用哪个.NET版本编译的并不重要,当我运行可执行文件时,我总是得到以下错误:

Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly 'netstandard, Version=2.1.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' or one of its dependencies. Impossible to find the scpecified file.
   in SimpleMathLibWrapper.{ctor}(SimpleMathLibWrapper* )
   in mainCRTStartup()

我所知道的是.NET版本的Runtime和SDK都已安装并在系统中正确引用. 无论我在C#VS项目中 Select 目标框架的哪个版本,结果都是相同的;只是程序集名称和版本发生了变化.

我也在互联网上寻找这个特定的问题,但大多数解决方案都是try 直接从.sln文件而不是从VS打开项目,这对我不起作用.

手动加载DLL会导致一系列我无法解决的不同问题,因此,我保留了代码以供参考,但放弃了try 修复它.

我知道这是一篇很长的帖子,我鼓励你问我更多的细节,以防我遗漏了什么. 希望在这里找到解决方案将能够在future 帮助更多的人.

谢谢!!

推荐答案

在看了我收到的一些 comments 后,我终于能够让一切都正常了!

首先,由于CLR的原因,C#DLL需要使用低于或等于4.7.2的.NET框架版本进行编译.

你应该怎么做才能做到这一点? 编辑.csproj文件,如下所示:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net472</TargetFramework>
    <ImplicitUsings>disable</ImplicitUsings>
    <Nullable>disable</Nullable>
    <SignAssembly>False</SignAssembly>
    <AssemblyOriginatorKeyFile>SimpleMathManaged</AssemblyOriginatorKeyFile>
  </PropertyGroup>

</Project>

确保<TargetFramework>指向正确的版本,<ImplicitUsing>被禁用,这样项目就不会使用GlobalUsing,这对于该框架是不可用的,您还需要禁用<Nullable>,也不支持.

After that, recompile the DLL, the wrapper (I didn't change anything there) and also the C++ executable to update to the latest wrapper LIB. Make sure the C# DLL is copied where the .exe file is (I forgot to mention this on my original post), run the project and everything should properly work !!! :) enter image description here Finally...

基本上,我最初的错误是使用.NET6.0(使用Visual Studio创建新的C#项目时的默认版本)编译DLL,该版本高于4.7.2并且不受支持. 包装器编译过程没有启动任何警告或错误,但当从可执行文件中使用时,它不起作用.

第二个错误是try 使用.Net Standard 2.1,同样不受支持,也没有抛出警告.

当我按照JonasH和Hans Passant在 comments 中的建议try .NET 4.8时,我终于收到了包装器的警告,告诉我目标.NET框架不受支持,因为版本高于4.7.2,然后,最后目标是正确的版本,一切都正常.

我真的希望这个解决方案能帮助每个有同样问题的人,因为找到解决方案并不有趣.

UPDATE !!!

For the sake of completeness, I would like to add another thing. I compiled the C++ wrapper as a static library (.lib) because when I tried to link the DLL in the C++ executable Visual Studio project: enter image description here I was getting this error:

Error LNK1302 only support linking safe .netmodules; unable to link ijw/native .netmodule
    SimpleMathLibUser D:\dev\C++\SimpleMathLibWrapper\x64\Debug\SimpleMathLibWrapper.dll

If you want to have your C++ wrapper as a dynamic library (.dll), the linker of the C++ executable Visual Studio project should link the .obj file instead of directly specifying the .dll one: enter image description here

Csharp相关问答推荐

等待限制选项似乎不适用于频道

O(N)测试失败

需要深入了解NpgSQL DateTimeOffset处理

使用yaml将Azure函数代码部署到FunctionApp插槽时出现问题(zip未找到)

如何使用ConcurentDictionary属性上的属性将自定义System.Text.Json JsonConverter应用于该属性的值?

在C#WinUI中,一个关于System的崩溃."由于未知原因导致执行不例外"

C#方法从AJAX调用接收NULL

在调整大小的控件上绘制

如何在没有前缀和可选后缀的情况下获取Razor Page Handler方法名称?

在静态模式下实例化配置

Cosmos SDK和Newtonsoft对静态只读记录的可能Mutations

C#使用TextFieldParser读取.csv,但无法使用";0";替换创建的列表空条目

.NET并发词典交换值

正在寻找新的.NET8 Blazor Web应用程序.如何将.js添加到.razor页面?

CRL已过期,但ChainStatus告诉我RevocationStatus未知

在等待OnGetAsync时打开Razor Page显示微调器

使用未赋值的、传递的局部变量

使用C#12中的主构造函数进行空判断

有没有更好的方法来使用LINQ获取整行的计算组

C#:我需要根据换行符拆分字符串,而不是在字符串中用双引号分隔换行符