How to Call C# Code from C++

Return Basic Data Types

1. First, create a C# class library project named CSharpLib.

How to Call C# Code from C++

Create a class named ExportClass and add a GetID function as follows:

public class ExplortClass    {        public int GetID()        {            return 1024;        }    }

2. Create a CLR Empty Project named CSBridge, which will serve as the intermediate bridging library.Change the output path of the CSBridge project to match that of the CSharpLib project.

How to Call C# Code from C++

Note: If you do not see CLR Empty, you can check and install it in the Visual Studio installer (search for cli directly).

How to Call C# Code from C++

Create a bridge.cpp file. Enter the following code:

#include <Windows.h>#include<msclr/marshal_cppstd.h>// Reference C# dll#using "./CSharpLib.dll"// Reference namespaceusing namespace msclr::interop;using namespace System;using namespace System::Runtime::InteropServices;using namespace CSharpLib;#define lib_export#ifdef lib_export#define cs_lib_api extern "C" __declspec(dllexport)#else#define cs_lib_api __declspec(dllimport)#endiftypedef int(__stdcall* funGetId)();  // Define function pointer// Export function for C++ to call// In this function, call the C# function as an intermediarycs_lib_api int GetID(){    CSharpLib::ExplortClass^ c = gcnew CSharpLib::ExplortClass();    auto id = c->GetID();    return id;}

Now you have a bridging project.

3. Create a C++ console application and enter the following code to test.

// CppInvoke.cpp : This file contains the 'main' function. Program execution begins and ends there.//#include <iostream>#include<Windows.h>typedef int(__stdcall* funGetId)();int main(){    HMODULE hInstance = LoadLibrary(L"CSBridge.dll");    if (hInstance)    {        funGetId getId = (funGetId)GetProcAddress(hInstance, "GetID");        if (getId)        {            auto result = getId();            std::cout << result << std::endl;        }    }}

The output result is: 1024

A more complex case, returning a structure:

Add a structure named Computer in CSharpLib:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]    public struct Computer    {        public int cpuId;        public string cpuName;        public int osVersion;    }

Add a function to get the Computer:

public Computer GetComputer(){     Computer computer = new Computer();     computer.cpuId = 100000000;     computer.cpuName = "Intel";     computer.osVersion = 11;     return computer;}

Then, in CSBridge, add a type for interacting with Computer named interop_Computer, which is used when called from C++. The C# Computer type can be converted to interop_Computer.

struct interop_Computer{    int cpuId;    wchar_t* cpuName;    int osVersion;};

Define a function pointer and add an intermediary function:

typedef interop_Computer(__stdcall* funGetComputer)();cs_lib_api interop_Computer GetComputer(){    CSharpLib::ExplortClass^ c = gcnew CSharpLib::ExplortClass();    auto computer = c->GetComputer(); // Call the C# function    System::IntPtr ptr = Marshal::AllocHGlobal(sizeof(interop_Computer));// Pre-allocate space    System::Runtime::InteropServices::Marshal::StructureToPtr(computer, ptr, false);// Copy the C# structure to IntPtr    interop_Computer* rt = (interop_Computer*)(void*)(ptr.ToPointer());// Cast IntPtr to interop_Computer    return *rt;}

Then add test code in CppInvoke:

HMODULE hInstance = LoadLibrary(L"CSBridge.dll");    if (hInstance)    {        funGetComputer getComputer = (funGetComputer)GetProcAddress(hInstance, "GetComputer");        if (getComputer)        {            auto computer = getComputer();            std::wcout << computer.cpuId << "\t" << computer.cpuName << "\t" << computer.osVersion << std::endl;        }        FreeLibrary(hInstance);    }

The output result is:

How to Call C# Code from C++

Another case is when you need to pass parameters from C++ to C#.

There are two methods to achieve this:

1. Marshal parameters from C++ to C#, similar to the method used to return structures above. The general idea is to convert the C++ structure to IntPtr, and then convert from IntPtr to the C# structure.

2. Convert the C# function to a C++ function and then call it. This way, you can directly use the C++ structure.

Implementation methods are as follows:

Add a function PrintComputer in C#, which takes a Computer structure as a parameter. Then add the corresponding delegate and a function to get the delegate.

public void PrintComputer(Computer computer)        {            Console.WriteLine(computer.cpuId);            Console.WriteLine(computer.cpuName);            Console.WriteLine(computer.osVersion);        }
public delegate void PrintComputerDelegate(Computer computer); // Declare delegatepublic PrintComputerDelegate GetComputerDelegate() => PrintComputer;  // Define a function to return the delegate

In CSBridge, define a function pointer and add an export function:

typedef void(__stdcall* funPrintComputer)(interop_Computer computer);cs_lib_api void PrintComputer(interop_Computer computer){    CSharpLib::ExplortClass^ c = gcnew CSharpLib::ExplortClass();    auto printDelegate = c->GetComputerDelegate();// Get delegate    IntPtr ptr = Marshal::GetFunctionPointerForDelegate(printDelegate);// Convert delegate to IntPtr    funPrintComputer funcPrint = (funPrintComputer)ptr.ToPointer();// Convert IntPtr to pointer, then to funPrintComputer    if (funcPrint)    {        funcPrint(computer);    }}

This allows parameters from C++ to be passed to C#.

The calling code in CppInvoke is as follows:

funPrintComputer printComputer = (funPrintComputer)GetProcAddress(hInstance, "PrintComputer");interop_Computer testComputer;testComputer.cpuId = 18;testComputer.cpuName = _tcsdup(L"AMD");testComputer.osVersion = 7;if (printComputer){     printComputer(testComputer);}

The output result is:

How to Call C# Code from C++

Example code (requires Visual Studio 2022)

https://github.com/zhaotianff/cnblog-demo-code/tree/main/CppInvokeCSDemo

Leave a Comment