Рейтинг:
9
Richard Andrew x64
Вы не можете передать делегат неуправляемому коду так, как вы пытаетесь это сделать. Если вы собираетесь передать его как указатель функции внутри структуры, вы должны вызвать:
IntPtr NativeFunctionPointer = Marshal.GetFunctionPointerForDelegate(connected);
Затем проходить
NativeFunctionPointer
как указатель функции.
Причина в том, что управляемый делегат состоит не только из адреса функции. Он имеет код и данные, как и многие другие классы .NET, поэтому передача его адреса не то же самое, что передача точки входа его функции.
lewisv
Это имеет смысл, так что вот на что я его изменил.
Изменил объявление в структуре на
public struct DPcbs
{
public uint size;
public IntPtr connectedCB;
... more callbacks
}
Настройка структуры у меня есть (я сделал элементы переменными-членами так, чтобы GC не избавлялся от них)
m_c = new ConnectedCB(connected);
m_p = Marshal.GetFunctionPointerForDelegate(m_c);
m_events.connectedCB = m_p;
Он стреляет и работает, но та же проблема. Он взрывается непосредственно у подключенного делегата финиша.
Richard Andrew x64
Я в замешательстве по вашей терминологии. Вы говорите: "он стреляет и бежит", затем вы говорите: "он взрывается", "делегат заканчивает".
Возможно, если бы вы могли быть конкретнее. Где именно находится нить, когда она "взрывается"?" Это происходит до, во время или после выполнения функции обратного вызова?
Вы получаете сообщение об ошибке о том, что стек несбалансирован?
Вы прошли через код с помощью отладчика?
lewisv
Мне жаль, если это было немного запутанно. Я запустил его через отладчик.
private void connected(ref DocProcHandle hdl)
{
// this code works fine
}
<--- it crashes here after it leaves my callback method and before anything else in the c# application runs
Нет никакого сообщения об ошибке, программа просто "перестает отвечать", а затем спрашивает меня, хочу ли я отлаживать. Нет выхода из отладчика, Да, он откроет другую visual studio, но затем скажите мне, что он не может отлаживать.
Если я оставлю обратный вызов, приложение продолжит работу без каких-либо проблем. Но, конечно, мне нужно справиться с обратными вызовами.
Я предполагаю здесь, но похоже, что, устанавливая/вызывая функцию обратного вызова, я путаю память. Таким образом, приложение аварийно завершает работу вне приложения c# и внутри библиотеки dll, вызывающей делегат.
Я знаю, что dll работает, у меня есть приложение c, которое вызывает его и работает. Чтобы убедиться, что я ничего не пропустил, я в основном перестраиваю приложение c на c# и вызываю те же методы.
Спасибо
Richard Andrew x64
Если он выходит из строя после выхода из обратного вызова, это очень сильный индикатор того, что стек поврежден на родной стороне. Я бы рекомендовал убедиться, что CDECL-это правильное соглашение о вызове для вызова управляемого кода.
lewisv
Прошло уже 10 лет с тех пор, как я делал какую-либо реальную работу на c/c++.
Но для меня это похоже на cdecl.
#define CDECLCALL_CONV __cdecl
typedef void (CDECLCALL_CONV *ConnectedCB) (DPHandle hdl);
BPS_DP_API unsigned long CDECLCALL_CONV getDPDevices(DPHandle devices[], unsigned long* count);
Что вы подразумеваете под тем, что cdecl подходит для вызова управляемого кода "into"? Есть ли что-то еще, что мне нужно сделать в моем управляемом коде c#?
Richard Andrew x64
Извините. Я не заметил, что вы должным образом украсили делегата с CallingConvention.Атрибут Cdecl. Я был предполагая, что обратного вызова, возможно, требуется конвенции нарушением соглашения о стандартном. Как и вы, прошло уже некоторое время с тех пор, как я делал native/managed interop.
Как выглядит код, вызывающий обратный вызов?
lewisv
Я думаю, что он у меня есть.
Спасибо за помощь.
Так что ваш первый пост был хорош, и я думаю, что это была одна из проблем.
Вторая проблема была связана с дескриптором, который передается в каждую процедуру.
Когда я открыл соединение, я получил IntPtr обратно.
IntPtr m_ptr;
m_ptr = обертка.Открывать(...)
Я превратил это в структуру
m_handle= Маршал.PtrToStructure<docprochandle>(m_ptrp);
Я передавал эту ручку обратно в библиотеку dll.
обертка.DPSetCallBacks(арт m_handle, m_events Реф );
Но обертке нужен был не DocProcHandle, а IntPtr. Когда я заменил его, он прошел через приложение просто отлично.
обертка.DPSetCallBacks(m_ptr, ref m_events);
Может быть, он прекрасно работал с не-обратными вызовами, но с обратными вызовами, потому что он снова был помещен в стек и испортил стек, как вы упомянули.