tags:

views:

123

answers:

1

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.

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.

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

I am also loading this C++ DLL from a Delphi DLL. Delphi DLL -> C++ DLL The feedback display is from the 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:

(*(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");
    }
+2  A: 

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

Your 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.

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:

  • 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.

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:

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.

vij
I have added the code. User input is a USB sensor device.
Robogal