Add a managed .net dll to a cpp dll project

Pages: 12
I am trying to create a cpp dll which will utilize the functions of a managed .net dll. How do I create a reference to the .net dll in my cpp project? The .net dll was created by others. I just have its function references.
Last edited on
I think you have to use C++/CLI in order to call "managed" (.NET) code from the "C++" (unmanaged) world:
https://en.wikipedia.org/wiki/C%2B%2B/CLI

See the example here:
https://en.wikipedia.org/wiki/C%2B%2B/CLI#Interoperability

You can write a "wrapper" DLL in C++/CLI that can be called from "native" C++ program and that forwards the call to the "managed" DLL.
Last edited on
I assume this can all be done easily in VS?
Don't know how you define "easily", but I think a somewhat recent VS is required, yes.
I have taken a crack at this using VS2019 and created a C++/CLI project, the end product of which, as I understand it, is a .lib file. A programming language called Easylanguage requires a dll which exports its functions using the __stdcall calling convention. The company (NinjaTrader) whose dll this is used to provide a dll which used the calling convention that Easylanguage (Tradestation) could call directly. Now, it will be a much more complicated communication, if it's even possible. That probably doesn't make much sense, but it's about all I know at this point.
Here is full example, hope it helps:

The "managed" DLL may look something like this (just an example):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* ------------------------- */
/* --- ManagedLibrary.cs --- */
/* ------------------------- */
using System;
using System.Text;

namespace ManagedLibrary
{
	public class ManagedClass
	{
		public string SayHello()
		{
			StringBuilder sb = new StringBuilder();
			sb.AppendLine("Hello from managed library!");
			sb.AppendLine("Running on.NET Framework v" + Environment.Version);
			return sb.ToString();
		}
	}
}

The "wrapper" DLL, written in C++/CLI language:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/* -------------------------- */
/* ---- WrapperLibrary.h ---- */
/* -------------------------- */

#pragma once

#ifdef WARPPERLIBRARY_EXPORTS
#define WARPPERLIBRARY_API __declspec(dllexport)
#else
#define WARPPERLIBRARY_API __declspec(dllimport)
#endif

#ifdef __cplusplus
extern "C"
{
#endif

WARPPERLIBRARY_API wchar_t *MySayHelloWrapper(void);
WARPPERLIBRARY_API void MyFreeString(wchar_t *const ptr);

#ifdef __cplusplus
}
#endif

/* ---------------------------- */
/* ---- WrapperLibrary.cpp ---- */
/* ---------------------------- */

#include "WrapperLibrary.h"
#include <string>
#include <stdlib.h>

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

wchar_t *MySayHelloWrapper(void)
{
	ManagedLibrary::ManagedClass^ instance = gcnew ManagedLibrary::ManagedClass();
	System::String^ result = instance->SayHello();
	return (wchar_t*)(void*)Marshal::StringToHGlobalUni(result);
}

void MyFreeString(wchar_t *const ptr)
{
	Marshal::FreeHGlobal(System::IntPtr::IntPtr((void*)ptr));
}

The "native" C++ application, which needs to be linked against "wrapper" DLL:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <WrapperLibrary.h>

int main()
{
	std::wcout << L"Hello from native application!\n" << std::endl;
	
	wchar_t *const buffer = MySayHelloWrapper();
	if (buffer)
	{
		std::wcout << buffer << std::endl;
		MyFreeString(buffer);
	}
	
	getchar();
}

Alternatively, you can load the "wrapper" DLL at runtime:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>
#include <Windows.h>

typedef wchar_t* (*MySayHelloWrapperFunc)(void);
typedef void (*MyFreeStringFunc)(wchar_t *const ptr);

int main()
{
	std::wcout << L"Hello from native application!\n" << std::endl;
	
	HMODULE wrapper = LoadLibraryW(L"WrapperLibrary.dll");
	if (wrapper)
	{
		MySayHelloWrapperFunc myDllEntryPoint = (MySayHelloWrapperFunc) GetProcAddress(wrapper, "MySayHelloWrapper");
		MyFreeStringFunc myFreeString = (MyFreeStringFunc) GetProcAddress(wrapper, "MyFreeString");
		if (myDllEntryPoint && myFreeString)
		{
			wchar_t *const buffer = myDllEntryPoint();
			if (buffer)
			{
				std::wcout << buffer << std::endl;
				myFreeString(buffer);
			}
		}
		else
		{
			std::wcerr << L"The required entry point could not be found!" << std::endl;
		}
	}
	else
	{
		std::wcerr << L"Failed to load DLL!" << std::endl;
	}
		
	getchar();
}
Last edited on
Thanks for doing that. I do recognize much of what is going on, but it is, for the most part, beyond my ken. I am going to turn this project over to a competent developer.
I am trying to work my way through taking the above example and building VS 2019 project(s). The first thing I did was create a project using the ManagedLibrary.cs file. This produced a dll called ClassLibrary3.dll.

Then I started a second project to which I added the files WrapperLibrary.h (header file) and WrapperLibrary.cpp (Source File). I think I need to refer to the dll created in the first project, or should all the files go into one project? If I am to refer to the dll, where/how?
The first thing I did was create a project using the ManagedLibrary.cs file. This produced a dll called ClassLibrary3.dll.

The "managed" library was just an example. Of course, you can create your own example "managed" DLL.
...but you probably want to use your existing "NinjaTrader.Client.dll" instead 😏

I think I need to refer to the dll created in the first project, or should all the files go into one project? If I am to refer to the dll, where/how

The "wrapper" library needs to be a project of its own. It needs to be a C++/CLI project!
And yes, you have to add a reference to the "managed" DLL (e.g. "NinjaTrader.Client.dll") to your "wrapper" library project.
...otherwise your "wrapper" code wouldn't be able to use classes (or call methods) from the "managed" DLL.

https://i.imgur.com/w0DIy2J.png


To make it clear, you can use one Visual Studio solution, but you need two or three separate projects:

[1] The "managed" library (must be C#/.NET project) – not required (obviously), if you use an existing "managed" DLL
[2] The "wrapper" library (must be C++/CLI project)
[3] The "native" C++ program (must be native C++ project)
Last edited on
My "project" has 3 parts, two of which I already have:
1. NinjaTrader.Client.dll
2. The Wrapper.dll (grass roots project)
3. Tradestation Easylanguage which is capable of calling dll functions that use __stdcall calling convention.

I am very experienced with Easylanguage (by Tradestation) having used it for over 20 years. I know how to use dll functions. I have been doing this up until now because Ninjatrader provided a different dll (NtDirect.dll), which Easylanguage could communicate with. They are discontinuing that on their next platform update and my broker managed trading software that I have developed over many years will be worthless unless I can figure out how to create this wrapper dll. This all has to happen within a couple of weeks. That's my story.

I have programming experience, but I'm not conversant in C++ or C# or VS. I'm trying to teach myself. This was just FYI. I may have more questions as I experiment. I appreciate your help.
When I try to add a reference (my ClassLibrary3.dll) from the first project, I'd expect to be able to browse to its location to reference it, but when I follow the example of the posted image and also the VS instructions, no browse options comes up.
Not sure what you mean 😕

When you add a reference, VS will give you various options from the "Add Reference" window.

To add a project (from current solution), choose "Projects -> Solution" on the left side menu, then choose the project from the list.

https://i.imgur.com/8naLPMD.png

To add an "external" Assembly (DLL), choose "Browse" instead and then click the "Browse..." button at the bottom...

https://i.imgur.com/kxDQ70r.png
Last edited on
This is what I have now.
https://imgur.com/a/Tc99WDC
As said before, you have to add your "ClassLibrary3" to the references of "CPPWrapper" project, in order to be able to use any classes (or call any methods) from "ClassLibrary3" inside the "CPPWrapper" code. Also, if you still intend to use "NinjaTrader.Client.dll", then add that as a reference to your "CPPWrapper" project – and get rid of project "ClassLibrary3" altogether. Whatever you prefer... 😀

Also, please make sure that your "CPPWrapper" project was created as project type: CLR Class Library (.NET Framework) [C++].
Last edited on
I have started over on the project. I have added two references, the C# hello example dll that you provided, and the NinjaTrader.Client.dll. The attached snip shows the Cpp code that you provided, and I assume that somewhere in there I need to add a call(?) to one of those referenced dll's. What does that look like?
https://imgur.com/a/a8Elonv
Last edited on
Yeah, inside your "wrapper" library you have to call the "managed" library. The exact code, of course, totally depends on what classes/methods of the "managed" library you want to call! So, simply adjust the code inside the "MySayHelloWrapper" function as needed. And, of course, the name of that function is arbitrary; you probably want to choose a more fitting name for what you do 😏

Note: In my example, the System::String that comes from the "managed" library was converted into a "native" C string (wchar_t* pointer), via Marshal::StringToHGlobalUni(), so that we can return it to the "native" C/C++ application. But, the string buffer must be de-allocated (eventually), via Marshal::FreeHGlobal(), in order to avoid memory leak! That's why the MyFreeString helper function was added.
Last edited on
The application that I use to access dll's requires this calling convention:
DLL functions must be exported using the ìstandard Cî calling convention in order for TradeStation to locate them. Additionally, exported functions should be listed in the EXPORTS section of the DLL projectís module-definition (.DEF) file. The C++ code example below illustrates the use of __stdcall notation in a function
prototype. This is the function prototype for a DLL function called XAMPLEONCREATE, a function which receives a pointer to an IEasyLanguageObject as its sole argument, and returns an integer:
int __stdcall EXAMPLEONCREATE( IEasyLanguageObject * pELObj )

How do I incorporate this?
DLL functions must be exported using the ìstandard Cî calling convention in order for TradeStation to locate them.
This is the function prototype for a DLL function called XAMPLEONCREATE, a function which receives a pointer to an IEasyLanguageObject as its sole argument, and returns an integer:
int __stdcall EXAMPLEONCREATE(IEasyLanguageObject * pELObj)

This seems contradictory 😕

The standard "C calling convention" (__cdecl) and the "Win32 API calling convention" (__stdcall) are two different things!
https://learn.microsoft.com/en-us/cpp/cpp/cdecl?view=msvc-170
https://learn.microsoft.com/en-us/cpp/cpp/stdcall?view=msvc-170

Anyhow, if you want __stdcall, then just add the keyword to the function declaration. Otherwise, you get __cdecl by default 😏

1
2
3
4
5
/* C calling convention (standard) */
WARPPERLIBRARY_API int MyFunctionNameHere(EasyLanguageObject *pELObj);

/* __stdcall calling convention */
WARPPERLIBRARY_API int __stdcall MyFunctionNameHere(EasyLanguageObject *pELObj);



Additionally, exported functions should be listed in the EXPORTS section of the DLL

This happens "automatically", if the function was declared with the __declspec(dllexport) keyword 😏

Use Dependencies GUI to verify which functions are exported from your DLL:
https://github.com/lucasg/Dependencies

Note: We define the macro WARPPERLIBRARY_API as __declspec(dllexport) when building the DLL, and we define it as __declspec(dllimport) when using the DLL. This way, we can use the same header file (.h) for both cases!
Last edited on

Thanks. Tradestation methods in this area are out of date. Nothing I can do about it.
That fails. I feel like I am taking up too much of your time, so I'll take some time to do more studying on how to use VS. Thanks for your help.
Pages: 12