I'm developing an ActiveX video player. It is in-process component in a DLL. I'm using Visual Studio 2010.
I need it to have a separate thread, which would start once, when the component is loaded, create Direct3D9 object and Direct3D9 device, then stop on the component unloading and destroy these objects. While the component is running I'd like this thread to periodically call TestCooperativeLevel and reset D3D device if needed.
I'm doing this since the client application can create several instances of my player, but it is strongly recommended to have only one instance of D3D9 object and device.
I've declared a class with static methods and members, whose constructor calls _beginthreadex() and starts the thread.
Here are the code excerpts (WITH ERRORS).
// .h
class D3DManager {
static mutex d3; // mutex is my own class, just a wrapper around CriticalSection
static LPDIRECT3D9 g_d3d;
static LPDIRECT3DDEVICE9 g_d3ddev;
static D3DPRESENT_PARAMETERS g_d3dpp;
static int g_d3d_counter;
static HANDLE hthread;
static HANDLE exitev;
static bool exit_flag;
static mutex exit_mutex;
public:
D3DManager();
~D3DManager();
static unsigned int __stdcall thread(void *);
static void stop(void) {
exit_mutex.lock();
exit_flag = true;
SetEvent(exitev);
exit_mutex.unlock(); }
static bool exit_signal(void) {
exit_mutex.lock();
bool result = exit_flag;
exit_mutex.unlock();
return exit_flag; }
static void CreateD3DDevice(LPDIRECT3D9& d3dobj, LPDIRECT3DDEVICE9& d3ddev);
static void DestroyD3DDevice(void);
static void GetSwapChain(HWND hwnd, LPDIRECT3DSWAPCHAIN9& chain);
static void release_d3d(void);
static void LockDevice(void) { d3.lock(); };
static void UnlockDevice(void) { d3.unlock(); };
};
//.cpp
mutex D3DManager::d3;
LPDIRECT3D9 D3DManager::g_d3d = NULL;
LPDIRECT3DDEVICE9 D3DManager::g_d3ddev = NULL;
D3DPRESENT_PARAMETERS D3DManager::g_d3dpp;
int D3DManager::g_d3d_counter = 0;
HANDLE D3DManager::hthread;
HANDLE D3DManager::exitev;
bool D3DManager::exit_flag = false;
mutex D3DManager::exit_mutex;
// this variable will be single and shared by all activeX instances
static D3DManager d3dm;
D3DManager::D3DManager()
{
exitev = CreateEvent(NULL, true, false, NULL);
hthread = (HANDLE)_beginthreadex(NULL, 0, thread, NULL, 0, NULL);
_OutputDebugString("D3DManager: thread created handle %x\n", hthread); // my wrapper around OutputDebugString
}
D3DManager::~D3DManager()
{
stop();
HRESULT hr = WaitForSingleObject(hthread, 1000);
if (hr == WAIT_ABANDONED) {
TerminateThread(hthread, 0);
release_d3d();
}
CloseHandle(exitev);
}
unsigned int __stdcall D3DManager::thread(void *)
{
create_d3d9();
while(!exit_signal()) {
WaitForSignleObject(exitev, 500);
d3.lock();
HRESULT hr = g_d3ddev->TestCooperativeLevel();
switch(hr) {
case S_OK:
break;
case D3DERR_DEVICENOTRESET :
// Fill DISPLAYPARAMETERS
g_d3ddev->Reset();
break;
default:
break;
}
d3.unlock();
}
///////// This text is never seen
OutputDebugString("D3dManagert exit from while loop\n");
////////
release_d3d();
_endthreadex(0);
return 0;
}
My component is embedded in the WindowsForms form, writtent in C#.
The problem is when I close the form, the thread is terminated inside while loop and never gets to the code after it. I've never seen the text from OutputDebugString, the release_d3d() is also never called, and I see a lot of messages from d3d debug about memory leak. If I set a breakpoint, it is never hit.
All I see is the message:
The thread 'Win32 Thread' (0x1044) has exited with code 0 (0x0)
When I set up a breakpoint in the destructor, I get it hit, but after the messages about video memory leaks.
I've also enabled debug break on C++ Exceptions and Win32 Exception in the Studio, but none were triggered.
Update.
Have read in the MSDN that all threads are terminated, when any of them calls exit, _exit, or abort or ExitProcess and tried stting in the constructor atexit handler:
D3DManager::D3DManager()
{
exitev = CreateEvent(NULL, true, false, NULL);
hthread = (HANDLE)_beginthreadex(NULL, 0, thread, NULL, 0, NULL);
_OutputDebugString("D3DManager: thread created handle %x\n", hthread);
atexit(&release_d3d);
}
Still no luck. release_d3d is called after I've got messages about video memory leak. Moreover I've got exception fault.
Update 2.
Here is the edited code
// .h
class D3DManager {
static mutex d3; // mutex is my own class, just a wrapper around CriticalSection
static LPDIRECT3D9 g_d3d;
static LPDIRECT3DDEVICE9 g_d3ddev;
static D3DPRESENT_PARAMETERS g_d3dpp;
static int g_d3d_counter;
static HANDLE hthread;
static HANDLE exitev;
public:
D3DManager();
~D3DManager();
static unsigned int __stdcall thread(void *);
static void stop(void) { SetEvent(exitev); }
static void CreateD3DDevice(LPDIRECT3D9& d3dobj, LPDIRECT3DDEVICE9& d3ddev);
static void DestroyD3DDevice(void);
static void GetSwapChain(HWND hwnd, LPDIRECT3DSWAPCHAIN9& chain);
static void release_d3d(void);
static void LockDevice(void) { d3.lock(); };
static void UnlockDevice(void) { d3.unlock(); };
};
//.cpp
mutex D3DManager::d3;
LPDIRECT3D9 D3DManager::g_d3d = NULL;
LPDIRECT3DDEVICE9 D3DManager::g_d3ddev = NULL;
D3DPRESENT_PARAMETERS D3DManager::g_d3dpp;
int D3DManager::g_d3d_counter = 0;
HANDLE D3DManager::hthread;
HANDLE D3DManager::exitev;
// this variable will be single and shared by all activeX instances
static D3DManager d3dm;
D3DManager::D3DManager()
{
exitev = CreateEvent(NULL, true, false, NULL);
hthread = (HANDLE)_beginthreadex(NULL, 0, thread, NULL, 0, NULL);
_OutputDebugString("D3DManager: thread created handle %x\n", hthread); // my wrapper around OutputDebugString
}
D3DManager::~D3DManager()
{
stop();
HRESULT hr = WaitForSingleObject(hthread, 1000);
if (hr == WAIT_TIMEOUT) {
TerminateThread(hthread, 0);
release_d3d();
}
CloseHandle(exitev);
}
unsigned int __stdcall D3DManager::thread(void *)
{
create_d3d9();
while(WAIT_TIMEOUT == WaitForSingleObject(exitev, 500)) {
d3.lock();
HRESULT hr = g_d3ddev->TestCooperativeLevel();
switch(hr) {
case S_OK:
break;
case D3DERR_DEVICENOTRESET :
// Fill DISPLAYPARAMETERS
g_d3ddev->Reset();
break;
default:
break;
}
d3.unlock();
}
///////// This text is never seen
OutputDebugString("D3dManagert exit from while loop\n");
////////
release_d3d();
_endthreadex(0);
return 0;
}
Why wait on the stop object and then, if signaled, still perform the body of the code? Try
while(WAIT_TIMEOUT==WaitForSingleObject(exitev, 500){
..
}
also, I'm not sure what all that !exit_signal() and exit_mutex is for? Why do you need a mutex, or, for that matter, an exit boolean when you already have an event to signal? Is there some more code that signals the event for some other reason than stopping? I notice that you have typo'd the WFSO - 'WaitForSignleObject', so you have not posted your real code.
Not sue about 'if (hr == WAIT_ABANDONED)' either. I hardly ever wait for threads to terminate and so I don't know for sure if/why that return is made when waiting on a thread handle.
I see that your exit_signal() copies the value but does not return it. There can be chances that the variable exit_flag is changed after you get out of the synchronized block of code and your exit_signal() returns false.
Related
I need a thread timer for a plugin. The code executes well. The problem is when closing the plugin must close the created thread.
When the thread closes, the whole program closes. How can I close the safe plugin without closing the whole parent program.
class MYCLASS {
public:
.............
static DWORD WINAPI threadProc(LPVOID innerThread) {
MYCLASS *pThis = reinterpret_cast<MYCLASS*>(innerThread);
if (pThis->process_on) {
// EXECUTE
}
Sleep(100);
}
exit(0);
}
MYCLASS::OnClose();
.............
private:
bool process_on;
HANDLE hThread; DWORD ThreadID;
.............
hThread=CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)threadProc, (void*)this, 0, &ThreadID);
..............
void MYCLASS::OnClose() {
thread_process=false;
DWORD dwExitCode;
GetExitCodeThread(hThread, &dwExitCode);
ExitThread(dwExitCode);
}
I have created a DLL that starts up a thread which polls a usb device, when the DLL gets a message from the USB device it calls a callback function.
Using this dll in code consists of creating an instance of the class from the DLL, then calling a method of the said class to set a callback function, then starting the thread so that a callback happens every time the USB device does something, the thread works, the callback happens (it is a keypad device so it just sends keypresses in the form of a LPCSTR) but when I pass this keypress value to an internal function it causes an access violation. Where as anything else calling this function with the same values works just fine.
this is the line that causes the access violation
InvokeHelper(0x60030024, DISPATCH_METHOD, VT_VARIANT, (void*)&result, parms,
num, TheStr);
containing function:
VARIANT C_BAXScreens::WriteToTextBox(short num, LPCTSTR TheStr)
{
VARIANT result;
static BYTE parms[] =
VTS_I2 VTS_BSTR;
InvokeHelper(0x60030024, DISPATCH_METHOD, VT_VARIANT, (void*)&result, parms,
num, TheStr);
return result;
}
DLL set callback:
void WINAPI ThalesEPPv6::SetGoodKeyPadPressCallback(OnGoodKeyPadPressCallback func, LPVOID dialog) { OnGoodKeyPadPress = func;}
DLL start thread:
void ThalesEPPv6::startMessageThread()
{
OnCheckResponseOK("Message Thread Started.");
stopThread = false;
if(!connected)reconnect(this);
HANDLE hThread;
unsigned threadID;
hThread = (HANDLE)_beginthreadex( NULL, 0, messageThread,this, 0, &threadID );
}
DLL message Thread:
unsigned int _stdcall ThalesEPPv6::messageThread(void* links)
{
ThalesEPPv6* link = (ThalesEPPv6*)links;
while(1)
{
link->send_Message(POLLIN,ENQ,MESSAGE_NOCOMMS);
link->send_Message(POLLIN,MSG,MESSAGE_NOCOMMS);
link->send_Message(POLLIN,EOT,MESSAGE_NOCOMMS);
//MESSAGE REPLY
link->send_Message(POLLIN,ACK,MESSAGE);
link->send_Message(POLLIN,ACK,MESSAGE_NOCOMMS);
Sleep(10);
if(stopThread)break;
}
// close USB handle
usb_close(usb_handle);
return 0;
}
I am developing an application with MFC.
The UI thread launch a worker thread and stop it when the app is closing. The issue is that the app is crashing each time it tries to delete the thread.
here is the code :
First the thread class and its implementation :
class FileThread : public CWinThread
{
public:
static FileThread* CreateWorkerThread(LPVOID params, UINT priority, UINT flags);
void InitThread();
void StopThread();
inline HANDLE GetStopHandle() const { return m_stopThread; }
inline HANDLE GetWaitHandle() const { return m_waitThread; }
private:
HANDLE m_stopThread;
HANDLE m_waitThread;
FileThread(): m_stopThread(NULL), m_waitThread(NULL) { }
static UINT MyThreadProc(LPVOID pParam);
};
FileThread* FileThread::CreateWorkerThread(LPVOID params, UINT priority, UINT flags)
{
return (FileThread*) AfxBeginThread(FileThread::MyThreadProc, params, priority, 0, flags);
}
void FileThread::InitThread()
{
m_stopThread = CreateEvent(0, TRUE, FALSE, 0);
m_waitThread = CreateEvent(0, TRUE, FALSE, 0);
}
void FileThread::StopThread()
{
::SetEvent(m_stopThread);
::WaitForSingleObject(m_waitThread, INFINITE);
::CloseHandle(m_stopThread);
::CloseHandle(m_waitThread);
}
UINT FileThread::MyThreadProc(LPVOID pParam)
{
ThreadData* pLink = (ThreadData*)pParam;
BOOL continueProcess = TRUE;
int returnCode = EXITCODE_SUCCESS;
while (continueProcess)
{
if(::WaitForSingleObject(pLink->pMe->GetStopHandle(), 0) == WAIT_OBJECT_0)
{
::SetEvent(pLink->pMe->GetWaitHandle());
continueProcess = FALSE;
}
// the thread is looking for some files...
}
delete pLink; // it was allocated from the UI thread
return returnCode;
}
Then, where I start the thread:
ThreadData * td = new ThreadData();
m_myFileThread = FileThread::CreateWorkerThread((LPVOID)td, THREAD_PRIORITY_LOWEST, CREATE_SUSPENDED);
td->pMe = m_myFileThread;
m_myFileThread->m_bAutoDelete = FALSE;
m_myFileThread->InitThread();
m_myFileThread->ResumeThread();
Finally, the stop (and the crash):
DWORD exitCode;
if (m_myFileThread != NULL && GetExitCodeThread(m_myFileThread->m_hThread, &exitCode) && (exitCode == STILL_ACTIVE))
{
m_myFileThread->StopThread();
if(::WaitForSingleObject(m_myFileThread->m_hThread, 5000) == WAIT_TIMEOUT)
{
TerminateThread(m_myFileThread->m_hThread, EXITCODE_ABORT);
}
}
if (m_myFileThread != NULL)
{
delete m_myFileThread; // => CRASH
}
It seems I try to delete something already deleted and end up with a heap corruption. I have try to set the m_bAutoDelete to TRUE and not delete the thread myself by I got the same crash (while the program was trying to call AfxEndThread).
The thread terminate its thread proc and return the exit code.
It looks to me like there is a problem here:
FileThread* FileThread::CreateWorkerThread(LPVOID params, UINT priority,
UINT flags)
{
return (FileThread*) AfxBeginThread(FileThread::MyThreadProc, params,
priority, 0, flags);
}
AfxBeginThread returns a CWinthread*, so just casting this to a derived class of your own does not make it an instance of that derived class. I'm surprised it works at all.
Rather than deriving FileThread from CWinThread, it might be better to hold a CWinthread* member variable inside your wrapper class and expose the thread handle via an accessor if necessary.
I am coding a programming to watch folder. I use FileWatch.h library. This is my FileWatch.h
#ifndef FILEWATCH_H
#define FILEWATCH_H
class FileChangeObserver
{
public:
virtual ~FileChangeObserver()
{
}
virtual void OnFileChanged() = 0;
};
// information concerning a directory being watched
class FileWatcher
{
public:
// Watching file modifications using a loop
void Init(LPCTSTR filefullpath);
bool CheckForChanges(DWORD waittime=0);
// Watching file modification via a thread
void StartWatchThread();
bool IsThreadRunning();
void SynchronousAbort();
FileWatcher(FileChangeObserver *observer) : hDir(NULL), curBuffer(0),
filePath(NULL), hWatchingThread(NULL), observer(observer)
{
ZeroMemory(&this->overl, sizeof(this->overl));
// create the event used to abort the "watching" thread
hEvtStopWatching = CreateEvent(NULL, TRUE, FALSE, NULL);
}
FileWatcher()
{
}
~FileWatcher()
{
SynchronousAbort();
delete observer;
free(filePath);
CloseHandle(hEvtStopWatching);
}
public:
HANDLE hDir; // handle of the directory to watch
FileChangeObserver *observer; // function called when a file change is detected
TCHAR * filePath; // path to the file watched
FILE_NOTIFY_INFORMATION buffer[2][512];
// a double buffer where the Windows API ReadDirectory will store the list
// of files that have been modified.
int curBuffer; // current buffer used (alternate between 0 and 1)
bool NotifyChange();
public:
// fields for use by the WathingThread
OVERLAPPED overl; // object used for asynchronous API calls
HANDLE hWatchingThread; // handle of the watching thread
HANDLE hEvtStopWatching; // this event is fired when the watching thread needs to be aborted
};
static DWORD WINAPI WatchingThread(void *param);
#endif
This is my FileWatch.cpp
#include "stdafx.h"
#include "FileWatch.h"
#include "assert.h"
#if _MSC_VER > 1600
extern "C" {
WINBASEAPI BOOL WINAPI
GetOverlappedResult(_In_ HANDLE hFile, _In_ LPOVERLAPPED lpOverlapped, _Out_ LPDWORD lpNumberOfBytesTransferred, _In_ BOOL bWait);
}
#endif
bool FileWatcher::IsThreadRunning()
{
return hWatchingThread && (WaitForSingleObject(hWatchingThread, 0) == WAIT_TIMEOUT);
}
// Ask for the thread to stop and waith until it ends
void FileWatcher::SynchronousAbort()
{
SetEvent(hEvtStopWatching);
if (hWatchingThread)
{
WaitForSingleObject(hWatchingThread, INFINITE);
CloseHandle(hWatchingThread);
Sleep(500);
hWatchingThread = NULL;
}
CloseHandle(overl.hEvent);
overl.hEvent = NULL;
CloseHandle(hDir);
hDir = NULL;
}
// Start watching a file for changes
void FileWatcher::StartWatchThread()
{
// if the thread already exists then stop it
if (IsThreadRunning())
SynchronousAbort();
assert(hDir);
if (!hDir)
{
return;
}
// reset the hEvtStopWatching event so that it can be set if
// some thread requires the watching thread to stop
ResetEvent(hEvtStopWatching);
DWORD watchingthreadID;
hWatchingThread = CreateThread(NULL, 0, WatchingThread, this, 0, &watchingthreadID);
}
void FileWatcher::Init(const TCHAR* fileFullPath)
{
// if the thread already exists then stop it
if (IsThreadRunning())
SynchronousAbort();
// str::ReplacePtr(&filePath, fileFullPath);
//TCHAR *dirPath = path::GetDir(filePath);
hDir = CreateFile(
L"C:\\", // pointer to the directory containing the tex files
FILE_LIST_DIRECTORY, // access (read-write) mode
FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE, // share mode
NULL, // security descriptor
OPEN_EXISTING, // how to create
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED , // file attributes
NULL); // file with attributes to copy
// free(dirPath);
ZeroMemory(&overl, sizeof(overl));
ZeroMemory(buffer, sizeof(buffer));
overl.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
// watch the directory
ReadDirectoryChangesW(
hDir, /* handle to directory */
&buffer[curBuffer], /* read results buffer */
sizeof(buffer[curBuffer]), /* length of buffer */
FALSE, /* monitoring option */
//FILE_NOTIFY_CHANGE_CREATION|
FILE_NOTIFY_CHANGE_LAST_WRITE, /* filter conditions */
NULL, /* bytes returned */
&overl, /* overlapped buffer */
NULL); /* completion routine */
}
// Thread responsible of watching the directory containg the file to be watched for modifications
DWORD WINAPI WatchingThread(void *param)
{
//qDebug()<<"in WatchingThread";
FileWatcher *fw = (FileWatcher *)param;
HANDLE hp[2] = { fw->hEvtStopWatching, fw->overl.hEvent };
for (;;)
{
DWORD dwObj = WaitForMultipleObjects((sizeof(hp)/(sizeof(hp[0])))
, hp, FALSE, INFINITE);
if (dwObj == WAIT_OBJECT_0) // the user asked to quit the program
{
//qDebug()<<"in WatchingThread the user asked to quit the program";
//exit(-1);
break;
}
if (dwObj != WAIT_OBJECT_0 + 1)
{
// BUG!
//assert(0);
// qDebug()<<"dwObj "<<dwObj<<" last error "<<GetLastError();
break;
}
//qDebug()<<"WatchingThread fw->NotifyChange() ";
//if (fw->wakeup)
fw->NotifyChange();
}
return 0;
}
// Call ReadDirectoryChangesW to check if the file has changed since the last call.
bool FileWatcher::CheckForChanges(DWORD waittime)
{
if (!overl.hEvent)
{
return false;
}
DWORD dwObj = WaitForSingleObject(overl.hEvent, waittime);
if (dwObj != WAIT_OBJECT_0)
{
return false;
}
return NotifyChange();
}
// Call the ReadDirectory API and determine if the file being watched has been modified since the last call.
// Returns true if it is the case.
bool FileWatcher::NotifyChange()
{
//qDebug()<<"in NotifyChange";
DWORD dwNumberbytes;
GetOverlappedResult(hDir, &overl, &dwNumberbytes, FALSE);
FILE_NOTIFY_INFORMATION *pFileNotify = (FILE_NOTIFY_INFORMATION *)buffer[curBuffer];
// Switch the 2 buffers
curBuffer = (curBuffer + 1) % (sizeof(buffer)/(sizeof(buffer[0])));
SecureZeroMemory(buffer[curBuffer], sizeof(buffer[curBuffer]));
// start a new asynchronous call to ReadDirectory in the alternate buffer
ReadDirectoryChangesW(
hDir, /* handle to directory */
&buffer[curBuffer], /* read results buffer */
sizeof(buffer[curBuffer]), /* length of buffer */
TRUE, /* monitoring option */
FILE_NOTIFY_CHANGE_FILE_NAME |
FILE_NOTIFY_CHANGE_DIR_NAME |
FILE_NOTIFY_CHANGE_ATTRIBUTES |
FILE_NOTIFY_CHANGE_SIZE |
FILE_NOTIFY_CHANGE_LAST_WRITE |
FILE_NOTIFY_CHANGE_LAST_ACCESS |
FILE_NOTIFY_CHANGE_CREATION |
FILE_NOTIFY_CHANGE_SECURITY,
//FILE_NOTIFY_CHANGE_LAST_WRITE, /* filter conditions */
NULL, /* bytes returned */
&overl, /* overlapped buffer */
NULL); /* completion routine */
// Note: the ReadDirectoryChangesW API fills the buffer with WCHAR strings.
for (;;)
{
if (pFileNotify->Action == FILE_ACTION_ADDED)
{
//qDebug()<<"in NotifyChange if ";
char szAction[42];
char szFilename[MAX_PATH] ;
memset(szFilename,'\0',sizeof( szFilename));
strcpy(szAction,"added");
wcstombs( szFilename, pFileNotify->FileName, MAX_PATH);
if(observer)
observer->OnFileChanged();
return true;
//OnFileChanged(szFilename,szAction);
// qDebug()<<"in NotifyChange after OnFileChanged ";
}
// step to the next entry if there is one
if (!pFileNotify->NextEntryOffset)
{
return false;
}
pFileNotify = (FILE_NOTIFY_INFORMATION *)((PBYTE)pFileNotify + pFileNotify->NextEntryOffset);
}
pFileNotify=NULL;
return true;
}
In main program, I have:
case IDM_ABOUT:
//DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
{
FileWatcher *cWatcher = new FileWatcher();
cWatcher->Init(L"AB");
cWatcher->NotifyChange();
break;
}
I have a error messeage that: Access violation reading location 0xba2f1498.
What's solution?
You have a default constructor that leaves all your variables uninitialized.
FileWatcher()
{
}
Which you use here.
FileWatcher *cWatcher = new FileWatcher();
cWatcher->Init(L"AB");
Init also leaves several variables uninitialized, such as curBuffer in this line.
&buffer[curBuffer], /* read results buffer */
That is likely why you are getting Access violation reading location 0xba2f1498
A good practice would be to make sure that your object is always completely valid before the constructor finishes.
In your destructor you have:
delete observer;
free(filePath);
but you do not check to make sure either one is allocated beforehand which is clearly a problem, especially since your default constructor does not initialize any of these variables. This is beyond the fact that you are mixing C style allocation with C++ style allocation.
I want to create a reusable background thread to queue a number of tasks that require access to a singleton resource. The thread should be created at the start of the program and a message will be sent whenever it is required to complete a task. At first I was trying to use a worker thread, as the background thread doesn't have a UI, but then I noticed that only UI threads have message pumps. Unfortunately, PostThreadMessage always returns ERROR_INVALID_THREAD_ID, but I am sure the thread has been created correctly.
Is it a good choice to use a UI thread rather than a worker thread?
Why isn't my PostThreadMessage being received?
UPDATE: By looking at the output messages, I now know that the message isn't being received because the thread is killed
Sample code
DWORD deviceControllerThread;
void post(){
BOOL res=PostThreadMessage(deviceControllerthread,ControllerThread, ENROLLMENT_BEGIN, (WPARAM) myDataPointer, 0);
...
}
void MFC_Init(){
CWinThread* thread=AfxBeginThread(RUNTIME_CLASS(MFC_thread), THREAD_PRIORITY_NORMAL, 0, 0);
deviceControllerThread=thread->m_nThreadID;
}
When your thread initializes it just needs to call PeekMessage to "create" a message queue. Then the other thread can post messages to it via PostThreadMessage. Also, that error code of INVALID_THREAD_ID is a symptom that your worker thread has actually exited (or was never created). Make sure you have sufficient debug spew or logging to validate that the worker thread got created and didn't prematurely exit. Also, make sure you are checking the return code for AfxBeginThread and that m_nThreadID is valid (because I'm assume you initialized it to zero).
I do similar threading exercises all the time. I've moved away from using the message queue and onto using my own eventing and queues for finer control.
If you don't need to guarantee ordering of work items, then another idea is to just use the Windows "thread pool" to do the work for you.
Below is an OUTLINE of how I usually structure a thread class in C++. It's just something I whipped together based on existing projects and is NOT production code. But it should demonstrate some concepts of how to manage a thread lifetime.
// CList is any generic "array" or "list" class (you can use std::list, CAtlArray, CSimpleArray, etc...)
// ThreadMessage is your data structure for holding data to indicate to the thread what to do
// e.g.
// struct ThreadMessage
//{
// enum type; // YOUR_CODE_TO_QUIT=0, WORK_MESSAGE=1, etc...
// workdata data;
//};
class CMyThread
{
private:
CRITICAL_SECTION m_cs; // lock such that m_queue is thread safe, can be replaced with CComAutoCriticalSection or equivalent
bool m_fNeedToExit; // signals to the worker thread that it is time to exit
HANDLE m_hEvent; // For waking up the worker thread to tell it a new message is available
HANDLE m_hThread; // handle to worker thread
HANDLE m_hStartEvent; // For the worker thread to signal back to the parent thread that is has finished initializing
bool m_fStarted; // Has Start() been called
DWORD m_dwThread; // threadID
CList<ThreadMessage> m_queue; // generic "array" of work items. Can be replaced with any list-type data structure
public:
CMyThread()
{
InitializeCriticalSection(&m_cs);
}
~CMyThread()
{
Stop();
DeleteCriticalSection(&m_cs);
}
HRESULT Start()
{
if (m_fStarted)
return S_FALSE;
// todo - check all return codes from the Create functions!
m_hEvent = CreateEvent(0,0,0,0); // unsignalled, unnamed, auto-reset event
m_hStartEvent = CreateEvent(0,0,0,0); // unsignalled, unnamed, auto-reset event
m_hThread = CreateThread(NULL, 0, CMyThread::ThreadProc, this, 0, &m_dwThreadID);
// PUT YOUR THREAD INITIALIZATION CODE HERE
// wait for the thread to intialize (you don't have to call this next line if the thread doesn't have any initialization to wait for */
WaitForSingleObject(m_hStartEvent, INFINITE);
m_fStarted = true;
return S_OK;
}
HRESULT Stop()
{
if (m_hThread)
{
m_fNeedToExit = true;
ThreadMessage quitmessage;
quitmessage.type = YOUR_CODE_TO_QUIT;
SendMessageToThread(&quitmessage);
// in a debug build, you may want to wait for X seconds and show an error message if the worker thread appears hung
WaitForSingleObject(m_hThread, INFINITE);
// cleanup
CloseHandle(m_hThread); m_hThread = NULL;
CloseHandle(m_hStartEvent); m_hStartEvent = NULL;
CloseHandle(m_hEvent); m_hEvent= NULL;
m_fStarted = true;
m_dwThread = 0;
m_queue.empty();
}
return S_OK;
}
HRESULT SendMessageToThread(Message* pMsg)
{
if (m_fStarted == false)
return E_FAIL;
EnterCriticalSection(&m_cs);
m_queue.enque(*pMsg); //push message onto queue
LeaveCriticalSection(&m_cs);
SetEvent(m_hEvent); // signal the thread to wakeup and process it's message queue
return S_OK;
}
void ThreadProcImpl()
{
// initialize thread if needed (e.g. call PeekMessage to initialize the message queue if you need one - in this implementation you don't)
// signal back to the main thread we're off and running
SetEvent(m_hThreadStarted);
while (m_fNeedToExit == false)
{
bool fGotMsg = false;
ThreadMessage msg;
EnterCriticalSection(&m_cs);
if (m_queue.size > 0)
{
msg = m_queue.deque(); // remove the first message from the queue (if any)
fGotMsg = true;
}
LeaveCriticalSection(&m_cs);
// if the queue is empty, then wait for another message to come in
if (fGotMsg == false)
{
WaitForSingleObject(m_hEvent, INFINITE); // on return m_hEvent is auto-reset to unsignalled
continue; // back to top of while loop to deque
}
if (m_fNeedToExit) // check exit condition
break;
if (msg.type == YOUR_CODE_TO_QUIT)
break;
// YOUR CODE TO HANDLE "ThreadMessage msg" goes here. (i.e. "do the work")
}
// thread cleanup code goes here (if any)
}
static DWORD __stdcall ThreadProc(void* pcontext)
{
CMyThread* pThis = (CMyThread*)pcontext;
pThis->ThreadProcImpl();
return 0;
}
};