C 到 C# 回调在一段时间后引发异常
我有一个棘手的错误,我找不到。我正在从 C# 到我编写的本机 DLL 进行后期绑定。 后期绑定似乎工作正常。添加回调后问题就开始了。
回调定义如下(在 c 中)(在 DLL 中的全局范围内):
typedef void (*FinishedDelegate) (ProcessResult a_bResult);
typedef void (*DownloadStatusUpdateDelegate) (int a_iParametersDownloaded, int a_iTotalParameters);
typedef void (*DownloadFinishedDelegate) (char* a_sConfiguration_values, ProcessResult a_bResult);
DownloadStatusUpdateDelegate pfDownloadStatusUpdateDelegate = NULL;
DownloadFinishedDelegate pfDownloadFinishedDelegate = NULL;
该函数被导出:
PLUGIN_API BOOL DownloadConfigration(DownloadStatusUpdateDelegate a_pfStatusDelegate, DownloadFinishedDelegate a_pfFinishedDelegate);
这是本机函数实现:
DWORD WINAPI DownloadThreadFunc(void* a_pParam)
{
while (g_iParameterDownloaded < PARAMETER_COUNT)
{
if (IsEventSignaled(g_hAbortEvent))
{
CallDownloadFinished(PROCESS_ABORT);
return 0;
}
Sleep(STATUS_DELAY);
CallDownloadStatusUpdate();
g_iParameterDownloaded += STATUS_PARAMS_JUMP;
}
CallDownloadFinished(PROCESS_SUCCESS);
return 0;
}
PLUGIN_API BOOL DownloadConfigration(DownloadStatusUpdateDelegate a_pfStatusDelegate, DownloadFinishedDelegate a_pfFinishedDelegate)
{
if (IsEventSignaled(g_hInProcessEvent))
return false;
pfDownloadStatusUpdateDelegate = a_pfStatusDelegate;
pfDownloadFinishedDelegate = a_pfFinishedDelegate;
g_iParameterDownloaded = 0;
DWORD l_dwResult = WaitForSingleObject(g_hThreadsStructGuardian, INFINITE);
if (l_dwResult == WAIT_OBJECT_0)
{
g_ahThreads[PLUGIN_THREAD_DOWNLOAD] = CreateThread(NULL, 0, DownloadThreadFunc, 0, 0, NULL);
ReleaseMutex(g_hThreadsStructGuardian);
return true;
}
return false;
}
在托管端,该函数在此处调用:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DownloadStatusUpdateDelegate(int a_iParametersDownloaded, int a_iTotalParameters);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void DownloadFinishedDelegate_Native(StringBuilder a_sConfigurationValues, EPluginProcessResult a_eResult);
private void OnDownloadStatusUpdate(int a_iParametersDownloaded, int a_iTotalParameters)
{
if (DownloadStatusUpdate != null)
{
DownloadStatusUpdate(a_iParametersDownloaded, a_iTotalParameters);
}
}
private void OnDownloadFinished(StringBuilder a_sConfigurationValues, EPluginProcessResult a_eResult)
{
if (DownloadFinished != null)
{
DownloadFinished(a_sConfigurationValues.ToString(), a_eResult);
}
}
public bool DownloadConfiguration()
{
bool l_bResult = DLLDownloadConfigration(OnDownloadStatusUpdate, OnDownloadFinished);
return l_bResult;
}
奇怪的是 - 它工作一段时间。一段时间后,我收到“特权指令”异常,但当我降低 STATUS_DELAY 时,这种情况发生的次数就会减少。异常出现在 IsEventSignaled 函数中 - 但那里什么也没有。
下载线程同步到 c# GUI 线程以更新 GUI。
我已经在这个问题上花了太多时间了。这看起来像是一个经典的调用约定问题,但我彻底验证了它!
有什么想法吗?
I have a tricky bug I can't find. I'm doing late-binding on from C# to a native DLL I wrote.
The late-binding seem to work fine. The problems started after I added the callbacks.
The callbacks are defined as so (in c)(On a global scale in the DLL):
typedef void (*FinishedDelegate) (ProcessResult a_bResult);
typedef void (*DownloadStatusUpdateDelegate) (int a_iParametersDownloaded, int a_iTotalParameters);
typedef void (*DownloadFinishedDelegate) (char* a_sConfiguration_values, ProcessResult a_bResult);
DownloadStatusUpdateDelegate pfDownloadStatusUpdateDelegate = NULL;
DownloadFinishedDelegate pfDownloadFinishedDelegate = NULL;
This function is exported:
PLUGIN_API BOOL DownloadConfigration(DownloadStatusUpdateDelegate a_pfStatusDelegate, DownloadFinishedDelegate a_pfFinishedDelegate);
And this is the native function implementation:
DWORD WINAPI DownloadThreadFunc(void* a_pParam)
{
while (g_iParameterDownloaded < PARAMETER_COUNT)
{
if (IsEventSignaled(g_hAbortEvent))
{
CallDownloadFinished(PROCESS_ABORT);
return 0;
}
Sleep(STATUS_DELAY);
CallDownloadStatusUpdate();
g_iParameterDownloaded += STATUS_PARAMS_JUMP;
}
CallDownloadFinished(PROCESS_SUCCESS);
return 0;
}
PLUGIN_API BOOL DownloadConfigration(DownloadStatusUpdateDelegate a_pfStatusDelegate, DownloadFinishedDelegate a_pfFinishedDelegate)
{
if (IsEventSignaled(g_hInProcessEvent))
return false;
pfDownloadStatusUpdateDelegate = a_pfStatusDelegate;
pfDownloadFinishedDelegate = a_pfFinishedDelegate;
g_iParameterDownloaded = 0;
DWORD l_dwResult = WaitForSingleObject(g_hThreadsStructGuardian, INFINITE);
if (l_dwResult == WAIT_OBJECT_0)
{
g_ahThreads[PLUGIN_THREAD_DOWNLOAD] = CreateThread(NULL, 0, DownloadThreadFunc, 0, 0, NULL);
ReleaseMutex(g_hThreadsStructGuardian);
return true;
}
return false;
}
On the managed side, the function is called here:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DownloadStatusUpdateDelegate(int a_iParametersDownloaded, int a_iTotalParameters);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void DownloadFinishedDelegate_Native(StringBuilder a_sConfigurationValues, EPluginProcessResult a_eResult);
private void OnDownloadStatusUpdate(int a_iParametersDownloaded, int a_iTotalParameters)
{
if (DownloadStatusUpdate != null)
{
DownloadStatusUpdate(a_iParametersDownloaded, a_iTotalParameters);
}
}
private void OnDownloadFinished(StringBuilder a_sConfigurationValues, EPluginProcessResult a_eResult)
{
if (DownloadFinished != null)
{
DownloadFinished(a_sConfigurationValues.ToString(), a_eResult);
}
}
public bool DownloadConfiguration()
{
bool l_bResult = DLLDownloadConfigration(OnDownloadStatusUpdate, OnDownloadFinished);
return l_bResult;
}
The weird thing is - It works for a while. I get a "Privileged Instruction" exception after some time, but as I lower STATUS_DELAY, it happens less. The exception shows up at the IsEventSignaled function - But there's simply nothing there.
The download thread syncs to the c# GUI thread to update the GUI.
I've been on this problem for way too much time. It looks like a classic calling conventions problem, but I verified it thoroughly!
Any ideas?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
代码不是很清楚,但这可能是问题所在。这将创建两个委托对象,垃圾收集器运行后回调炸弹并删除对象。它无法跟踪对非托管代码的托管对象引用。您需要显式创建委托并将它们存储在类成员中,以便垃圾收集器始终至少看到一个引用。
The code isn't very clear, but this is the likely trouble spot. This creates two delegate objects, the callback bombs after the garbage collector runs and deletes the objects. It cannot track managed object references into unmanaged code. You'll need to create the delegates explicitly and store them in a class member so the garbage collector always sees at least one reference.
您尝试过使用 lambda 吗?如:
我对此的理论是它失败了,因为您的 OnDownloadStatusUpdate 和 OnDownloadFinished 方法不是静态的。底层 IL 期望“this”对象作为第一个不可见参数,但 C 方法在调用回调时不会传递它。
编辑:我认为我上面的回答者是正确的,但是任何人都可以阐明编组器实际上如何处理这个问题吗?
Have you tried using a lambda? As in:
My theory on this is that it is failing because your OnDownloadStatusUpdate and OnDownloadFinished methods aren't static. The underlying IL expects the 'this' object as the first invisible arg, but the C-method is not passing it when calling the callback.
EDIT: I think the answerer above me is correct, but can anyone shed some light on how the marshaller actually handles this?