I'm working on a project that uses both C++ and C#.
However, when passing C#, i get a copy instead of the actual object, which doesn't work for me because i need to read and write to it both on on C# and C++.
on C++ the class looks like this:
class TestClass {
public:
float f1;
float f2;
}
whereas on C# the class looks like this
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x8)]
public class TestClass {
[FieldOffset(0x0)] public float f1;
[FieldOffset(0x4)] public float f2;
}
then the way i read it in C# is by doing this:
public class TestProgram {
public static CTestClass Test;
public static delegate void SetTestDelegate(IntPtr pTest);
public static void SetTest(IntPtr pTest) {
if (pTest == IntPtr.Zero)
return;
object? TestObj = Marshall.PtrToStrucutre(pTest, typeof(TestClass))
if (TestObj == null)
return;
Test = (TestClass)TestObj;
}
}
the SetTest method gets invoked on C++ like this:
typedef void (*tSetTest) (TestClass* pClass):
void SetNetTest() {
// code that gets the library path and stuff here
tSetTest SetTest;
hostfxr_load_assembly(wsLibPath.c_str(), L"TestProgram, testlib", L"SetTest", L"TestProgram+SetTestDelegate, testlib", nullptr, (void**)&SetTest);
TestClass* pTestClass = new TestClass();
SetTest(pTestClass);
}
but this just returns a copy of the object at the time of reading it rather than an actual instance of it.
is there any way i can change that without using unsafe code or writing setters/getters in C++?
Related
I have a CLI wrapper to a C++ class. The C++ class has a callback that passes
a char* array, and the length of that array.
void (CPP_CALLBACK*)(char* data, unsigned int dataLength)
In C# land, I have this callback
private delegate void AppPacketReceivedDelegate(byte[] data);
My CLI wrapper receives the CPP_CALLBACK callback, and must then somehow call the C# delegate.
Any ideas on how to do this ? I tried
System::Action<cli::array<char>^>
but I am not sure how to match the delegate to this Action.
Update:
I convert the delegate into a function pointer, and then I can call the function pointer from CLI using this syntax:
typedef void(__stdcall * WRAPPER_APP_PACKET_CALLBACK) (cli::array<unsigned char>^);
But, when I call this, the array is always of size 1 !
Here a simple example, not exactly matching your situation, but it could be quickly adapted.
CppClass.h
namespace MyCppNamespace
{
typedef void (__stdcall *callback_function)(int, const char*);
class CppClass
{
private:
callback_function _callbackFunc;
public:
void DoSomething(callback_function callbackFunc);
}
}
DotNetClass.cs
namespace MyManagedNamespace
{
public delegate void ManagedCallback(int size, string message);
}
CliWrapperClass.h
#include "CppClass.h"
using namespace MyManagedNamespace;
namespace MyCliNamespace
{
public ref class CliWrapperClass
{
private:
CppClass *_cppClass;
public:
void DoSomething(ManagedCallback ^ callback);
}
}
CliWrapperClass.cpp
#include "CliWrapperClass.h"
namespace MyCliNamespace
{
void CliWrapperClass::DoSomething(ManagedCallback ^ callback)
{
System::IntPtr callbackPtr = System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(callback);
_cppClass->DoSomething(static_cast<callback_function>(callbackPtr.ToPointer()));
}
}
In the .Net class, you can create an instance of CliWrapperClass and call its DoSomething(...) function in this way:
namespace MyManagedNamespace
{
public class ManagedClass
{
private ManagedCallback _callback = MyCallback;
private void MyCallback(int size, string message)
{
// Do what you want ...
}
public void MyFunction()
{
CliWrapperClass wrapper = new CliWrapperClass();
wrapper.DoSomething(_callback);
}
}
}
In the end, I marked the C# class and the corresponding delegate as unsafe, and
then I was able to call the delegate from C++ and pass over the C-style array. On the C# side, I created a managed array and copied the bytes over using Marshal.Copy
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...");
...
}
}
This is maybe an obvious question but I have been googling for 2 hours and can't figure it out. So I've got a class in C++ that looks like this:
class MyClass
{
public:
static void Function(float& r) { r = 10; }
};
I have an interop wrapper class that looks like this:
public ref class Wrapper
{
public:
static void WrappedFunction(float& r) { MyClass::Function(r); }
};
And now I want to call it from C#, like this:
float r;
Wrapper.WrappedFunction(ref r);
However I get a compile error on the C# side which says this:
cannot convert from 'ref float' to 'float*'
I have a vague notion that I need to do something in the Wrapper class but I don't know what. Also, I know this use case is trivial with dllimport/pinvoke but I want to wrap this with C++/CLI for performance reasons.
Any help is greatly appreciated!
Wrapper class needs to be
public ref class Wrapper
{
public:
static void WrappedFunction(float% r)
{ float copy = r; MyClass::Function(copy); r = copy; }
};
References defined with % work almost exactly like ones defined using &, except they work together with the garbage collector.
Another option is
public ref class Wrapper
{
public:
static void WrappedFunction(float% r)
{ pin_ptr<float> p = &r; MyClass::Function(*p); }
};
Which will pin the object in memory preventing the garbage collector from using it, but have the native code work directly on the managed variable. Not usually important, but if you had another thread needing to see intermediate values, this is how.
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.