本文只介紹《COM應用程序框架》的主要設計部分,更多、更詳細的文檔信息請參見下 載文件包中的文檔和源代碼。
一、設計說明
《COM應用程序框架》是把標准的Microsoft Windows多文檔處理應用程序使用COM技術 來設計.所以針對多文檔處理應用程序的需求,不再多寫。如果您不了解請參見MSDN或者 是其它編程基礎方面的書籍。
《COM應用程序框架》分為兩種,一種是COM多文檔應用程序框架,第二種是單文檔應 用程序框架。在這裡我們只介紹多文檔應用程序,不介紹單文檔應用程序。
《COM應用程序框架》建立在一個單獨的AIFrame.DLL文件中,所有的功能都通過COM接 口進行操作.《COM應用程序框架》將使用如下兩種庫的組合進行設計,1.使用MFC+ATL組 合, 2.使用WTL+ATL組合,下面分別對這兩種組合的優點和缺點說明一下,最後選擇一種 最優組合。
1.使用MFC+ATL組合圖:
2.使用WTL+ATL組合圖:
從上面兩附圖中很容易看出,使用MFC+ATL組合開發COM應用程序框架,MFC存在一層函 數調用,代碼執行速度會慢一些。小程序可能看不出來,大程序也就明顯了。 如果 使用WTL+ATL組合開發COM應用程序框架,就不會多一層調用,代碼執行速度非常快,就像 是用Win32 SDK編寫代碼一樣沒有什麼區別,因為WTL是模板代碼,在編譯後不會存在一層 函數調用。所以《COM應用程序框架》將采用WTL+ATL組合進行設計,這可能是最佳方案。
二、通用設計
1.數據視圖
所謂數據視圖,就是添加到《COM應用程序框架》中每一個窗口,無論這個窗口是用做 什麼,還是什麼形狀的,統稱為數據視圖。
所有客戶端程序向《COM應用程序框架》添加的數據視圖必須從IDataView純虛接口繼 承下來,必須是.這樣《COM應用程序框架》才能工作正常。
數據視圖純虛接口:IDataView。父類是IDispatch。
2.命令的響應函數
函數名稱:NotifyCommand(UINT codeNotify, UINT cmdID, VARIANT_BOOL *bHandle);
參數:codeNotify - 通報代碼,現在沒有使用。
cmdID – 某個命令ID,可以是菜單也可以是工具格中的按鈕。
bHandle – 如果命令還繼續向下路徑設置為VARIANT_TRUE,不向下路徑設置為 VARIANT_FALSE.
三、《COM多文檔應用程序框架》設計
1.框架
《COM多文檔(MDI)應用程序框架》只有一個MDI主窗口,但是可以擁有N多MDI子窗口, 每個MDI子窗口可以擁有多個數據視圖,每一個數據視圖必須從IDataView接口繼承下來。
MDI主窗口框架類名:CMDIFrame,接口是IMDIFrame,是所有MDI子窗口、停靠數據視圖 、工具條、菜單的容器,並負責把命令分發給當前活動的MDI子窗口或者是數據視圖。MDI 子窗口框架類名:CMDIChildFrame,接口是IMDIChildFrame。可以擁有N多數據視圖,並把 命令分發給當前活動的數據視圖。
MDI子窗口框架集合類名:CMDIChildFrames,接口是IMDIChildFrames,用來管理所有 的MDI子窗口和創建DMI子窗口。
《COM多文檔應用程序框架》接口圖:
2.事件
MDIFrame事件 目前僅提供兩個事件。
OnQuit(VARIANT_BOOL *vbQuit)
NotifyCommand(UINT codeNotify, UINT cmdID, VARIANT_BOOL *bHandle)
IMDIChildFrame事件 目前僅提供一個事件。
NotifyCommand(UINT codeNotify, UINT cmdID, VARIANT_BOOL *bHandle)
3.命令路由
MDI主窗口框架、MDI子窗口框架事件類和數據視圖都擁有一個命令處理方法 NotifyCommand。《COM多文檔應用程序框架》就是通過NotifyCommand向客戶端發送命令 處理。
MDI主窗口事件首先接收到菜單或者是工具條命令,處理完必後返回或者是發送給MDI 子窗口框架事件處理,在MDI子窗口框架處理完必後返回或者是發送給讓當前活動的數據 視圖處理.完成後命令返回。
命令是否向下路由是有NotifyCommand方法的bHandle變量決定,設置為VARIANT_TRUE 命令向下路由,設置為VARIANT_FALSE命令不向下路由。
4.UML類圖
用COM設計的類比較長抓圖不太方便,UML圖、類的信息和函數成員請參見源代碼。
四、 客戶端設計
客戶端應用程序必須實現兩個功能塊,第一個是數據視圖,第二個是命令處理。
五、 項目文件夾說明 文件夾名稱 類型 說明 AiFrame .dll 《COM應用程序框架》全部代碼所在文件夾。 inc .h 《COM應用程序框架》客戶端使用的一些公用頭文件和已經設計的類。 MFC_Test_AiFrame .exe 使用MFC調用《COM應用程序框架》的實例 WTL_Test_AiFrame .exe 使用WTL調用《COM應用程序框架》的實例 Bin Bin 生成的.dll 和.exe文件存放的文件夾
六、講解使用MFC調用《COM應用程序框架》的實例
這個實例使用MFC庫調用《COM應用程序框架》建立起的一個簡單的文本程序。以方便 了解如何使用《COM應用程序框架》的流程和主要部分,和《COM應用程序框架》方便之處 。 使用MFC項目向導,建立一個對話框項目應用(項目名稱自己定義)假如是 MFC_Test_AiFrame項目。在stdafx.h文件加入。
#include "..\inc\aiframeimpl.h"
在某個 .cpp中加入
#include "..\inc\AiFrame_i.c"
向項目中添加一個文本編輯視圖類,如下圖.
新建立的類名是CTextView, 基類(Base class)是CeditView類。單擊完成按鈕。
打開TextView.h文件加入如下代碼:
CDataViewImp是IDataView數據視圖接口實現模板類。
class CTextView : public CEditView
,public CDataViewImpl
{
....
NC_BEGIN_MAP()
//在這裡加入您的視圖命令處理.
NC_END_MAP()
virtual HRESULT STDMETHODCALLTYPE CreateWnd(HWND hWndParent);...
}
打開TextView.cpp文件加入如下代碼: //必須有CreateWnd方法,有《COM應用 程序框架》調用。
HRESULT STDMETHODCALLTYPE CTextView::CreateWnd(HWND hWndParent)
{
CWnd *pWnd = (CWnd*)this;
CWnd *wndParent = CWnd::FromHandle(hWndParent);
CRect _Rect(0,0, 100, 100);
if (pWnd->Create(NULL,
("MFC CEditView "),
WS_HSCROLL|WS_VSCROLL|WS_CLIPSIBLINGS|WS_CLIPCHILDREN|WS_VISIBLE|ES_MULTILINE| ES_AUTOHSCROLL,
_Rect, wndParent, 0))
{
m_pEdit = &GetEditCtrl();
return S_OK;
}
return S_FALSE;
}
CxxxxxApp類中主要完成一個功能.繼承CNotifyObjectImpl對象,用來處理 《COM應用程序框架》發送來的命令.CNotifyObjectImpl類是InotifyObject接口的實現 模板類。建立主窗口和一個新建文件的子菜單響應該命令.
class CFrameApp : public CWinApp
,public CComObjectRootEx
,public IDispEventImpl
{
public:
CFrameApp();
IMDIFrame *m_lpMdiFrame;
void WinMain();
BEGIN_SINK_MAP(CFrameApp)
//不使用 _ATL_FUNC_INFO結構 iFrmae 在調用事件的時候返回"沒有注冊 的庫"。這是為什麼?
//SINK_ENTRY_EX(EventType, DIID__IMDIFrameEvents, 0x1, OnQuit)
//SINK_ENTRY_EX(EventType, DIID__IMDIFrameEvents, 0x2, NotifyCommand)
SINK_ENTRY_INFO(EventType, DIID__IMDIFrameEvents, DISPID_SHOW, OnQuit, &OnShowInfo1)
SINK_ENTRY_INFO(EventType, DIID__IMDIFrameEvents, DISPID_SHOW2, NotifyCommand, &OnShowInfo3)
END_SINK_MAP()
NC_BEGIN_MAP()
NC_COMMAND_ID_HANDLER(ID_FILE_NEW,FileNew)
NC_COMMAND_ID_HANDLER(ID_FILE_EXIT,OnFileExit)
NC_COMMAND_ID_HANDLER(ID_HELP_ABOUT,OnAbout)
NC_COMMAND_ID_HANDLER(ID_HELP_WINDOWS, OnWindowWindows)
NC_END_MAP()
HRESULT CreateMain();
void FileNew(UINT codeNotify, UINT cmdID, VARIANT_BOOL *bHandle);
void OnFileExit(UINT codeNotify, UINT cmdID, VARIANT_BOOL *bHandle);
void OnAbout(UINT codeNotify, UINT cmdID, VARIANT_BOOL *bHandle);
void OnWindowWindows(UINT codeNotify, UINT cmdID, VARIANT_BOOL *bHandle);
STDMETHODIMP OnQuit(VARIANT_BOOL *vbQuit)
{
//MessageBox(0, _T("Events.Quit"), _T(""), 0);
return S_OK;
}
// 重寫
public:
virtual BOOL InitInstance();
// 實現
DECLARE_MESSAGE_MAP()
};
HRESULT CFrameApp::CreateMain()
{
CreateMDIStruct lpMDI={0};
HRESULT hr = 0;
hr = CoCreateInstance(CLSID_MDIFrame, NULL, CLSCTX_ALL, IID_IMDIFrame, (VOID**)&m_lpMdiFrame);
if (FAILED(hr))
{
ATLASSERT(0);
return hr;
}
lpMDI.cbSize = sizeof(CreateMDIStruct);
lpMDI.lParam = NULL;
lpMDI.lpszWindowName = L"Test COM MDIFrame";
lpMDI.hMenu = LoadMenu(AfxGetResourceHandle(), MAKEINTRESOURCE (IDR_MAINFRAME));
hr = m_lpMdiFrame->CreateWnd(&lpMDI);
m_lpMdiFrame->ShowMe(VARIANT_TRUE);
hr = this->DispEventAdvise((IUnknown*)m_lpMdiFrame);
if (FAILED(hr))
{
ATLASSERT(0);
}
return hr;
}
處理新建文件菜單的命令:
void CFrameApp::FileNew(UINT codeNotify, UINT cmdID, VARIANT_BOOL *bHandle)
{
IMDIChildFrames *lpFrames = NULL;
IMDIChildFrame *lpChildFrame = NULL;
HRESULT hr = 0;
//從主窗口IMDIFrame接口引出IMDIChildFrames 集合.
hr = m_lpMdiFrame->get_MDIChildFrames((IDispatch**)&lpFrames);
if (FAILED(hr))
{
ATLASSERT(0);
}
//用IMDIChildFrames 集合建立一個mdi子窗口.lpChildFrame,反回子窗口接口類 .
CTextView *lpTexView = NULL;
lpTexView = new CTextView();
lpFrames->CreateChildFrame((IDataView*)lpTexView, &lpChildFrame);
//設置子窗口標題.
lpChildFrame->put_Title(L"MFC TextView");
}
七、其它信息
下載COM應用程序框架:http://www.vckbase.com/code/downcode.asp?id=2963