繁体   English   中英

将C#类对象传入和传出C ++ DLL类

[英]Passing a C# class object in and out of a C++ DLL class

我一直在研究在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#类和包装器:

[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#对象)。 我相信我错过了类编组或内存/指针问题。 我错过了什么?

编辑:我改变了上面的代码,以反映邦德建议的C#/ C ++中方法/函数定义的变化。 传入的值(1234)现在由C#代码正确检索。 这暴露了C ++ DLL中的另一个问题。 1234值不可用于C ++代码。 相反,该对象在DLL内部的值为0。 我想使用预定义的C ++函数来编辑DLL中的对象。 非常感谢任何更多的帮助。 谢谢!

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-aC-Class

希望这可以帮助其他人节省比我试图解决的更多时间!

我相信你应该像这样声明你的返回方法

__declspec(dllexport) void getDLLObj(__out CClass* &obj)

分别是C#原型

public static extern void GetDLLObj([MarshalAs(UnmanagedType.LPStruct)] 
      ref CSObject csObj);

入站方法也应该使用指向CClass的指针,C#原型是可以的。

如果你只使用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);
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM