Passing a C# class object in and out of a C++ DLL class
c#, c++, dllimport, marshalling, pinvoke
Solution
Bond was correct, I can't pass an object between managed and unmanaged code and still have it retain its stored information.
I ended up just calling C++ functions to create an object and pass the pointer back into C#'s IntPtr type. I can then pass the pointer around to any C++ function I need (provided it's extern) from C#. This wasn't excatly what we wanted to do, but it will serve its purpose to the extent we need it.
Here's C# the wrapper I'm using for example/reference. (Note: I'm using StringBuilder instead of the 'int intTest' from my example above. This is what we wanted for our prototype. I just used an integer in the class object for simplicity.):
class LibWrapper
{
[DllImport("CPPDLL.dll")]
public static extern IntPtr CreateObject();
[DllImport("CPPDLL.dll")]
public static extern void SetObjectData(IntPtr ptrObj, StringBuilder strInput);
[DllImport("CPPDLL.dll")]
public static extern StringBuilder GetObjectData(IntPtr ptrObj);
[DllImport("CPPDLL.dll")]
public static extern void DisposeObject(IntPtr ptrObj);
}
public static void CallDLL()
{
try
{
IntPtr ptrObj = Marshal.AllocHGlobal(4);
ptrObj = LibWrapper.CreateObject();
StringBuilder strInput = new StringBuilder();
strInput.Append("DLL Test");
MessageBox.Show("Before DLL Call: " + strInput.ToString());
LibWrapper.SetObjectData(ptrObj, strInput);
StringBuilder strOutput = new StringBuilder();
strOutput = LibWrapper.GetObjectData(ptrObj);
MessageBox.Show("After DLL Call: " + strOutput.ToString());
LibWrapper.DisposeObject(ptrObj);
}
...
}
Of course the C++ performs all the needed modifications and the only way for C# to access the contents is, more or less, by requesting the desired contents through C++. The C# code does not have access to the unmanged class contents in this way, making it a little longer to code on both ends. But, it works for me.
This is the references I used to come up with the basis of my solution: http://www.codeproject.com/Articles/18032/How-to-Marshal-a-C-Class
Hopefully this can help some others save more time than I did trying to figure it out!
Problem
I've been working on a prototype code application that runs in C# and uses classes and functions from older C++ code (in the form of an imported DLL). The code requirement is to pass in a class object to the unmanaged C++ DLL (from C#) and have it be stored/modified for retrieval later by the C# application. Here's the code I have so far... Simple C++ DLL Class: ``` class CClass : public CObject { public: int intTest1 }; ``` C++ DLL Functions: ``` CClass *Holder = new CClass; extern "C" { // obj always comes in with a 0 value. __declspec(dllexport) void SetDLLObj(CClass* obj) { Holder = obj; } // obj should leave with value of Holder (from SetDLLObj). __declspec(dllexport) void GetDLLObj(__out CClass* &obj) { obj = Holder; } } ``` C# Class and Wrapper: ``` [StructureLayout(LayoutKind.Sequential)] public class CSObject { public int intTest2; } class LibWrapper { [DLLImport("CPPDLL.dll")] public static extern void SetDLLObj([MarshalAs(UnmanagedType.LPStruct)] CSObject csObj); public static extern void GetDLLObj([MarshalAs(UnmanagedType.LPStruct)] ref CSObject csObj); } ``` C# Function Call to DLL: ``` class TestCall { public static void CallDLL() { ... CSObject objIn = new CSObject(); objIn.intTest2 = 1234; // Just so it contains something. LibWrapper.SetDLLObj(objIn); CSObject objOut = new CSObject(); LibWrapper.GetDLLObj(ref objOut); MessageBox.Show(objOut.intTest2.ToString()); // This only outputs "0". ... } } ``` Nothing but junk values appear to be available within the DLL (coming from the passed in C# object). I believe I am missing something with the class marshalling or a memory/pointer issue. What am I missing? Edit: I changed the above code to reflect changes to the method/function definitions, in C#/C++, suggested by Bond. The value (1234) being passed in is retrieved by the C# code correctly now. This has exposed another issue in the C++ DLL. The 1234 value is not available to the C++ code. Instead the object has a value of 0 inside the DLL. I would like to use predefined C++ functions to edit the object from within the DLL. Any more help is greatly appreciated. Thanks!