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();
}
Related
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 have spent a lot of days trying to solve this problem. I have a c++ dll function that takes as a parameter a "std::vector<int*> *list", I need to pass a compatible pointer from c# in Unity.
I tried in this way:
UPDATE
c++ code:
#include <stdio.h>
#include <array>
#include <vector>
#if _MSC_VER
#define EXPORT_API
#else
#define EXPORT_API __declspec(dllexport)
#endif
extern "C"{
/* Other function */
EXPORT_API void GetBlocks(std::vector<int*> *list, int* listSize){
list->push_back(std::vector<int>(16*16*16, 1).data());
(*block_size)++;
}
}
c# code:
[DllImport("dllLib", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
unsafe private static extern void GetBlocks(ref List<int*[]> block, ref int blockSize);
void Start() {
int blockSize = 0;
List<int*[]> block = new List<int*[]>();
unsafe
{
GetBlocks(ref block, ref blockSize);
}
}
Unity returns this error:
MarshalDirectiveException: Type System.Collections.Generic.List`1<int[]> which is passed to unmanaged code must have a StructLayout attribute.
*
I suppose that I have to manage somehow the conversion using Marshal, but right now I don't know.
Maybe there could be another type instead of List<int*[]> to pass a list of the pointer to an array.
Can you help me?
Thanks
I'm currently working on an C# (.NET Framework 4.7.2) application using some business logic from an unmanaged C++ library. I try to pass data (interop) back and forth from C# to C++. I may not use C++/CLI, no common language runtime allowed in my project.
It works fine for int. Unfortunately as soon as I try to send another datatype I'm getting an conversion error e.g. float 4.2f becomes 1 and string "fourtytwo" turns into -1529101360.
My C# code looks like this:
// works fine, creates an instance of TestClass
var test = TestProxy.Wrapper_Create("test");
// int, works fine, a = 42
var a = TestProxy.TryInt(test, 42);
// float, problem, b = 1
var b = TestProxy.TryFloat(test, 4.2f);
// string, problem, c = -159101360
var c = TestProxy.TryString(test, "fourtytwo");
My C# Interop Proxy class to call the native (unmanaged) C++ code looks like this:
public static class TestProxy
{
private const string coreDLL = "test.core.dll";
[DllImport(coreDLL, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr Wrapper_Create(string name);
[DllImport(coreDLL, EntryPoint = "?TryInt#TestClass##XXX#X", CallingConvention = CallingConvention.ThisCall)]
public static extern int TryInt(IntPtr instance, int n);
[DllImport(coreDLL, EntryPoint = "?TryFloat#TestClass##XXX#X", CallingConvention = CallingConvention.ThisCall)]
public static extern int TryFloat(IntPtr instance, float n);
[DllImport(coreDLL, EntryPoint = "?TryString#TestClass##XXX#X", CallingConvention = CallingConvention.ThisCall)]
public static extern int TryString(IntPtr instance, string n);
}
My native (unmanaged) C++ looks like that:
the header file:
#ifdef TESTCORE_EXPORTS
#define TESTCORE_API __declspec(dllexport)
#endif
#pragma once
extern "C"
{
class TESTCORE_API TestClass
{
private:
char* name;
public:
TestClass(char*);
int TryInt(int);
float TryFloat(float);
char* TryString(char*);
};
TESTCORE_API TestClass* Wrapper_Create(char* name);
}
the implementation file:
#include "stdafx.h"
#include "TESTCore.h"
TestClass::TestClass(char* n)
{
name = n;
}
int TestClass::TryInt(int n)
{
return n; // works fine
}
float TestClass::TryFloat(float n)
{
return n; // something goes wrong here
}
char* TestClass::TryString(char* n)
{
return n; // something goes wrong here
}
extern "C"
{
TESTCORE_API TestClass * Wrapper_Create(char* name)
{
return new TestClass(name);
}
TESTCORE_API int TryInt(TestClass * instance, int n)
{
if (instance != NULL)
{
return instance->TryInt(n);
}
}
TESTCORE_API float TryFloat(TestClass * instance, float n)
{
if (instance != NULL)
{
return instance->TryFloat(n);
}
}
TESTCORE_API char* TryString(TestClass * instance, char* n)
{
if (instance != NULL)
{
return instance->TryString(n);
}
}
}
Do you know how to correctly marshal float, string from C# to C++ and back?
Thank you!
C++ doesn't have standard ABI. It's rarely a good idea to use C++ classes across DLLs, even when you have same language on both sides.
There're better ways.
Replace your __thiscall class methods with global functions, cdecl or stdcall whichever you like (but note C# and C++ have different defaults, if you'll do nothing C++ will use cdecl, C# will import as stdcall). You can pass "this" pointer of the class in the first argument, IntPtr in C#, just like you're doing now. Also if you'll write extern "C" or use a module definition file, they will have human-readable names.
If you want objects, use COM. Declare an interface that inherits from IUnknown, implement it in C++ (I usually use ATL), and export a global function to create an instance of that object (2 lines in ATL, CComObject<T>::CreateInstance followed by AddRef). No need to register, type libraries, you just need to implement IUnknown (but see this if you want to use them from multiple threads)
Update: strings are indeed harder. Apply [MarshalAs(UnmanagedType.LPTStr)] to the argument. Apply [return: MarshalAs(UnmanagedType.LPTStr)] to the function. Specify PreserveSig=true in your DllImport. Finally, modify the C++ code to return a copy of the string, i.e. call strlen then CoTaskMemAlloc (don't forget about the '\0') then strcpy.
Easier way to deal with strings is like this:
HRESULT TryString( TestClass *instance, BSTR i, BSTR *o )
At least there're CComBSTR and _bstr_t built-in classes to deal with memory management.
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;
}
};
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