I have a C++ project containing a nonmanaged class method used to display string in a user interface :
void MyProject::displayIHM(std::string mystring);
This project is compiled with /clr because it calls another one made with C#/.NET 4.0. The goal of the .NET project is to make heavy computation tasks. During computation, we want to get back from it some information to the user interface.
My idea was to create two new methods in the C++-cli project :
void MyProject::displayFromDotNet(String^ mystring)
{
displayIHM(ToStdString(mystring));
}
string ToStdString ( String ^ s)
{
const char* chars = (const char*)(Marshal::StringToHGlobalAnsi(s)).ToPointer();
string os = chars;
Marshal::FreeHGlobal(IntPtr((void*)chars));
return os;
}
Until now, everything is ok but now the difficult part : how to provide displayFromDotNet to the .NET project. My idea was to provide a function pointer in the constructor of the .NET class and then to launch the process :
void (MyProject::*pointeurFunc)(String^) = &MyProject::displayFromDotNet;
ComputationProject^ kernel = gcnew ComputationProject((this->*pointeurFunc));
kernel->Compute();
The second line does not work. The constructor of ComputationProject has a IntPtr parameter but I do not know if I can convert a function pointer to an IntPtr^ in C++. I also made some attempts using Marshal::GetDelegateForFunctionPointer but it could not compile.
I do not know what to do, any help would be appreciated!
EDIT : yes ComputationProject is my C#/.NET project. The error with line 2 is "cannot convert parameter 1 from 'overloaded function type' to 'System::IntPtr'".
I finally find a (ugly) way.
My main problem was I could not pass a method pointer to C# because it is not a real function pointer (so I cannot cast it to IntPtr).
I decided to create a second class containing a static MyProject object and a static method calling displayIHM on the static object :
class StaticMyProject
{
public :
static MyProject staticObject;
static void DisplayInIHM(char *);
};
In cpp :
MyProject StaticMyProject::objetStatique;
void StaticMyProject::DisplayInIHM(char *message)
{
std::string message2(message);
staticObject.displayIHM(message2);
}
Now for calling Compute method of ComputationProject, I modified the code like this :
StaticMyProject::objetStatique = *this;
void (*funcPointer)(char*) = StaticMyProject::DisplayInIHM;
ComputationProject^ kernel = gcnew ComputationProject((IntPtr)funcPointer);
kernel->Compute();
And in my ComputationProject.cs :
public class ComputationProject
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void FunctionPointer([MarshalAs(UnmanagedType.LPStr)]string message);
public readonly FunctionPointer DisplayMethod;
public ComputationProject(IntPtr ptr)
{
this.DisplayMethod = (FunctionPointer)Marshal.GetDelegateForFunctionPointer(ptr, typeof(FunctionPointer));
}
public int Compute()
{
this.DisplayMethod("Beginning computation...");
...
}
}
Related
There is an unexplained ambiguity in C#, where I explicitly try to call a constructor but the compiler thinks it is a different constructor. I will start with showing a short C# architecture we use. Then show a small "working" example I created, and the possible solution to this, but still I like to understand why this happens.
The Architecture:
CLR DLL which bridges the C++ API.
C# API which uses the bridge level.
C# Client applications that use the C# API.
Note that the C# Clients are not allowed to use the CLR level.
Example I created
A class in the CLR DLL:
#pragma once
#include <string>
using namespace System;
namespace Inner {
public ref class AInner
{
public:
AInner() : _data(new std::wstring(L"")) {}
~AInner() {
delete _data;
}
property String^ Val
{
String^ get()
{
return gcnew String((*_data).data());
}
void set(String^ value) {
System::IntPtr pVal = System::Runtime::InteropServices::Marshal::StringToHGlobalUni(value);
*_data = (const wchar_t*)pVal.ToPointer();
System::Runtime::InteropServices::Marshal::FreeHGlobal(pVal);
}
}
private:
std::wstring* _data;
};
}
Class wrapping the CLR level, in a DLL:
using System;
using Inner;
namespace Outer
{
public class A
{
public A()
{
_inner.Val = String.Empty;
}
public A(string val)
{
init(val);
}
public string Val
{
get
{
return _inner.Val;
}
set
{
_inner.Val = value;
}
}
internal A(AInner inner)
{
_inner = inner;
}
private void init(string Val)
{
_inner = new AInner();
_inner.Val = String.Empty;
}
private AInner _inner;
}
}
Note that there is an internal constructor and a public constructor.
Executable Client using the C# API DLL:
using Outer;
namespace OneClient
{
class Program
{
static void Main(string[] args)
{
string myString = "Some String";
A testA = new A(myString);
}
}
}
Twist in the story:
In the DLL wrapping the CLR level, not ALL API should be used by external clients, but can be used by internal clients, thus the internals are exposed to the internal clients by adding [assembly: InternalsVisibleTo("OneClient")] to the 'AssemblyInfo.cs' of the DLL wrapping the CLR level.
The issue
When compiling the Client code I get the following error:
error CS0012: The type 'AInner' is defined in an assembly that is not referenced. You must add a reference to assembly 'InnerOne, Version=1.0.7600.28169, Culture=neutral, PublicKeyToken=null'.
I cannot use InnerOne because clients are not allowed to use this level.
The client is exposed to both A(string val) and A(AInner inner) constructors.
Possible Workarounds:
Remove the [assembly: InternalsVisibleTo("OneClient")] - This is unacceptable due to other classes internals that the specific client needs to use.
Change the A(string val) constructor to A(string val, bool unique=true) and use it A testA = new A(myString, true) - Not a nice solution.
Use default constructor A() and call testA.Val = myString; - This is actually OK but to much code.
Change the client code from A testA = new A(myString) to A testA = new A(val:myString); - This is actually the chosen solution.
Question
Why does this ambiguity happen?
I call the A(string val) with the myString which is actually a string value
This is very strange.
Is this a bug in Microsoft compiler?
Example Sources:
Source Code One.zip
Why does this ambiguity happen?
Because to satisfy the constructor overload resolution, the compiler needs to know what all the argument types are, and it doesn't know what an AInner is.
Why not expose the AInner version as a factory method:
static internal A Create(AInner inner)
{
return new A { _inner = inner };
}
I don't see any issue in this, the problem is we are used to do the things in a wrong/briefly way.
The correct answer fot this is:
A testA = new A(val:myString);
Furthermore, all your calls (in this way is a call to a constructor/initializer but it's a call anyway) should be with the parameter name. No one (even me) writes them, but...
I have a C++ project X with calling convention set as __stdcall (/Gz) and a C# project Y.
I have defined a class myClass in BOTH these projects.
class myClass
{
private:int mem1;
};
In the C# definition of the class, I have prefixed it with
[StructLayout(LayoutKind.Sequential)]
The C++ function is
_declspec (dllexport) void getLen(myClass* str)
{
printf("%s",sizeof(int));
}
In Y, I have defined the function as follows
[DllImport("X.dll")]
private static extern void getLen(ref myClass str);
And I am calling it like this:
getLen(ref str);
where str is an object of type myClass.
Why is this error coming up when I run this solution?
Your problem is in the printf() call: "%s" expects a pointer to a null-terminated string of characters, but you are providing a size_t.
I have this code in C++
class MyClass { ... };
typedef MyClass (*Callback)();
Callback theCB;
static void RegisterCallback( Callback cb ) { theCB = cb; };
static void CallCallback() {
MyClass obj = theCB();
}
I am using swig but for simplicity (if you don't know swig) I have this wrapper in C#
public class MyClassWrapper
{
public IntPtr ptrToNativeObj; // pointer to native MyClass object
public MyClassWrapper()
{
ptrToNativeObj = call to native code that creates
and returns a new instance of MyClass in C++
}
};
Now I want to support the callback mechanism in C# so I set it up like this:
public MyClassWrapper MyFunction()
{
return new MyClassWrapper();
}
delegate MyClassWrapper CallbackDotNet();
static void main()
{
var fct = new CallbackDotNet( MyFunction );
P/Invoke call to native function RegisterCallback( fct );
then finally:
P/Invoke call to native function CallCallback();
}
I have all this code setup to work properly.
The native code in CallCallback will call MyFunction properly.
But now I need to handle the returned object properly...
MyFunction returns a C# reference while the callback in C++ is returning by value so this would not work for free:
static void CallCallback() {
MyClass obj = theCB();
}
How can I marshall the "reference" to a MyClassWrapper object, returned from MyFunction, so that C++ receives "by-value" a MyClass object ?
Should I go ahead and write a custom marshaller ?
http://msdn.microsoft.com/en-us/library/zk0a8dea(v=vs.90).aspx
Then use it like here
[return: MarshalAs(UnmanagedType.CustomMarshaler,
MarshalType = "MyCustomMarshaler")]
delegate MyClassWrapper CallbackDotNet();
I looked at the documentation for custom marshallers and it's quite complex.
Looks like the interesting method to implement is the following:
IntPtr MarshalManagedToNative( Object ManagedObj );
And the code will be something like
IntPtr MarshalManagedToNative( Object ManagedObj )
{
MyClassWrapper val = ManagedObj as MyClassWrapper;
return val.ptrToNativeObj;
}
But this will return a MyClass* back to the native code, not a MyClass value that this C++ code expects !
static void CallCallback() {
MyClass obj = theCB();
}
Will the marshaller be smart enough to dereference the pointer ?
Thank you all for your comments
Looks like the custom marshaler is the way to go !
I did a simple test case and everything works fine. It works as expected.
Here is the marshaler in case you are interested:
public class MyCustomMarshaler : ICustomMarshaler
{
public static ICustomMarshaler GetInstance(String cookie)
{
return new MyCustomMarshaler();
}
public IntPtr MarshalManagedToNative(Object ManagedObj)
{
MyClassWrapper val = ManagedObj as MyClassWrapper;
return val.ptrToNativeObj;
}
...
}
[return: MarshalAs(UnmanagedType.CustomMarshaler,MarshalType = "MyCustomMarshaler")]
public delegate MyClassWrapper CallbackDotNet();
With this marshaler when C++ calls the C# callback the function MarshalManagedToNative is called on return from that C# callback and this enables to convert the C# reference (to MyClassWrapper) to a pointer to the C++ class MyClass.
This seems sufficient enough and P/Invoke will then take care of dereferencing this MyClass* to a MyClass value.
It wasn't as hard as I thought...
I am very new to C# and currently having some trouble with marshalling structs to C functions in a dll. The struct in question contains a few ints, float and one char* in C as follows:
struct Bin
{
char* m_name;
float m_start;
float m_end;
int m_OwnerID;
int m_SelfID;
}
The corresponding C# struct defintion, I think should be:
public struct Bin
{
public string m_name;
public float m_start;
public float m_end;
public int m_OwnerID;
public int m_SelfID;
}
I also implemented a parser/reader function in C# that reads a text file, creates the Bin objects and stores them in a List object, which is a class member of the main app class. At the same time, I want to pass references to each struct object in the list to a unmanaged C++ Class through a Dll function call so it can be referred to during other function calls without the need to duplicate data on the unmanaged side.
class ProgramMain
{
public List<Bin> m_BinList;
static void Main()
{
//Some functions calls
//Fills up List<Bin>
LoadBinData(string inFile);
//Iterate each bin in list and pass reference to each bin to C++ class via
//Dll Imported function
PassPtrsToLibrary(); //???
}
public void LoadBinData(string inFile)
{
.....
}
public void PassPtrsToLibrary()
{
????
}
/* PassByReferenceIn */
[DllImport ("mylib")]
public static extern
void PassBinPtrIn(ref Bin theBin);
//Some other function that reads the stored Bin pointers' data and perform calculations
[DllImport ("mylib")]
public static extern
int ProcessBin();
}
On the C dll side, internally a global C++ class object stores the pointer to each struct in a std::vector container:
BinManager theBinManager;
void PassBinPtrIn(Bin* theBin)
{
theBinManager.AddBin(theBin);
}
void BinManager::AddBin(Bin* theBin)
{
m_BinPtrList.push_back(theBin);//a std::vector<Bin*> type
}
However I am facing some problems when writing the PassPtrsToLibrary() C# sharo function. After adding the Bin struct into the list, I can never get the actual pointer or reference to the bin inside the list. I have tried Marshall.StructureToPtr but it always crash my application. I also read that it is difficult to pass a managed string in struct as pointer/reference into C code. Please give me some help or advice as how to solve this problem. Thank you for reading this lengthy post.
I have an unmanaged dll with a class "MyClass" in it.
Now is there a way to create an instance of this class in C# code? To call its constructor? I tried but the visual studio reports an error with a message that this memory area is corrupted or something.
Thanks in advance
C# cannot create class instance exported from native Dll. You have two options:
Create C++/CLI wrapper. This is .NET Class Library which can be added as Reference to any other .NET project. Internally, C++/CLI class works with unmanaged class, linking to native Dll by standard C++ rules. For .NET client, this C++/CLI class looks like .NET class.
Write C wrapper for C++ class, which can be used by .NET client with PInvoke. For example, over-simplified C++ class:
class MyClass()
{
public:
MyClass(int n){data=n;}
~MyClass(){}
int GetData(){return data;}
private:
int data;
};
C API wrapper for this class:
void* CreateInstance()
{
MyClass* p = new MyClass();
return p;
}
void ReleaseInstance(void* pInstance)
{
MyClass* p = (MyClass*)pInstance;
delete p;
}
int GetData(void* pInstance)
{
MyClass* p = (MyClass*)pInstance;
return p->GetData();
}
// Write wrapper function for every MyClass public method.
// First parameter of every wrapper function should be class instance.
CreateInstance, ReleaseInstance and GetData may be declared in C# client using PInvoke, and called directly. void* parameter should be declared as IntPtr in PInvoke declaration.
The solution is create C++/CLI wrapper like:
#include "DllExportClass.h"
public ref class ManagedOperationHelper
{
public:
double Sum(double add1, double add2)
{
CDllExportClass obj;
double ret=obj.Sum(add1, add2);
return ret;
}
double Mult(double mult1, double mult2)
{
CDllExportClass obj;
double ret=obj.Mult(mult1, mult2);
return ret;
}
};
where CDllExportClass is the class exported from native code. Above is the .h of the C++/CLI. Take care to let find the lib to this dll. Put the dll and the lib in the same directory and compile the C++/CLI code.In the managed code directory put the native dll and the C++/CLI dll. In the managed project put the reference of the C++/CLI project. Instanciate in the maged code the C++/CLI class like:
ManagedOperationHelper obj = new ManagedOperationHelper();
double ret=obj.Sum(10, 20);
It's all.
You can not use unmanged C++ code directly in C#. The interoperability can be done using PInvoke. There are a lot of issues related to this topic, especially when calling functions which have pointers as arguments.
The basic procedure goes like this:
C# part
namespace MyNamespace {
public class Test {
[DllImport("TheNameOfThe.dll")]
public static extern void CreateMyClassInstance();
public void CallIt() {
CreateMyClassInstance(); // calls the unmanged function via PInvoke
}
}
}
C++ part
class MyClass {
public: MyClass() { /** Constructor */ }
};
MyClass* staticObject;
extern "C" void CreateMyObjectInstance() {
staticObject = new MyClass(); // constructor is called
}