TN041:MFC/OLE1迁移到MFC/OLE 2

备注

以下技术声明,则它在联机文档,首先包括了不更新。因此,某些过程和主题可能已过时或不正确。有关最新信息,建议您搜索议题在联机文档的索引。

一般迁移相关问题。

一个 OLE 的设计目标在 MFC 2.5 的 2 类 (和更高) 是保留放在适当的位置的许多同一结构建立在 OLE 1.0 的 MFC 2.0 支持。 因此,许多在 MFC 2.0 相同的 OLE 类仍然存在于 MFC (COleDocumentCOleServerDocCOleClientItemCOleServerItem) 的此版本。 此外,许多这些类的 API 完全相同。 但是, OLE 2 是动态地与 OLE 1.0 不同,因此可以项目的某些细节已更改。 如果您熟悉 MFC 2.0 的 OLE1 支持,您按 home 将看上去与 MFC 2.0 的支持。

如果您采用现有 MFC/OLE1 应用程序并将 OLE 2 功能。它,应先阅读此说明。 此说明包括某些常规问题可能遇到,将您的 OLE1 功能。 MFC/OLE 2 然后讨论找到时出现问题,将在 MFC 中包含的两个应用程序时: 2.0MFC OLE 示例 OCLIENTHIERSVR

MFC 文档/视图结构很重要。

如果应用程序不使用 MFC 文档/视图结构,并且要添加 OLE 2 支持对您的应用程序,现在处于打开状态时移动文档/视图。 ,一旦的应用程序使用 MFC,体系结构和组件的许多 MFC 的 OLE 的优点 2 类只能体会到。

实现一个服务器或容器不使用 MFC 体系结构是可能的,但是,不建议。

使用 MFC 实现不必

MFC “罐装实现”类别 (如 CToolBarCStatusBar,并且, CScrollView 具有 OLE 2 的内置专用代码支持。 因此,因此,如果在应用程序中使用这些类将受益于该工作放置到它们使它们 OLE 识别。 同样,可以为卷 “hello”类在此处为这些目的,但是,不建议。 如果您需要实现类似的函数,则 MFC 源代码非常好的相关引用某些细则 OLE (尤其是在处理就地激活)。

检查 MFC 代码示例

具有由 OLE 功能的 MFC 示例。 这些应用程序中的每一实现从不同的角度的 OLE:

  • HIERSVR 意味着主要用作服务器应用程序。 在 MFC 2.0 包括为 MFC/OLE1 应用程序和转换添加到 MFC/OLE 2 然后扩展了这样它实现许多 OLE 功能以及 OLE 2。

  • OCLIENT 这是一个独立容器应用程序,则被视为演示许多从容器位置的 OLE 功能。 它从 MFC 2.0 还移植过来的然后,将扩展以支持许多更高级的 OLE 功能,如自定义剪贴板格式并链接到嵌入项。

  • DRAWCLI 此应用程序实现 OLE 容器支持这与 OCLIENT,不同之处在于,它都在现有面向对象的绘图程序框架内。 本演练还演示如何实现 OLE 容器支持并将其集成到现有的应用程序。

  • SUPERPAD 此应用程序,以及是一个好的独立应用程序,还为 OLE 服务器。 服务器支持它实现了最联机提供的。 特殊的优点是它如何使用 OLE 剪贴板服务将数据复制到剪贴板,但是,使用内置功能 windows “编辑”控件实现剪贴板粘贴功能。 这将显示传统的 windows API 用法以及集成有趣的组合使用新的 OLE API。

有关示例应用程序的更多信息,请参见 “MFC 示例帮助”。

用例处理:从 MFC 2.0 的 OCLIENT

如上所述, OCLIENT 在 MFC 2.0 包括并实现了 MFC/OLE1 的 OLE。 此应用程序最初将使用 MFC/OLE 2 类的步骤下述。 ,在最初的端口完成以便更好地阐释 MFC/OLE 类后,许多功能添加了。 这些函数不会复盖此处;请参见示例。有关这些高级功能的更多信息。

备注

编译器错误和分步过程使用 Visual C++ 2.0 创建的。特定错误消息和位置可能更改了 Visual C++ 4.0,,但概念性信息保持活动状态。

获取它将运行

方法接受对端口为 MFC/OLE 的 OCLIENT 示例将启动通过生成并修复将发生的清单的编译器错误。 如果您执行从 MFC 2.0 的 OCLIENT 示例并对其进行编译 MFC 下此版本的,您会发现不解决许多的错误。 按错误的发生顺序下述。

生成并修复错误

\oclient\mainview.cpp(104) : error C2660: 'Draw' : function does not take 4 parameters

第一个错误相关 COleClientItem::Draw。 在 MFC/OLE1 比 MFC/OLE 版本作为采取了多个参数。 额外的参数通常不是必需的并且通常为空 (在此示例中)。 MFC 此版本的能自动确定 lpWBounds 的值,在绘制时的 CDC 是图元文件 dc:date。 此外,在中,因为框架将同时一个从 “属性 DC 传递的” pDC, pFormatDC 参数不再是必需的。 因此解决此问题,请移除两个额外的 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 '

在结果列表中的错误与该条件所有在 MFC/OLE1 函数需要唯一名称传入表示该项目。 这是基础 OLE API 的要求。 这不是必需的。 MFC/OLE 2,这是因为 OLE 2 不使用 DDE 为基础通信机制 (该名称用于 DDE 对话)。 若要解决此问题,可以移除 CreateNewName 功能以及对该对象的所有引用。 查看很容易了每个 MFC/OLE 函数在此版本需要通过将光标放在调用并按 F1。

大大不同的另一个区域是 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。 此外,新的 OLE 项。 CLSID 创建而不是 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>也是必需的,包含 MFC 以及其他标准对话框的声明中提供的 COleInsertObject 对话框类。

\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::GetBoundsSetBounds 用于查询和操作项目的区域 ( 左对齐top 成员始终为零)。 在 MFC/OLE 2 由 COleClientItem::GetExtentSetExtent更直接支持,相关 大小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 这不是必需的,因此,您可以一起移动 OnCommand 重写在 CMainFrame 所有。

此时 OCLIENT 将编译和链接。

其他必要的更改

具有但是不提交将阻止运行的 OCLIENT,尽可能少的操作。 现在解决这些问题而不是之后最好。

首先,初始化 OLE 库是必需的。 这是通过调用从 InitInstanceAfxOleInit 完成:

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 映射 COleClientDocCOleDocument 简化移植 MFC/OLE1 应用程序到的 MFC/OLE 2,例如 OCLIENT。 COleClientDoc 提供 COleDocument 未提供的一个功能是标准命令消息映射项。 这样做,以便服务器应用程序,也可以使用 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 应用程序中借用的支持。

首先,添加要使用的菜单资源是必要的,因为具有处于就地活动状态的项。 在 Visual C++ 中创建此额外的菜单资源通过复制 IDR_OCLITYPE 资源和移除所有,排除文件和 windows 弹出窗口。 两个分隔线插入到文件和弹出窗口之间指明组 (它的分离应类似于:文件||窗口)。 有关的更多信息这些分隔符表示和服务器和容器菜单如何合并看到 “菜单和资源:合并” (《OLE 2 类的菜单。

一旦让这些菜单后,需要调用框架它们。 这是通过调用文档模板的 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++ 创建的特定就地资源。

若要启用就地激活,在 CView 的一些事项 (CMainView) 派生类以及 COleClientItem 派生类 (CRectItem) 需要更改。 重写 AppWizard 提供所有这些,并且大部分实现将直接来自默认 AppWizard 应用程序。

在第一步此端口,就地激活通过重写的 COleClientItem::CanActivate完全禁用。 应移除此重写允许就地激活。 此外,并传递给对的所有调用 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) 通过单击在项目外和 (2) 按 ESCAPE 键。

对于转义键,添加映射 VK_ESCAPE 键的命令与 Visual C++ 的快捷键, 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,,但概念性信息保持活动状态。

获取它将运行

方法接受对端口为 MFC/OLE 的 HIERSVR 示例将启动通过生成并修复将发生的清单的编译器错误。 如果您执行从 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 服务器创建和修改代码根据需要。 这是记住几点:

通过调用 AfxOleInit初始化 OLE 库是必需的。

调用文档模板对象的 SetServerInfo 设置服务器不能设置与 CDocTemplate 构造函数的资源句柄和运行时类信息。

/Embedding,如果存在命令行,则不要公开应用程序的主窗口。

您将需要您的 GUID 文档。 这是您的文件类型 (128 位) 唯一标识符。 AppWizard 将创建一个您的 —因此,如果使用该技术所述将新的 AppWizard 生成服务器应用程序的新代码,您可以按照窃取”从该应用程序的 GUID。 或者,可以在 bin 目录中使用 GUIDGEN.EXE 实用工具。

是必需的 “通过调用 COleTemplateServer::ConnectTemplate连接”对文档模板的 COleTemplateServer 对象。

,当应用程序是运行的独立后,请更新系统注册表。 ,这样,如果用户将应用程序的 .EXE,运行它从其新位置将更新 windows 系统注册数据库指向新位置。

在应用基于的所有之后这些更改了 AppWizard 为 InitInstanceInitInstance (及相关 GUID) 导致 HIERSVR 中应如下所示:

// 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;
}

您将注意到上面的代码引用新的资源 ID, IDR_HIERSVRTYPE_SRVR_EMB。 这是要使用的菜单资源,而在另一个容器中嵌入的文档编辑器。 在 MFC/OLE1 特定于编辑嵌入式项目即时修改了菜单项。 使用完整不同的菜单结构,当编辑嵌入式项目而非编辑基于文件的文档便于为两个单独的模式提供不同的用户界面。 因为稍后将看到,使用一个完全不同的菜单资源,编辑就地某个嵌入的对象。

若要创建此资源,加载资源脚本到 Visual C++ 并复制现有 IDR_HIERSVRTYPE 菜单资源。 对新资源重命名为 IDR_HIERSVRTYPE_SRVR_EMB (这是 AppWizard 使用) 的同一命名约定。 下将 “保存文件” to “文件更新”;为命令 ID ID_FILE_UPDATE。 并将 “保存文件”更改为 “保存的文件复制”;为命令 ID 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

,因为它是指 OLESTATUS 类型,有多种错误来源于 OnSetData重写。 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 重写的某些 “纯 virtual' 功能。 很可能是由该情况引起 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 功能项目范围在 m_rectBounds转换为 HIMETRIC 并存储。 COleServerItem 的未记录的 “m_rectBounds”成员不存在 (它是 m_sizeExtent部分替换了,但是,在 OLE 2 此成员比 m_rectBounds 在 OLE1 执行) 都有一个略有不同的用法。 而不是设置 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 OLE 示例 HIERSVR 的 MFC 3.0 版本通过重写的 COleServerItem::OnRenderFileData实现此功能。 该功能。这个基本的端口并不重要,因此,您可以移除 OnGetTextData 重写。

在无法解析的 svritem.cpp 的许多错误。 它们不是 “实际”错误 —前面的错误引起的错误。

\hiersvr\svrview.cpp(325) : error C2660: 'CopyToClipboard' : function does not take 2 parameters

COleServerItem::CopyToClipboard 不再支持 “bIncludeNative 标志。 本机数据 (服务器项目的写出的数据序列化功能) 始终复制,因此,您移除第一个参数。 此外, 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   
}

虽然比为 OCLIENT 的具有多个错误由 HIERSVR 的 MFC 2.0 版本编译版本相同,实际上有少量更改。

此时 HIERSVR 将编译和链接并用作 OLE 服务器,,但,而无需就地编辑功能,接下来将要实现。

添加 “可视化编辑”

若要添加 “编辑的可视” (或就地激活) 到此服务器应用程序,必须负责仅的某些事件:

  • 利用,可以将该项是就地活动状态时,需要特定菜单资源使用。

  • 此应用程序有工具栏,因此,您需要具有普通工具栏的一个子集的工具栏与菜单命令可从服务器 (与上面提到的菜单资源)。

  • 您需要提供就地用户界面 COleIPFrameWnd 从派生的新类 (这与 CMainFrame,从派生 CMDIFrameWnd,提供 MDI 用户界面)。

  • 需要通知有关这些特定的资源和类的结构。

菜单资源可以很容易地创建。 运行 Visual C++ 中,复制菜单资源 IDR_HIERSVRTYPE 调用 IDR_HIERSVRTYPE_SRVR_IP 的菜单资源。 修改菜单,以便仅编辑和 " 帮助 " 菜单弹出左侧。 添加两个分隔符到编辑器和 " 帮助 " 菜单 (它之间的菜单应类似于:编辑||帮助)。 有关的更多信息这些分隔符表示和服务器和容器菜单如何合并,请参见 “菜单和资源:合并” (《OLE 2 类的菜单。

子集工具栏的位图可以通过复制一个从中轻松创建新 AppWizard 生成的应用程序会检查的 “server”选项。 此位图就可以导入到 Visual C++。 确保为位图 IDR_HIERSVRTYPE_SRVR_IP ID。

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/OLE1 的 MFC/OLE 2。 为您可能希望实现的功能的更多概念参见 HIERSVR 示例。 某些功能 HIERSVR 实现下面所列:

  • 缩放,为 true WYSISYG 行为有关容器。

  • 拖放和自定义剪贴板格式。

  • 容器窗口为选择更改移动。

在 MFC 3.0 的 HIERSVR 示例为其服务器项目还使用一个略有不同的模型。 这有助于节省内存和使您的链接更灵活。 HIERSVR 的 2.0 版在树 属于COleServerItem的每个节点。 COleServerItem 比为这些节点具有更高的开销中的每一个必需的,但是, COleServerItem 对于每个活动链接是必需的。 但是,与大多数,在任何给定时间有极少数活动链接。 为了使此过程更加高效,在 MFC 的此版本的 HIERSVR 从 COleServerItem将节点。 它具有 CServerNode 和 CServerItem 类。 CServerItem (从派生 COleServerItem) 根据仅需要创建。 对于使用该特定链接的容器 (或容器) 停止该特定的节点, CServerItem 对象与 CServerNode 被删除。 此模型更加高效且更灵活。 其灵活性中,当处理多个选定链接时。 这两个条件都不 HIERSVR 的这两个版本不支持多重选择,但是,添加 (和) 支持连接到此选择) 更方便与 HIERSVR 的 MFC 3.0 版本中,,因为 COleServerItem 从本机数据分隔。

请参见

其他资源

由Number "技术说明

技术说明按类别