OK, below is an example, assuming you create a worker thread that will be running till the application exits. The user can pause and resume data retrieval and logging. No need to employ APC for this, as noted in the comments, instead synchronization is used to control the worker thread. The synchronization object I'm proposing is the manual-reset Event. It acts as a "synchronization boolean", set and cleared by the main (UI) thread - the worker thread waits on it if it's non-signaled.
1. Variables and Initialization
BOOL bInitOK = FALSE; // Worker Thread Successfully Initialized
BOOL bChkRTD = FALSE; // Retrieval & Logging of RTD Enabled
HANDLE m_hEvt = CreateEvent(NULL, TRUE, FALSE, NULL); // Controlling Retrieval & Logging of RTD
CWinThread *m_pLogPointThread = AfxBeginThread(AddLogPointFN, this); // Worker Thread - Created once
Please note that AddLogPointFN
here is a AFX_THREADPROC
function, not a class. This creates a thread without a message-queue. Also you don't need to define and use any thread-local data (ie data instantiated per thread), as the thread is instantiated only once, ie your data are essentially "global" (although they may be contained into another single-instance class, like the main window or view). Alternatively you could create your thread using the CreateThread()
function and a LPTHREAD_START_ROUTINE
- this one is _stdcall
. The event controls whether the worker thread looks for data; it kind of "duplicates" the bChkRTD
variable, which is set by the user - an event is a waitable object, while a boolean variable is not.
2. Thread Procedure
UINT AddLogPointFN(LPVOID lParam)
{
// CLogView* variable - For convenience
CLogView* pView = (CLogView*)lParam; // Assuming lParam is CLogView
// Add one-time, resource-heavy initialization here, eg connections
.
.
bool bInit = InitConnections();
if (!bInit)
{
// Initialization Failed - Tell UI to display an error-message or exit
pView->PostMessage(USERMESSAGE_ALPNOTIFY, 1);
return 1;
}
// Initialization Successful - Tell UI to enable RTD request menus or buttons
pView->PostMessage(USERMESSAGE_ALPNOTIFY, 2);
// Wait if m_hEvt is non-signaled
while(WaitForSingleObject(pView->m_hEvt,INFINITE)==WAIT_OBJECT_0)
{
// Retrieve RTD
.
.
// Data retrieved, tell UI to update
pView->PostMessage(USERMESSAGE_ALPNOTIFY, 3, (LPARAM)p_Data);
}
return 0;
}
RTD retrieval is controlled by waiting on the event object. The event is manual-reset, ie the wait function won't reset it - it is set/reset by the UI thread only. The thread doesn't modify the (global) bInitOK
variable directly (this would require synchronized access) instead it notifies the UI thread to set it.
3. Main Thread Items and Routines
// Start/Stop Command and UI Update
void CLogView::OnStartStoplogger()
{
bChkRTD = !bChkRTD; // Check-box behavior, see also ON_UPDATE_COMMAND_UI
if (bChkRTD) SetEvent(m_hEvt);
else ResetEvent(m_hEvt);
}
void CLogView::OnUpdateStartStoplogger(CCmdUI *pCmdUI)
{
pCmdUI->Enable(bInitOK); // Enabled if Initialization Successful
pCmdUI->SetCheck(bChkRTD); // Checked if RTD Retrieval Enabled
}
// USERMESSAGE_ALPNOTIFY Handler
afx_msg LRESULT CLogView::OnALPNotify(WPARAM wParam, LPARAM lParam)
{
switch(wParam)
{
case 1: // Initialization Failed - Show some message or exit
.
.
break;
case 2: // Initialization Successful - Enable Start/Stop UI item
bInitOK = TRUE;
break;
case 3: // New RTD Available - Display the data
// E.g. copy the date returned from the worker thread to some local structure
CopyMyData(m_pMyData, (MY_DATA*)lParam);
GlobalFree((HGLOBAL)lParam)
UpdateData(FALSE);
break;
}
}