调整
将函数调用的参数从一个进程的地址空间传到另一个进程的地址空间。
COM直接使用了Win32线程,但仍有细微差异。
Win32线程:用户界面线程、工作线程
COM线程:套间线程(类似于用户界面线程)、自由线程(类似于工作线程)
自由线程
若某个组件是有自由线程创建的,则任意线程均可在任意时候调用它,COM不能使之同步,同步工作将由COM移至组件本身。COM不能同步对组件的调用,所以自由线程不需要消息循环。
调整与同步
使用套间线程的情况下,所有的调整与同步均由COM完成。自由线程情况下,调整可能不需要,同步需要组件完成。
进程间的调用将被调整
同一线程中的调用将不被调整
对于套间线程中组件的调用将被调整
对于自由线程中组件的调用并不是总被调整
对于套间线程的调用将被同步
对于自由线程的调用将被同步
同一线程中的调用将由此线程本身完成同步
何时需要对接口手动调整?
跨越套间边界但并没有通过COM进行通信时。
例子:
在后台增大对某个组件的一个计数器,并偶尔需要对显示进行刷新。
主线程将创建此套间线程。套间线程中创建一个组件并周期性地修改此组件中的计数器。套间线程将给主线程传递一个接口指针,以便主线程可以获得组件当前的计数值并显示出来。需要:
1> 初始化COM库
2> 具有一个消息循环
3> 对传给主线程的接口进行调整
CSimpleApartment
为是套间概念更为具体,创建了CSimpleApartment这个类。
CSimpleApartment将在另外一个线程中创建组件的过程封装起来。它将跨越两个不同的线程,一部分由原线程调用,一部分由新的线程调用。
CSimpleApartment中的函数:
StartThread调用CreateThread创建一个新的线程。
CreateComponent接收一个被创建组件的CLSID并在StartThread启动的线程上创建它。CreateComponent由原线程调用,使用一个事件(m_hCreateComponentEvent)以指示新套间线程调用CreateComponentOnThread创建新的组件。
它将完成4主要操作:1、将传给它的参数复制到成员变量中;2、创建相应的组件;3、等待组件创建完成;4、对所请求的指向组件的接口指针进行反调整。
CreateComponentOnThread是一个纯虚函数,由CSimpleApartment的派生类定义。创建玩新的组件后,设置m_hComponentReadyEvent表示组件已创建完成。
它将完成2个主要操作:1、调用CreateComponentOnThread纯虚版本,并将传给CreateComponent的参数传给它;2、对接口进行调整。
ClassThreadProc是在套间线程中运行的函数,通过两次使用m_hComponentReadyEvent指示线程的启动与结束。使用MsgWaitForMultipleObjects等待三件事的发生:m_hCreateComponentEvent、一个Windows消息、一个超时事件,并作出相应处理。
WaitWithMessageLoop等待某个事件的发生,并处理Windows消息。
代码:
//
// Apart.cpp
// - A simple C++ class, CSimpleApartment, encapsulates
// creating a component on an apartment thread.
//
#include
#include
#include
#include
#define __OUTPROC_SERVER_ // To get the proper definition of trace
#include "Util.h"
#undef __OUTPROC_SERVER_
#include "Apart.h"
static inline void trace(char* msg)
{ Util::Trace("Apartment", msg, S_OK) ;}
static inline void trace(char* msg, HRESULT hr)
{ Util::Trace("Apartment", msg, hr) ;}
///////////////////////////////////////////////////////////
//
// Constructor
//
CSimpleApartment::CSimpleApartment()
{
m_ThreadId = 0 ;
m_hThread = NULL ;
m_hCreateComponentEvent = NULL ;
m_hComponentReadyEvent = NULL ;
m_pIStream = NULL ;
m_hr = S_OK ;
m_piid = NULL ;
m_pclsid = NULL ;
m_WaitTime = 500 ;
}
///////////////////////////////////////////////////////////
//
// Destructor
//
CSimpleApartment::~CSimpleApartment()
{
// The thread must be stopped before we are deleted
// because the WorkerFunction is in the derived class.
assert(m_hThread == NULL) ;
}
///////////////////////////////////////////////////////////
//
// StartThread
// - Create and start the thread.
//
BOOL CSimpleApartment::StartThread(DWORD WaitTime)
{
if (IsThreadStarted())
{
return FALSE ;
}
// 创建新线程
m_hThread = ::CreateThread(NULL, // Default security
0, // Default stack size
RealThreadProc,
(void*)this,
CREATE_SUSPENDED, // Create the thread suspended.
&m_ThreadId) ; // Get the Thread ID.
if (m_hThread == NULL)
{
trace("StartThread failed to create thread.", GetLastError()) ;
return FALSE ;
}
trace("StartThread successfully created thread.") ;
// Create an event to signal the thread to create the component.
m_hCreateComponentEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL) ;
if (m_hCreateComponentEvent == NULL)
{
return FALSE ;
}
// Create an event for the thread to signal when it is finished.
m_hComponentReadyEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL) ;
if (m_hComponentReadyEvent == NULL)
{
return FALSE ;
}
trace("StartThread successfully created the events.") ;
// Initialize the wait time.
m_WaitTime = WaitTime ;
// Thread was created suspended; start the thread.
DWORD r = ResumeThread(m_hThread) ;
assert(r != 0xffffffff) ;
// Wait for the thread to start up before we continue.
WaitWithMessageLoop(m_hComponentReadyEvent) ;
return TRUE ;
}
///////////////////////////////////////////////////////////
//
// Stop Thread
//
void CSimpleApartment::StopThread()
{
if (m_hThread != NULL)
{
// Stop the thread.
PostThreadMessage(m_ThreadId, WM_QUIT, 0,0) ;
// Wait for thread to stop.
WaitWithMessageLoop(m_hComponentReadyEvent) ;
m_hThread = NULL ;
}
}
///////////////////////////////////////////////////////////
//
// Current thread status
//
BOOL CSimpleApartment::IsThreadStarted()
{
return (m_hThread != NULL) ;
}
///////////////////////////////////////////////////////////
//
// Thread procedure
//
DWORD WINAPI CSimpleApartment::RealThreadProc(void* pv)
{
CSimpleApartment* pApartment = reinterpret_cast(pv) ;
return pApartment->ClassThreadProc() ;
}
///////////////////////////////////////////////////////////
//
// Thread procedure
//
DWORD CSimpleApartment::ClassThreadProc()
{
// 初始化 COM 库
HRESULT hr = CoInitialize(NULL) ;
if (SUCCEEDED(hr))
{
// 第1次使用m_hComponentReadyEvent
// 指示新线程已经启动
SetEvent(m_hComponentReadyEvent) ;
// Wait for the signal to create a component.
BOOL bContinue = TRUE ;
while (bContinue )
{
// 消息循环
// 比GetMessage/DispatchMessage效率高
switch(::MsgWaitForMultipleObjects(
1,
&m_hCreateComponentEvent,
FALSE,
m_WaitTime,
QS_ALLINPUT))
{
// 创建组件
// 当事件m_hCreateComponentEvent被设置
// MsgWaitForMultipleObjects将停止等待
// 然后将调用CreateComponentOnThread
case WAIT_OBJECT_0:
CreateComponentOnThread() ;
break ;
// 处理Windows消息
case (WAIT_OBJECT_0 + 1):
MSG msg ;
// 取出消息
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
bContinue = FALSE ;
break ;
}
DispatchMessage(&msg) ; //将消息分发出去
}
break ;
// Do background processing.
// 等待超时,调用CSimpleApartment::WorkerFunction
// 此函数由CSimpleApartment派生类实现
case WAIT_TIMEOUT:
WorkerFunction() ;
break ;
default:
trace("Wait failed.", GetLastError()) ;
}
}
// Uninitialize the COM Library.
CoUninitialize() ;
}
// 第2次使用m_hComponentReadyEvent
// 指示线程已经结束了
SetEvent(m_hComponentReadyEvent) ;
return 0 ;
}
///////////////////////////////////////////////////////////
//
// CreateComponent helper function
//
HRESULT CSimpleApartment::CreateComponent(const CLSID& clsid,
const IID& iid,
IUnknown** ppI)
{
// 初始化共享数据
m_pIStream = NULL ;
m_piid = &iid ;
m_pclsid = &clsid ;
// 指示何时需要套间线程调用CreateComponentEvent创建相应的组件
// 当设置m_hCreateComponentEvent,ClassThreadProc将调用
// CreateComponentOnThread
SetEvent(m_hCreateComponentEvent) ;
// Wait for the component to be created.
trace("Wait for the component to be created.") ;
if (WaitWithMessageLoop(m_hComponentReadyEvent))
{
trace("The wait succeeded.") ;
if (FAILED(m_hr)) // Did GetClassFactory fail?
{
return m_hr ;
}
if (m_pIStream == NULL) // Did the marshaling fail?
{
return E_FAIL ;
}
trace("Unmarshal the interface pointer.") ;
// Unmarshal the interface.
HRESULT hr = ::CoGetInterfaceAndReleaseStream(m_pIStream,
iid,
(void**)ppI) ;
m_pIStream = NULL ;
if (FAILED(hr))
{
trace("CoGetInterfaceAndReleaseStream failed.", hr) ;
return E_FAIL ;
}
return S_OK ;
}
trace("What happened here?") ;
return E_FAIL ;
}
///////////////////////////////////////////////////////////
//
// CreateComponentOnThread helper function
// - This function packages the parameters for the
// CoCreateComponentOnThread function.
//
void CSimpleApartment::CreateComponentOnThread()
{
IUnknown* pI = NULL ;
// Call the derived class to actually create the component.
// 调用CreateComponentOnThread的纯虚版本
// 并将传递给CreateComponent的参数传递给它
m_hr = CreateComponentOnThread(*m_pclsid, *m_piid, &pI) ;
if (SUCCEEDED(m_hr))
{
trace("Successfully created component.") ;
// Marshal the interface pointer to the server.
// 对接口进行调整
HRESULT hr = ::CoMarshalInterThreadInterfaceInStream(
*m_piid, //待调整的接口的IID
pI, //待调整的接口
&m_pIStream) ; //推向待调整接口的流
assert(SUCCEEDED(hr)) ;
// Release the pI Pointer.
pI->Release() ;
}
else
{
trace("CreateComponentOnThread failed.", m_hr) ;
}
trace("Signal the main thread that the component is ready.") ;
// 设置m_hComponentReadyEvent
// 以告诉CreateComponent已完成了组件的创建
SetEvent(m_hComponentReadyEvent) ;
}
///////////////////////////////////////////////////////////
//
// BOOL WaitWithMessageLoop(HANDLE hEvent)
// 等待某个事件的发生,处理Windows消息
//
BOOL CSimpleApartment::WaitWithMessageLoop(HANDLE hEvent)
{
while (TRUE)
{
// Wait for the event and for messages.
DWORD dwReturn = ::MsgWaitForMultipleObjects(1,
&hEvent,
FALSE,
INFINITE,
QS_ALLINPUT) ;
if (dwReturn == WAIT_OBJECT_0)
{
// Our event happened.
return TRUE ;
}
else if (dwReturn == WAIT_OBJECT_0 + 1)
{
// Handle message to keep client alive.
MSG msg ;
while(::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
::DispatchMessage(&msg) ;
}
}
else
{
trace("WaitWithMessageLoop failed.", GetLastError()) ;
return FALSE ;
}
}
}
CClientApartment
CClientApartment继承CSimpleApartment,实现两个虚拟函数:CreateComponentOnThread(调用CoCreateInstance创建新组件)、WorkerFunction。
// CreateTheComponent
// 希望供在另一个线程中创建组件的客户使用
// 重载CreateComponentOnThread以调用CoCreateInstance
//
HRESULT CClientApartment::CreateComponentOnThread(const CLSID& clsid,
const IID& iid,
IUnknown** ppI)
{
HRESULT hr = ::CoCreateInstance(clsid,
NULL,
CLSCTX_INPROC_SERVER,
iid,
(void**)ppI) ;
if (SUCCEEDED(hr))
{
// 查询IX接口,以对WorkerFunction处理
hr = (*ppI)->QueryInterface(IID_IX, (void**)&m_pIX) ;
if (FAILED(hr))
{
// If we can't use it, don't let anybody use it.
(*ppI)->Release() ;
return E_FAIL ;
}
}
return hr ;
}
///////////////////////////////////////////////////////////
//
// WorkerFunction - This function is called by the worker thread.
//
void CClientApartment::WorkerFunction()
{
if (m_pIX)
{
m_pIX->Tick() ;
}
}
client.cpp
客户收到WM_TIMER消息时,将调用OnTick,OnTick将调用IX::GetCurrentCount并将此数值显示在窗口,此调用将被调整,以便能正确地跨越套间边界。而WorkerFunction调用IX::Tick时,由于调用是在同一套间线程中,不需要被调整。
//
// Client.cpp - Client implementation
//
#include
#include
#include
#include
#define __OUTPROC_SERVER_ // To get the proper definition of trace
#include "Util.h"
#undef __OUTPROC_SERVER_
#include "Iface.h"
#include "Apart.h"
#include "Cli-Apt.h"
static inline void trace(char* msg)
{ Util::Trace("Client", msg, S_OK) ;}
static inline void trace(char* msg, HRESULT hr)
{ Util::Trace("Client", msg, hr) ;}
///////////////////////////////////////////////////////////
//
// The client application
//
#include "resource.h"
///////////////////////////////////////////////////////////
//
// Global variables for main apartment
//
// Module handle
HINSTANCE g_hModule = NULL ;
// Handle to child listbox
HWND g_hWndListBox = NULL ;
// ID of the timer
static UINT g_TimerId = 0 ;
// Pointer to the interface on our component
static IX* g_pIX = NULL ;
// Pointer to apartment thread class
CClientApartment* g_pApartment = NULL ;
///////////////////////////////////////////////////////////
//
// Functions prototypes
//
// Create and initialize the main window.
HWND InitWindow(int nCmdShow) ;
// Create the child listbox control.
BOOL CreateChildListbox(HWND hWndParent, int cx, int cy) ;
// The main window procedure
extern "C" LONG APIENTRY MainWndProc(HWND hWnd,
UINT message,
UINT wParam,
LONG lParam) ;
// InitializeApartment - Create the thread and the component.
void InitializeApartment(HWND hWndMain) ;
// Timer tick message handler
void OnTick() ;
// Delete and tidy.
void CleanUp(HWND hWnd) ;
///////////////////////////////////////////////////////////
//
// WinMain function
//
extern "C" int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// Initialize the COM Library.
HRESULT hr = CoInitialize(NULL) ;
if (FAILED(hr))
{
return 0 ;
}
// Create the main window.
HWND hWndMain = InitWindow(nCmdShow) ;
if (hWndMain)
{
// Initialize the apartment.
InitializeApartment(hWndMain) ;
// Wait for a message.
MSG msg ;
while (::GetMessage(&msg, 0, 0, 0) > 0)
{
::DispatchMessage(&msg) ;
}
}
// Uninitialize the COM Library.
CoUninitialize() ;
return 0 ;
}
///////////////////////////////////////////////////////////
//
// Initialize window.
//
HWND InitWindow(int nCmdShow)
{
// Fill in window class structure with parameters
// that describe the main window.
WNDCLASS wcListview ;
wcListview.style = 0 ;
wcListview.lpfnWndProc = (WNDPROC)MainWndProc ;
wcListview.cbClsExtra = 0 ;
wcListview.cbWndExtra = 0 ;
wcListview.hInstance = g_hModule ;
wcListview.hIcon = ::LoadIcon(g_hModule,
MAKEINTRESOURCE(IDC_ICON)) ;
wcListview.hCursor = ::LoadCursor(NULL, IDC_ARROW) ;
wcListview.hbrBackground = ::GetStockObject(WHITE_BRUSH) ;
wcListview.lpszMenuName = NULL ;
wcListview.lpszClassName = "MyServerWinClass" ;
BOOL bResult = ::RegisterClass(&wcListview) ;
if (!bResult)
{
return NULL ;
}
HWND hWndMain ;
hWndMain = ::CreateWindow("MyServerWinClass",
"Component Server",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL,
NULL,
g_hModule,
NULL) ;
// If window could not be created, return "failure".
if (!hWndMain)
{
return NULL ;
}
// Make the window visible; update its client area;
// and return "success".
::ShowWindow(hWndMain, nCmdShow) ;
::UpdateWindow(hWndMain) ;
return hWndMain ;
}
///////////////////////////////////////////////////////////
//
// Create the listbox child control in the main window.
//
BOOL CreateChildListbox(HWND hWndParent, int cx, int cy)
{
// Create a listbox for output.
g_hWndListBox =
::CreateWindow("LISTBOX",
NULL,
WS_CHILD | WS_VISIBLE | LBS_USETABSTOPS
| WS_VSCROLL | LBS_NOINTEGRALHEIGHT,
0, 0, cx, cy,
hWndParent,
NULL,
g_hModule,
NULL) ;
if (g_hWndListBox == NULL)
{
// Listbox not created
::MessageBox (NULL, "Listbox not created!", NULL, MB_OK ) ;
return FALSE ;
}
else
{
return TRUE ;
}
}
///////////////////////////////////////////////////////////
//
// InitializeApartment - Create the thread and the component.
//
void InitializeApartment(HWND hWnd)
{
// Create a simple apartment object.
g_pApartment = new CClientApartment ;
// Start the thread.
if (g_pApartment->StartThread())
{
trace("Successfully started thread.") ;
// Create the component.
HRESULT hr = g_pApartment->CreateComponent(CLSID_Component,
IID_IX,
(IUnknown**)&g_pIX) ;
if (SUCCEEDED(hr))
{
trace("Successfully created component.") ;
// Initialize the component.
HRESULT hr = g_pIX->SetStartCount(1000) ;
if (FAILED(hr))
{
trace("SetStartCount failed.", hr) ;
}
// Start a timer.
g_TimerId = SetTimer(hWnd, 369, 2*1000, NULL) ;
assert(g_TimerId != 0) ;
return ;
}
}
}
///////////////////////////////////////////////////////////
//
// OnTick - Called when the window gets a WM_TIMER message
//
void OnTick()
{
if (g_pIX != NULL)
{
// Get the current count.
long c = 0 ;
//当客户调用IX::GetCurrentCount时,此调用将被调整,以便能正确
//跨越套间边界
HRESULT hr = g_pIX->GetCurrentCount(&c) ;
assert(SUCCEEDED(hr)) ;
// Display the count.
strstream sout ;
sout << "The current count is : "
<< c
<< "."
<< ends ;
trace(sout.str()) ;
}
}
///////////////////////////////////////////////////////////
//
// Main window procedure
//
extern "C" LONG APIENTRY MainWndProc(
HWND hWnd, // window handle
UINT message, // type of message
UINT wParam, // additional information
LONG lParam) // additional information
{
DWORD dwStyle ;
switch (message)
{
case WM_CREATE:
{
// Create listbox control
CREATESTRUCT* pcs = reinterpret_cast(lParam) ;
if (!CreateChildListbox(hWnd, pcs->cx, pcs->cy))
{
return -1 ;
}
}
break ;
case WM_SIZE:
::MoveWindow(g_hWndListBox,
0, 0,
LOWORD(lParam),
HIWORD(lParam),
TRUE) ;
break ;
case WM_TIMER:
OnTick() ; //调用IX::GetCurrentCount并将计数值显示在窗口上
break ;
case WM_DESTROY: // message: window being destroyed
::PostQuitMessage(0) ;
break ;
case WM_CLOSE:
CleanUp(hWnd) ;
//Fall through
default:
return (DefWindowProc(hWnd, message, wParam, lParam));
}
return 0 ;
}
///////////////////////////////////////////////////////////
//
// CleanUp
//
void CleanUp(HWND hWnd)
{
// The list box is going away.
g_hWndListBox = NULL ;
// Kill the timer.
if (g_TimerId != 0)
{
BOOL b = KillTimer(hWnd, g_TimerId) ;
assert(b = TRUE) ;
g_TimerId = 0 ;
}
// Remove interface pointer.
if (g_pIX != NULL)
{
g_pIX->Release() ;
g_pIX = NULL ;
}
if (g_pApartment != NULL)
{
g_pApartment->StopThread() ;
delete g_pApartment ;
g_pApartment = NULL ;
}
}
当创建一个自由线程时,此时创建的不再是一个线程而是一个套间。
CSimpleFree
CSimpleAparatment变为了CSimpleFree。这里,CSimpleFree只能被在一个套间线程中运行的客户来创建自由线程。
与CSimpleAparatment仅有的差别:ClassThreadProc
调用CoInitializeEx(0, COINIT_MULTITHREADED),在调用之前需要做2件事:1、定义_WINNT32_WINNT为0x0400或定义_WIN32_DCOM,否则OBJBASE.H将不包含CoInitializeEx的定义;2、运行时检查操作系统是否支持CoInitializeEx
//
// Thread procedure
//
BOOL CSimpleFree::ClassThreadProc()
{
BOOL bReturn = FALSE ;
// 检查CoInitializeEx是否存在
typedef HRESULT (__stdcall *FPCOMINITIALIZE)(void*, DWORD) ;
FPCOMINITIALIZE pCoInitializeEx =
reinterpret_cast(
::GetProcAddress(::GetModuleHandle("ole32"),
"CoInitializeEx")) ;
if (pCoInitializeEx == NULL)
{
trace("This program requires the free-thread support in DCOM.") ;
SetEvent(m_hComponentReadyEvent) ;
return FALSE ;
}
// 初始化 COM 库
HRESULT hr = pCoInitializeEx(0, COINIT_MULTITHREADED) ; //@
if (SUCCEEDED(hr))
{
// Signal that we are starting.
SetEvent(m_hComponentReadyEvent) ;
// Set up array of events.
HANDLE hEventArray[2] = { m_hCreateComponentEvent,
m_hStopThreadEvent } ;
// Wait for the signal to create a component.
BOOL bContinue = TRUE ;
while (bContinue)
{
//@
// 创建的是自由线程,不需要消息循环
// 将MsgWaitForMMultipleObjects替换掉
// 但被主线程调用,仍需要对消息进行处理
// 以使用户界面不致被锁起
switch(::WaitForMultipleObjects(2,
hEventArray,
FALSE,
m_WaitTime))
{
// Create the component.
case WAIT_OBJECT_0:
CreateComponentOnThread() ;
break ;
// Stop the thread.
case (WAIT_OBJECT_0 +1):
bContinue = FALSE ;
bReturn = TRUE ;
break ;
// Do background processing.
case WAIT_TIMEOUT:
WorkerFunction() ;
break ;
default:
trace("Wait failed.", GetLastError()) ;
}
}
// Uninitialize the COM Library.
CoUninitialize() ;
}
// Signal that we have finished.
SetEvent(m_hComponentReadyEvent) ;
return bReturn ;
}
CClientFree
CClientFree与前面的CClientAparament是等价的,CClientFree继承CSimepleFree并实现CreateComponent与WorkerFunction两个虚拟函数,并添加了两个新函数:
ShareUnmarshaledInterfacePointer—返回CClientFree在其WorkerFunction所用的IX接口指针。此接口未经调整,因此,仅能从一个自由线程中使用它。
UseUnmarshaledInterfacePointer—将设置CClientFree对象在其WorkerFunction中使用的指针。
client.cpp:
在调用了InitializeThread后,客户讲调用InitializeThread2。
//
// InitializeThread - 创建一个自由线程和一个组件
//
BOOL InitializeThread(HWND hWnd)
{
// Create a simple thread object.
g_pThread = new CClientFree ;
// Start the thread.
if (g_pThread->StartThread())
{
trace("Successfully started thread.") ;
// Create the component.
HRESULT hr = g_pThread->CreateComponent(CLSID_Component,
IID_IX,
(IUnknown**)&g_pIX) ;
if (SUCCEEDED(hr))
{
trace("Successfully created component.") ;
// Initialize the component.
HRESULT hr = g_pIX->SetStartCount(5000) ;
if (FAILED(hr))
{
trace("SetStartCount failed.", hr) ;
return FALSE ;
}
// Start a timer.
g_TimerId = SetTimer(hWnd, 369, 500, NULL) ;
assert(g_TimerId != 0) ;
}
else
{
trace("Failed to create the component.") ;
return FALSE ;
}
}
else
{
trace("Failed starting thread.") ;
return FALSE ;
}
return TRUE ;
}
///////////////////////////////////////////////////////////
//
// InitializeThread2
// - Create a second thread, but use the component
// from the first thread.
// 建立第2个线程,但没有建立第2个组件,而是共享了第1个线程创建的组件
// 将创建一个CClientFree2对象而不是CClientFree对象
// CClientFree2与CClientFree的唯一差别在于WorkerFunction实现不同
//
BOOL InitializeThread2()
{
if (g_pThread == NULL)
{
return FALSE ;
}
// 创建第2个线程
// This thread has a different WorkerFunction.
g_pThread2 = new CClientFree2 ;
// Start the thread.
if (g_pThread2->StartThread())
{
trace("Successfully started second thread.") ;
// Get the same pointer used by the first thread.
IX* pIX = NULL ;
pIX = g_pThread->ShareUnmarshaledInterfacePointer() ;
assert(pIX != NULL) ;
// Use this pointer in the second thread.
g_pThread2->UseUnmarshaledInterfacePointer(pIX) ;
pIX->Release() ;
return TRUE ;
}
else
{
trace("Failed to start second thread.") ;
return FALSE ;
}
}
CLI-FREE.cpp
//
// WorkerFunction - This function is called by the worker thread.
//
void CClientFree::WorkerFunction()
{
CSimpleLock Lock(m_hInterfaceMutex) ;
if (m_pIX)
{
m_pIX->Tick(1) ;
m_pIX->Left() ;
}
}
CLI-FREE.H
class CClientFree2 : public CClientFree
{
public:
virtual void WorkerFunction() ;
};
对组件的修改
对组件增加了新的成员外,还应使之成为线程安全的,这是因为有两个线程分别增大和减少组件的计数值。
//
// Lock.h
// - This class provides a simple locking mechanism.
//
class CSimpleLock
{
public:
// Lock
CSimpleLock(HANDLE hMutex)
{
m_hMutex = hMutex ;
WaitForSingleObject(hMutex, INFINITE) ;
}
// Unlock
~CSimpleLock()
{
// 释放指定的互斥量
ReleaseMutex(m_hMutex) ;
}
private:
HANDLE m_hMutex ;
};
使用此类时,需要为其构造函数提供一个互斥量的句柄。构造函数将一直等待,直到指定的互斥量成为有信号的。
使用自由线程时,线程间的同步由程序员完成。
使用示例(CMPNT.CPP):
//
// Interface IX - Implementation
//
HRESULT __stdcall CA::Tick(int delta)
{
//m_hCountMutex包含对m_count的访问
CSimpleLock Lock(m_hCountMutex) ;
m_count += delta ;
return S_OK ;
}
HRESULT __stdcall CA::Left()
{
//m_hHandMutex保护对m_bRightHand的访问
CSimpleLock Lock(m_hHandMutex) ;
m_bRightHand = FALSE ;
return S_OK ;
}
自由线程参数调整的优化
该优化是可实现的:自由线程中的组件直接将其指针传递给同一进程中的套间线程。
CMPNT.CPP:
CoCreateFreeThreadedMarshaler实现的是一个具有IMarshal接口的组件,可决定接口的客户是否在同一进程中。若在同一进程,对接口调整时,所有指针都被原封不动地传过去。若客户在另外进程中,接口将被标准的调整程序进行调整处理。这样,程序员无需关心客户在什么地方。
NondelegatingQueryInterface将IMarshal查询转发给所创建的调整程序。
//
// Initialize the component by creating the contained component
//
HRESULT CA::Init()
{
HRESULT hr = CUnknown::Init() ;
if (FAILED(hr))
{
return hr ;
}
// Create a mutex to protect the count.
m_hCountMutex = CreateMutex(0, FALSE, 0) ;
if (m_hCountMutex == NULL)
{
return E_FAIL ;
}
// Create a mutex to protect the hand.
m_hHandMutex = CreateMutex(0, FALSE, 0) ;
if (m_hHandMutex == NULL)
{
return E_FAIL ;
}
// Aggregate the free-threaded marshaler.
hr = ::CoCreateFreeThreadedMarshaler(
GetOuterUnknown(),
&m_pIUnknownFreeThreadedMarshaler) ;
if (FAILED(hr))
{
return E_FAIL ;
}
return S_OK ;
}
//
// NondelegatingQueryInterface Implementation
//
HRESULT __stdcall CA::NondelegatingQueryInterface(const IID& iid,
void** ppv)
{
if (iid == IID_IX)
{
return FinishQI(static_cast(this), ppv) ;
}
else if (iid == IID_IMarshal)
{
return m_pIUnknownFreeThreadedMarshaler->QueryInterface(iid,
ppv) ;
}
else
{
return CUnknown::NondelegatingQueryInterface(iid, ppv) ;
}
}
线程模型的注册表关键字
COM需要知道进程中组件支持的线程模型,以便在跨越线程边界时对其接口进行合适的调整与同步。为登记进程总组件的线程模型,可以给组件的InprocServer32关键字加上一个名为ThreadingModel的值。
ThreadingModel有3个可能的值:
Apartment:套间线程使用的组件
Free:自由线程中使用的组件
Both:以上两者都可以
若组件不关心线程问题,可以不指定任何值,COM将假定此组件不支持多线程。
一个进程中服务器提供的所有组件都应具有相同的线程模型。