I am working in C++ and creating library which analyses the data. I have created few classes which have functions taking C++ vector. Now I want to create UI in C# and call these classes. I am thinking to create API to call from C#.
Since data are arrays/vector then how can I call it from C#?
I would have just made this a comment, but my rep isn't high enough. There are some complications when using STL classes (such as vector or string) in a class library with C++. You can check here for some more info and possible solutions: I can pass std::string for a Dll and what i can do with DLL´s?
You need to create your own C++/CLI interop to achieve this.
Strongly recommend a nice book, "Expert C++/CLI" by Marcus Heege, quite a good read.
Here's my brief example:
// Program.cs
static void Main(string[] args)
{
List<string> someStringList = new List<string>();
someStringList.Add("Betty");
someStringList.Add("Davis");
someStringList.Add("Eyes");
NativeClassInterop nativeClass = new NativeClassInterop();
string testString = nativeClass.StringCat(someStringList);
}
// NativeClass.h, skipping this, it's obvious anyways
// NativeClass.cpp, normal C++ class, this was in some DLL project, don't need exports
#include "stdafx.h"
#include "NativeClass.h"
std::string NativeClass::StringCat(std::vector<std::string> stringList)
{
std::string result = "";
for(unsigned int i = 0; i < stringList.size(); i++)
{
if(i != 0)
{
result += " ";
}
result += stringList[i];
}
return result;
}
// NativeClassInterop.cpp, in same DLL project, but compile this file with /clr switch
#include <gcroot.h>
#using <System.dll>
#include <vector>
#include <string>
#include "NativeClass.h"
// Helper method
static std::string nativeStringFromManaged(System::String^ str)
{
System::IntPtr hGlobal =
System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(str);
std::string nativeString((hGlobal.ToPointer() == 0)
? "" : (char*)hGlobal.ToPointer());
System::Runtime::InteropServices::Marshal::FreeHGlobal(hGlobal);
return nativeString;
}
// C++/CLI wrapper class
public ref class NativeClassInterop
{
public:
System::String^ StringCat(System::Collections::Generic::List<System::String^>^ someStringList)
{
// You get to do the marshalling for the inputs
std::vector<std::string> stringList;
for(int i = 0; i < someStringList->Count; i++)
{
stringList.push_back(nativeStringFromManaged(someStringList[i]));
}
NativeClass nativeClass;
std::string nativeString = nativeClass.StringCat(stringList);
// And for the outputs ;-)
System::String^ managedString = gcnew System::String(nativeString.c_str());
return managedString;
}
};
Related
I am trying to handle a C++ DLL which in turn connects to a C# DLL.
My problem is when I want to send or receive arrays between C++ and C# DLLs.
I have created the C# DLL and C++ DLL, so I can modified all the files. Within the C# DLL, I must necessarily have an array of string an another of double because that is where I make use of other functions unrelated to this question. (The declaration of the function that I have to use in C# DLL is Error SetMultipleChannelValues(string[] names,double[] values).
I am using CLI in Visual Studio 2015 to compile and generate all DLLs and projects.
Here it is the C# code that I have:
public static Error SetMultipleSignals(string[] signals, double[] values)
{
Error error = null;
error = workspace.SetMultipleChannelValues(signals, values);
if (error.IsError)
Console.WriteLine("[DLL] Error in SetMultipleChannelValues(). Code: " + error.Code + ". Message: " + error.Message);
return error;
}
Here it is the C++ code that I have:
bool setMultipleSignals(double* setSignal_values)
{
array<double, DEFAULT_SETSIGNAL_SIZE> values;
for (int index = 0 ; index < DEFAULT_SETSIGNAL_SIZE ;index++)
{
values[index] = *(setSignal_values + index);
}
if (!veristand_wrapper_cs::VeriStand_dll::SetMultipleSignals(setSignals_path, values)) // This is the call to C# function
return true;
else
return false;
}
Here it is the C++ header that I have:
#pragma once
#define VERISTAND_WRAPPER_CPP __declspec(dllexport)
#include <string>
#include <iostream>
#include <windows.h>
#include <array>
using namespace std;
using namespace System;
using System::Runtime::InteropServices::Marshal;
#using "veristand_wrapper_cs.dll" // This is the C# DLL
#define DEFAULT_SETSIGNAL_SIZE 100
extern "C" VERISTAND_WRAPPER_CPP bool setMultipleSignals(double* setSignal_values); // This function is called from API C++
I pass the double array with a pointer to double as a parameter from my C++ application, but I have to pass to C# DLL an array, so I have tried to build a whole array of double before pass it.
The array setSignals_path is created as a global array in this C++ DLL as array<String^, DEFAULT_SETSIGNAL_SIZE> setSignals_path;.
The problem is that the call of C# function provides me an error that says that I can not call the function with these arguments. It expects array<System::String^> ^signals, array<double> ^values and I am passing std::array<System::String^, 100Ui64>, std::array<double, 100Ui64>
The concept of my idea is simple. From the C++ API, pass a pointer to the array of doubles, so that the function of my DLL in C++ passes the array of doubles and strings to the DLL of C#, and it returns the array of modified doubles to be able to indicate to the API from C++ that the values have been modified from that memory address.
Would anyone know how to approach this problem or how to do it in some other way to make it work?
It looks like you are using c++/cli. So you can actually use .Net types in native code.
In C++/cli you need to explicitly declare if a variable is a reference, that is what the ^ is for. Your arrays is declared without this, but your method needs a reference. You will also need to allocate your array on the managed heap. So your array should probably be declared as
array<double>^ values = gcnew array< double>(DEFAULT_SETSIGNAL_SIZE);
But it was a while ago I coded c++/cli, so I may have missed something. See How to use arrays in c++/cli. It also looks like you may be able to use ref new instead of gcnew
Use following. The size of array must be 100, or add another parameter before the string array indicating length. The string in code is a byte array terminating with '\0'. c++ must use a method that will read the null terminated string. A double is 8 bytes so the 100 double can be consecutive memory locations (800 bytes).
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct StringArray
{
public IntPtr[] unmanagedArray;
}
public struct DoubleArray
{
public double[] values;
}
public static Error SetMultipleSignals(IntPtr signals, IntPtr values)
{
const int DEFAULT_SETSIGNAL_SIZE = 100;
StringArray stringStruct = new StringArray();
stringStruct.unmanagedArray = new IntPtr[DEFAULT_SETSIGNAL_SIZE];
Marshal.PtrToStructure(signals, stringStruct);
string[] strArray = new string[DEFAULT_SETSIGNAL_SIZE];
for(int i = 0; i < DEFAULT_SETSIGNAL_SIZE; i++)
{
strArray[i] = Marshal.PtrToStringAnsi(stringStruct.unmanagedArray[i]);
}
DoubleArray dArray = new DoubleArray();
dArray.values = new double[DEFAULT_SETSIGNAL_SIZE];
dArray = Marshal.PtrToStructure<DoubleArray>(values);
return null;
Here is going the other way.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct StringArray
{
public IntPtr[] unmanagedArray;
}
static void Main(string[] args)
{
string[] strArray = { "Message 1", "Message 2", "Message 3"};
StringArray stringStruct = new StringArray();
stringStruct.unmanagedArray = new IntPtr[strArray.Length];
for (int i = 0; i < strArray.Length; i++)
{
stringStruct.unmanagedArray[i] = Marshal.StringToBSTR(strArray[i]);
}
IntPtr unmanagedArray = Marshal.AllocHGlobal(Marshal.SizeOf(stringStruct));
Marshal.StructureToPtr(stringStruct, unmanagedArray, false);
}
I want to use functions from DLL in C++ with C#.
I store string data in a vector.
My C++ file contains:
#include "stdafx.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
extern "C" __declspec(dllexport) std::vector<std::string> GetProduct();
std::vector<std::string> GetProduct()
{
std::vector<std::string> vectProduct;
vectProduct.push_back("Citroen");
vectProduct.push_back("C5");
vectProduct.push_back("MOP-C5");
return vectProduct;
}
In C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Runtime.InteropServices;
namespace ConsoleApplication
{
class Program
{
[DllImport("ProductLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern StringBuilder GetProduct();
static void Main(string[] args)
{
StringBuilder vectProduct_impl = GetProduct();
}
}
}
I don't know how to continue to browse the array in c#.
I don't know if the use of vector is optimal. if you have other solution I'm ready.
Please help.
My favourite way for passing an array of strings C++-->C# is by using a delegate.
C#:
// If possible use UnmanagedType.LPUTF8Str
// or under Windows rewrite everything to use
// wchar_t, std::wstring and UnmanagedType.LPWStr
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void AddAnsi([MarshalAs(UnmanagedType.LPStr)] string str);
[DllImport("CPlusPlusSide.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void TestReturnArrayStrings(AddAnsi add);
and then
var lst = new List<string>();
TestReturnArrayStrings(lst.Add);
foreach (string str in lst)
{
Console.WriteLine(str);
}
And C++:
#include <string>
#include <vector>
extern "C"
{
__declspec(dllexport) void TestReturnArrayStrings(void (add)(const char* pstr))
{
std::string str1 = "Hello";
std::string str2 = "World";
add(str1.data());
add(str2.data());
// Example with std::vector
add("--separator--"); // You can even use C strings
std::vector<std::string> v = { "Foo", "Bar" };
// for (std::vector<std::string>::iterator it = v.begin(); it != v.end(); ++it)
for (std::vector<std::string>::const_iterator it = v.begin(); it != v.end(); ++it)
{
add(it->data());
}
add("--separator--"); // You can even use C strings
// With C++ 11
// for (auto& it: v)
for (const auto& it: v)
{
add(it.data());
}
}
}
Here the "trick" is that C# passes to C++ a delegate to the List<string>.Add() method, and C++ "fills" directly the C# List<>. The memory managed by C++ remains in the C++ side, the memory managed by the C# remains in the C# side. No problems of cross-memory ownership. As you can imagine, it is quite easy to expand the "trick" to any other .Add() method, like HashSet<string>, or Dictionary<string, string>.
As a sidenote, I've created a github with many examples about marshaling between C/C++ and C# (both .NET Framework and .NET Core/5.0).
One way to do it is to use COM's SAFEARRAY structure as it's supported by .NET (the .NET Allocator used by P/Invoke is the COM allocator), including most of associated sub types, like BSTR.
So, in C/C++, you can define this:
extern "C" __declspec(dllexport) LPSAFEARRAY GetProduct();
LPSAFEARRAY GetProduct()
{
LPSAFEARRAY psa = SafeArrayCreateVector(VT_BSTR, 0, 3);
LONG index = 0;
// _bstr_t is a smart class that frees allocated memory automatically
// it needs #include <comdef.h>
// but you can also use raw methods like SysAllocString / SysFreeString
_bstr_t s0(L"Citroen"); // could use "Citroen" if you really want ANSI strings
// note SafeArrayPutElement does a copy internally
SafeArrayPutElement(psa, &index, s0.GetBSTR());
index++;
_bstr_t s1(L"C5");
SafeArrayPutElement(psa, &index, s1.GetBSTR());
index++;
_bstr_t s2(L"MOP - C5");
SafeArrayPutElement(psa, &index, s2.GetBSTR());
index++;
return psa;
}
And in C#, you can define this:
[DllImport("ProductLibrary.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.SafeArray)]
public static extern string[] GetProduct();
I am in the need of using some functions in a C made program. To test I defined the following :
This is my .h file :
namespace amt
{
class AMT_EXPORT FaceRecognition
{
public:
std::string amt_test_string(std::string in);
};
};
This is my .cpp file :
#include <memory.h>
#include <string>
#include <iostream>
#include <fstream>
#include "api_shared.h"
#include <sys/stat.h>
using namespace std;
std::string amt::FaceRecognition::amt_test_string (std::string in)
{
std::string s="in: "+in;
std::cout<<s<<std::endl;
return s;
}
I am trying to invoke the method like this :
const string str = "C:\\minimal.dll";
[DllImport(str)]
public static extern string amt_test_string(string input);
static void Main(string[] args)
{
string myinput = "12";
string myoutput = "";
myoutput = amt_test_string(myinput);
Console.WriteLine(myoutput);
Console.Read();
}
But im getting an error saying that it cannot find the entry point named amt_test_string..why so? I am a newbie in C btw
That's not a C DLL, that's a C++ DLL. C and C++ are not the same language. In particular, C++ has name mangling, so the function name which gets exported to the DLL is decorated.
I'd strongly recommend that you avoid having C++ exports in your DLL for that reason. If you use only C exports, the symbol name will be predictable (i.e. will not depend on the specific details of how your C++ compiler decorates names), and you won't have to worry about runtime differences, like how your C++ standard library implements std::string.
I'd recommend your DLL export look like this:
extern "C" // This says that any functions within the block have C linkage
{
// Input is 'in', output gets stored in the 'out' buffer, which must be 'outSize'
// bytes long
void DLLEXPORT amt_FaceRecogniztion_amt_test_string(const char *in, char *out, size_t outSize)
{
...
}
}
This interface does not rely on any particular library's std::string implementation, and C# knows how to martial char* parameters as C strings. However, memory management is more complicated, as you need to figure out an upper bound on how big the output is going to be and pass in an appropriately sized bufer.
I am currently trying to call a function from a C# DLL from an unmanaged C++ app.
After searching for hours on the web and SO, I found I have a few options.
I can use COM, DllExport, or use reverse PInvoke with delegates. The last sounded most appealing to me, so after searching SO I ended up here.
It states that the article shows how to use reverse PInvoke, but it looks like the C# code has to first import the C++ Dll, before it can be used.
I need to be able to use C++ to call my C# Dll functions, without running a C# app first.
Maybe reverse PInvoke isn't the way to do it, but I am quite inexperienced when it comes to low level stuff, so any pointers or tips on how to do this would be great.
The code in the link is
C#
using System.Runtime.InteropServices;
public class foo
{
public delegate void callback(string str);
public static void callee(string str)
{
System.Console.WriteLine("Managed: " +str);
}
public static int Main()
{
caller("Hello World!", 10, new callback(foo.callee));
return 0;
}
[DllImport("nat.dll",CallingConvention=CallingConvention.StdCall)]
public static extern void caller(string str, int count, callback call);
}
C++
#include <stdio.h>
#include <string.h>
typedef void (__stdcall *callback)(wchar_t * str);
extern "C" __declspec(dllexport) void __stdcall caller(wchar_t * input, int count, callback call)
{
for(int i = 0; i < count; i++)
{
call(input);
}
}
Meh, just spin up your own CLR host and run what you need to:
#include <mscoree.h>
#include <stdio.h>
#pragma comment(lib, "mscoree.lib")
void Bootstrap()
{
ICLRRuntimeHost *pHost = NULL;
HRESULT hr = CorBindToRuntimeEx(L"v4.0.30319", L"wks", 0, CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (PVOID*)&pHost);
pHost->Start();
printf("HRESULT:%x\n", hr);
// target method MUST be static int method(string arg)
DWORD dwRet = 0;
hr = pHost->ExecuteInDefaultAppDomain(L"c:\\temp\\test.dll", L"Test.Hello", L"SayHello", L"Person!", &dwRet);
printf("HRESULT:%x\n", hr);
hr = pHost->Stop();
printf("HRESULT:%x\n", hr);
pHost->Release();
}
int main()
{
Bootstrap();
}
I am writing a C# dll wrapper to wrap a third party C# dll.
I also need to expose this as Java methods for which I am using an intermediate C++ layer which wraps my C# dll and provides a JNI mechanism to expose the same using java.
However, I am having an issue in passing string as argument to the callback function when its invoked in C++. Here is the code.
#include "stdafx.h"
#include "JavaInclude.h"
#include <iostream>
#using "Class1.netmodule"
#using <mscorlib.dll>
using namespace std;
using namespace System;
int CSomeClass::MemberFunction(void* someParam)
{
cout<<"Yaay! Callback"<<endl;
return 0;
}
static int __clrcall SomeFunction(void* someObject, void* someParam, String^ strArg)
{
CSomeClass* o = (CSomeClass*)someObject;
Console::WriteLine(strArg);
return o->MemberFunction(someParam);
}
JNIEXPORT void JNICALL Java_de_tum_kinect_KinectSpeechInit_initConfig
(JNIEnv *env, jobject obj)
{
array<String^>^ strarray = gcnew array<String^>(5);
for(int i=0; i<5; i++)
strarray[i] = String::Concat("Number ",i.ToString());
CSomeClass o;
void* p = 0;
CSharp::Function(System::IntPtr(SomeFunction), System::IntPtr(&o), System::IntPtr(p), strarray);
}
Here is my C# class
using System;
using System.Runtime.InteropServices;
public class CSharp
{
delegate int CFuncDelegate(IntPtr Obj, IntPtr Arg, string strArg);
public static void Function(IntPtr CFunc, IntPtr Obj, IntPtr Arg, String[] pUnmanagedStringArray)
{
CFuncDelegate func = (CFuncDelegate)Marshal.GetDelegateForFunctionPointer(CFunc, typeof(CFuncDelegate));
for (int i = 0; i < pUnmanagedStringArray.Length; i++)
{
Console.WriteLine(pUnmanagedStringArray[i]);
}
string strArg = "Test String";
int rc = func(Obj, Arg, strArg);
}
}
When I did Console::WriteLine(strArg); in my C++ dll, it just prints a blank string!
Would really appreciate if anyone can help me as I am pretty new to all this.
Thanks,
Deepak
The most likely issue is that C++ expects ANSI strings where as C# creates Unicodes ones.
So if you replace with this
delegate int CFuncDelegate(IntPtr Obj, IntPtr Arg, [MarshalAs (UnmanagedType.LPSTR)] string strArg);
You can check out more information here: http://msdn.microsoft.com/en-us/library/s9ts558h