TN041:MFC/OLE1 移轉到 MFC/OLE 2
注意
下列技術提示自其納入線上文件以來,未曾更新。 因此,有些程序和主題可能已過期或不正確。 如需最新資訊,建議您在線上文件索引中搜尋相關的主題。
與移轉相關的一般問題
MFC 2.5 中 OLE 2 類別的設計目標之一(和更新版本)是保留 MFC 2.0 中為 OLE 1.0 支援的相同架構。 因此,MFC 2.0 中的許多相同 OLE 類別仍存在於這個版本的 MFC ( COleDocument
、 COleServerDoc
、 COleClientItem
、 COleServerItem
。 此外,這些類別中的許多 API 完全相同。 不過,OLE 2 與 OLE 1.0 大不相同,因此您可以預期部分詳細資料已變更。 如果您熟悉 MFC 2.0 的 OLE1 支援,您將熟悉 MFC 的 2.0 支援。
如果您要取得現有的 MFC/OLE1 應用程式,並在其中新增 OLE 2 功能,您應該先閱讀此附注。 此附注涵蓋將 OLE1 功能移植到 MFC/OLE 2 時可能會遇到的一些一般問題,然後討論移植 MFC 2.0 中包含的兩個應用程式時發現的問題:MFC OLE 範例 OCLIENT 和 HIERSVR 。
MFC 檔/檢視架構很重要
如果您的應用程式未使用 MFC 的檔/檢視架構,而且您想要將 OLE 2 支援新增至您的應用程式,現在是移至檔/檢視的時候了。 MFC OLE 2 類別的許多優點只有在您的應用程式使用 MFC 的內建架構和元件之後才會實現。
您可以實作伺服器或容器而不使用 MFC 架構,但不建議這麼做。
使用 MFC 實作,而不是您自己的實作
MFC「罐裝實作」類別,例如 CToolBar
、 CStatusBar
和 CScrollView
具有 OLE 2 支援的內建特殊案常式序代碼。 因此,如果您在應用程式中使用這些類別,您將受益于投入到這些類別中,讓他們知道 OLE。 同樣地,基於這些目的,您可以在這裡「推出您自己的」類別,但不建議使用。 如果您需要實作類似的功能,MFC 原始程式碼是處理 OLE 某些更精細點的絕佳參考(特別是在就地啟用時)。
檢查 MFC 範例程式碼
有數個 MFC 範例包含 OLE 功能。 這些應用程式都會從不同的角度實作 OLE:
HIERSVR 主要用來作為伺服器應用程式。 它包含在 MFC 2.0 中作為 MFC/OLE1 應用程式,並已移植到 MFC/OLE 2,然後擴充,使其實作 OLE 2 中可用的許多 OLE 功能。
OCLIENT 這是獨立的容器應用程式,旨在從容器的觀點示範許多 OLE 功能。 它也從 MFC 2.0 移植,然後擴充以支援許多更進階的 OLE 功能,例如自訂剪貼簿格式和內嵌專案的連結。
DRAWCLI 此應用程式會實作 OLE 容器支援,就像 OCLIENT 一樣,不同之處在于它會在現有物件導向繪圖程式的架構內執行此動作。 它說明如何實作 OLE 容器支援,並將其整合到現有的應用程式中。
SUPERPAD 這個應用程式,以及一個優秀的獨立應用程式,也是 OLE 伺服器。 其實作的伺服器支援相當極簡。 特別感興趣的是,它如何使用 OLE 剪貼簿服務將資料複製到剪貼簿,但會使用 Windows「編輯」控制項內建的功能來實作剪貼簿貼上功能。 這會顯示傳統 Windows API 使用方式以及與新 OLE API 整合的有趣組合。
如需範例應用程式的詳細資訊,請參閱
案例研究:MFC 2.0 中的 OCLIENT
如上所述, OCLIENT 包含在 MFC 2.0 中,並使用 MFC/OLE1 實作 OLE。 此應用程式最初轉換成使用 MFC/OLE 2 類別的步驟如下所述。 初始埠完成之後已新增許多功能,以更清楚地說明 MFC/OLE 類別。 此處不會涵蓋這些功能;如需這些進階功能的詳細資訊,請參閱範例本身。
注意
編譯器錯誤和逐步程式是使用 Visual C++ 2.0 建立的。 Visual C++ 4.0 可能會變更特定錯誤訊息和位置,但概念資訊仍然有效。
啟動並執行
將 OCLIENT 範例移植到 MFC/OLE 所採取的方法,是從建置它開始,並修正將產生的明顯編譯器錯誤。 如果您從 MFC 2.0 取得 OCLIENT 範例,並在此版本的 MFC 下編譯,您會發現沒有多少錯誤可解決。 錯誤發生的順序如下所述。
編譯和修正錯誤
\oclient\mainview.cpp(104) : error C2660: 'Draw' : function does not take 4 parameters
第一個錯誤涉及 COleClientItem::Draw
。 在 MFC/OLE1 中,它所採用的參數比 MFC/OLE 版本多。 額外的參數通常並非必要,通常為 Null(如本範例所示)。 當繪製至 的 CDC 是中繼檔 DC 時,此版本的 MFC 可以自動判斷 lpWBounds 的值。 此外,pFormatDC 參數已不再需要,因為架構會從傳入之 pDC 的 「attribute DC」 建置一個參數。 因此,若要修正此問題,您只需移除 Draw 呼叫的兩個額外的 Null 參數即可。
\oclient\mainview.cpp(273) : error C2065: 'OLE_MAXNAMESIZE' : undeclared identifier
\oclient\mainview.cpp(273) : error C2057: expected constant expression
\oclient\mainview.cpp(280) : error C2664: 'CreateLinkFromClipboard' : cannot convert parameter 1 from 'char [1]' to 'enum ::tagOLERENDER '
\oclient\mainview.cpp(286) : error C2664: 'CreateFromClipboard' : cannot convert parameter 1 from 'char [1]' to 'enum ::tagOLERENDER '
\oclient\mainview.cpp(288) : error C2664: 'CreateStaticFromClipboard' : cannot convert parameter 1 from 'char [1]' to 'enum ::tagOLERENDER '
上述錯誤是由於 COleClientItem::CreateXXXX
MFC/OLE1 中的所有函式都需要傳遞唯一名稱來表示專案。 這是基礎 OLE API 的需求。 MFC/OLE 2 中不需要這樣做,因為 OLE 2 不會使用 DDE 作為基礎通訊機制(在 DDE 交談中使用名稱)。 若要修正此問題,您可以移除函 CreateNewName
式以及其所有參考。 只要將游標放在呼叫上並按 F1,即可輕鬆瞭解每個 MFC/OLE 函式在此版本中預期的情況。
另一個明顯不同的區域是 OLE 2 剪貼簿處理。 使用 OLE1 時,您使用 Windows 剪貼簿 API 與剪貼簿互動。 使用 OLE 2 時,會使用不同的機制來完成。 MFC/OLE1 API 假設剪貼簿在將物件複製到 COleClientItem
剪貼簿之前已開啟。 這已不再需要,而且會導致所有 MFC/OLE 剪貼簿作業失敗。 當您編輯程式碼以移除 相依性時 CreateNewName
,您也應該移除開啟並關閉 Windows 剪貼簿的程式碼。
\oclient\mainview.cpp(332) : error C2065: 'AfxOleInsertDialog' : undeclared identifier
\oclient\mainview.cpp(332) : error C2064: term does not evaluate to a function
\oclient\mainview.cpp(344) : error C2057: expected constant expression
\oclient\mainview.cpp(347) : error C2039: 'CreateNewObject' : is not a member of 'CRectItem'
這些錯誤來自 CMainView::OnInsertObject
處理常式。 處理 「插入新物件」命令是另一個已變更相當多的區域。 在此情況下,最簡單的方式就是將原始實作與 AppWizard 為新的 OLE 容器應用程式所提供的實作合併。 事實上,這是一種技術,您可以套用至移植其他應用程式。 在 MFC/OLE1 中,您藉由呼叫 AfxOleInsertDialog
函式來顯示 [插入物件] 對話方塊。 在此版本中,您會建構 COleInsertObject
對話方塊物件並呼叫 DoModal
。 此外,會使用 CLSID 建立新的 OLE 專案,而不是 classname 字串。 最終結果看起來應該像這樣
COleInsertDialog dlg;
if (dlg.DoModal() != IDOK)
return;
BeginWaitCursor();
CRectItem* pItem = NULL;
TRY
{
// First create the C++ object
pItem = GetDocument()->CreateItem();
ASSERT_VALID(pItem);
// Initialize the item from the dialog data.
if (!dlg.CreateItem(pItem))
AfxThrowMemoryException();
// any exception will do
ASSERT_VALID(pItem);
// run the object if appropriate
if (dlg.GetSelectionType() == COleInsertDialog::createNewItem)
pItem->DoVerb(OLEIVERB_SHOW, this);
// update right away
pItem->UpdateLink();
pItem->UpdateItemRectFromServer();
// set selection to newly inserted item
SetSelection(pItem);
pItem->Invalidate();
}
CATCH (CException, e)
{
// clean up item
if (pItem != NULL)
GetDocument()->DeleteItem(pItem);
AfxMessageBox(IDP_FAILED_TO_CREATE);
}
END_CATCH
EndWaitCursor();
注意
針對您的應用程式,插入新物件可能不同):
此外,還必須包含 < afxodlgs.h > ,其中包含對話方塊類別的宣告 COleInsertObject
,以及 MFC 所提供的其他標準對話。
\oclient\mainview.cpp(367) : error C2065: 'OLEVERB_PRIMARY' : undeclared identifier
\oclient\mainview.cpp(367) : error C2660: 'DoVerb' : function does not take 1 parameters
這些錯誤是由某些 OLE1 常數在 OLE 2 中變更,即使概念相同,也會導致這些錯誤。 在此情況下 OLEVERB_PRIMARY
,已變更為 OLEIVERB_PRIMARY
。 在 OLE1 和 OLE 2 中,當使用者按兩下專案時,通常會由容器執行主要動詞。
此外, DoVerb
現在會採用額外的參數 — 檢視指標 ( CView
*)。 此參數僅用於實作「視覺編輯」(或就地啟用)。 現在您已將該參數設定為 Null,因為您目前並未實作這項功能。
若要確定架構永遠不會嘗試就地啟用,您應該覆寫 COleClientItem::CanActivate
,如下所示:
BOOL CRectItem::CanActivate()
{
return FALSE;
}
\oclient\rectitem.cpp(53) : error C2065: 'GetBounds' : undeclared identifier
\oclient\rectitem.cpp(53) : error C2064: term does not evaluate to a function
\oclient\rectitem.cpp(84) : error C2065: 'SetBounds' : undeclared identifier
\oclient\rectitem.cpp(84) : error C2064: term does not evaluate to a function
在 MFC/OLE1 中, COleClientItem::GetBounds
用來 SetBounds
查詢和操作專案的範圍( left
和 top
成員一律為零)。 在 MFC/OLE 2 中,與 會處理 SIZE 或 改為更直接地支援 COleClientItem::GetExtent
SetExtent
。 CSize
新 SetItemRectToServer 和 UpdateItemRectFromServer 呼叫的程式碼如下所示:
BOOL CRectItem::UpdateItemRectFromServer()
{
ASSERT(m_bTrackServerSize);
CSize size;
if (!GetExtent(&size))
return FALSE; // blank
// map from HIMETRIC to screen coordinates
{
CClientDC screenDC(NULL);
screenDC.SetMapMode(MM_HIMETRIC);
screenDC.LPtoDP(&size);
}
// just set the item size
if (m_rect.Size() != size)
{
// invalidate the old size/position
Invalidate();
m_rect.right = m_rect.left + size.cx;
m_rect.bottom = m_rect.top + size.cy;
// as well as the new size/position
Invalidate();
}
return TRUE;
}
BOOL CRectItem::SetItemRectToServer()
{
// set the official bounds for the embedded item
CSize size = m_rect.Size();
{
CClientDC screenDC(NULL);
screenDC.SetMapMode(MM_HIMETRIC);
screenDC.DPtoLP(&size);
}
TRY
{
SetExtent(size); // may do a wait
}
CATCH(CException, e)
{
return FALSE; // links will not allow SetBounds
}
END_CATCH
return TRUE;
}
\oclient\frame.cpp(50) : error C2039: 'InWaitForRelease' : is not a member of 'COleClientItem'
\oclient\frame.cpp(50) : error C2065: 'InWaitForRelease' : undeclared identifier
\oclient\frame.cpp(50) : error C2064: term does not evaluate to a function
在 MFC/OLE1 同步 API 呼叫中,從容器對伺服器 進行模擬 ,因為在許多情況下,OLE1 原本就是非同步。 在處理使用者的命令之前,必須先檢查進行中未處理的非同步呼叫。 MFC/OLE1 提供 COleClientItem::InWaitForRelease
函式來進行此動作。 在 MFC/OLE 2 中,這並非必要,因此您可以一起移除 CMainFrame 中 OnCommand 的覆寫。
此時,OCLIENT 將會編譯和連結。
其他必要的變更
不過,沒有完成的動作會讓 OCLIENT 無法執行。 現在最好是修正這些問題,而不是稍後的問題。
首先,必須初始化 OLE 程式庫。 這是藉由從 InitInstance
呼叫 AfxOleInit
來完成:
if (!AfxOleInit())
{
AfxMessageBox("Failed to initialize OLE libraries");
return FALSE;
}
檢查虛擬函式是否有參數清單變更也是個好主意。 其中一個這類函式是 COleClientItem::OnChange
,在每個 MFC/OLE 容器應用程式中覆寫。 藉由查看線上說明,您會看到已新增額外的 'DWORD dwParam'。 新的 CRectItem::OnChange 如下所示:
void
CRectItem::OnChange(OLE_NOTIFICATION wNotification, DWORD dwParam)
{
if (m_bTrackServerSize && !UpdateItemRectFromServer())
{
// Blank object
if (wNotification == OLE_CLOSED)
{
// no data received for the object - destroy it
ASSERT(!IsVisible());
GetDocument()->DeleteItem(this);
return; // no update (item is gone now)
}
}
if (wNotification != OLE_CLOSED)
Dirty();
Invalidate();
// any change will cause a redraw
}
在 MFC/OLE1 中,容器應用程式衍生自 COleClientDoc
的檔類別。 在 MFC/OLE 2 中,這個類別已被移除並取代 COleDocument
(這個新組織可讓您更輕鬆地建置容器/伺服器應用程式)。 有一個 #define 對應 COleClientDoc
來 COleDocument
簡化 MFC/OLE1 應用程式的移植到 MFC/OLE 2,例如 OCLIENT。 所提供的其中一個功能 COleDocument
COleClientDoc
是標準命令訊息對應專案。 如此一來,除非伺服器應用程式是容器/伺服器應用程式,否則伺服器應用程式也會使用 (間接使用 COleDocument
),否則不會攜帶這些命令處理常式的額外負荷。 您必須將下列專案新增至 CMainDoc 訊息對應:
ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE, OnUpdatePasteMenu)
ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE_LINK, OnUpdatePasteLinkMenu)
ON_UPDATE_COMMAND_UI(ID_OLE_EDIT_LINKS, OnUpdateEditLinksMenu)
ON_COMMAND(ID_OLE_EDIT_LINKS, COleDocument::OnEditLinks)
ON_UPDATE_COMMAND_UI(ID_OLE_VERB_FIRST, OnUpdateObjectVerbMenu)
ON_UPDATE_COMMAND_UI(ID_OLE_EDIT_CONVERT, OnUpdateObjectVerbMenu)
ON_COMMAND(ID_OLE_EDIT_CONVERT, OnEditConvert)
所有這些命令的實作都在 COleDocument
中,這是您檔的基類。
此時,OCLIENT 是功能 OLE 容器應用程式。 可以插入任何類型的專案(OLE1 或 OLE 2)。 由於未實作啟用就地啟用的必要程式碼,因此專案會在與 OLE1 一樣的不同視窗中編輯。 下一節將討論啟用就地編輯所需的變更(有時稱為「視覺編輯」)。
新增「視覺編輯」
OLE 最有趣的功能之一是就地啟用(或「視覺編輯」)。 這項功能可讓伺服器應用程式接管容器使用者介面的部分,為使用者提供更順暢的編輯介面。 若要對 OCLIENT 實作就地啟用,必須新增一些特殊資源,以及一些額外的程式碼。 這些資源和程式碼通常是由 AppWizard 提供,事實上,這裡的大部分程式碼都是直接從具有「容器」支援的全新 AppWizard 應用程式借用。
首先,當有專案就地使用時,必須新增要使用的功能表資源。 您可以複製IDR_OCLITYPE資源,並移除檔案和視窗快顯視窗,以在 Visual C++ 中建立此額外的功能表資源。 在 [檔案] 和 [視窗] 快顯視窗之間插入兩個分隔線,以指出群組的分隔線(看起來應該像: File || Window
。 如需這些分隔符號的意義,以及伺服器和容器功能表合併方式的詳細資訊,請參閱 功能表和資源:功能表合併 。
建立這些功能表之後,您必須讓架構知道這些功能表。 這會先呼叫 CDocTemplate::SetContainerInfo
檔範本,再將它新增至 InitInstance 中的檔範本清單。 要註冊檔範本的新程式碼如下所示:
CDocTemplate* pTemplate = new CMultiDocTemplate(
IDR_OLECLITYPE,
RUNTIME_CLASS(CMainDoc),
RUNTIME_CLASS(CMDIChildWnd), // standard MDI child frame
RUNTIME_CLASS(CMainView));
pTemplate->SetContainerInfo(IDR_OLECLITYPE_INPLACE);
AddDocTemplate(pTemplate);
IDR_OLECLITYPE_INPLACE資源是在 Visual C++ 中建立的特殊就地資源。
若要啟用就地啟用,在 (CMainView) 衍生類別以及 COleClientItem
衍生類別 (CRectItem) 中 CView
,有一些專案需要變更。 所有這些覆寫都是由 AppWizard 提供,大部分的實作都直接來自預設的 AppWizard 應用程式。
在此埠的第一個步驟中,完全藉由覆寫 COleClientItem::CanActivate
來停用就地啟用。 應該移除此覆寫以允許就地啟用。 此外,Null 已傳遞至所有呼叫 DoVerb
(有兩個呼叫),因為提供檢視只是就地啟用的必要專案。 若要完全實作就地啟用,必須在呼叫中 DoVerb
傳遞正確的檢視。 下列其中一個呼叫位於 CMainView::OnInsertObject
中:
pItem->DoVerb(OLEIVERB_SHOW, this);
另一個位於 CMainView::OnLButtonDblClk
:
m_pSelection->DoVerb(OLEIVERB_PRIMARY, this);
必須覆寫 COleClientItem::OnGetItemPosition
。 這會告訴伺服器在專案就地啟動時,將視窗相對於容器視窗放置的位置。 針對 OCLIENT,實作是微不足道的:
void CRectItem::OnGetItemPosition(CRect& rPosition)
{
rPosition = m_rect;
}
大部分的伺服器也會實作所謂的「就地調整大小」。這可讓使用者在編輯專案時調整伺服器視窗的大小和移動。 容器必須參與此動作,因為移動或調整視窗大小通常會影響容器檔案本身的位置和大小。 OCLIENT 的實作會同步處理m_rect與新位置和大小所維護的內部矩形。
BOOL CRectItem::OnChangeItemPosition(const CRect& rectPos)
{
ASSERT_VALID(this);
if (!COleClientItem::OnChangeItemPosition(rectPos))
return FALSE;
Invalidate();
m_rect = rectPos;
Invalidate();
GetDocument()->SetModifiedFlag();
return TRUE;
}
此時,有足夠的程式碼可讓專案就地啟用,並在專案使用中時處理調整大小和移動專案,但沒有任何程式碼可讓使用者結束編輯會話。 雖然有些伺服器會藉由處理逸出金鑰來自行提供這項功能,但建議容器提供兩種方式來停用專案:(1)按一下專案外部,然後按 ESCAPE 鍵(2)。
針對 ESCAPE 索引鍵,使用 Visual C++ 新增快速鍵,將VK_ESCAPE索引鍵對應至命令,ID_CANCEL_EDIT會新增至資源。 此命令的處理常式如下:
// The following command handler provides the standard
// keyboard user interface to cancel an in-place
// editing session.void CMainView::OnCancelEdit()
{
// Close any in-place active item on this view.
COleClientItem* pActiveItem =
GetDocument()->GetInPlaceActiveItem(this);
if (pActiveItem != NULL)
pActiveItem->Close();
ASSERT(GetDocument()->GetInPlaceActiveItem(this) == NULL);
}
若要處理使用者按一下專案外部的情況,請將下列程式碼新增至 開頭 CMainView::SetSelection
:
if (pNewSel != m_pSelection || pNewSel == NULL)
{
COleClientItem* pActiveItem =
GetDocument()->GetInPlaceActiveItem(this);
if (pActiveItem != NULL&& pActiveItem != pNewSel)
pActiveItem->Close();
}
當專案就地作用中時,它應該會有焦點。 若要確定這是您處理 OnSetFocus 的情況,當您的檢視收到焦點時,焦點一律會傳送到使用中專案:
// Special handling of OnSetFocus and OnSize are required
// when an object is being edited in-place.
void CMainView::OnSetFocus(CWnd* pOldWnd)
{
COleClientItem* pActiveItem =
GetDocument()->GetInPlaceActiveItem(this);
if (pActiveItem != NULL &&
pActiveItem->GetItemState() == COleClientItem::activeUIState)
{
// need to set focus to this item if it is same view
CWnd* pWnd = pActiveItem->GetInPlaceWindow();
if (pWnd != NULL)
{
pWnd->SetFocus(); // don't call the base class
return;
}
}
CView::OnSetFocus(pOldWnd);
}
當檢視調整大小時,您必須通知使用中專案裁剪矩形已變更。 若要這樣做,請提供 的 OnSize
處理常式:
void CMainView::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
COleClientItem* pActiveItem =
GetDocument()->GetInPlaceActiveItem(this);
if (pActiveItem != NULL)
pActiveItem->SetItemRects();
}
案例研究:MFC 2.0 的 HIERSVR
HIERSVR 也包含在 MFC 2.0 中,並使用 MFC/OLE1 實作 OLE。 此附注簡短說明此應用程式最初轉換成使用 MFC/OLE 2 類別的步驟。 初始埠完成之後已新增許多功能,以更清楚地說明 MFC/OLE 2 類別。 此處不會涵蓋這些功能;如需這些進階功能的詳細資訊,請參閱範例本身。
注意
編譯器錯誤和逐步程式是使用 Visual C++ 2.0 建立的。 Visual C++ 4.0 可能會變更特定錯誤訊息和位置,但概念資訊仍然有效。
啟動並執行
將 HIERSVR 範例移植到 MFC/OLE 的方法,是從建置它開始,並修正將產生的明顯編譯器錯誤。 如果您從 MFC 2.0 取得 HIERSVR 範例,並在此版本的 MFC 下編譯,您會發現解決的錯誤並不多(雖然 OCLIENT 範例還多)。 錯誤發生的順序如下所述。
編譯和修正錯誤
\hiersvr\hiersvr.cpp(83) : error C2039: 'RunEmbedded' : is not a member of 'COleTemplateServer'
第一個錯誤指出伺服器函 InitInstance
式發生較大問題。 OLE 伺服器所需的初始化可能是您必須對 MFC/OLE1 應用程式進行的最大變更之一,才能讓它執行。 最好的作法是查看 AppWizard 為 OLE 伺服器建立的內容,並視需要修改您的程式碼。 以下是一些要記住的要點:
您必須呼叫 來初始化 OLE 程式庫 AfxOleInit
在檔範本物件上呼叫 SetServerInfo,以設定您無法使用 CDocTemplate
建構函式設定的伺服器資源控制碼和執行時間類別資訊。
如果命令列上有 /Embedding,請勿顯示應用程式的主視窗。
您將需要 檔的 GUID 。 這是檔案類型的唯一識別碼(128 位)。 AppWizard 會為您建立一個程式碼,因此,如果您使用此處所述的技術,從新的 AppWizard 產生的伺服器應用程式複製新程式碼,您只要從該應用程式「竊取」GUID 即可。 如果沒有,您可以在 BIN 目錄中使用 GUIDGEN.EXE 公用程式。
您必須呼叫 COleTemplateServer::ConnectTemplate
來「連接」物件 COleTemplateServer
至檔範本。
當您的應用程式獨立執行時,請更新系統登錄。 如此一來,如果使用者移動應用程式的 .EXE,則從其新位置執行它將會更新 Windows 系統註冊資料庫,以指向新的位置。
根據 AppWizard 針對 InitInstance
所建立的內容套用所有這些變更之後, InitInstance
HIERSVR 的 (和相關 GUID)應該如下所示:
// this is the GUID for HIERSVR documents
static const GUID BASED_CODE clsid =
{ 0xA0A16360L, 0xC19B, 0x101A, { 0x8C, 0xE5, 0x00, 0xDD, 0x01, 0x11, 0x3F, 0x12 } };
/////////////////////////////////////////////////////////////////////////////
// COLEServerApp initialization
BOOL COLEServerApp::InitInstance()
{
// OLE 2 initialization
if (!AfxOleInit())
{
AfxMessageBox("Initialization of the OLE failed!");
return FALSE;
}
// Standard initialization
LoadStdProfileSettings(); // Load standard INI file options
// Register document templates
CDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(IDR_HIERSVRTYPE,
RUNTIME_CLASS(CServerDoc),
RUNTIME_CLASS(CMDIChildWnd),
RUNTIME_CLASS(CServerView));
pDocTemplate->SetServerInfo(IDR_HIERSVRTYPE_SRVR_EMB);
AddDocTemplate(pDocTemplate);
// create main MDI Frame window
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
m_pMainWnd = pMainFrame;
SetDialogBkColor(); // gray look
// enable file manager drag/drop and DDE Execute open
m_pMainWnd->DragAcceptFiles();
EnableShellOpen();
m_server.ConnectTemplate(clsid, pDocTemplate, FALSE);
COleTemplateServer::RegisterAll();
// try to launch as an OLE server
if (RunEmbedded())
{
// "short-circuit" initialization -- run as server!
return TRUE;
}
m_server.UpdateRegistry();
RegisterShellFileTypes();
// not run as OLE server, so show the main window
if (m_lpCmdLine[0] == '\0')
{
// create a new (empty) document
OnFileNew();
}
else
{
// open an existing document
OpenDocumentFile(m_lpCmdLine);
}
pMainFrame->ShowWindow(m_nCmdShow);
pMainFrame->UpdateWindow();
return TRUE;
}
您會注意到上述程式碼指的是新的資源識別碼,IDR_HIERSVRTYPE_SRVR_EMB。 這是編輯內嵌在另一個容器中的檔時要使用的功能表資源。 在 MFC/OLE1 中,編輯內嵌專案的特定功能表項目會即時修改。 在編輯內嵌專案而不是編輯以檔案為基礎的檔時,使用完全不同的功能表結構,可讓您更輕鬆地為這兩種不同的模式提供不同的使用者介面。 如您稍後所見,在就地編輯内嵌物件時,會使用完全獨立的功能表資源。
若要建立此資源,請將資源腳本載入 Visual C++,並複製現有的IDR_HIERSVRTYPE功能表資源。 將新資源重新命名為 IDR_HIERSVRTYPE_SRVR_EMB (這是 AppWizard 所使用的相同命名慣例)。 接下來將 「檔案儲存」變更為「檔案更新」;提供命令識別碼ID_FILE_UPDATE。 同時將 「檔案另存新檔」變更為「檔案另存新檔」;提供命令識別碼ID_FILE_SAVE_COPY_AS。 架構提供這兩個命令的實作。
\hiersvr\svritem.h(60) : error C2433: 'OLESTATUS' : 'virtual' not permitted on data declarations
\hiersvr\svritem.h(60) : error C2501: 'OLESTATUS' : missing decl-specifiers
\hiersvr\svritem.h(60) : error C2146: syntax error : missing ';' before identifier 'OnSetData'
\hiersvr\svritem.h(60) : error C2061: syntax error : identifier 'OLECLIPFORMAT'
\hiersvr\svritem.h(60) : error C2501: 'OnSetData' : missing decl-specifiers
覆寫 OnSetData
會產生許多錯誤,因為它指的是 OLESTATUS 類型。 OLESTATUS 是 OLE1 傳回錯誤的方式。 這已變更為 OLE 2 中的 HRESULT,不過 MFC 通常會將 HRESULT COleException
轉換成 包含錯誤的 。 在此特定案例中,不再需要 覆寫 OnSetData
,因此最簡單的方法是移除它。
\hiersvr\svritem.cpp(30) : error C2660: 'COleServerItem::COleServerItem' : function does not take 1 parameters
建 COleServerItem
構函式會採用額外的 'BOOL' 參數。 此旗標會決定在 物件上 COleServerItem
執行記憶體管理的方式。 藉由將它設定為 TRUE,架構會處理這些物件的記憶體管理,在不再需要這些物件的記憶體管理時加以刪除。 HIERSVR 會使用 CServerItem
(衍生自 COleServerItem
) 物件做為其原生資料的一部分,因此您會將此旗標設定為 FALSE。 這可讓 HIERSVR 判斷何時刪除每個伺服器專案。
\hiersvr\svritem.cpp(44) : error C2259: 'CServerItem' : illegal attempt to instantiate abstract class
\hiersvr\svritem.cpp(44) : error C2259: 'CServerItem' : illegal attempt to instantiate abstract class
由於這些錯誤所暗示,CServerItem 中有一些尚未覆寫的「純虛擬」函式。 很可能是因為 OnDraw 的參數清單已變更。 若要修正此錯誤,請如下所示變更 CServerItem::OnDraw
(以及 svritem.h 中的宣告):
BOOL CServerItem::OnDraw(CDC* pDC, CSize& rSize)
{
// request from OLE to draw node
pDC->SetMapMode(MM_TEXT); // always in pixels
return DoDraw(pDC, CPoint(0, 0), FALSE);
}
新的參數是 'rSize'。 這可讓您在方便時填入繪圖的大小。 此大小必須位於 HIMETRIC 中 。 在此情況下,填入此值並不容易,因此架構會呼叫 OnGetExtent
來擷取範圍。 若要讓運作,您必須實 OnGetExtent
作 :
BOOL CServerItem::OnGetExtent(DVASPECT dwDrawAspect, CSize& rSize)
{
if (dwDrawAspect != DVASPECT_CONTENT)
return COleServerItem::OnGetExtent(dwDrawAspect, rSize);
rSize = CalcNodeSize();
return TRUE;
}
\hiersvr\svritem.cpp(104) : error C2065: 'm_rectBounds' : undeclared identifier
\hiersvr\svritem.cpp(104) : error C2228: left of '.SetRect' must have class/struct/union type
\hiersvr\svritem.cpp(106) : error C2664: 'void __pascal __far DPtoLP(struct ::tagPOINT __far *,
int)__far const ' : cannot convert parameter 1 from 'int __far *' to 'struct ::tagPOINT __far *'
在 CServerItem::CalcNodeSize 函式中,專案大小會轉換成 HIMETRIC ,並儲存在 m_rectBounds 中。 未記載的 ' m_rectBounds' 成員不存在(它已部分由 m_sizeExtent 取代,但在 OLE 2 中,這個成員 COleServerItem
的使用方式 與 OLE1 中的m_rectBounds 稍有不同)。 您將傳回它,而不是將 HIMETRIC 大小設定為此成員變數。 這個傳回值用於 先前實作的 OnGetExtent
。
CSize CServerItem::CalcNodeSize()
{
CClientDC dcScreen(NULL);
m_sizeNode = dcScreen.GetTextExtent(m_strDescription,
m_strDescription.GetLength());
m_sizeNode += CSize(CX_INSET * 2, CY_INSET * 2);
// set suggested HIMETRIC size
CSize size(m_sizeNode.cx, m_sizeNode.cy);
dcScreen.SetMapMode(MM_HIMETRIC);
dcScreen.DPtoLP(&size);
return size;
}
CServerItem 也會覆寫 COleServerItem::OnGetTextData
。 此函式在 MFC/OLE 中已經過時,且由不同的機制取代。 MFC 3.0 版 MFC OLE 範例 HIERSVR 會覆寫 來實作 COleServerItem::OnRenderFileData
這項功能。 這項功能對於這個基本埠並不重要,因此您可以移除 OnGetTextData 覆寫。
svritem.cpp 中有更多尚未解決的錯誤。 它們不是「實際」錯誤,只是先前錯誤所造成的錯誤。
\hiersvr\svrview.cpp(325) : error C2660: 'CopyToClipboard' : function does not take 2 parameters
COleServerItem::CopyToClipboard
不再支援 bIncludeNative
旗標。 原生資料(伺服器專案的 Serialize 函式所寫的資料一律會複製,因此您可以移除第一個參數。 此外, CopyToClipboard
會在發生錯誤時擲回例外狀況,而不是傳回 FALSE。 變更 CServerView::OnEditCopy 的程式碼,如下所示:
void CServerView::OnEditCopy()
{
if (m_pSelectedNode == NULL)
AfxThrowNotSupportedException();
TRY
{
m_pSelectedNode->CopyToClipboard(TRUE);
}
CATCH_ALL(e)
{
AfxMessageBox("Copy to clipboard failed");
}
END_CATCH_ALL
}
雖然編譯 MFC 2.0 版 HIERSVR 所產生的錯誤比 OCLIENT 版本多,但實際上變更較少。
此時,HIERSVR 會編譯和連結和運作為 OLE 伺服器,但沒有就地編輯功能,接下來會實作此功能。
新增「視覺編輯」
若要將「視覺編輯」(或就地啟用)新增至此伺服器應用程式,您只需要處理一些事項:
當專案就地作用中時,您需要特殊的功能表資源。
此應用程式具有工具列,因此您需要只有一部分一般工具列的工具列,才能符合伺服器可用的功能表命令(符合上述功能表資源)。
您需要衍生自
COleIPFrameWnd
的新類別,以提供就地使用者介面(與衍生自CMDIFrameWnd
的 CMainFrame 類似,提供 MDI 使用者介面)。您必須告訴架構這些特殊資源和類別。
功能表資源很容易建立。 執行 Visual C++,將功能表資源IDR_HIERSVRTYPE複製到名為 IDR_HIERSVRTYPE_SRVR_IP 的功能表資源。 修改功能表,只剩下 [編輯] 和 [說明] 功能表快顯視窗。 將兩個分隔符號新增至 [編輯] 和 [說明] 功能表之間的功能表(看起來應該像: Edit || Help
。 如需這些分隔符號的意義,以及伺服器和容器功能表合併方式的詳細資訊,請參閱 功能表和資源:功能表合併 。
從已核取 [伺服器] 選項的全新 AppWizard 產生的應用程式,即可輕鬆地建立子集工具列的點陣圖。 然後,此點陣圖可以匯入 Visual C++。 請務必為點陣圖提供IDR_HIERSVRTYPE_SRVR_IP識別碼。
衍生自 COleIPFrameWnd
的類別也可以從具有伺服器支援之 AppWizard 產生的應用程式複製。 複製這兩個檔案 IPFRAME。CPP 和 IPFRAME。H 並將其新增至專案。 請確定 LoadBitmap
呼叫參考IDR_HIERSVRTYPE_SRVR_IP,這是在上一個步驟中建立的點陣圖。
現在已建立所有新的資源和類別,請新增必要的程式碼,讓架構知道這些內容(而且知道此應用程式現在支援就地編輯)。 這可藉由在 函式中 InitInstance
將更多參數新增至 SetServerInfo
呼叫來完成:
pDocTemplate->SetServerInfo(IDR_HIERSVRTYPE_SRVR_EMB,
IDR_HIERSVRTYPE_SRVR_IP,
RUNTIME_CLASS(CInPlaceFrame));
它現在已準備好在任何也支援就地啟用的容器中就地執行。 但是,程式碼中仍有一個小 Bug 潛伏。 HIERSVR 支援操作功能表,當使用者按下滑鼠右鍵時顯示。 此功能表可在 HIERSVR 完全開啟時運作,但在編輯就地內嵌時無法運作。 原因可以釘選到 CServerView::OnRButtonDown 中的這一行程式碼:
pMenu->TrackPopupMenu(TPM_CENTERALIGN | TPM_RIGHTBUTTON,
point.x,
point.y,
AfxGetApp()->m_pMainWnd);
請注意 的 AfxGetApp()->m_pMainWnd
參考。 當伺服器就地啟動時,它會有主視窗並已設定m_pMainWnd,但通常是看不見的。 此外,此視窗是指 應用程式的主 視窗、當伺服器完全開啟或執行獨立時出現的 MDI 框架視窗。 它不會參考使用中的框架視窗,當就地啟動時,會是衍生自 COleIPFrameWnd
的框架視窗。 若要取得正確的使用中視窗,即使就地編輯,此版本的 MFC 會新增新的函式 AfxGetMainWnd
。 一般而言,您應該使用此函式, AfxGetApp()->m_pMainWnd
而不是 。 此程式碼必須變更,如下所示:
pMenu->TrackPopupMenu(TPM_CENTERALIGN | TPM_RIGHTBUTTON,
point.x,
point.y,
AfxGetMainWnd());
現在您已將 OLE 伺服器基本啟用為就地啟用功能。 但是 MFC/OLE 2 仍有許多功能,MFC/OLE1 中無法使用。 如需您可能想要實作之功能的詳細資訊,請參閱 HIERSVR 範例。 HIERSVR 實作的一些功能如下所列:
縮放,以取得與容器相關的真實 WYSIWYG 行為。
拖放和自訂剪貼簿格式。
在選取範圍變更時捲動容器視窗。
MFC 3.0 中的 HIERSVR 範例也會針對其伺服器專案使用稍微不同的設計。 這有助於節省記憶體,並讓您的連結更具彈性。 使用 2.0 版的 HIERSVR,樹 狀結構中的每個節點都是 a COleServerItem
。 COleServerItem
具有比每個這些節點絕對必要的額外負荷一點,但 COleServerItem
每個作用中連結都需要 。 但在大多數情況下,在任何指定時間都有很少的作用中連結。 為了讓這個更有效率,此 MFC 版本中的 HIERSVR 會將節點與 區隔開 COleServerItem
。 它同時具有 CServerNode 和 類別 CServerItem
。 CServerItem
(衍生自 COleServerItem
) 只會視需要建立。 一旦容器(或容器)停止使用該特定節點的特定連結,就會刪除與 CServerNode 相關聯的 CServerItem 物件。 此設計更有效率且更有彈性。 處理多個選取連結時,其彈性也隨之而來。 這兩個版本的 HIERSVR 都不支援多重選取,但是使用 MFC 3.0 版 HIERSVR 來新增或支援這類選取專案的連結會比較容易,因為 COleServerItem
與原生資料分開。