Вызов функции обратного вызова основного потока из рабочего потока - PullRequest
2 голосов
/ 28 июля 2010

Сценарий: У меня есть c ++ DLL. В этой DLL я создал рабочий поток. В рабочем потоке у меня есть цикл, который ожидает ввода пользователя через аппаратное USB-устройство. Цикл заканчивается только тогда, когда пользовательский ввод на USB-устройстве соответствует некоторым критериям. Кроме того, мне необходимо в реальном времени отображать отзывы пользователей об использовании устройства USB для отображения на экране. Для обратной связи используется графический интерфейс Delphi.

Когда пользователь использует устройство USB, система Windows выполняет функцию обратного вызова. Эта функция обратного вызова записана в том же файле DLL C ++ и передана в качестве параметра в функцию инициализации USB-устройства.

Я использовал глобальную переменную в DLL в качестве флага, чтобы определить, когда этот цикл должен завершиться.

Я также загружаю эту библиотеку C ++ из библиотеки Delphi. Delphi DLL -> C ++ DLL Дисплей обратной связи взят из библиотеки Delphi.

По сути, проблема, с которой я сейчас сталкиваюсь, заключается в том, что функция ptr, funcptr, вообще не может быть вызвана. Там нет обратной связи в режиме реального времени на экране. Это функция в Delphi DLL. Это строка кода:

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

У кого-нибудь есть решение для этого?

Я новичок и ценю любые ответы вообще. Спасибо за помощь.

    //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");
    }

1 Ответ

2 голосов
/ 28 июля 2010

Во-первых, я бы не рекомендовал использовать переменные для связи между потоками.Для вашей цели используйте событие.

Ваша 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);
    }
}

Это заботится о битах init / shutdown / worker.А теперь самая сложная часть.

Во-первых, вы не можете манипулировать битами пользовательского интерфейса из вашего рабочего потока.Я не могу точно вспомнить, почему - это связано с владением очередью сообщений Windows, которая принадлежит первичному потоку.Если все, что вам нужно сделать, это отобразить что-то, что должно обновить, вы должны сделать следующее:

  • объявить очередь ваших выходных данных.это может быть просто массив с круговым управлением.что бы ни работало.
  • объявляет мьютекс для его защиты.необязательно, если ваша очередь уже поточно-ориентирована.
  • объявляет функцию get и процедуру put, которая проверяет мьютекс перед доступом.
  • объявляет пользовательские окна, даже если вы можете публиковать их в очереди сообщений Windows.(проверьте пользовательское окно сообщения в MSDN).и объявите обработчик для этого в вашем главном окне, который использует ваш get () и обновляет отображение.

Предполагая, что выше, остальная часть кода становится ... (Обратите внимание, что я непроделайте это в течение некоторого времени, чтобы имена и объявления могли немного отличаться, но принцип тот же)

Ваша основная программа:

// 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;

Ваша 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);
}

Я работал над всем этим по памяти, так что я уверен, что что-то не так.Надеюсь, вы все равно получите принципы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...