We have been given a C# DLL which contains code required for loading and manipulating a proprietary data format. Our codebase is primarily native C++. So we can access the supplied library, I wrote a DLL that consists of
a native C++ wrapper class that implements the necessary API
A C++/CLI bridge class that marshals data in and out and and delegates calls to the C# DLL.
I wrote a basic (native) C++ console application to test that the API does what I wanted. It did. I then wrote a (native, Microsoft Visual Studio) unit test project that calls the API. Surprisingly, it crashes with some unstated exception when constructing the interface object.
Some minimal code:
InteropAPI.h:
#pragma once
#pragma unmanaged
#if defined(INTEROP_API_EXPORTS)
#define INTEROP_API __declspec(dllexport)
#else
#define INTEROP_API __declspec(dllimport)
#endif
#include <string>
class InteropBridgeArchive;
struct InteropRecord
{
std::string Name;
int Id;
std::string Data;
};
class INTEROP_API InteropAPI
{
public:
InteropAPI();
~InteropAPI();
InteropRecord GetRecord(int id) const;
private:
InteropBridgeArchive * impl_;
};
InteropAPI.cpp:
#pragma managed
#include "InteropBridge.h"
#pragma unmanaged
#include "InteropAPI.h"
InteropAPI::InteropAPI()
: impl_(new InteropBridge())
{
}
InteropAPI::~InteropAPI()
{
delete impl_;
}
InteropRecord InteropAPI::GetRecord(const int id) const
{
return impl_->GetRecord(id);
}
InteropBridge.h
#pragma once
#pragma unmanaged
#include "InteropAPI.h"
#pragma managed
#include <vcclr.h>
class InteropBridge
{
public:
InteropArchive();
InteropRecord GetRecord(System::Int32 id) const;
private:
gcroot<InteropCSharpDll::Archive^> archive_;
};
InteropBridge.cpp:
#pragma unmanaged
#include <string>
#pragma managed
#include "InteropBridge.h"
#include <msclr/marshal_cppstd.h>
InteropBridge::InteropBridge()
{
archive_ = gcnew InteropCSharpDll::Archive;
}
InteropRecord InteropBridge::GetRecord(const System::Int32 id) const
{
auto return_value = archive_->GetRecord(id);
InteropRecord record;
record.Name = msclr::interop::marshal_as<std::string>(return_value.Name);
record.Id = return_value.Id;
record.Data = msclr::interop::marshal_as<std::string>(return_value.Data);
return record;
}
The test C# DLL has this:
using System.Linq;
namespace InteropCSharpDll
{
public struct Record
{
public string Name;
public int Id;
public string Data;
}
public class Archive
{
public Record GetRecord(int id)
{
return _records.First(r => r.Id == id);
}
private readonly Record[] _records =
{
new Record {Name = "Foo", Id = 0, Data = "Foo.Data"},
new Record {Name = "Bar", Id = 2, Data = "Bar.Data"},
new Record {Name = "Egg", Id = 7, Data = "Egg.Data"},
};
}
}
Clearly, if this hybrid structure can't be unit tested as native code, we can't test anything that touches it, so it's quite important to get to the bottom of why it crashes. This code runs nicely as a native C++ project:
#include <cstdio>
#include <iostream>
#include "../InteropCppCliBridgeDll/InteropAPI.h"
#pragma comment(lib, "../x64/Debug/InteropCppCliBridgeDll.lib")
int main(int , char ** )
{
const InteropAPI archive;
const auto record = archive.GetRecord(0);
std::cout << "Name: " << record.Name << "\n";
std::cout << "Id: " << record.Id<< "\n";
std::cout << "Data: " << record.Data<< "\n\n";
}
whereas in a native unit test project, the crash occurs in or around the line
archive_ = gcnew InteropCSharpDll::Archive;
Related
I created a simple DLL in Java with JNI . It contains one function the return a string "hello from java dll".
The dll works fine when i use "java " in the cmd.
Now I'm trying to load this DLL in another DLL that I wrote using c++ which already contains 2 working functions.
So I did this:
char* MyFunctions::HelloFromJava() {
HMODULE myDll = LoadLibrary(L"TestJavaDll.dll");
if (myDll != NULL) {
auto fun = (fun_ptr)GetProcAddress(myDll,"HelloFromJava");
if (fun != NULL)
return fun();
else
return "Can't find HelloFromJava";
FreeLibrary(myDll);
}
else {
return "Can't find TestJavaDll.dll";
return "GetLastError()=";
}
}
And in the header:
static __declspec(dllexport) char* HelloFromJava();
And the cpp and header files of the Java dll are:
#include <jni.h>
#include <stdio.h>
#include <windows.h>
#include "TestJavaDll.h"
JNIEXPORT jstring JNICALL
Java_TestJavaDll_HelloFromJava(JNIEnv *env, jobject obj)
{
return env->NewStringUTF("Hello From Java Dll");
}
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class TestJavaDll */
#ifndef _Included_TestJavaDll
#define _Included_TestJavaDll
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: TestJavaDll
* Method: HelloFromJava
* Signature: ()V
*/
JNIEXPORT jstring JNICALL Java_TestJavaDll_HelloFromJava
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
To test the c++ DLL i created a .net application that imports all methods in the c++ DLL. So, in this case, I am doing this:
[DllImport("HELLODLL3", EntryPoint = "?HelloFromJava#MyFunctions#HelloCPPLibrary##SAPADXZ")]
public static extern IntPtr HelloFromJava();
And then i print the message:
Console.WriteLine(Marshal.PtrToStringAnsi(HelloFromJava()));
But i get the error:
Unable to find an entry point named '?' in DLL 'HELLODLL3'
where HELLODLL3 is the name of the c++ DLL.
You do not have the correct mangled name for the DllImport:
?HelloFromJava#MyFunctions#HelloCPPLibrary##SAPADXZ
See here for details of how to get it.
I am working on a C# project that uses some unmanaged C++ code encapsulated in a dll. I have to load that dll as a Reference (not DllImport), so I'm writing a managed C++ Wrapper as a bridge between C# and the dll.
I can easily call the dll functions from C#, just calling the equivalent functions in the wrapper from C#, and then calling the dll functions from the wrapper. However, there are some callback functions in the dll that should call C# functions, and that part doesn't work. I have studied several tutorials about this kind of interop (stackoverflow, microsoft, codeproject), but they are quite old and I can't make them work.
The idea is that the dll is processing real time data, and everytime the dll generates some results, it should call a function in Program.cs, which uses those results.
The code I'm working on is huge, so I've made a very simple scenario that includes everything that doesn't work.
In Program.cs I'm trying to tell the dll: "take this function Test and call it when you are done". Then, to test it, I force it to run the callback function. But I find several errors.
Although wrapper compiles and generates wrapper.dll, when I load it in the C# project, the function M_setCallback isn't there ¿?.
I wonder if somebody could give me a hand or show me a tutorial that explains how to do that (the tutorials I found don't have the 3 projects I must use, and I can't make them work). I have tried using delegates, but the program crashes.
Or maybe there is a simpler way to do this.
Thanks!
Here is the code, it's very simple:
First, I have the unmanaged dll:
C++ Project, Win32 Console Application, compiled into a dll
defs.h
namespace JJLibrary
{
typedef void (__stdcall *MYCALLBACK) (bool mybool, int myint);
}
unmanaged_dll.h
#ifndef JJAPI_H
#define JJAPI_H
#ifdef UNMANAGED_EXPORTS
#define JJ_API __declspec(dllexport)
#else
#define JJ_API __declspec(dllimport)
#endif
#include "defs.h"
namespace JJLibrary
{
JJ_API void U_helloWorld();
JJ_API void U_setCallback( MYCALLBACK theCallback );
JJ_API void U_runCallback();
}
#endif //JJAPI_H
unmanaged_dll.cpp
#include "unmanaged_dll.h"
#include <iostream>
using namespace std;
namespace JJLibrary
{
MYCALLBACK m_callback = 0;
JJ_API void U_helloWorld()
{
cout << "Unmanaged: Hello world" << endl;
}
JJ_API void U_setCallback( MYCALLBACK theCallback )
{
cout << "Unmanaged: Setting callback" << endl;
m_callback = theCallback;
}
JJ_API void U_runCallback()
{
bool b = true;
int i = 25;
cout << "Unmanaged: Running callback: " << b << " " << i << endl;
if (m_callback)
m_callback(b, i);
// In the final application b and i are generated here after processing data, I give them values for testing
// When they are generated, call the function in C#
}
}
Wrapper:
C++ CLR Class Project
wrapper.h
#include "defs.h"
#include <iostream>
using namespace System;
using namespace System::Runtime::InteropServices;
using namespace JJLibrary;
using namespace std;
namespace wrapper {
public ref class Wrapper
{
public:
void M_helloWorld();
void M_setCallback( MYCALLBACK theCallback );
void M_runCallback( bool mybool, int myint );
};
}
wrapper.cpp
#include "stdafx.h"
#include "wrapper.h"
#include "defs.h"
#include <iostream>
using namespace wrapper;
using namespace JJLibrary;
using namespace std;
void Wrapper::M_helloWorld()
{
cout << "Managed: Hello World" << endl;
U_helloWorld();
}
void Wrapper::M_setCallback( MYCALLBACK theCallback )
{
cout << "Managed: Setting callback" << endl;
U_setCallback( theCallback);
}
void Wrapper::M_runCallback(bool mybool, int myint)
{
cout << "Managed: Running callback" << endl;
U_runCallback(mybool, myint);
}
And finally, Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using wrapper;
namespace CS
{
class Program
{
void Test(bool mybool, int myint)
{
Console.WriteLine(!mybool);
Console.WriteLine(2*myint);
}
static void Main(string[] args)
{
Wrapper myWrapper = new Wrapper();
myWrapper.M_helloWorld();
myWrapper.M_setCallback(Test);
myWrapper.M_runCallback(true, 25); // --> false 50
string s = Console.ReadLine(); // Pause
}
}
}
Too much code to really understand, but my solution to a similar problem involved a delegate that looks like this.
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void UserFunctionCallback(IntPtr context, int nvalues,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]IntPtr[] values);
If that helps I can add some more code.
I have this error when calling managed code library from unmanaged code:
Run-Time Check Failure #2 - Stack around the variable 'BridgeObj' was corrupted.
The code I created is:
Managed lib:
using System;
namespace My.Name.Space
{
public class Sample
{
public int Request(string xml_input, out string xml_output)
{
xml_output = "Retun string.";
return 0;
}
}
}
Wrapper C++/CLI:
#include "stdafx.h"
#include <msclr\auto_gcroot.h>
#using "..\link\ManagedLib.dll"
using namespace System::Runtime::InteropServices; // Marshal
struct ManagedModul
{
public:
msclr::auto_gcroot<My::Name::Space::Sample^> SampleModul;
};
class __declspec(dllexport) Bridge
{
private:
ManagedModul _private;
public:
Bridge()
{
_private.SampleModul = gcnew My::Name::Space::Sample();
};
~Bridge()
{
}
int Request ( const char * xmlin, char ** xmlout )
{
System::String ^ps;
_private.SampleModul->Request(gcnew System::String(xmlin), ps);
* xmlout = (char*) (Marshal::StringToHGlobalAnsi(ps)).ToPointer();
return 0;
}
};
Sample usage:
#include "stdafx.h"
#include <Windows.h>
#pragma comment ( lib, "..\\link\\Wrapper.lib" )
class Bridge
{
public:
Bridge();
~Bridge();
int Request ( const char * xmlin, char ** xmlout );
};
int _tmain(int argc, _TCHAR* argv[])
{
Bridge BridgeObj;
char * buffer = NULL;
BridgeObj.Request("aaaaa", & buffer );
LocalFree ( buffer );
return 0;
}
class Bridge
{
public:
Bridge();
~Bridge();
int Request ( const char * xmlin, char ** xmlout );
};
This is a very, very bad practice. Instead of using a .h file that is used in both projects, you redeclared the Bridge class. And got it wrong, you missed the _private member. This always goes wrong. If not immediately then in a year from now when you modify the real Bridge class.
What happens next is pretty inevitable. The real class object is larger than what the compiler thinks when it compiles your redeclared class. So it doesn't reserve enough space on the stack to store the object. Inevitable, that will cause another variable on the stack to get whacked, overwritten whenever the real class assigns the _private member.
Buy Microsoft a cigar for building in this diagnostic, stack corruption like this is normally extraordinarily hard to diagnose. And use a .h file to declare Bridge.
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 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();
}