Передача объекта класса 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);
}
}