Я думаю, что я в основном понял, как писать делегаты c # для обратных вызовов, но это меня смущает.
Определение C ++ выглядит следующим образом:
typedef int (__stdcall* Callback)(
long lCode,
long lParamSize,
void* pParam
);
и мой подход C # будет:
unsafe delegate int CallbackDelegate (int lCode, int lParamSize, IntPtr pParam);
Хотя это кажется неправильным, потому что я получаю ошибку PInvokeStackInbalance, что означает, что мое определение делегата неверно.
Остальные параметры функции являются строками или целыми числами, что означает, что они не могут вызвать ошибку, и если я просто передам IntPtr.Zero вместо делегата (что будет означать, что я указываю на несуществующую функцию обратного вызова) Я получаю ошибку AccessViolation, которая также имеет смысл.
Что я делаю неправильно?
РЕДАКТИРОВАТЬ:
Полная функция C ++:
int
__stdcall
_Initialize (
const char* FileName,
Callback cbFunction,
int Code,
const char* Name,
unsigned int Option,
unsigned int Option2
);
Моя версия C #:
[DllImport("MyDll.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int _Initialize (string FileName, CallbackDelegate cbFunction, int Code, string Name, uint Options, uint Options2);
Функция (для тестирования) вызывается только из основной подпрограммы консольного приложения:
static void Main(string[] args)
{
CallbackDelegate del = new CallbackDelegate(onCallback);
Console.Write(_Initialize("SomeFile.dat", del, 1000, "", 0, 4));
Console.Read();
}
где onCallback
это:
static int onCallback(int lCode, int lParamSize, IntPtr pParam)
{
return 0;
}
Я получаю PInvokeStackInbalance
ошибка на линии, куда я звоню _Initialize
если я передам IntPtr.Zero
вместо делегата, и измените определение функции на IntPtr
вместо CallbackDelegate
тогда я получаю AccessViolationException
,
Я добавил ваш код в свой текущий проект, где я много взаимодействую с C # / C ++ в VS2012. И хотя я ненавижу использовать «это работало на моей машине», оно работало нормально для меня. Код, который я запустил, приведен ниже, чтобы проиллюстрировать, что я не внес и фундаментальные изменения.
Мое предложение состоит в том, чтобы вы построили новый собственный dll с функцией-заглушкой для _Initialize, как показано ниже, и посмотрите, работает ли он, когда вы можете контролировать обе стороны интерфейса. Если это работает, но настоящая dll не работает, это сводится либо к настройкам компилятора на нативной стороне, либо к ошибкам на нативной стороне, и это топает по стеку.
extern "C" {
typedef int (__stdcall* Callback)(long lCode,long lParamSize,void* pParam );
TRADITIONALDLL_API int __stdcall _Initialize (const char* FileName,Callback cbFunction, int Code,
const char* Name,unsigned int Option,unsigned int Option2)
{
cbFunction(0, 0, nullptr);
return 0;
}
}
На стороне C # я добавил объявления в мой класс интерфейса:
public delegate int CallbackDelegate(int lCode, int lParamSize, IntPtr pParam);
[DllImport("XXX.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int _Initialize(string FileName, CallbackDelegate cbFunction, int Code, string Name, uint Options, uint Options2);
А потом по-моему:
private int onCallback(int lCode, int lParamSize, IntPtr pParam)
{
return 0;
}
XXXInterface.CallbackDelegate del = new XXXInterface.CallbackDelegate(onCallback);
Console.Write(XXXInterface._Initialize("SomeFile.dat", del, 1000, "", 0, 4));
ЧИСТАЯ long
это 64 бита. C ++ long
может быть всего 32 бита. Проверьте с помощью компилятора C ++, который скомпилировал это определение относительно того, какой это размер long
с.
[UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)]
unsafe delegate int CallbackDelegate (int lCode, int lParamSize, IntPtr pParam);
Если .NET предполагает использование cdecl вместо stdcall, ваш стек наверняка будет храниться.