Передача объекта класса C# в класс DLL C++ и из него

Я работал над прототипом приложения кода, который работает на C# и использует классы и функции из старого кода C++ (в виде импортированной DLL). Требование кода состоит в том, чтобы передать объект класса в неуправляемую DLL C++ (из C#) и сохранить/изменить его для последующего извлечения приложением C#. Вот код, который у меня есть...

простой класс DLL C++:

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# правильно сейчас. Это выявило еще одну проблему в DLL C++. Значение 1234 недоступно для кода C++. Вместо этого объект имеет значение 0 внутри DLL. Я хотел бы использовать предопределенные функции C++ для редактирования объекта из библиотеки DLL. Любая дополнительная помощь очень ценится. Спасибо!

3 ответов


Бонд был прав, я не могу передать объект между управляемым и неуправляемым кодом и все еще сохранить его сохраненную информацию.

Я закончил тем, что просто вызвал функции C++ для создания объекта и передал указатель обратно в тип IntPtr C#. Затем я могу передать указатель на любую функцию C++, которая мне нужна (при условии, что это extern) из C#. Это было не совсем то, что мы хотели сделать, но это будет служить своей цели в той мере, в какой нам это нужно.

вот c# обертка, которую я использование, например, / reference. (Примечание: Я использую StringBuilder вместо "int intTest" из моего примера выше. Это то, что мы хотели для нашего прототипа. Я просто использовал целое число в объекте class для простоты.):

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

надеюсь, это может помочь некоторым другим сэкономить больше времени, чем я пытаюсь выяснить это!


Я считаю, что вы должны объявить свой метод возвращает такой

__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);
    }
}