分享
 
 
 

COM样例(三)——组件端代码

王朝vc·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

COM样例(三)

——组件端代码

本文为此系列文章的重点,前面设计的接口都只是辅助性质,与COM线程模型没有一点关系。由于客户和和组件实现的接口IModule、IModuleSite等都包有界面的传递,出于利用MFC的界面包装功能而都使用MFC来实现,故全部运行在STA套间中,并可使得组件的窗口亦使用客户端主线程来派送消息。

假设调用远程组件的方法以实现业务逻辑,由于远程及大数据量操作的关系,决定对于每次界面发起的数据操作(如查找),均发起一个线程,然后在这个线程中调用远程方法。决定使用一个类包装任务,其实现ITask接口,以提供任务管理的服务,而线程函数Task就理所当然的是ITask的实现类CTask的静态成员函数。由于CTask是一个内部对象,不需要拥有CLSID,故样例中直接通过静态成员函数CreateInstance获得其实例。

出于示范的目的,考虑如何表现进度,在此提出两种方式。

一. 假设远程组件方法是同步的,则调用线程将由于远程组件方法的迟迟不返而被挂起,因此无法表示进度。对于此,可以开启一个计时器(Timer),每隔固定时间就由前面的CTask通知ITaskManager增加了一定的进度,并给个上限,超出后就不再计时,进而进度不再增加直到方法返回或ITaskManager终止;如果是异步的,则上面说的发起一个线程这个工作由COM运行时期库(以后简称COM)干了,在此也就没有意义了,并且其必须在Win2000及以后版本发行的COM上才有效,这也就是说客户端必须是在Win2000及后续版本的操作系统上运行,这不是一个好提议。

二. 假定ITask由远程业务组件实现,而并不是上面说的一个内部对象仅为提供包装使用,则在远程业务组件的方法中每过一段代码,其调用ITaskNotify以设置进度,并在方法结束时调用ITaskNotify以结束任务。

上面的第一种情况中的CTask没有存在于MTA套间中的必要——其只会被客户主线程调用(不管是ITaskManager的实现者还是部门组件的操作界面发起的任务,都是通过客户端主线程操作的)。因此其应该是一个Apartment组件,使用MFC实现。但由于本样例主要是演示套间间的访问调用,故在此依旧将其设计成Free组件(即使没这个必要),使用ATL实现,以演示如何跨套间调用。但由于其是通过静态成员函数CreateInstance直接创建的,并没有CLSID及相关注册表项以说明是Free组件。由于在客户主线程创建它,即客户主线程获得其直接指针,因此它是存在于客户主线程相关的STA套间内(即使以Free组件的要求编写)。

而对于发起线程(既CTask::Task静态成员函数)的终止,如果将发起线程和MTA线程关联(即线程开始时COINIT_MULTITHREADED作为参数调用CoInitializeEx),而业务组件是一个Free组件,将存在于MTA中,则非常不幸地发起线程是直接调用业务组件的方法,即发起线程也是业务组件方法的执行者(假设业务组件是本地组件)。不幸地原因就是如果业务组件的方法中有一个死循环或运行时间很长的代码,且其又不提供任何终止的方法(比如短时间等待一个事件),则终止调用的唯一方法就是强行终止线程,进而不能正确调用业务组件的Release,并导致COM的一些效率低下(CoUninitialize没有调用,COM的某些资源未能及时释放)。如果通过代理调用(而不是直接指针),则可以通过ICancelMethodCalls取消掉未决的调用——即发起线程中的对业务组件的调用。这是COM提供的一个接口,以使得客户可以取消同步调用。这是双方面的工作,如果组件端一直占着线程资源不放也依旧和前面一样,必须强行终止,但这样至少提供了一种途径使得可以回复发起线程的运行,进而调用CoUninitialize退出。其坏处和前面的异步调用一样,必须是Win2000及其以上版本的操作系统所发布的COM。本样例使用后者,故发起线程使用CoInitialize进入STA而不是MTA以获得代理对象的指针而非直接指针进而得以取消调用(有兴趣可以自己试下,将CTask::Task中的CoInitialize换成CoInitializeEx以进入MTA,任务将终止失败)。但由于此线程中没有生成任何COM组件对象,即此STA套间内不包含任何对象,因此无需编写消息循环。

第二种情况中,假设在每次发起的调用线程中都通过调用CoCreateInstance以获得业务组件的一个全新实例,然后再调用其上的方法,则实际上其只会被发起的调用线程这一个线程调用,没有存在于MTA套间中的必要。这是非常符合MTS提供的编程模型的编写方式,通过将业务组件注册成MTS组件,并实现对象池功能和开启即时激活特性(Just-In-Time Activation)则上面由于每次调用CoCreateInstance而导致的损耗几乎等于没有,但程序的结构却非常简单,不需要复杂的逻辑。

不过此样例并没有考虑编写为MTS组件,并且上面的好处正是本文的坏处,无法演示多线程对业务组件的调用处理。故提供一个全局的业务组件对象,使其为Free组件,并在每次发起的调用线程中都通过ITaskNotify通知正确的线程任务的进度(实际只有一个线程会被通知——客户端主线程。这里只是说明如何使用正确的代理对象进行通知,但由于业务组件是在MTA套间中,且只有一个线程会被通知,因此并没有保留ITaskNotify*的中立形式的必要,即各调用线程的代理对象就意义上是一样的了,也许实现上会有细微差别,视COM运行时期库的实现。但在此作为演示还是使用中立形式保存ITaskNotify*)。

而对于第二种情况线程的终止(参看下面的代码),我只是让业务逻辑就是WaitForSingleObject一个事件来及时地响应终止事件和模拟工作的耗时,实际中当然不可能这样。实际中的业务代码如果是一个循环,则可以通过每次循环时短时间等待一个事件(或调用ICancelMethodCalls的方法),没有事件则继续循环工作(这种情况下最好是监视一个全局变量的值而不是等待一个事件)。如果不是循环却又是很耗时的操作,且操作没有提供任何中断的接口,则只能强行终止了。

本样例就上面两种情况,分别实现业务组件接口中的两个方法Task1和Task2,此业务组件为Free类型的进程内组件。由于代码较多,在此仅列出第二种情况中的业务组件代码和部门组件中的发起调用的代码。如果欲获得其他相关源代码,请于《COM样例(四)》中下载。

第二种情况下的调用发起代码

……

// 列集IExample1Logic接口指针

IStream *pStream = NULL;

if( FAILED( ::CoMarshalInterThreadInterfaceInStream( IID_IExample1Logic,

m_pLogic,

&pStream ) ) )

{

m_pError->ReportHardError( __WFILE__,

__LINE__,

L"列集IExample1Logic接口失败!" );

return;

}

// 发起线程

HANDLE hThread = CreateThread( NULL, 0, Task2, pStream, 0, NULL );

if( !hThread )

{

pStream->Release();

m_pError->ReportHardError( __WFILE__,

__LINE__,

L"任务线程创建失败!" );

return;

}

::CloseHandle( hThread );

……

第二种情况下的调用线程代码

static DWORD WINAPI Task2( LPVOID pParam )

{

ASSERT( pParam );

// 将此线程与MTA套间绑定

::CoInitializeEx( NULL, COINIT_MULTITHREADED );

// 散集出IExample1Logic接口指针

IExample1Logic *pLogic = NULL;

if( FAILED( ::CoGetInterfaceAndReleaseStream(

reinterpret_cast< IStream* >( pParam ),

IID_IExample1Logic,

reinterpret_cast< void** >( &pLogic ) ) ) )

return static_cast< DWORD >( -1 );

// 执行任务

pLogic->Task2();

// 释放资源

pLogic->Release();

::CoUninitialize();

return 0;

}

第二种情况下的业务组件

业务组件的接口定义

[

object,

uuid(348FF439-26B3-495D-85C1-4464CAA98CD9),

pointer_default(unique)

]

interface IExample1Logic : IUnknown

{

HRESULT Task1();

HRESULT Task2();

};

[

object,

uuid(348FF439-26B3-495D-85C2-4464CAA98CD9),

pointer_default(unique)

]

interface ISetTaskManager : IUnknown

{

HRESULT SetTaskManager( [in] ITaskManager *pManager );

};

ITask实现类的头文件代码

class CExample1Logic;

class ATL_NO_VTABLE CTask :

public CComObjectRootEx< CComMultiThreadModel >,

public ITask

{

// ATL定义宏

DECLARE_NOT_AGGREGATABLE( CTask )

DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP( CTask )

COM_INTERFACE_ENTRY( ITask )

END_COM_MAP()

// 成员变量

protected:

float m_Rate; // 当前进度

DWORD m_Cookie; // ITaskManager::AddTask返回的cookie

HANDLE m_hEvent; // 等待事件,与CExample1Logic共同合作

// 构造、析构

public:

CTask() : m_Rate( 0.0f ),

m_Cookie( static_cast< DWORD >( -1 ) ),

m_hEvent( NULL )

{

// 什么都不做

}

~CTask()

{

::CloseHandle( m_hEvent );

}

// 接口实现

public:

// ITask

STDMETHOD(GetProcessRateOfTask)( float *pRate );

STDMETHOD(TerminateTask)();

// 友员类

friend class CExample1Logic;

};

ITask实现类的源文件代码

STDMETHODIMP CTask::GetProcessRateOfTask( float *pRate )

{

if( !pRate )

return E_INVALIDARG;

*pRate = m_Rate;

return S_OK;

}

STDMETHODIMP CTask::TerminateTask()

{

if( !m_hEvent )

return E_FAIL;

return ::SetEvent( m_hEvent ) ? S_OK : E_FAIL;

}

业务组件的头文件代码

class ATL_NO_VTABLE CExample1Logic :

public CComObjectRootEx< CComMultiThreadModel >,

public CComCoClass< CExample1Logic, &CLSID_Example1Logic >,

public IExample1Logic,

public ISetTaskManager

{

// ATL定义宏

public:

DECLARE_PROTECT_FINAL_CONSTRUCT()

DECLARE_REGISTRY_RESOURCEID( IDR_EXAMPLE1LOGIC )

BEGIN_COM_MAP( CExample1Logic )

COM_INTERFACE_ENTRY( IExample1Logic )

COM_INTERFACE_ENTRY( ISetTaskManager )

END_COM_MAP()

// 构造、析构

blic:

CExample1Logic() : m_Cookie( static_cast< DWORD >( -1 ) )

{

::InitializeCriticalSection( &m_CS );

}

~CExample1Logic();

// 成员变量

protected:

DWORD m_Cookie; // ITaskManager的GIT的cookie

CRITICAL_SECTION m_CS; // 用于保护m_TaskManagerCookie

// 接口实现

public:

// IExample1Logic

STDMETHOD(Task1)();

STDMETHOD(Task2)();

// ISetTaskManager

STDMETHOD(SetTaskManager)( ITaskManager *pManager );

};

业务组件的源文件代码

extern IGlobalInterfaceTable *g_pGIT;

CExample1Logic::~CExample1Logic()

{

::DeleteCriticalSection( &m_CS );

ATLASSERT( g_pGIT );

// 注销原来的m_Cookie

if( m_Cookie != static_cast< DWORD >( -1 ) )

ATLVERIFY( SUCCEEDED(

g_pGIT->RevokeInterfaceFromGlobal( m_Cookie ) ) );

}

////////////////////////////IExample1Logic///////////////////////

STDMETHODIMP CExample1Logic::Task1()

{

// Sleep一段时间以模拟长时间的调用

for( DWORD i = 0; i < 30; i++ )

{

Sleep( 300 );

if( ::CoTestCancel() == RPC_E_CALL_CANCELED )

return E_ABORT;

}

return S_OK;

}

STDMETHODIMP CExample1Logic::Task2()

{

// 通过GIT获得正确的ITaskManager的代理

ITaskNotify *pNotify = NULL;

ITaskManager *pManager = NULL;

::EnterCriticalSection( &m_CS );

if( m_Cookie != static_cast< DWORD >( -1 ) )

{

ATLASSERT( g_pGIT );

// 不论GetInterfaceFromGlobal是否成功,都必须完成工作

if( SUCCEEDED( g_pGIT->GetInterfaceFromGlobal(

m_Cookie,

IID_ITaskManager,

reinterpret_cast< void** >( &pManager ) ) ) )

pManager->QueryInterface( IID_ITaskNotify,

reinterpret_cast< void** >( &pNotify ) );

}

::LeaveCriticalSection( &m_CS );

// 开始任务

static DWORD index = 1;

ITask *pTask = NULL;

CComObject< CTask > *pTaskObject = NULL;

if( FAILED( CComObject< CTask >::CreateInstance( &pTaskObject ) ) )

return E_FAIL;

ATLVERIFY( SUCCEEDED( pTaskObject->QueryInterface(

IID_ITask,

reinterpret_cast< void** >( &pTask ) ) ) );

DWORD cookie = static_cast< DWORD >( -1 );

WCHAR temp[10];

wsprintf( temp, L"任务二%d", index++ );

pTaskObject->m_hEvent = ::CreateEvent( NULL, TRUE, FALSE, NULL );

if( pManager && FAILED( pManager->AddTask( temp, pTask, &cookie ) ) )

cookie = static_cast< DWORD >( -1 );

SafeRelease( pManager );

// Sleep各段时间以模拟各步工作的完成

if( ::WaitForSingleObject( pTaskObject->m_hEvent, 4000 ) == WAIT_OBJECT_0 )

goto TERMINATE;

pTaskObject->m_Rate = 0.3f;

if( pNotify )

pNotify->ProcessRateChange( cookie );

if( ::WaitForSingleObject( pTaskObject->m_hEvent, 4000 ) == WAIT_OBJECT_0 )

goto TERMINATE;

pTaskObject->m_Rate = 0.6f;

if( pNotify )

pNotify->ProcessRateChange( cookie );

if( ::WaitForSingleObject( pTaskObject->m_hEvent, 4000 ) == WAIT_OBJECT_0 )

goto TERMINATE;

pTaskObject->m_Rate = 0.9f;

if( pNotify )

pNotify->ProcessRateChange( cookie );

if( pNotify )

{

if( cookie != static_cast< DWORD >( -1 ) )

pNotify->TaskOver( cookie );

pNotify->Release();

}

pTask->Release();

return S_OK;

TERMINATE:

if( pNotify )

{

if( cookie != static_cast< DWORD >( -1 ) )

pNotify->TaskOver( cookie );

pNotify->Release();

}

pTask->Release();

return RPC_E_CALL_CANCELED;

}

/////////////////////////////ISetTaskManager////////////////////////

STDMETHODIMP CExample1Logic::SetTaskManager( ITaskManager *pManager )

{

if( !pManager )

return E_POINTER;

pManager->AddRef();

ATLASSERT( g_pGIT );

// 注销原来的m_Cookie

::EnterCriticalSection( &m_CS );

if( m_Cookie != static_cast< DWORD >( -1 ) )

ATLVERIFY( SUCCEEDED(

g_pGIT->RevokeInterfaceFromGlobal( m_Cookie ) ) );

// 注册新的m_Cookie

if( FAILED( g_pGIT->RegisterInterfaceInGlobal( pManager,

IID_ITaskManager,

&m_Cookie ) ) )

::InterlockedExchange( reinterpret_cast< LONG* >( &m_Cookie ),

static_cast< DWORD >( -1 ) );

::LeaveCriticalSection( &m_CS );

pManager->Release();

return S_OK;

}

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有