2025-11-27 16:46:48 +09:00

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.
{
}