I have been struggling with this topic for 1 week now and nothing I tried seems to work. I made a very simple C# class:
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:

I did follow a few online tutorial but the one that gave me less problems was the one about creating a C++ wrapper that uses CLR and compile it as a static library (.lib). Here are the scripts I added in this wrapper:
// 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:
moreover, I added the compiled C# DLL reference to the project

Now, for the C++ executable project, it is a Visual Studio C++ console project with the following script:
// 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;
}
As you can see, for this I tried 2 approaches:
- loading the wrapper manually
- using the linker
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):

It doesn't matter what .NET version the C# dll is compiled with, when I run the executable, I always get the following error:
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()
What I know is that both Runtime and SDK for the .NET version are installed and properly referenced in the system. Whatever version of the target framework I select in the C# VS project, the result is the same; only the assembly name and version changes.
I also looked for this specific issue on the internet but most of the solutions where to try to open the project directly from the .sln file and not from VS which didn't work for me.
Loading the DLL manually causes a set of different issues that I wasn't able to figure out, so, I kept the code for reference but bailed on trying to fix it.
I know this is a very long post and I encourage you to ask me for more details in case I missed something. Hopefully, finding a solution here will be able to help a lot more people in the future.
Thanks !!



LoadLibraryandGetProcAddressfailed.GetProcAddresscan only find functions which are listed in the export table of the DLL. The export table is a very different beast from the .NET metadata that enables other .NET projects to consume the DLL (and also runtime reflection). .NET DLLs can have exports, but you need to use MSIL to create them. There are some post-build tools that can be used with a DLL produced by the C# compiler, but C# alone cannot create exports.SumFloatandSumInt, it has non-static member functions which don't match in name, don't match in signature, and don't match in calling convention.