Compartilhar via


TN041: Migração de MFC/OLE1 a MFC/OLE 2

ObservaçãoObservação

A seguinte nota técnica não foi atualizada desde que foi incluída pela primeira vez na documentação online.Como resultado, alguns procedimentos e tópicos podem estar desatualizado ou incorreto.As informações mais recentes, é recomendável que você procure o tópico de interesse no índice de documentação on-line.

Problemas gerais relacionados à migração

Uma das metas de design para as classes de OLE 2 no MFC 2.5 (e superiores) foi manter muito a mesma arquitetura de colocar no lugar no MFC 2.0 para suporte a OLE 1.0.Como resultado, muitas das mesmas classes OLE no MFC 2.0 ainda existem nesta versão do MFC (COleDocument, COleServerDoc, COleClientItem, COleServerItem).Além disso, muitas APIs nessas classes são exatamente os mesmos.No entanto, OLE 2 é totalmente diferente de OLE 1.0 você pode esperar que alguns detalhes foram alterados.Se você estiver familiarizado com o suporte de OLE1 da MFC 2.0, você se sentirá em casa com suporte 2.0 do MFC.

Se você estiver levando um aplicativo MFC/OLE1 existente e adicionar funcionalidade OLE 2 a ele, você deve primeiro ler esta nota.Esta nota aborda alguns problemas gerais que você pode encontrar durante a portagem de sua funcionalidade OLE1 a MFC/OLE 2 e, em seguida, discute os problemas revelados ao portar dois aplicativos incluídos no MFC 2.0: os exemplos MFC OLE OCLIENT e HIERSVR.

Arquitetura de exibição de documento do MFC É importante

Se seu aplicativo não usar a arquitetura de exibição de documento do MFC e você deseja adicionar suporte a OLE 2 para seu aplicativo, agora é hora de mover a exibição do documento.Muitos dos benefícios de classes do MFC OLE 2 são percebidos apenas quando seu aplicativo está usando a arquitetura interna e componentes do MFC.

Implementar um servidor ou um recipiente sem usar a arquitetura do MFC é possível, mas não recomendado.

Usar MFC implementação em vez de seu próprio

Implementação de MFC "gravados" classes como CToolBar, CStatusBar, e CScrollView interna código caso especial para suporte a OLE 2.Portanto, se você pode usar essas classes em seu aplicativo beneficiará o esforço colocar neles para conscientizá-los OLE.Novamente, é possível "roll-sua própria" classes aqui para esses fins, mas não é sugerido.Se você precisar implementar a funcionalidade semelhante, o código-fonte do MFC é uma excelente referência para lidar com alguns dos pontos mais refinados de OLE (especialmente quando se trata de ativação in-loco).

Examine o código de exemplo do MFC

Há um número de amostras MFC incluem funcionalidade OLE.Cada um desses aplicativos implementa OLE de um ângulo diferente:

  • HIERSVR destinado principalmente para uso como um aplicativo de servidor.Ele foi incluído no MFC 2.0 como um aplicativo MFC/OLE1 e foi portado para MFC/OLE 2 e então estendido que ele implementa vários recursos OLE disponíveis no OLE 2.

  • OCLIENT é um aplicativo de contêiner independente, destinado a demonstrar muitos dos recursos OLE do ponto de vista do recipiente.Ele também foi portado do MFC 2.0 e, em seguida, estendido para oferecer suporte a muitos dos recursos mais avançados do OLE, como formatos de área de transferência personalizada e links para itens incorporados.

  • DRAWCLI esse aplicativo implementa suporte de contêiner OLE muito como OCLIENT, exceto que ele faz isso dentro da estrutura de um programa de desenho orientado a objeto existente.Ele mostra como você pode implementar suporte de contêiner OLE e integrar seu aplicativo.

  • SUPERPAD esse aplicativo, bem como sendo um aplicativo autônomo fino, também é um servidor OLE.Ele implementa o suporte de servidor é bastante minimalista.De interesse particular é como ele usa os serviços de área de transferência OLE para copiar dados para a área de transferência, mas usa a funcionalidade incorporada ao controle Windows "edit" para implementar a funcionalidade de colar da área de transferência.Mostra uma mistura interessante de uso da API do Windows tradicional bem como integração com o novo OLE APIs.

Para obter mais informações sobre os aplicativos de exemplo, consulte o MFC exemplo "Ajuda".

Estudo de caso: OCLIENT do MFC 2.0

Como discutido acima, OCLIENT foi incluído no MFC 2.0 e implementado OLE com MFC/OLE1.As etapas pelas quais este aplicativo inicialmente foi convertido para usar as classes do MFC/OLE 2 são descritas abaixo.Uma série de recursos foram adicionada depois de concluída a porta inicial para melhor ilustrar as classes do MFC/OLE.Esses recursos não serão abordados aqui; Consulte o exemplo para obter mais informações sobre esses recursos avançados.

ObservaçãoObservação

O processo passo a passo e erros do compilador foi criado com Visual C++ 2.0.Locais e mensagens de erro específicas podem ter alterado com Visual C++ 4.0, mas as informações conceituais permanecem válidas.

Colocá-lo em funcionamento

A abordagem utilizada para o exemplo OCLIENT para MFC/OLE da porta é começar construindo ele e corrigir os erros de compilador óbvio resultarão.Se você levar o exemplo OCLIENT do MFC 2.0 e compilá-lo nesta versão do MFC, você encontrará que não há que muitos erros para resolver.Erros na ordem em que eles ocorreram estão descritos abaixo.

Compilar e corrigir erros

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

Primeiras preocupações de erro COleClientItem::Draw.MFC/OLE1 levou mais parâmetros do que leva a versão do MFC/OLE.Parâmetros extras geralmente não eram necessárias e geralmente nulo (como no exemplo).Esta versão do MFC pode determinar automaticamente os valores para o lpWBounds quando o CDC é desenhado para um metarquivo DC.Além disso, o parâmetro pFormatDC não é mais necessário desde que a estrutura será construir uma o "atributo DC" do pDC passado.Para corrigir esse problema, basta remover os dois nulo extra parâmetros para a chamada de desenho.

\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 '

Os erros acima o resultado do fato de que todos os COleClientItem::CreateXXXX funções no MFC/OLE1 necessários um nome exclusivo ser passado para representar o item.Isso era um requisito da subjacente OLE API.Não é necessário no MFC/OLE 2 pois a OLE 2 não usar DDE como o mecanismo subjacente de comunicações (o nome foi usado em conversações DDE).Para corrigir esse problema, você pode remover o CreateNewName função, bem como todas as referências a ele.É fácil descobrir o que cada função do MFC/OLE é esperado nesta versão simplesmente, colocando o cursor na chamada e pressionando F1.

Outra área que é significativamente diferente é o tratamento da área de transferência OLE 2.Com OLE1, você usou transferência Windows que APIs interagem com a área de transferência.Com OLE 2 isso é feito com um mecanismo diferente.As APIs do MFC/OLE1 presume-se a área de transferência foi aberta antes de copiar um COleClientItem o objeto na área de transferência.Isso não é mais necessário e fará com que todas as operações de transferência do MFC/OLE falha.Enquanto você edita o código para remover dependências em CreateNewName, você também deve remover o código que abre e fecha a área de transferência do 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'

Esses erros resultam de CMainView::OnInsertObject manipulador.Lidar com o comando "Inserir novo objeto" é outra área onde as coisas mudaram um pouco.Nesse caso, é mais fácil simplesmente mesclar a implementação original com fornecido pelo AppWizard para um novo aplicativo recipiente OLE.Na verdade, essa é uma técnica que você pode aplicar a portagem de outros aplicativos.No MFC/OLE1 é exibida a caixa de diálogo "Insert Object" chamando AfxOleInsertDialog função.Nesta versão você construir um COleInsertObject o objeto de diálogo e chamada DoModal.Além disso, os novos itens OLE são criados com um CLSID em vez de uma seqüência de caracteres de nome de classe.O resultado final deve parecer semelhante a esta

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();
ObservaçãoObservação

Inserir novo objeto pode ser diferente para seu aplicativo):

Também é necessário incluir <afxodlgs.h>, que contém a declaração para o COleInsertObject classe de diálogo, bem como os outros diálogos padrão fornecidos pelo MFC.

\oclient\mainview.cpp(367) : error C2065: 'OLEVERB_PRIMARY' : undeclared identifier
\oclient\mainview.cpp(367) : error C2660: 'DoVerb' : function does not take 1 parameters

Esses erros são causados pelo fato de que algumas constantes OLE1 foram alterados no OLE 2, embora o conceito são os mesmos.Nesse caso OLEVERB_PRIMARY foi alterado para OLEIVERB_PRIMARY.OLE1 e OLE 2 verbo primário geralmente é executado por um contêiner quando o usuário clica duas vezes em um item.

Além disso, DoVerb agora leva um parâmetro extra — um ponteiro para um modo de exibição (CView*).Este parâmetro é usado somente para implementar "Edição Visual" (ou ativação in-loco).Agora defina esse parâmetro como NULL, porque esse recurso não estiver implementando neste momento.

Para certificar-se de que a estrutura nunca tenta in-loco ativar, você deve substituir COleClientItem::CanActivate da seguinte maneira:

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

No MFC/OLE1 COleClientItem::GetBounds e SetBounds foram usados para consultar e manipular a extensão de um item (o esquerda e superior membros eram sempre zero).No MFC/OLE 2 isso é suportado pelo mais diretamente COleClientItem::GetExtent e SetExtent, que lidam com uma tamanho ou CSize em vez disso.

O código para o novo SetItemRectToServer, e UpdateItemRectFromServer chamadas tenha esta aparência:

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

API síncrona MFC/OLE1 foram chamadas de um recipiente para um servidor simulados, porque OLE1 era inerentemente assíncronas em muitos casos.Foi necessário procurar uma chamada assíncrona pendente em andamento antes de processar os comandos do usuário.MFC/OLE1 fornecido a COleClientItem::InWaitForRelease função para fazer isso.No MFC/OLE 2 isso não é necessário, para que você possa para remover a substituição de OnCommand CMainFrame todos juntos.

Neste ponto OCLIENT irá compilar e vincular.

Outras alterações necessárias

Há algumas coisas que não são feitas irá manter OCLIENT de execução, no entanto.É melhor corrigir esses problemas agora, em vez de mais tarde.

Primeiro, é necessário inicializar as bibliotecas OLE.Isso é feito chamando AfxOleInit de InitInstance:

if (!AfxOleInit())
{
  AfxMessageBox("Failed to initialize OLE libraries");
  return FALSE;
}

Também é uma boa idéia para verificar as funções virtuais para alterações de lista de parâmetro.Uma tal função é COleClientItem::OnChange, substituído em cada aplicativo de contêiner do MFC/OLE.Examinando ajuda on-line, você verá um extra 'DWORD dwParam foi adicionado.CRectItem::OnChange nova aparência da seguinte maneira:

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
}

Em MFC/OLE1 aplicativos recipientes derivada da classe de documento do COleClientDoc.No MFC/OLE 2 Esta classe foi removida e substituída por COleDocument (essa nova organização facilita a criação de aplicativos de contêiner/servidor).Há um #define que mapeia COleClientDoc para COleDocument para simplificar a portagem de aplicativos MFC/OLE1 a MFC/OLE 2, como OCLIENT.Um dos recursos não fornecidos pela COleDocument que foram fornecida por COleClientDoc é a mensagem de comando padrão entradas de mapa.Isso é feito que aplicativos de servidor, usam também COleDocument (indiretamente), não carregam com eles a sobrecarga desses manipuladores de comando, a menos que eles são um aplicativo de contêiner/servidor.Você precisa adicionar as seguintes entradas de mapa da mensagem 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)

A implementação de todos esses comandos está em COleDocument, que é a classe base para seu documento.

Neste ponto, OCLIENT é um aplicativo de contêiner OLE funcional.É possível inserir itens de qualquer tipo (OLE1 ou OLE 2).Desde que o código necessário para habilitar ativação in-loco não está implementado, itens são editados em uma janela separada muito como com OLE1.A próxima seção aborda as alterações necessárias para ativar a edição in-loco (às vezes chamado de "Edição Visual").

Adicionando "Edição Visual"

Um dos recursos mais interessantes do OLE é a ativação no local (ou "Edição Visual").Esse recurso permite que o aplicativo de servidor assumir partes da interface do usuário do contêiner fornecido uma interface de edição mais perfeita para o usuário.Para implementar a ativação in-loco para OCLIENT, alguns recursos especiais precisam ser adicionado, bem como alguns códigos adicionais.Esses recursos e o código são normalmente fornecidos por AppWizard — na verdade, muito do código aqui foi emprestado diretamente a partir de um aplicativo AppWizard atualizado com suporte "Recipiente".

Primeiro, é necessário adicionar um recurso de menu a ser usado quando existe um item que está ativo no local.Você pode criar esse recurso de menu extra no Visual C++, copiando o recurso IDR_OCLITYPE e removendo todos, exceto os arquivo e a janela pop-ups.Duas barras de separação são inseridas entre os arquivo e janela de pop-ups para indicar a separação de grupos (deve parecer com: arquivo | | Janela).Para obter mais informações sobre o que significam esses separadores e como os menus do servidor e o contêiner são mesclados consulte "Menus e recursos: Menu mesclagem" em Classes OLE 2.

Depois de ter esses menus criados, você precisará informar o framework sobre eles.Isso é feito chamando CDocTemplate::SetContainerInfo para o modelo de documento antes de adicioná-lo à lista de modelos de documento na sua InitInstance.O novo código para registrar o modelo de documento tem esta aparência:

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);

O recurso IDR_OLECLITYPE_INPLACE é o recurso local especial criado no Visual C++.

Para habilitar a ativação in-loco, existem algumas coisas que precisam alterar em ambos o CView (CMainView) classe derivada, bem como o COleClientItem (CRectItem) de classe derivada.Todas essas substituições são fornecidas pelo AppWizard e a maior parte da implementação virão diretamente de um aplicativo de AppWizard padrão.

Na primeira etapa esta porta, ativação in-loco foi desabilitada completamente substituindo COleClientItem::CanActivate.Essa substituição deve ser removida para permitir a ativação in-loco.Além disso, nula foi passada para todas as chamadas para DoVerb (há dois deles) porque fornecer o modo de exibição somente foi necessária para ativação in-loco.Para implementar completamente a ativação in-loco, é necessário passar o modo correto de DoVerb chamar.Uma dessas chamadas está em CMainView::OnInsertObject:

pItem->DoVerb(OLEIVERB_SHOW, this);

A outra é na CMainView::OnLButtonDblClk:

m_pSelection->DoVerb(OLEIVERB_PRIMARY, this);

É necessário substituir COleClientItem::OnGetItemPosition.Isso informa ao servidor onde colocar sua janela relativa à janela do recipiente quando o item é ativado no lugar.OCLIENT, a implementação é trivial:

void CRectItem::OnGetItemPosition(CRect& rPosition)
{
    rPosition = m_rect;
}

A maioria dos servidores também implementar o que é chamado "in-loco redimensionamento." Isso permite que a janela do servidor ser dimensionada e movidos enquanto o usuário edita o item.O recipiente deve participar essa ação, como mover ou redimensionar a janela geralmente afeta a posição e tamanho no próprio documento recipiente.A implementação de OCLIENT sincroniza o retângulo interno mantido por m_rect com a nova posição e tamanho.

BOOL CRectItem::OnChangeItemPosition(const CRect& rectPos)
{
    ASSERT_VALID(this);

    if (!COleClientItem::OnChangeItemPosition(rectPos))
        return FALSE;

    Invalidate();
    m_rect = rectPos;
    Invalidate();
    GetDocument()->SetModifiedFlag();

    return TRUE;
}

Neste ponto, há código suficiente para permitir que um item a ser ativado no lugar e lidar com dimensionando e movendo o item quando ele estiver ativo, mas nenhum código permitirá que o usuário sair da sessão de edição.Embora alguns servidores fornecerá esta funcionalidade próprios manipulando a tecla escape, é recomendável que os contêineres fornecem duas maneiras de desativar um item: (1), clique fora do item e (2), pressionando a tecla ESCAPE.

Para a chave ESCAPE, adicionar um acelerador com o Visual C++ que mapeia a chave VK_ESCAPE para um comando, ID_CANCEL_EDIT é adicionada aos recursos.O manipulador para o comando a seguir:

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

Para manipular o caso onde o usuário clica fora do item, você deve adicionar o código a seguir ao início do CMainView::SetSelection:

if (pNewSel != m_pSelection || pNewSel == NULL)
{
    COleClientItem* pActiveItem = 
        GetDocument()->GetInPlaceActiveItem(this);
    if (pActiveItem != NULL && pActiveItem != pNewSel)
        pActiveItem->Close();
}
    

Quando um item está ativo no local, ele deve ter o foco.Se que for esse o caso você manipular OnSetFocus para que o foco sempre é transferido para o item ativo quando sua exibição recebe o foco:

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

Quando o modo de exibição é redimensionado, você precisa notificar o item ativo mudou o retângulo de recorte.Para fazer isso, forneça um manipulador de 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();
}

Estudo de caso: HIERSVR do MFC 2.0

HIERSVR também foi incluído no MFC 2.0 e implementado OLE com MFC/OLE1.Esta nota descreve brevemente as etapas pelas quais este aplicativo inicialmente foi convertido para usar as classes do MFC/OLE 2.Uma série de recursos foram adicionada depois de concluída a porta inicial para melhor ilustrar as classes do MFC/OLE 2.Esses recursos não serão abordados aqui; Consulte o exemplo para obter mais informações sobre esses recursos avançados.

ObservaçãoObservação

O processo passo a passo e erros do compilador foi criado com Visual C++ 2.0.Locais e mensagens de erro específicas podem ter alterado com Visual C++ 4.0, mas as informações conceituais permanecem válidas.

Colocá-lo em funcionamento

A abordagem utilizada para o exemplo HIERSVR para MFC/OLE da porta é começar construindo ele e corrigir os erros de compilador óbvio resultarão.Se você levar o exemplo HIERSVR do MFC 2.0 e compilá-lo nesta versão do MFC, você encontrará que não são muitos erros de resolver (embora haja mais com o exemplo OCLIENT).Erros na ordem em que eles ocorrem normalmente são descritos abaixo.

Compilar e corrigir erros

\hiersvr\hiersvr.cpp(83) : error C2039: 'RunEmbedded' : is not a member of 'COleTemplateServer'

Esse primeiro erro aponta um problema muito maior com o InitInstance função para servidores.A inicialização necessária para um servidor OLE é provavelmente uma das maiores alterações que você terá que fazer seu aplicativo MFC/OLE1 para obtê-lo em execução.A melhor coisa a fazer é examinar o que AppWizard cria um servidor OLE e modificar seu código conforme apropriado.Aqui estão alguns pontos em mente:

É necessário inicializar as bibliotecas OLE chamando AfxOleInit

Chamada SetServerInfo no objeto de modelo de documento para definir identificadores de recurso de servidor e informações de classe de tempo de execução não pode ser definida com o CDocTemplate construtor.

Não mostra a janela principal do aplicativo, se a opção /Embedding estiver presente na linha de comando.

Você precisará de um GUID para o seu documento.Este é um identificador exclusivo para o tipo do documento (128 bits).AppWizard criará um para você — portanto, se você usar a técnica descrita aqui de copiar o novo código de um novo aplicativo de servidor AppWizard gerado, você pode simplesmente "roubar" o GUID do aplicativo.Se não, você pode usar o GUIDGEN.Utilitário do EXE no diretório BIN.

É necessário para "conectar" sua COleTemplateServer o objeto de modelo de documento chamando COleTemplateServer::ConnectTemplate.

Atualize o registro do sistema quando seu aplicativo é executado autônomo.Dessa forma, se o usuário move o.EXE para seu aplicativo, executá-lo de seu novo local irá atualizar o banco de dados de registro de sistema do Windows para apontar para o novo local.

Depois de aplicar todas essas alterações com base no qual AppWizard cria para InitInstance, o InitInstance (e relacionados ao GUID) para HIERSVR deve ler da seguinte maneira:

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

Você irá notar que o código acima se refere a um novo ID de recurso IDR_HIERSVRTYPE_SRVR_EMB.Este é o recurso de menu a ser usado quando um documento está incorporado em outro recipiente é editado.No MFC/OLE1 itens de menu específicos para editar um item incorporado foram modificados imediatamente.Usar uma estrutura de menu inteiramente diferente ao editar um item incorporado em vez de editar um documento do arquivo se torna muito mais fácil fornecer interfaces de usuário diferente para esses dois modos separados.Como você verá posteriormente, um recurso de menu inteiramente separado é usado ao editar um objeto incorporado no lugar.

Para criar esse recurso, carregar o script de recurso no Visual C++ e copie o recurso de menu existente do IDR_HIERSVRTYPE.Renomeie o novo recurso para IDR_HIERSVRTYPE_SRVR_EMB (essa é a mesma convenção de nomeação AppWizard usa).Em seguida altere "Salvar arquivo" para "Arquivo de atualização"; Dê a ele ID de comando ID_FILE_UPDATE.Também alterar "Arquivo Salvar como" para "Arquivo Salvar cópia como"; Dê a ele ID de comando ID_FILE_SAVE_COPY_AS.O framework fornece a implementação de ambos esses comandos.

\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

Há um número de erros resultantes da substituição do OnSetData, pois ele está se referindo a OLESTATUS tipo.OLESTATUS foi a maneira OLE1 retornaram erros.Isso foi alterado para HRESULT no OLE 2, embora o MFC geralmente converte um HRESULT em um COleException que contém o erro.Nesse caso específico, a substituição de OnSetData não é mais necessário, portanto, é a coisa mais fácil a fazer para removê-lo.

\hiersvr\svritem.cpp(30) : error C2660: 'COleServerItem::COleServerItem' : function does not take 1 parameters

O COleServerItem construtor utiliza um parâmetro de 'BOOL' extra.Este sinalizador determina como gerenciamento de memória é feito a COleServerItem objetos.Definido como TRUE, a estrutura lida com o gerenciamento de memória desses objetos — excluí-los quando eles não são mais necessários.HIERSVR usa CServerItem (derivado de COleServerItem) objetos como parte de seus dados nativos, portanto, vou definir esse sinalizador como FALSE.Isso permite HIERSVR determinar quando cada item do servidor é excluído.

\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

Como esses erros implicam, existem algumas funções 'puras virtuais' que não foram substituídas em CServerItem.Provavelmente isso é causado pelo fato de que a lista de parâmetro do OnDraw mudou.Para corrigir esse erro, altere CServerItem::OnDraw da seguinte maneira (bem como a declaração de 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);
}

O novo parâmetro é 'rSize'.Isso permite que você preencha o tamanho do desenho, se for conveniente.Esse tamanho deve ser em HIMÉTRICAS.Nesse caso, não é conveniente preencher esse valor, portanto, a estrutura chama OnGetExtent para recuperar a extensão.Para que isso funcione, você terá que implementar 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 *'

Na função CServerItem::CalcNodeSize o tamanho do item é convertido em HIMÉTRICAS e armazenados em m_rectBounds.A umentadas 'm_rectBounds' membro de COleServerItem não existe (foi parcialmente substituído por m_sizeExtent, mas no OLE 2 Este membro tem um uso ligeiramente diferente de m_rectBounds no OLE1).Em vez da configuração de DÉCIMOS tamanho para essa variável de membro irá retorná-lo.Esse valor de retorno é usado em OnGetExtent, implementada anteriormente.

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

Também substitui CServerItem COleServerItem::OnGetTextData.Esta função é obsoleta no MFC/OLE e substituída por um mecanismo diferente.A versão do MFC OLE exemplo MFC 3.0 HIERSVR implementa essa funcionalidade, substituindo COleServerItem::OnRenderFileData.Essa funcionalidade não é importante para esta porta básica para que você possa remover a substituição de OnGetTextData.

Há muitos mais erros no svritem.cpp que não foram abordados.Não são erros "reais" — apenas erros causados por erros anteriores.

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

COleServerItem::CopyToClipboardnão suporta o sinalizador 'bIncludeNative'.Os dados nativos (os dados gravados pela função de Serialize do item do servidor) é sempre copiados para remover o primeiro parâmetro.Além disso, CopyToClipboard lançará uma exceção quando ocorre um erro em vez de retornar FALSE.Altere o código de CServerView::OnEditCopy da seguinte maneira:

void CServerView::OnEditCopy()
{
    if (m_pSelectedNode == NULL)
        AfxThrowNotSupportedException();
        
    TRY
    {
        m_pSelectedNode->CopyToClipboard(TRUE);
    }
    CATCH_ALL(e)
    {
        AfxMessageBox("Copy to clipboard failed");
    }
    END_CATCH_ALL   
}

Embora houvesse mais erros resultantes da compilação da versão 2.0 do MFC do HIERSVR que havia para a mesma versão do OCLIENT, havia realmente menos alterações.

Neste ponto HIERSVR irá compilar e vincular e funcionar como um servidor OLE, mas sem o recurso de edição no local será implementado em seguida.

Adicionando "Edição Visual"

Para adicionar "Edição Visual" (ou ativação in-loco) para este aplicativo de servidor, existem apenas algumas coisas, de que você deve cuidar:

  • Você precisa de um recurso de menu especial para ser usado quando o item está ativo no local.

  • Este aplicativo tem uma barra de ferramentas, você precisará de uma barra de ferramentas com apenas um subconjunto da barra de ferramentas normal para coincidir com os comandos de menu disponíveis no servidor (corresponde ao recurso de menu mencionado acima).

  • Você precisa de uma nova classe derivada de COleIPFrameWnd que fornece a interface de usuário local (assim como CMainFrame, derivada de CMDIFrameWnd, fornece a interface do usuário MDI).

  • Você precisa dizer ao framework sobre essas classes e recursos especiais.

O recurso de menu é fácil criar.Executar o Visual C++, copiar o recurso de menu IDR_HIERSVRTYPE para um recurso de menu chamado IDR_HIERSVRTYPE_SRVR_IP.Modificar o menu para que somente os popups de menu Ajuda e edição são deixados.Adicionar dois separadores de menu entre menus de ajuda e edição (deve parecer com: editar | | Ajuda).Para obter mais informações sobre o que significam esses separadores e como os menus do servidor e o contêiner são mesclados, consulte "Menus e recursos: Menu mesclagem" em Classes OLE 2.

O bitmap para a barra de ferramentas do subconjunto pode ser criado facilmente copiando um de um novo aplicativo AppWizard gerado com a opção "Servidor" marcada.Este bitmap pode então ser importado para o Visual C++.Certifique-se de dar uma identificação de IDR_HIERSVRTYPE_SRVR_IP de bitmap.

A classe derivada de COleIPFrameWnd podem ser copiadas de um aplicativo AppWizard gerado com suporte de servidor.Copie os arquivos IPFRAME.CPP e IPFRAME.H e adicioná-los ao projeto.Certifique-se de que o LoadBitmap chamada refere-se a IDR_HIERSVRTYPE_SRVR_IP, o bitmap criado na etapa anterior.

Agora que todos os novos recursos e classes são criadas, adicione o código necessário para que o framework sabe sobre esses (e sabe que esse aplicativo agora oferece suporte à edição in-loco).Isso é feito adicionando mais alguns parâmetros para o SetServerInfo chamar o InitInstance função:

pDocTemplate->SetServerInfo(IDR_HIERSVRTYPE_SRVR_EMB,
    IDR_HIERSVRTYPE_SRVR_IP, RUNTIME_CLASS(CInPlaceFrame));

Agora está pronto para executar no local em qualquer recipiente também oferece suporte a ativação in-loco.Mas, há um bug secundário à espreita ainda no código.HIERSVR oferece suporte a um menu de contexto exibido quando o usuário pressiona o botão direito do mouse.Esse menu funciona quando HIERSVR é totalmente aberto, mas não funciona quando um incorporação no local de edição.O motivo pode ser fixado para baixo para essa única linha de código em CServerView::OnRButtonDown:

pMenu->TrackPopupMenu(TPM_CENTERALIGN | TPM_RIGHTBUTTON,
    point.x, point.y, AfxGetApp()->m_pMainWnd);

Observe a referência AfxGetApp() - > m_pMainWnd.Quando o servidor está ativado no lugar, ele tem uma janela principal e m_pMainWnd é definida, mas é geralmente invisível.Além disso, essa janela se refere a principal janela do aplicativo, a janela de quadro MDI que aparece quando o servidor está totalmente abrir ou executar autônoma.Não faz referência à janela do quadro ativo — que ao local ativado é um quadro de janela derivadas de COleIPFrameWnd.Para obter a janela ativa correta mesmo quando o local de edição, esta versão do MFC adiciona uma nova função, AfxGetMainWnd.Geralmente, você deve usar essa função em vez de AfxGetApp() - > m_pMainWnd.Esse código precisa da seguinte forma:

pMenu->TrackPopupMenu(TPM_CENTERALIGN | TPM_RIGHTBUTTON,
    point.x, point.y, AfxGetMainWnd());

Agora você tem um servidor OLE minimamente habilitado para ativação in-loco funcional.Mas ainda há muitos recursos disponíveis com MFC/OLE 2 que não estavam disponíveis no MFC/OLE1.Consulte o exemplo HIERSVR para obter mais idéias sobre recursos que talvez você queira implementar.Alguns dos recursos que implementa HIERSVR estão listados abaixo:

  • Aplicar zoom para true comportamento WYSISYG com relação ao contêiner.

  • Arrastar / soltar e um formato de área de transferência personalizada.

  • Rolar a janela de contêiner conforme a seleção é alterada.

O exemplo HIERSVR no MFC 3.0 também usa um design ligeiramente diferente para itens do servidor.Isso ajuda a conservar a memória e torna os links mais flexível.Com a versão 2.0 do HIERSVR cada nó na árvore de é umCOleServerItem.COleServerItemcarrega um pouco mais sobrecarga do que é estritamente necessário para cada um de nós, mas uma COleServerItem é necessária para cada link ativo.Mas na maior parte, há muito poucos links ativos a qualquer momento.Para tornar mais eficiente, HIERSVR nesta versão do MFC separa o nó a partir do COleServerItem.Ele tem dois CServerNode e um CServerItem classe.O CServerItem (derivado de COleServerItem) é criado somente quando necessário.Depois que o recipiente (ou recipientes) parar de usar que determinado link de nó específico, o objeto de CServerItem associado a CServerNode é excluído.Esse design é mais eficiente e mais flexível.Sua flexibilidade vem ao lidar com vários links de seleção.Nenhuma dessas duas versões do HIERSVR suporte à seleção múltipla, mas seria muito mais fácil para adicionar (e suporte a links para essas seleções) com a versão 3.0 do MFC do HIERSVR, desde que o COleServerItem separados de dados nativos.

Consulte também

Outros recursos

Notas técnicas por número

Notas técnicas por categoria