0

TL;DR

I have legacy c++ code which does stuff (sometimes returns stuff), calls other cpp code, but is not a full class/obj. This code I cannot alter. I am making fresh c# code which I am looking to call the c++ code from. I don't understand whether to create a dll that calls the original legacy c++, or create a CLR?? which also calls the legacy cpp. Below I have example code that I have implemented (with problems).

Main

I have legacy.cpp and legacy.h which I can not alter.

This is not a class/object and only has public functions, values, and #defines. legacy.cpp and .h both #include other files and cpp libraries to do its job.

I have a new project where I can add C#, C++ code

I am having trouble understanding what I need to do/research in order to call any of the functions defined in legacy.cpp (or the values/defines) from within my new C# code.

Some of what I have looked at include

CLR

I have currently tried to create a CLR (thought I feel like it is not what I need in this situation), but I had problems in the clr_wrapper.cpp, it could not find the reference to foo()

//Wrapper.cpp #include "Wrapper.h" #include "abs\path\to\legacy\code\legacy.h" #include "abs\path\to\legacy\code\legacy.cpp" int foo_Wrapper() { return foo(); //int foo() is declared in legacy.h and defined in legacy.cpp } 
#pragma once #include "abs\path\to\legacy\code\legacy.h" #include "abs\path\to\legacy\code\legacy.cpp" using namespace System; //What things use this? //Can I just prepend System:: to whatever needs it? namespace Wrapper { public ref class Wrapper { public: int foo_Wrapper(); }; } 

the foo_Wrapper() is not able to call foo().

My confusion with this method is that it looks like I would need to make the clr wrapper an object class with member functions that will be called as needed. Leading to a syntax of obj.foo(). Is this what needs to be done if I chose to do some sort of CLR wrapper?

DLL

I have also looked at making this all a dll like in (How to call C++ DLL in C#)

However I am confused on setting this up. My current idea is to have a cpp dll call the original cpp (ie create legacyDll.dll which would make calls to foo(), then my main c# would call the __declspec(dllexport) functions defined within extern "C" {}

current setup (from "How to call c dll in c sharp") dllmain.cpp

// dllmain.cpp : Defines the entry point for the DLL application. #include "pch.h" #include <iostream> #include <Windows.h> using namespace std; extern "C" { __declspec(dllexport) void bar_Dll() { cout << "calling bar() in legacy code" << endl; } __declspec(dllexport) int foo_Dll() { cout << "calling foo() in legacy code" << endl; //realistically I would have, //return foo() } } 

Class1.cs

using System; using System.Runtime.InteropServices; namespace Test_DLL_Calling { class Class1 { [DllImport("dllmain.dll", CallingConvention = CallingConvention.Cdecl)] public static extern void bar_Dll(); [DllImport("dllmain.dll", CallingConvention = CallingConvention.Cdecl)] public static extern int foo_Dll(); public static void Main(string[] arg) { bar_Dll(); //The specified module could not be found (Exception) Console.WriteLine(foo_Dll()); //Im guessing this will also ^^ } } } 

This part I don't follow. What and why are the attributes done the way they are?

3
  • 1
    "What and why are the attributes done the way they are?" That's what the documentation is for. The response to such a question should be to look up the documentation for those attributes to learn what they do or indicate. Microsoft offers a handy .NET API browser that makes it dead simple to lookup any class/type (including attribute types, of course) and its memebers you want to know about. (Alternatively, in Visual Studio, move the text caret/cursor on an attribute you want to know more about, and hit that F1 key) Commented Jul 3, 2019 at 14:11
  • A dll has multiple entry points. Each entry point is a public function in the dll. So the dll has two methods bar_Dll and foo_Dll and each returns an integer. Commented Jul 3, 2019 at 14:14
  • Make a new c++/CLI .DLL (c++/CLI dlls are a hybrid of both native and managed code) "P" that will act as a simple proxy/facade pattern around your existing c/c++ .DLL "E". Your C# code calls the types in "P" and does not see/care about "E". "P" routes calls into "E" and returns results (if any) as managed types. No need to fidlle about with tedious dllexport and/or p-invoke. By the way, depending on your needs you don't need to necessarily expose all c++ methods to .NET if you don't want to. Keep the wrapper simple Commented Jul 3, 2019 at 14:22

1 Answer 1

1

Alright, so you have a header and cpp which you need to use. In order to use it you have to have make the c++ into C code. This is pretty much what you see in the DLL example code that you showed. However, I do suggest that you remove the includes from the header as I'm unsure how that would translate to the C code. Put the includes in the cpp files instead.

I find it rather difficult to answer this questions other than just showing a whole bunch of example code. Full code in: https://github.com/ze413X/Cpp-Code-Gems/ Where "CSharpExampleUsingCpp" calls from MainWindow.xaml.cs the "DLLExample" which uses the file in the directory includes and source.

DLL:

The header which is exposing functions to be used:

#pragma once #define DLLExport _declspec(dllexport) extern "C" DLLExport void __cdecl GetCppText(char* str, int* strLength); extern "C" DLLExport void __cdecl DLLPrint(); 

.cpp

#include "../includes/DLLExampleCode.h" #include <string> #include <iostream> void __cdecl GetCppText(char* str, int* strLength) { std::string text = "This is called from within the DLL.\0"; if (*strLength < text.length() || str == nullptr) { return; } memset((void*)str, 0, (*strLength) * sizeof(char)); strcpy_s(str, *strLength,text.c_str()); *strLength = text.length(); } void __cdecl DLLPrint() { std::cout << "This is printed from inside DLLExample.dll.\n"; } 

C#:

using System.Runtime.InteropServices; namespace CSharpExampleUsingCpp { public partial class MainWindow : Window { const string PATH = "DLLExample.dll"; [DllImport(PATH, CallingConvention = CallingConvention.Cdecl)] public static unsafe extern void GetCppText(byte[] str, out System.Int32 strLength); .... private void CppInteropButton_Click(object sender, RoutedEventArgs e) { System.Int32 size = 256; System.Byte[] str = new byte[size]; for (int i = 0; i < size; i++) { str[i] = (byte)'1'; } GetCppText(str, out size); string result = System.Text.Encoding.UTF8.GetString(str, 0, size); CppInteropButtonTextBox.Text = result; } 

Although, rereading my solution of obtaining a string might not be the best way of doing it. You could probably marshal that thing to avoid all this stupid char* conversions. I probably had some good reason at that point in time when I wrote it. That should be much easier to google though.

Sign up to request clarification or add additional context in comments.

2 Comments

So im somewhat following this, where do I fine/create the <name>.dll which in your example is DLLExample.dll? Do I create it?
The project is a Visual studio 2017 project for Windows. However, in case you do not use that, you can just browse the files either way. Stuff.sln contains 3 projects. Stuff, DLLExample and CSharpExampleUsingCpp. So setting startup project to CSharpExampleUsingCpp, you can easily follow the code along with a debugger. But in case you can't follow that along, you find all the necessary code for "DLLExample" in the Stuff/DLLExample directory. There is even an instructions.txt how the .dll is set up in VS2017. Note; the project is not uploaded built. It is always assumed you do it yourself.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.