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

标签 c# c++ pinvoke marshalling dllimport

我一直在开发一个原型(prototype)代码应用程序,该应用程序在 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);
}

DLL 的 C# 函数调用:

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# 对象)。我相信我在类编码或内存/指针问题上遗漏了一些东西。我错过了什么?

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

最佳答案

Bond 是正确的,我不能在托管代码和非托管代码之间传递一个对象并且仍然保留其存储的信息。

我最终只是调用 C++ 函数来创建对象并将指针传回 C# 的 IntPtr 类型。然后我可以将指针从 C# 传递到我需要的任何 C++ 函数(前提是它是外部函数)。这并不是我们真正想要做的,但它会在我们需要的范围内达到目的。

这是我用作示例/引用的 C# 包装器。 (注意:我使用的是 StringBuilder 而不是上面示例中的“int intTest”。这就是我们想要的原型(prototype)。为了简单起见,我只是在类对象中使用了一个整数。):

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

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

关于c# - 将 C# 类对象传入和传出 C++ DLL 类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11123899/

相关文章:

c# - 从 C# 中的流对象创建一个临时文件

c++ - 使用 std::basic_istringstream<unsigned short> 时的 std::bad_cast

c++ - 使用静态库的 undefined reference

c++ - 段错误(核心转储)- 代码在 VS 中有效,但在 Linux 终端中无效

c# - 在密封类上实现 IDisposable

c# - 如何从 native (C++) 代码返回文本

c# - 使用 p12 key 进行身份验证时出现错误 用户无法访问帐户

c# - 循环遍历组合框的项目

c# - 如何使用 Hardware ID Extractor 修复 Ntdll.dll APPCRASH?

c# - HTTP PUT 不工作