285 lines
8.7 KiB
C++
285 lines
8.7 KiB
C++
// ==========================================================================
|
|
// Class Implementation : COXOwnThread
|
|
// ==========================================================================
|
|
|
|
// Source file : OXOwnThread.h
|
|
|
|
// This software along with its related components, documentation and files ("The Libraries")
|
|
// is © 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is
|
|
// governed by a software license agreement ("Agreement"). Copies of the Agreement are
|
|
// available at The Code Project (www.codeproject.com), as part of the package you downloaded
|
|
// to obtain this file, or directly from our office. For a copy of the license governing
|
|
// this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900.
|
|
|
|
// //////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "stdafx.h"
|
|
#include "OXOwnThread.h"
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Remarks about the protected data members
|
|
// BOOL m_bHasOwnThread; // if the own thread is enabled or not
|
|
// HANDLE m_hCreatedEvent; // event for synchronisation during thread startup
|
|
// HANDLE m_hThreadEvent; // see 'SetThreadEvent' and 'OnThreadEvent' functions
|
|
// HANDLE m_hEndEvent; // event for synchronisation during thread cleanup
|
|
// HRESULT m_hCreateResult; // result value of the creation of the own thread
|
|
// LPUNKNOWN m_pMarshalledPunk; // the marshalled object pointer; to be passed to the Owner thread
|
|
// BOOL m_bEndThread; // ends the Own thread when set to TRUE
|
|
// DWORD m_nOwnerThreadID; // ThreadID of the owner thread (thread that created the object)
|
|
// DWORD m_nOwnThreadID; // ThreadID of the Own thread
|
|
// COXDispatchMarshaller
|
|
// m_dispatchMarshaller; // Encapsulation of the marshal stream
|
|
// COXFinalReleaseWnd
|
|
// m_finalReleaseWnd; // Helper window for re-routing the releasing of the object from
|
|
// // the Own thread to the Owner thread
|
|
//
|
|
// // The COXFinalReleaseWnd must be able to call a protected member function
|
|
// // of this COXOwnThread object in order to release this object
|
|
// friend COXFinalReleaseWnd;
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// COXOwnThread
|
|
|
|
COXOwnThread::COXOwnThread() :
|
|
m_hCreatedEvent(NULL),
|
|
m_hThreadEvent(NULL),
|
|
m_hEndEvent(NULL),
|
|
m_pMarshalledPunk(NULL),
|
|
m_bEndThread(FALSE),
|
|
m_nOwnerThreadID(0),
|
|
m_nOwnThreadID(0),
|
|
#pragma warning(disable : 4355) // turn off the
|
|
m_finalReleaseWnd(this),
|
|
#pragma warning (default : 4355) // Turn the warning back on
|
|
m_bHasOwnThread(FALSE)
|
|
// --- In : none
|
|
// --- Out : none
|
|
// --- Returns : nothing
|
|
// --- Effect : Constructs the object and retrieves the thread ID of the Owners thread
|
|
// in order to be able to re-route later the releasing from the Own thread
|
|
// back to the Owners Thread.
|
|
{
|
|
m_nOwnerThreadID = AfxGetThread()->m_nThreadID;
|
|
}
|
|
|
|
COXOwnThread::~COXOwnThread()
|
|
// --- In : none
|
|
// --- Out : none
|
|
// --- Returns : nothing
|
|
// --- Effect : Destructs the object and waits until the own thread is terminated
|
|
{
|
|
// The destructor can only be called from the same thread
|
|
// that called the constructor !
|
|
ASSERT(AfxGetThread()->m_nThreadID==m_nOwnerThreadID);
|
|
m_finalReleaseWnd.DestroyWindow();
|
|
EndThread();
|
|
if (m_hCreatedEvent) CloseHandle(m_hCreatedEvent);
|
|
if (m_hThreadEvent) CloseHandle(m_hThreadEvent);
|
|
if (m_hEndEvent) CloseHandle(m_hEndEvent);
|
|
}
|
|
|
|
BOOL COXOwnThread::EnableOwnThread()
|
|
{
|
|
// This function can only be called from the same thread
|
|
// that called the constructor !
|
|
ASSERT(AfxGetThread()->m_nThreadID==m_nOwnerThreadID);
|
|
|
|
if (m_dispatchMarshaller.CheckStream() && m_finalReleaseWnd.m_hWnd)
|
|
{
|
|
|
|
m_hCreatedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
m_hThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
m_hEndEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
|
|
AfxBeginThread(StartThread, this);
|
|
|
|
WaitForSingleObject(m_hCreatedEvent, INFINITE);
|
|
if (m_hCreateResult==S_OK)
|
|
{
|
|
// unmarshal an IUnknown from the scratch stream. if unmarshalling
|
|
// fails, it takes care of releasing the object inside the marshal-data
|
|
HRESULT hr = m_dispatchMarshaller.Unmarshal((void **)&m_pMarshalledPunk);
|
|
if (hr==S_OK)
|
|
m_bHasOwnThread = TRUE;
|
|
else
|
|
{
|
|
m_bEndThread=TRUE;
|
|
SetThreadEvent();
|
|
WaitForSingleObject(m_hEndEvent, INFINITE);
|
|
}
|
|
}
|
|
}
|
|
#ifdef _DEBUG
|
|
if (!m_bHasOwnThread)
|
|
TRACE(_T("Can't enable own thread in 'COXOwnThreadCmdTarget::EnableOwnThread' \n'OLE_NO_OWNTRHEAD'-macro defined !\n"));
|
|
#endif
|
|
return m_bHasOwnThread;
|
|
}
|
|
|
|
BOOL COXOwnThread::HasOwnThread()
|
|
{
|
|
return m_bHasOwnThread;
|
|
}
|
|
|
|
BOOL COXOwnThread::FinalReleaseRouter()
|
|
{
|
|
if (AfxGetThread()->m_nThreadID==m_nOwnerThreadID)
|
|
return FALSE;
|
|
else if (AfxGetThread()->m_nThreadID==m_nOwnThreadID)
|
|
{
|
|
if (m_finalReleaseWnd.m_hWnd)
|
|
m_finalReleaseWnd.PostMessage(WMOX_FINAL_RELEASE);
|
|
}
|
|
#ifdef _DEBUG
|
|
else
|
|
{
|
|
ASSERT(FALSE);
|
|
TRACE(_T("Call from an illegal thread !\n"));
|
|
}
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
void COXOwnThread::SetThreadEvent()
|
|
{
|
|
SetEvent(m_hThreadEvent);
|
|
}
|
|
|
|
// protected function
|
|
|
|
void COXOwnThread::Run()
|
|
// --- In : none
|
|
// --- Out : none
|
|
// --- Returns : nothing
|
|
// --- Effect : this function keeps the Own thread running until m_bEndThread is set
|
|
// to TRUE.
|
|
{
|
|
MSG msg;
|
|
|
|
m_nOwnThreadID = AfxGetThread()->m_nThreadID;
|
|
|
|
// initialize COM
|
|
m_hCreateResult = CoInitialize(NULL);
|
|
if (m_hCreateResult==S_OK)
|
|
{
|
|
m_hCreateResult = m_dispatchMarshaller.Marshal(GetIDispatch(FALSE));
|
|
SetEvent(m_hCreatedEvent);
|
|
|
|
// apartment message/event loop
|
|
// (see SDK documentation for MsgWaitForMultipleObjects)
|
|
// here worker message loops last forever. in situations without a
|
|
// static number of worker threads, the loop could easily be terminated by
|
|
// WM_QUITs sent from the main thread which might manage the worker thread
|
|
// pool more carefully.
|
|
|
|
if (m_hCreateResult==S_OK)
|
|
{
|
|
DWORD dwWaitResult;
|
|
|
|
while (!m_bEndThread)
|
|
{
|
|
// wait for any message sent or posted to this queue
|
|
// or for one of the passed handles to become signaled
|
|
dwWaitResult = MsgWaitForMultipleObjects(1, &m_hThreadEvent, FALSE, INFINITE, QS_ALLINPUT);
|
|
|
|
// result tells us the type of event we have:
|
|
// a message or a signaled handle
|
|
|
|
// if there are one or more messages in the queue ...
|
|
if (dwWaitResult == (WAIT_OBJECT_0 + 1))
|
|
{
|
|
// dispatch all of the messages in this next loop
|
|
// (here is where we'd check for WM_QUITs to end this
|
|
// worker thread if we wanted to)
|
|
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
|
DispatchMessage(&msg);
|
|
}
|
|
else if (!m_bEndThread)
|
|
OnThreadEvent();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
SetEvent(m_hCreatedEvent);
|
|
OnThreadDestroy();
|
|
CoUninitialize();
|
|
SetEvent(m_hEndEvent);
|
|
}
|
|
|
|
UINT __cdecl COXOwnThread::StartThread(LPVOID pOwnThread)
|
|
// --- In : pOwnThread: the this pointer passed by the creating of the thread
|
|
// --- Out : none
|
|
// --- Returns : always zero
|
|
// --- Effect : this is a static function so we pass the control to a non static function (Run)
|
|
{
|
|
((COXOwnThread*) pOwnThread)->Run();
|
|
return 0;
|
|
}
|
|
|
|
LPUNKNOWN COXOwnThread::GetInterfaceHook(const void*)
|
|
// --- In : none
|
|
// --- Out : none
|
|
// --- Returns : the marshalled IDispatch handle if the call is done by the Owner thread
|
|
// --- Effect : This is a first chance hook function.
|
|
// If the call is done by the Own thread we return NULL (in this case
|
|
// we don't want to hook). If the call is done by the Owner thread
|
|
// we return the marshalled IDispatch handle.
|
|
{
|
|
if (m_bHasOwnThread)
|
|
{
|
|
if (AfxGetThread()->m_nThreadID==m_nOwnerThreadID)
|
|
return m_pMarshalledPunk;
|
|
#ifdef _DEBUG
|
|
else if (AfxGetThread()->m_nThreadID!=m_nOwnThreadID)
|
|
{
|
|
ASSERT(FALSE);
|
|
TRACE(_T("Call from an illegal thread !\n"));
|
|
}
|
|
#endif
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void COXOwnThread::EndThread()
|
|
// --- In : none
|
|
// --- Out : none
|
|
// --- Returns : nothing
|
|
// --- Effect : This function is called by the destructor and ends
|
|
// the own thread.
|
|
{
|
|
if (HasOwnThread())
|
|
{
|
|
m_bEndThread=TRUE;
|
|
SetThreadEvent();
|
|
WaitForSingleObject(m_hEndEvent, INFINITE);
|
|
}
|
|
}
|
|
|
|
void COXOwnThread::OnThreadEvent()
|
|
// --- In : none
|
|
// --- Out : none
|
|
// --- Returns : nothing
|
|
// --- Effect : You can override this function, if you want to let the
|
|
// Own thread handle some specific things.
|
|
// If you call the SetThreadEvent from within the Owner thread
|
|
// then this notification will be handled by the Own thread.
|
|
{
|
|
}
|
|
|
|
void COXOwnThread::OnThreadDestroy()
|
|
// --- In : none
|
|
// --- Out : none
|
|
// --- Returns : nothing
|
|
// --- Effect : You can override this function, if you have some things
|
|
// to be cleaned up by the own thread.
|
|
{
|
|
}
|
|
|