将C#class对象传入和传出C++;DLL类 我一直在研究C代码中运行的原型代码应用程序,并使用旧的C++代码(以导入的DLL的形式)使用类和函数。代码要求是将类对象传递给非托管C++ DLL(从C语言中),并将其存储/修改以供C语言应用程序稍后检索。这是我到目前为止的代码 简单C++ DLL类: class CClass : public CObject { public: int intTest1 };

将C#class对象传入和传出C++;DLL类 我一直在研究C代码中运行的原型代码应用程序,并使用旧的C++代码(以导入的DLL的形式)使用类和函数。代码要求是将类对象传递给非托管C++ DLL(从C语言中),并将其存储/修改以供C语言应用程序稍后检索。这是我到目前为止的代码 简单C++ DLL类: class CClass : public CObject { public: int intTest1 };,c#,c++,pinvoke,marshalling,dllimport,C#,C++,Pinvoke,Marshalling,Dllimport,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).

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

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

如果您仅从C#使用该类,则应使用GCHandle

编辑:


我相信你应该像这样申报你的退货方式

__declspec(dllexport) void getDLLObj(__out CClass* &obj)
分别是C#原型

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

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

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

我最后只调用C++函数来创建一个对象,并将指针传递回C的IpPTR类型。然后我可以把指针传递给我需要的任何C++函数(如果是外部的话)。这当然不是我们想要做的,但它将在我们需要的程度上达到它的目的

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

当然,C++执行所有需要的修改,C++访问内容的唯一方法或多或少是通过C++请求所需内容。C#代码不能以这种方式访问未管理的类内容,这使得在两端编写代码的时间稍长。但是,这对我很有效

这是我用来提出解决方案基础的参考资料:


希望这可以帮助其他人节省更多的时间,比我试图找出它

我在试图编译DLL时遇到“指针指向引用非法”错误。此外,将C++函数定义为引用,为公共类提供了“CONNE访问类中的私有成员”错误。如果我将返回函数定义为指向类的指针,它确实会更改C#app中的输出,但是我提到的传递到DL中的类垃圾会改为0值。这可能与另一个问题有关吗?我的错误是,它必须是指向指针的引用或指向指针的指针-我修复了我的示例代码。您需要在getDLLObj中使用对指针的引用,在SetDLLObj中只使用一个指针,并通过在参数上添加ref关键字来修复getDLLObj的C#prototype。感谢Bond,您的建议有助于删除存储在DLL中的垃圾值。但是,我仍然存在传递0值的问题,应该是1234。相信@user629926提到的非托管内存和托管内存之间仍然存在问题。我感谢你的帮助和任何其他想法将是伟大的!垃圾到底是什么?SetDLLObj(类obj)中obj的intTest1字段?你能用你现在的密码更新你的问题吗?呃,我刚看到这个,但是。。。您不能像这样将托管对象(CSObject)封送到非托管对象(CCClass)。这是行不通的,布局是不同的-你的类继承自CObject。。。啊。。。我认为这是不可能的,我想你应该使用COM来实现这一点,或者使用托管C++(C++/CLI)这很有趣,因为我觉得这与两者之间的内存访问有关,我还没有运行accross GCHandle。但是使用它,我需要使用IntPtr类型来代替传递我的CSObject。是否有某种方法可以将CSObject类转换为IntPtr?否则我无法通过课堂内容。谢谢您只需将CClass替换为CClass*或void*,就可以在C#端将其用作IntPtr。你是否已经在C++方面改变了它的值?另外,C#类CClass会自动封送为CClass*。您可以尝试将类声明为struct并使用public static extern void GetDLLObj(out CSObject csObj);是的,我必须改变C++的值。当1234值返回时,我能够在C#端获得传入的值(感谢@Bond),但我在代码中尝试的任何更改都不会改变out/return对象值。在C++代码中,我还显示对象值为0。我确实尝试将Cype类转换为结构,但它产生了一些编组问题。由于某种原因,int Test2不能被封送给C++对应的.Sy-DeScript(DLLuturn)VoUT StdLLBoJ(CClass obj){HORK=和OBJ;}会使本地CClass的指针损坏,这样你可能会得到它的垃圾。持有者似乎是存储了进入C++代码的值,以便以后检索。我可以看一看,但我现在主要关心的是从传递到DLL的对象中提取一个值。这正是我想要的。谢谢,不需要object,但是object ref当然可以。很好地保持了模式的功能
public static extern void GetDLLObj([MarshalAs(UnmanagedType.LPStruct)] 
      ref CSObject csObj);
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);
    }
    ...
}