我一直致力于在C#中运行的原型代码应用程序,并使用旧C ++代码中的类和函数(以导入的DLL的形式)。代码要求是将类对象传递给非托管C ++ DLL(来自C#),并将其存储/修改以供C#应用程序稍后检索。这是我到目前为止的代码......
简单C ++ DLL类:
class CClass : public CObject
{
public:
int intTest1
};
C ++ DLL函数:
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#函数调用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".
...
}
}
DLL中没有任何垃圾值可用(来自传入的C#对象)。我相信我错过了类编组或内存/指针问题。我错过了什么?
编辑: 我更改了上面的代码,以反映Bond建议的C#/ C ++中方法/函数定义的更改。传入的值(1234)现在由C#代码正确检索。这暴露了C ++ DLL中的另一个问题。 1234值不可用于C ++代码。相反,该对象在DLL内部的值为0。我想使用预定义的C ++函数来编辑DLL中的对象。非常感谢任何更多的帮助。谢谢!
答案 0 :(得分:7)
Bond是正确的,我无法在托管代码和非托管代码之间传递对象,但仍保留其存储的信息。
我最终只是调用C ++函数来创建一个对象并将指针传回C#的IntPtr类型。然后我可以将指针传递给我需要的任何C ++函数(假设它是extern)来自C#。这并不是我们想做的事情,但它会在我们需要的范围内达到目的。
这是C#我正在使用的包装器/示例/参考。 (注意:我正在使用StringBuilder而不是上面示例中的'int intTest'。这是我们原型所需的。我只是在类对象中使用了一个整数来简化。):
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);
}
...
}
当然,C ++执行所有必需的修改,C#访问内容的唯一方法是或多或少地通过C ++请求所需的内容。 C#代码无法以这种方式访问未管理的类内容,使得两端的代码变得更长。但是,它对我有用。
这是我用来提出解决方案基础的参考资料: http://www.codeproject.com/Articles/18032/How-to-Marshal-a-C-Class
希望这可以帮助其他人节省更多的时间,而不是试图解决这个问题!
答案 1 :(得分:4)
我相信你应该像这样声明你的返回方法
__declspec(dllexport) void getDLLObj(__out CClass* &obj)
分别是C#原型
public static extern void GetDLLObj([MarshalAs(UnmanagedType.LPStruct)]
ref CSObject csObj);
入站方法也应该带一个指向CClass的指针,C#原型就可以了。
答案 2 :(得分:0)
如果您仅使用C#中的课程,则应使用GCHandle http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.gchandle%28v=vs.110%29.aspx
编辑:
CClass *Holder;
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);
}
}