COM样例(四)
——客户端代码
本文为此系列文章的最后一篇,罗列客户端的源代码。客户端使用MFC实现,其中的框架类CMainFrame实现了IModuleSite、IErrorReport和ICompanyInfo,而另一个窗口包装类CTaskManager实现ITaskManager,并由CMainFrame聚合它以表现出CMainFrame实现了ITaskManager。由于代码较长,本篇只罗列CMainFrame::OnCreate和CMainFrame的定义,其中实现了获取部门组件的实例(通过COM的组件类别功能进行记录,而非通过注册表),并进行管理。
如果欲查看本样例的所有代码,可于文末下载。而本样例的可执行文件也请于文末下载。由于本样例只是我很早以前编的一个程序精简修改而成,代码中可能有不少幼稚的地方,我现在也没兴致再仔细修改,只是大致修整了一下,还请见谅。
CMainFrame的定义
#include "NewStatusBar.h"
class CMainFrame : public CFrameWnd
{
// MFC定义宏
DECLARE_DYNCREATE( CMainFrame )
DECLARE_MESSAGE_MAP()
DECLARE_INTERFACE_MAP()
// 辅助结构
private:
struct TEMPSTRUCT
{
HWND m_hWnd;
HMENU m_hMenu;
IModule *m_pModule;
CLSID m_CLSID;
TEMPSTRUCT() : m_hWnd( NULL ), m_hMenu( NULL ), m_pModule( NULL )
{
// 什么都不做
}
~TEMPSTRUCT()
{
SafeRelease( m_pModule );
}
};
// 构造、析构
public:
CMainFrame();
~CMainFrame();
// 操作
public:
void UpdateErrorState() // 更新状态条上的错误标志以表示指示最新错误的发生
{
ASSERT_VALID( this );
if( m_wndStatusBar.GetSafeHwnd() )
m_wndStatusBar.Invalidate();
}
// 辅助函数
protected:
// 根据菜单项ID确定是否是基本框架的菜单命令
BOOL BeMenuOfBase( DWORD nID ) const;
// 成员变量
protected:
CNewStatusBar m_wndStatusBar;
CToolBar m_wndToolBar;
DWORD m_Selected;
long m_OldViewID;
IUnknown *m_pTaskManager;
CTypedPtrList< CPtrList, TEMPSTRUCT* > m_ModuleList;
// 重载
protected:
void GetMessageString( UINT nID, CString &rMessage ) const;
BOOL OnCommand( WPARAM wParam, LPARAM lParam );
// 窗口消息
protected:
afx_msg int OnCreate( LPCREATESTRUCT lpCreateStruct );
afx_msg void OnActivate( UINT nState, CWnd* pWndOther, BOOL bMinimized );
afx_msg void OnSetFocus( CWnd *pOldWnd );
afx_msg void OnInitMenuPopup( CMenu *pPopupMenu, UINT nIndex, BOOL bSysMenu );
afx_msg void OnInitMenu( CMenu *pMenu );
afx_msg BOOL OnEraseBkgnd( CDC *pDC );
afx_msg void OnClose();
// 菜单消息
protected:
void OnModule( UINT nID );
void OnUpdateModule( CCmdUI *pCmdUI );
// 自定消息
protected:
afx_msg LRESULT OnAllTaskTerminated( WPARAM, LPARAM );
// 接口映射
public:
// IModuleSite
BEGIN_INTERFACE_PART( ModuleSite, IModuleSite )
INIT_INTERFACE_PART( CMainFrame, ModuleSite )
STDMETHOD(ChangeModule)( REFCLSID clsid,
WCHAR *pModuleName,
ULONG command,
ULONG param );
STDMETHOD(GetFrameWindow)( HWND *pHwnd );
END_INTERFACE_PART_STATIC( ModuleSite )
// IErrorReport
BEGIN_INTERFACE_PART( ErrorReport, IErrorReport )
INIT_INTERFACE_PART( CMainFrame, ErrorReport )
STDMETHOD(ReportSoftError)( WCHAR *fileName,
ULONG row,
WCHAR *errorString );
STDMETHOD(ReportHardError)( WCHAR *fileName,
ULONG row,
WCHAR *errorString );
END_INTERFACE_PART_STATIC( ErrorReport )
// ICompanyInfo
BEGIN_INTERFACE_PART( CompanyInfo, ICompanyInfo )
INIT_INTERFACE_PART( CMainFrame, CompanyInfo )
STDMETHOD(GetDataServerInfo)( WCHAR **pLoaction,
WCHAR **pServer,
WCHAR **pPassword );
END_INTERFACE_PART_STATIC( CompanyInfo )
};
CMainFrame::OnCreate代码
{
if( CFrameWnd::OnCreate( lpCreateStruct ) == -1 )
return -1;
// 创建基本工具条及状态条
EnableDocking( CBRS_ALIGN_ANY );
if( !m_wndToolBar.CreateEx( this,
TBSTYLE_FLAT,
WS_CHILD | WS_VISIBLE | CBRS_TOP |
CBRS_GRIPPER | CBRS_TOOLTIPS |
CBRS_FLYBY | CBRS_SIZE_DYNAMIC,
CRect( 0, 0, 0, 0 ) ) )
WriteSoftErrorLog( __LINE__,
__WFILE__,
IDS_MAINFRM_CREATETOOLBAR );
else
{
m_wndToolBar.EnableDocking( CBRS_ALIGN_ANY );
DockControlBar( &m_wndToolBar );
}
if( !m_wndStatusBar.Create( this ) ||
!m_wndStatusBar.SetIndicators( indicators,
sizeof( indicators ) / sizeof( UINT ) ) )
{
WriteSoftErrorLog( __LINE__,
__WFILE__,
IDS_MAINFRM_CREATESTATUSBAR );
return -1;
}
else
{
CCmdTarget *pTarget = m_wndStatusBar.Initial();
if( !pTarget )
{
WriteSoftErrorLog( __LINE__,
__WFILE__,
IDS_MAINFRM_CREATESTATUSBAR );
return -1;
}
ASSERT_VALID( pTarget );
// 设置任务管理器的指针以使得CMainFrame聚合CTaskManager
pTarget->m_pOuterUnknown = GetControllingUnknown();
m_pTaskManager = reinterpret_cast< IUnknown* >( &pTarget->m_xInnerUnknown );
ASSERT( m_pTaskManager );
}
// 创建组件类别管理器并获得模块CLSID枚举器
IEnumCLSID *pEnum = NULL;
ICatInformation *pCat = NULL;
CATID tempCATID[1] = { CATID_Example };
if( FAILED( CoCreateInstance( CLSID_StdComponentCategoriesMgr,
NULL,
CLSCTX_INPROC_SERVER,
IID_ICatInformation,
reinterpret_cast< void** >( &pCat ) ) ) ||
FAILED( pCat->EnumClassesOfCategories( 1, tempCATID, 0, NULL, &pEnum ) ) )
{
MessageBox( L"致命错误!系统即将退出。", g_SysCaption );
WriteHardErrorLog( __LINE__,
__WFILE__,
IDS_KILLINGERROR );
SafeRelease( pCat );
SafeRelease( pEnum );
return -1;
}
pCat->Release();
// 枚举每个模块信息
g_theApp.BeginWaitCursor();
CLSID clsid;
CStringW temp;
TEMPSTRUCT *pStruct = NULL;
IModuleUI *pUI = NULL;
IMenuUpdate *pMenuUp = NULL;
long index = -1;
IModuleSite *pSite = static_cast< IModuleSite* >(
GetInterface( &IID_IModuleSite ) );
pSite->AddRef();
while( pEnum->Next( 1, &clsid, NULL ) == S_OK )
{
++index;
pStruct = new TEMPSTRUCT;
if( !pStruct )
{
WriteSoftErrorLog( __LINE__,
__WFILE__,
IDS_OUTOFMEMORY );
continue;
}
// 创建模块对象
if( FAILED( CoCreateInstance( clsid,
NULL,
CLSCTX_INPROC_SERVER,
IID_IModule,
reinterpret_cast< void** >(
&pStruct->m_pModule ) ) ) )
{
WriteSoftErrorLog( __LINE__,
__WFILE__,
IDS_MAINFRM_CREATEMODULE );
delete pStruct;
continue;
}
pStruct->m_CLSID = clsid;
// 初始化模块
if( FAILED( pStruct->m_pModule->InitialModule( pSite,
IDC_MODULE_VIEW +
index –
1 ) ) )
{
WriteSoftErrorLog( __LINE__,
__WFILE__,
IDS_MAINFRM_CREATEMODULE );
delete pStruct;
continue;
}
// 获取模块窗口
if( FAILED( pStruct->m_pModule->QueryInterface(
IID_IModuleUI,
reinterpret_cast< void** >( &pUI ) ) ) )
{
WriteSoftErrorLog( __LINE__,
__WFILE__,
IDS_MAINFRM_GETMODULEWND );
delete pStruct;
continue;
}
if( FAILED( pUI->GetMainWindow( &pStruct->m_hWnd ) ) || !pStruct->m_hWnd )
{
WriteSoftErrorLog( __LINE__,
__WFILE__,
IDS_MAINFRM_GETMODULEWND );
pUI->Release();
delete pStruct;
continue;
}
pUI->Release();
{
// 修改窗口风格,确保模块窗口具有WS_EX_CLIENTEDGE风格
if( !::SetWindowLong( pStruct->m_hWnd,
GWL_EXSTYLE,
::GetWindowLong( pStruct->m_hWnd,
GWL_EXSTYLE ) |
WS_EX_CLIENTEDGE ) )
WriteSoftErrorLog( __LINE__,
__WFILE__,
IDS_SETWINDOWSTYLE );
}
// 隐藏模块窗口
::ShowWindow( pStruct->m_hWnd, SW_HIDE );
// 获取模块的菜单句柄,并插入到MainFrame
if( SUCCEEDED( pStruct->m_pModule->QueryInterface(
IID_IMenuUpdate,
reinterpret_cast< void** >( &pMenuUp ) ) ) )
{
// 模块提供了菜单,获取菜单句柄
if( FAILED( pMenuUp->GetMenu( &pStruct->m_hMenu ) ) )
WriteSoftErrorLog( __LINE__,
__WFILE__,
IDS_MAINFRM_CREATEMODULEMENU );
pMenuUp->Release();
}
m_ModuleList.AddTail( pStruct );
}
pSite->Release();
pEnum->Release();
// 一个模块都不激活
m_Selected = static_cast< ULONG >( -1 );
ULONG count = m_ModuleList.GetCount();
if( count )
{
// 获取模块的图标,并将其插入到模块工具条和菜单中
m_wndToolBar.SetSizes( CSize( 23, 22 ), CSize( 16, 15 ) );
if( !m_wndToolBar.SetButtons( NULL, count ) )
WriteSoftErrorLog( __LINE__,
__WFILE__,
IDS_CREATETOOLBARBUTTON );
else
{
// 将图标画到位图中
CBitmap bitmap;
CDC *pScreen = CDC::FromHandle( ::GetDC( NULL ) );
CDC sdc;
if( !bitmap.CreateCompatibleBitmap( pScreen,
ICONWIDTH * count,
ICONHEIGHT ) ||
!sdc.CreateCompatibleDC( pScreen ) )
{
WriteSoftErrorLog( __LINE__,
__WFILE__,
L"创建缓冲位图失败!" );
return 0;
}
sdc.SelectObject( &bitmap );
sdc.FillSolidRect( 0,
0,
ICONWIDTH * count,
ICONHEIGHT,
RGB( 0xC0, 0xC0, 0xC0 ) );
HICON hIcon = NULL;
ULONG i = 0;
POSITION pos = m_ModuleList.GetHeadPosition();
CMenu *pMenu = GetMenu()->GetSubMenu(0)->GetSubMenu(0);
ASSERT_VALID( pMenu );
// 清空原来的占位符
while( pMenu->DeleteMenu( 0, MF_BYPOSITION ) );
while( pos )
{
pStruct = m_ModuleList.GetNext( pos );
ASSERT( pStruct );
// 将图标画到位图上
if( SUCCEEDED( pStruct->m_pModule->GetIcon( &hIcon ) ) )
::DrawIconEx( sdc.GetSafeHdc(),
i * ICONWIDTH,
0,
hIcon,
ICONWIDTH,
ICONHEIGHT,
0,
NULL,
DI_NORMAL );
else
WriteSoftErrorLog( __LINE__,
__WFILE__,
IDS_MAINFRM_GETMODULEICON );
// 添加菜单
{
// 获取模块的名字
WCHAR *io = NULL;
if( FAILED( pStruct->m_pModule->GetName( &io ) ) )
WriteSoftErrorLog( __LINE__,
__WFILE__,
IDS_MAINFRM_GETMODULENAME );
else
{
if( !pMenu->AppendMenu( MF_STRING, ID_MODULE + i, io ) )
WriteSoftErrorLog( __LINE__,
__WFILE__,
IDS_MAINFRM_CREATEMODULEMENU );
::CoTaskMemFree( io );
}
}
// 设置工具条上的按钮
m_wndToolBar.SetButtonInfo( i, ID_MODULE + i, TBBS_BUTTON, i );
i++;
}
// 将位图设置到工具条中
if( !m_wndToolBar.SetBitmap(
static_cast< HBITMAP >( bitmap.Detach() ) ) )
WriteSoftErrorLog( __LINE__,
__WFILE__,
IDS_SETTOOLBARBITMAP );
}
}
g_theApp.EndWaitCursor();
return 0;
}