windows 从工作线程调用主线程回调函数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3350375/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 14:54:48  来源:igfitidea点击:

Call main thread callback function from worker thread

c++windows

提问by Robogal

Scenario: I have a c++ DLL. In this DLL, I created a worker thread. In the worker thread, I have a loop that waits for user input via a USB hardware device. The loop ends only when the user input on USB device meets some criteria. Additionally, I need to feedback the user usage feedback of the USB device real-time to display on screen. It is using a Delphi GUI for feedback.

场景:我有一个 C++ DLL。在这个 DLL 中,我创建了一个工作线程。在工作线程中,我有一个循环通过 USB 硬件设备等待用户输入。只有当用户在 USB 设备上的输入满足某些条件时,循环才会结束。另外,我需要实时反馈USB设备的用户使用反馈以显示在屏幕上。它使用 Delphi GUI 进行反馈。

When user uses the USB device, a callback function will be made by Windows system. This callback function is written in the same C++ DLL file and passed in as a parameter in an Initialization function of the USB device.

当用户使用 USB 设备时,Windows 系统会进行回调函数。这个回调函数写在同一个 C++ DLL 文件中,并作为参数传入 USB 设备的初始化函数中。

I used a global variable in the DLL as a flag to determine when this loop must exit.

我在 DLL 中使用了一个全局变量作为标志来确定该循环何时必须退出。

I am also loading this C++ DLL from a Delphi DLL. Delphi DLL -> C++ DLL The feedback display is from the Delphi DLL.

我也在从 Delphi DLL 加载这个 C++ DLL。Delphi DLL -> C++ DLL 反馈显示来自 Delphi DLL。

Basically, the problem I face now is that the function ptr, funcptr, cannot be called at all. There is no real-time feedback on screen. This is a function in Delphi DLL. This is the line of code:

基本上,我现在面临的问题是函数ptr,funcptr,根本无法调用。屏幕上没有实时反馈。这是 Delphi DLL 中的一个函数。这是代码行:

(*(reinterprete_cast<FUNCPTR>(funcPtr)))("this is the feedback msg displayed on Delphi GUI");

Does anyone has a solution for this?

有没有人对此有解决方案?

I'm a novice and appreciate any answers at all. Thanks for the help.

我是新手,非常感谢任何答案。谢谢您的帮助。

    //Global variable
    BOOL flag = TRUE;

    //A function type in Delphi calling app
    typedef void (__stdcall *FUNCPTR)(PCHAR);

    //Functions start here.....
    DWORD WINAPI ThreadProc(LPVOID lpParameter)
    { 
        do {} while (flag);
    }

    function_1st_CalledFromDelphiDLL(FUNCPTR funcPtr)
    {
        Initialize_USBDevice(handleUSBDeviceEvent_callback, funcPtr);
    }

    function_2nd_CalledFromDelphiDLL()
    {
        DWORD threadID;
        HANDLE hWorkerThread;

        hWorkerThread = CreateThread(NULL,0,ThreadProc, 0, 0 , &threadID);

        if (hWorkerThread!=NULL)
        {
            WaitForSingleObject(hWorkerThread, 30000);
        }
    }

    //This is the callback function, called by Windows system when user meddles with the USB device
    handleUSBDeviceEvent_callback(void *funcPtr)
    {
        flag = FALSE; //so loop in ThreadProc can exit
       //The following code cannot be executed at all. Even when i Try MessageBox( NULL,L"msg",NULL,NULL), the message box doesn't popup too. But, I can write something to a filestream here.
        (*(reinterprete_cast<FUNCPTR>(funcPtr)))("this is the feedback msg displayed on Delphi GUI");
    }

采纳答案by vij

First off, I would not recommend using variables to communicate between threads. For your purpose, use an event.

首先,我不建议使用变量在线程之间进行通信。出于您的目的,请使用事件。

Your DLL:

你的DLL:

HANDLE _exitNow = NULL;
HANDLE _queueLock = NULL; // for part 2 below

// call this from your main at start
bool DLL_Initialize()
{
    _exitNow = CreateEvent(...);
    _queueLock = CreateMutex(...);
    ... initialize your device, add the callback ...
}

// call this from your main at exit
void DLL_Shutdown()
{
    SetEvent(_exitNow);
}

// your worker thread
void DLL_Worker()
{
    // use options so WaitFor...() does not block
    int result = WaitForSingleObject(_exitNow, ...);
    if(result indicates _exitNow was fired)
    {
        CloseHandle(_exitNow);
        ... shutdown sequence, unhook your callback, deinit your device ...
        CloseHandle(_queueLock);
    }
}

This takes care of the init/shutdown/worker bits. And now the hard part.

这会处理 init/shutdown/worker 位。现在是困难的部分。

First, you cannot manipulate the UI bits from your worker thread. I can't remember exactly why -- it has to do with ownership of the windows message queue, which is owned by the primary thread. If all you have to do is have something displayed that should update, you should do the following:

首先,您不能从您的工作线程操作 UI 位。我不记得确切原因——它与 Windows 消息队列的所有权有关,它由主线程拥有。如果您所要做的就是显示一些应该更新的内容,您应该执行以下操作:

  • declare a queue of your output data. this could just be a circularly managed array. whatever works.
  • declare a mutex to guard it. optional if your queue is already thread-safe.
  • declare get function and a put procedure that checks for mutex before access.
  • declare a custom windows even you could post in the windows message queue. (check custom window message in msdn). and declare a handler for that in your main window which uses your get() and updates the display.
  • 声明一个输出数据队列。这可能只是一个循环管理的数组。无论什么工作。
  • 声明一个互斥锁来保护它。如果您的队列已经是线程安全的,则可选。
  • 声明 get 函数和一个在访问前检查互斥锁的 put 过程。
  • 声明一个自定义窗口,即使您可以在 windows 消息队列中发布。(检查 msdn 中的自定义窗口消息)。并在您的主窗口中声明一个处理程序,它使用您的 get() 并更新显示。

Assuming the above, the rest of the code becomes... (Note that I haven't done this for some time so the names and declarations might be slightly off, but the principle is the same)

假设如上,剩下的代码就变成了...(注意我已经有一段时间没有这样做了,所以名称和声明可能会稍微偏离,但原理是一样的)

Your main program:

你的主程序:

// double check how to do this exactly. I haven't done this in a long time. 
const CUSTOM_WINDOW_EVENT = WINDOWS_CUSTOM_MESSAGE + [SOMETHING]; 

// check for proper syntax
function Form.CustomHandler: Integer; handles CUSTOM_WINDOW_EVENT; 
var
  S: String;
begin
  S := GetDataFromDLL();
  ... update display based on S ...  
end;

Your DLL:

你的DLL:

const CUSTOM_WINDOW_EVENT = WINDOWS_CUSTOM_MESSAGE + [SOMETHING]; 
TQueue queue; // find a suitable type. std::queue<> works fine

// delphi will call this
<String-type> DLL_GetStatus()
{
    ... wait on mutex using WaitForSingleObject() ...
    ... read one entry from queue ...
    ... release mutex ...
    ... return it ...
}

void PutStatus(String statusData)
{
    ... wait on mutex using WaitForSingleObject() ...
    ... write to queue ...
    ... release mutex ...
     ... push the custom message to the windows message queue, use PostMessage() IIRC ...
}

<whatever> handleUSBDeviceEvent_callback(void *funcPtr)
{
    ... read device, compose status data ...
    PutStats(statusData);
}

I worked on all this from memory, so I'm sure there'd be something wrong. Hopefully, you get the principles anyway.

我是凭记忆完成所有这些工作的,所以我确定肯定会有问题。希望无论如何你都能掌握这些原则。