Compartilhar via


TN041: migração de MFC/OLE1 para MFC/OLE 2

Dica

A nota técnica a seguir não foi atualizada desde que ela foi incluída pela primeira vez na documentação online.Como resultado, alguns procedimentos e tópicos podem estar incorretos ou expirados.Para obter as informações mais recentes, é recomendável que você procure o tópico de interesse no índice de documentação online.

Problemas gerais referentes à migração

Um dos metas de design para OLE 2 (MFC classes em 2,5 e superior) era reter muito da arquitetura colocada no lugar de MFC 2.0 para o suporte do OLE 1.0. No resultado, muitas das mesmas classes em MFC 2.0 ainda existam nesta versão de MFC (COleDocument, COleServerDoc, COleClientItem, COleServerItem). Além disso, muitas das APIs nessas classes são exatamente iguais. No entanto, OLE 2 é drasticamente diferente de 1,0 OLE para que você pode esperar que alguns dos detalhes são alterados. Se você estiver familiarizado com o suporte OLE1 de MFC 2.0, você sentirá em casa com suporte de MFC 2.0.

Se estiver fazendo um aplicativo MFC/OLE1 existente e você está adicionando funcionalidade OLE 2, leia esta observação primeiro. Essa observação abrange alguns problemas gerais que você pode localizar ao mover sua funcionalidade OLE1 a MFC/OLE 2 e discute os problemas encontrados ao mover dois aplicativos incluídos em MFC 2.0: os exemplos de OLE MFC OCLIENT e HIERSVR.

O documento MFC/arquitetura da exibição é importantes

Se seu aplicativo não usa a arquitetura do documento/exibição MFC e você desejar adicionar o suporte do OLE 2 para o seu aplicativo, agora é a hora de passar para o documento/exibição. Muitos dos benefícios de OLE MFC 2 classes são mantidos apenas uma vez que seu aplicativo estiver usando a arquitetura do e os componentes de MFC.

Implementar um servidor ou um contêiner sem usar a arquitetura de MFC é possível, mas não recomendado.

Use a implementação de MFC em vez de suas próprias

O MFC “implementação enlatada” classificar como CToolBar, CStatusBar, e CScrollView tiver código interno de casos especiais para o suporte do OLE 2. Assim, se você pode usar essas classes em seu aplicativo você se beneficiará de busca colocado nelas para fazer o OLE ciente. Além disso, é possível a “rolo-seu- próprias” aqui essas classes para fins, mas não se sugere. Se você precisa implementar a funcionalidade semelhante, o código-fonte MFC é uma referência pendente para controlar qualquer dos pontos mais refinados OLE (principalmente quando se trata de ativação in-loco).

Examine o código de exemplo MFC

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

  • HIERSVR significou principalmente para uso como um aplicativo de servidor. Foi incluído em MFC 2.0 como um aplicativo MFC/OLE1 e foi movido para MFC/OLE 2 e estendidas em modo que implementa vários recursos do disponíveis no OLE. 2.

  • OCLIENT isso é um aplicativo autônomo de contêiner, significado demonstram vários recursos do de um ponto de vista do contêiner. Foi movido muito MFC 2.0, e estendidas em para oferecer suporte a muitos dos recursos DE mais avançadas, como formatos personalizados da área de transferência e links para itens inseridos.

  • DRAWCLI este aplicativo implementa o suporte OLE do contêiner bem como OCLIENT faz, exceto que faz isso no escopo de um programa de desenho orientado por objeto existente. Mostra como você pode implementar o suporte OLE do contêiner e o integrar seu aplicativo existente.

  • SUPERPAD esse aplicativo, bem como sendo um aplicativo autônomo, é preciso também um servidor OLE. Suporte de servidor que implementa é suficiente minimalista. De interesse específico é como usar os serviços do da área de transferência para copiar dados para a área de transferência, mas usa a funcionalidade criada no controle de “edition” do windows para implementar a funcionalidade da pasta da área de transferência. Isso mostra uma mistura interessante de uso bem como de integração tradicionais de API do windows com os novos APIs OLE habilitados.

Para obter mais informações sobre aplicativos de exemplo, consulte a ajuda de MFC exemplo”.

Estudo de caso: OCLIENT de MFC 2.0

Como discutido acima, OCLIENT foi incluído em MFC 2.0 e tiver implementado com OLE MFC/OLE1. As etapas pelo qual esse aplicativo foi convertido inicialmente usar as classes de MFC/OLE 2 são descritas abaixo. Vários recursos do foram adicionados depois que a porta inicial foi concluída para ilustrar melhor as classes de MFC/OLE. Esses recursos não serão incluídos aqui; consulte o exemplo próprio para obter mais informações sobre esses recursos avançados.

Dica

Os erros do compilador e o processo passo a passo foram criados com Visual C++ 2,0.As mensagens de erro e os locais específicos podem ter sido alteradas com Visual C++ 4.0, mas informações conceituais permaneça válida.

Obtendo as no serviço

A abordagem adotada para mover o exemplo de OCLIENT a MFC/OLE será iniciar a criação do e corrigir os erros óbvias do compilador que resultarão. Se o exemplo de OCLIENT de MFC 2.0 e o compila nesta versão MFC, descobrirá que não há muitos erros que a resolver. Os erros na ordem em que ocorreram são descritos a seguir.

Criar e corrigir erros

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

O primeiro erro na COleClientItem::Draw. Em MFC/OLE1 levará mais parâmetros do que a versão de MFC/OLE. Os parâmetros adicionais não foram frequentemente necessários e geralmente NULL (como neste exemplo). Esta versão MFC pode determinar automaticamente valores para os lpWBounds quando o CDC a que está sendo extraído é uma DC. de metarquivo. Além disso, o parâmetro de pFormatDC não é mais necessário desde que a estrutura criará um “DC. atributo” de pDC passado. Para corrigir esse problema, assim você remover apenas os dois parâmetros NULL adicionais na chamada de descompasso.

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

O resultado anterior de erros do fato de que qualquer COleClientItem::CreateXXXX funciona em MFC/OLE1 exigia que um nome exclusivo foi passado para representar o item. Este é um requisito de API OLE subjacente. Isso não é necessário em MFC/OLE 2 desde que o OLE 2 não usa DDE como o mecanismo subjacente de comunicações (o nome foi usado em conversas de DDE). Para corrigir esse problema, você pode remover a função de CreateNewName bem como as referências a ele. É fácil descobrir o que cada função de MFC/OLE está aguardando nesta versão somente colocando o cursor na chamada e pressionando F1.

Outra área que é significativamente diferente é manipulação da área de transferência OLE 2. Com OLE1, você usou o windows as APIs da área de transferência que interagem com a área de transferência. Com 2 OLE isso é feito com um mecanismo diferente. As APIs MFC/OLE1 assumiram que a área de transferência esteve aberta antes de copiar um objeto de COleClientItem para a área de transferência. Isso não é mais necessário e fará com que todas as operações da área de transferência de MFC/OLE falhar. Quando você editar o código para remover as dependências em CreateNewName, também deverá 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'

Resultado desses erros do manipulador de CMainView::OnInsertObject . Tratar de “comando do novo objeto insert” é outra área onde as coisas modifiquem o suficiente para um bit. Nesse caso, é mais fácil mesclar somente a implementação original com esse valor fornecido por AppWizard para um novo aplicativo de contêiner OLE. De fato, esta é uma técnica que você possa aplicar movendo outros aplicativos. Em MFC/OLE1, você exibiu a caixa de diálogo do objeto insert” chamando a função de AfxOleInsertDialog . Nesta versão cria um objeto da caixa de diálogo de COleInsertObject e chama DoModal. Além disso, os novos itens com OLE DB são criados com CLSID em vez de uma cadeia de caracteres de classname. O resultado final deve ter esta aparência

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

Dica

O novo objeto de inserção pode ser diferente para seu aplicativo):

Também é necessário incluir <afxodlgs.h, que>contém a declaração da classe da caixa de diálogo de COleInsertObject bem como as outras caixas de diálogo padrão fornecidas por 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 as constantes qualquer OLE1 foram alterados no OLE 2, mesmo que o conceito sejam as mesmas. Nesse caso OLEVERB_PRIMARY foi alterada a OLEIVERB_PRIMARY. Em OLE1 e OLE 2, o verbo primário é executado com frequência por um contêiner quando o usuário clicar duas vezes em um item.

Além disso, DoVerb agora tem um parâmetro adicional — um ponteiro para uma exibiçãoCView(*). Esse parâmetro só é usado para implementar “a edição visual” (ou a ativação in-loco). Por enquanto você definir esse parâmetro PARA NULL, pois você não estiver implementando esse recurso neste momento.

Para garantir que a estrutura nunca tentar a in-loco ativar, você deve substituir COleClientItem::CanActivate como segue:

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

Em MFC/OLE1, COleClientItem::GetBounds e SetBounds foram usados para consultar e manipular a extensão de um item (os membros de left e de top eram sempre zero). Em MFC/OLE 2 com suporte mais diretamente por COleClientItem::GetExtent e por SetExtent, que controlam TAMANHO ou CSize em vez disso.

O código para o novo SetItemRectToServer, e as chamadas de UpdateItemRectFromServer são semelhantes a este:

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

Em chamadas à API MFC/OLE1 síncronas de um contêiner para um servidor foram simulados, porque OLE1 foi inerentemente assíncrona em muitos casos. Era necessário verificar uma chamada assíncrona pendente em andamento antes de processar comandos do usuário. MFC/OLE1 forneceu a função de COleClientItem::InWaitForRelease fazendo isso. Em MFC/OLE 2 isso não é necessário, você pode remover juntamente qualquer substituição de OnCommand em CMainFrame.

Neste momento OCLIENT compilará e vinculará.

Outras alterações necessárias

Há algumas coisas que não são feitas que o manterá OCLIENT de execução, porém. Agora é melhor corrigir esses problemas em vez de posterior.

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

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

Também é uma boa ideia verificar se existem funções virtuais para alterações da lista de parâmetros. Essa função é uma COleClientItem::OnChange, substituído em cada aplicativo de contêiner de MFC/OLE. Examinando a ajuda online, verá que “um dwParam adicional DWORD” esteve adicionado. O novo CRectItem::OnChange examina como segue:

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

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

Adicionando “a edição visual”

Um dos recursos mais interessantes OLE é a ativação local (ou “edição visual”). Esse recurso permite que o aplicativo para servidores assumir partes da interface do usuário do contêiner oferece uma interface mais tranquila de edição do usuário. Para implementar a ativação na OCLIENT, alguns recursos especiais precisam ser adicionados, bem como qualquer código adicional. Esses recursos e o código são fornecidos normalmente por AppWizard — de fatos, muito de código aqui foi solicitado diretamente de um aplicativo de AppWizard atualizado com suporte “contêiner”.

Antes de mais nada, é necessário adicionar um recurso de menu a ser usado quando há um item que está ativa no local. Você pode criar esse recurso do menu no Visual C++ copiando o recurso de IDR_OCLITYPE e removendo todos mas em Arquivo e na janela PNF- UPS. Duas barras separadoras são inseridas entre o Arquivo e a janela PNF- UPS para indicar a separação de grupos (deve ter a seguinte aparência: Arquivo | | Janela). Para obter mais informações sobre o que a média desses separadores e como os menus do servidor e do contêiner são mesclados consulte a “menus e a recursos: Menu de mesclagem” no OLE 2 classes.

Depois que você tiver esses menus criados, você precisa sair da estrutura saber sobre eles. Isso é feito chamando CDocTemplate::SetContainerInfo para o modelo de documento antes que você adicioná-lo à lista de modelo de documento em seu InitInstance. O novo código para registrar o modelo de documento 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 de IDR_OLECLITYPE_INPLACE é o recurso in-loco especial criado no Visual C++.

Para habilitar a ativação no local, há algumas coisas que precisam alterar na classe derivada de CView () CMainView bem como da classe derivada de COleClientItem (CRectItem). Todas essas substituições serão fornecidas por AppWizard e a maioria de implementação virão diretamente de um aplicativo padrão de AppWizard.

Na primeira etapa dessa porta, o ativação no local foi desabilitado completamente substituindo COleClientItem::CanActivate. Essa substituição deve ser removida para permitir a ativação local. Além disso, NULL foi passado para todas as chamadas a DoVerb (há duas delass) como fornecer a exibição foi necessária apenas para o ativação local. Para implementar completamente o ativação no local, é necessário passar a exibição correta na chamada de DoVerb . Um dessas chamadas está em CMainView::OnInsertObject:

pItem->DoVerb(OLEIVERB_SHOW, this);

Outro está em CMainView::OnLButtonDblClk:

m_pSelection->DoVerb(OLEIVERB_PRIMARY, this);

É necessário substituir COleClientItem::OnGetItemPosition. Isso diz ao servidor onde colocar a janela em relação à janela do contêiner quando o item é in-loco ativado. Para OCLIENT, a implementação é trivial:

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

A maioria dos servidores também implementam o que é chamado “redimensionar in-loco.” Isso permite que a janela do servidor é feita dimensionar e movida quando o usuário editar o item. O contêiner deve participar nessa ação, como mover ou redimensionar a janela geralmente afeta a posição e o tamanho do documento contêiner própria. A implementação de OCLIENT sincroniza o retângulo interno mantido por m_rect com a nova posição e o 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;
}

Nesse ponto, há bastante código para permitir que um item seja ativado in-loco e trata o tamanho e mover o item quando estiver ativa, mas nenhum código permitirá que o usuário encerrar a sessão de edição. Embora alguns servidores fornece essa funcionalidade próprios executando a chave de escape, sugere-se que os contêineres fornecem duas maneiras de desativar um item: (1) clicando fora do item, e (2) pressionando a tecla ESCAPE.

Para a chave ESCAPE, adicione um acelerador com o Visual C++ que mapeia a chave de VK_ESCAPE a um comando, ID_CANCEL_EDIT é adicionado aos recursos. O manipulador deste comando seguinte:

// 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 controlar casos em que o usuário clicar fora do item, adicione o seguinte código para o início de CMainView::SetSelection:

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

Quando um item está ativa no local, deve ter o foco. Para verificar esse é o caso que você trata OnSetFocus de modo que o foco seja 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 a exibição é redimensionada, você precisa notificar o item ativo do retângulo de recuo foi alterado. Para fazer isso você fornece um manipulador para 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 de MFC 2.0

HIERSVR também foi incluído em MFC 2.0 e tiver implementado com OLE MFC/OLE1. Essa observação descreve brevemente as etapas pelo qual esse aplicativo foi convertido inicialmente usar as classes de MFC/OLE 2. Vários recursos do foram adicionados depois que a porta inicial foi concluída para ilustrar melhor as classes de MFC/OLE 2. Esses recursos não serão incluídos aqui; consulte o exemplo próprio para obter mais informações sobre esses recursos avançados.

Dica

Os erros do compilador e o processo passo a passo foram criados com Visual C++ 2,0.As mensagens de erro e os locais específicos podem ter sido alteradas com Visual C++ 4.0, mas informações conceituais permaneça válida.

Obtendo as no serviço

A abordagem adotada para mover o exemplo de HIERSVR a MFC/OLE será iniciar a criação do e corrigir os erros óbvias do compilador que resultarão. Se o exemplo de HIERSVR de MFC 2.0 e o compila nesta versão MFC, descobrirá que não há muitos erros a resolver (embora houver mais do que com o exemplo de OCLIENT). Os erros na ordem em que ocorrem geralmente são descritos abaixo.

Criar e corrigir erros

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

Este primeiro erro indica um problema muito maior com a função de InitInstance para servidores. A inicialização necessárias para um servidor OLE provavelmente uma das alterações que as maiores você precisará fazer a seu aplicativo MFC/OLE1 obter o que executa o. É a melhor a fazer é ter o que AppWizard cria para um servidor OLE e altera seu código conforme apropriado. Veja alguns pontos a serem considerados:

É necessário inicializar as bibliotecas do chamando AfxOleInit

A chamada SetServerInfo objeto de modelo de documento para identificadores de conjunto de recursos do servidor e o tempo de execução classificam as informações que você não pode definir com o construtor de CDocTemplate .

Não mostrar a janela principal do seu aplicativo /Embedding se estiver presente na linha de comando.

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

É necessário “conectar-se” o objeto de COleTemplateServer ao modelo de documento chamando COleTemplateServer::ConnectTemplate.

Atualizar o Registro do sistema quando seu aplicativo autônomo é executado. Deste modo, se o usuário move o .EXE para seu aplicativo, executando o de seu novo local atualizará o base de dados do registro do sistema do windows para apontar para o novo local.

Após a aplicação de todas essas alterações com base no que AppWizard cria para InitInstance, InitInstance e relacionado (GUID) para HIERSVR deve ler a 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ê observará que o código anterior se refere a uma nova ID de recurso, IDR_HIERSVRTYPE_SRVR_EMB. Este é o recurso de menu a ser usado quando um documento que seja digitado em outro contêiner é editado. Em MFC/OLE1 os itens de menu específicos para editar um item inserido estavam em rapidamente alterada. Usando um menu completamente diferente para editar um item quando inserido em vez de editar um documento baseado facilita forneça interfaces de usuário diferentes para esses dois modos separados. Como veremos posteriormente, um recurso completamente separado do menu é usado para editar um objeto inserido no local.

Para criar esse recurso, carregar o script de recursos no Visual C++ e copie o recurso existente no menu de IDR_HIERSVRTYPE. Renomeie o novo recurso a IDR_HIERSVRTYPE_SRVR_EMB (esta é a mesma convenção de nomenclatura que o usa de AppWizard). Seguinte alteração de “salvar Arquivo” de “a atualização Arquivo”; atribua a ID ID_FILE_UPDATEde comando. Também alterado de “em salvar Arquivo como” “para arquivar a cópia de salvar como”; atribua a ID ID_FILE_SAVE_COPY_ASde comando. A estrutura fornece a implementação de ambos os 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 de OnSetData, desde que se referir ao tipo de OLESTATUS . OLESTATUS foi os erros retornados OLE1 de maneira. Isso foi alterada a HRESULT no OLE 2, embora o MFC convertesse geralmente HRESULT em COleException que contém o erro. Nesse caso particular, a substituição de OnSetData não for mais necessária, à maneira mais fácil de fazer é removê-lo.

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

O construtor de COleServerItem usar um parâmetro do acréscimo “BOOL”. Esse sinalizador determina como o gerenciamento de memória é feito nos objetos de COleServerItem . Definindo o FOR RETIFICAR, a estrutura controla o gerenciamento de memória desses objetos — excluí-los quando não são mais necessários. HIERSVR usa objetos de CServerItem (derivado de COleServerItem) como parte dos dados nativos, você definirá esse sinalizador como FALSE. Isso permite que 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 isso, há algumas funções “puro-” virtuais que não foram preteridos em CServerItem. Isso é muito provavelmente causado pelo fato de que a lista de parâmetros de OnDraw foi alterado. Para corrigir esse erro, altere CServerItem::OnDraw os seguintes (como também na declaração 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 de desenho, se conveniente. Esse tamanho deve estar em HIMETRIC. Nesse caso, não é conveniente preencher esse valor, a estrutura chama OnGetExtent para recuperar a extensão. Para que funcione, você precisará 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 de CServerItem::CalcNodeSize o tamanho do item é convertido em HIMETRIC e armazenado em m_rectBounds. O membro não documentado de 'm_rectBounds' de COleServerItem não existir (parcialmente foi substituído por m_sizeExtent, mas em 2 OLE esse membro tem um uso ligeiramente diferente do que m_rectBounds fez em OLE1). Em vez de definir o tamanho de HIMETRIC nessa variável de membro, você retornar-o-&z. 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;
}

CServerItem também substitui COleServerItem::OnGetTextData. Essa função é obsoleta em MFC/OLE e é substituída por um mecanismo diferente. MFC a versão 3,0 do exemplo HIERSVR MFC OLE implementa esta funcionalidade substituindo COleServerItem::OnRenderFileData. Essa funcionalidade não é importante para essa porta básica, assim você pode remover a substituição de OnGetTextData.

Há muito mais erros em svritem.cpp que não foram resolvidos. Não há erros “real” — apenas erros causados por erros anteriores.

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

COleServerItem::CopyToClipboard não oferece o sinalizador “bIncludeNative”. Os dados nativos (os dados gravados fora pelo item do servidor são serializados a função) são copiados sempre, portanto você remover o primeiro parâmetro. Além disso, CopyToClipboard gerará uma exceção quando ocorre um erro em vez de retornar FALSE. Alterar o código para CServerView::OnEditCopy como segue:

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 há mais erros resultantes da compilação da versão MFC 2.0 de HIERSVR de que houve para a mesma versão de OCLIENT, havia realmente menos alterações.

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

Adicionando “a edição visual”

Para adicionar “a edição visual” (ou a ativação in-loco) para este aplicativo de servidor, há apenas algumas ações a serem adotadas de:

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

  • Esse aplicativo tem uma barra de ferramentas, portanto você precisará de uma barra de ferramentas com somente um subconjunto da barra de ferramentas normal de corresponder aos comandos de menu disponíveis no servidor (corresponde o recurso de menu mencionado acima).

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

  • Você precisa saber a estrutura sobre esses recursos e classes especiais.

O recurso de menu é fácil de criar. Execute o Visual C++, copie o recurso IDR_HIERSVRTYPE de menu a um recurso do menu chamado IDR_HIERSVRTYPE_SRVR_IP. Modifique o menu de forma que no menu somente os popups de edição e de ajuda a serem deixados. Adicionar dois separadores no menu entre os menus de edição e de ajuda (deve ter a seguinte aparência: Edição | | Ajuda). Para obter mais informações sobre o que a média desses separadores e como os menus do servidor e do contêiner são mescladas, consulte “menus e recursos: Menu de mesclagem” no OLE 2 classes.

O bitmap da barra de ferramentas do subconjunto pode ser facilmente criado copiando um de um aplicativo gerado AppWizard atualizado com um padrão de “servidor” verificada. Esse bitmap pode ser importado no Visual C++. Certifique-se de fornecer ao bitmap uma ID de IDR_HIERSVRTYPE_SRVR_IP.

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

Agora que todos os novos recursos e classes são criados, adicione o código necessário de forma que a estrutura nem sobre esses (e sabe que esse aplicativo agora da suporte ao editar no local). Isso é feito adicionando um pouco mais de parâmetros na chamada de SetServerInfo na função de InitInstance :

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

Agora é in-loco pronto para ser executado em qualquer contêiner que também oferecer suporte à ativação local. Mas, há um bug menor que ainda espreita no código. HIERSVR oferece suporte a um menu de contexto exibido quando o usuário, pressionar o botão direito do mouse em. Esse menu funciona quando HIERSVR está completamente aberto, mas não funciona ao editar uma inserção no local. O motivo poderá ser fixada para baixo a esta ú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 a AfxGetApp()->m_pMainWnd. Quando o servidor é ativado no local, tem uma janela principal e o m_pMainWnd for definido, mas é geralmente invisível. Além disso, essa janela refere-se à janela principal do aplicativo, a janela do quadro MDI que aparece quando o servidor é totalmente aberto ou execução autônoma. Não se refere a janela ativa do quadro — que quando é ativado em uma janela do quadro derivada de COleIPFrameWnd. Para obter a janela ativa correta mesmo quando o editar no local, essa versão MFC adiciona uma nova função, AfxGetMainWnd. Em geral, você deve usar essa função em vez de AfxGetApp()->m_pMainWnd. Esse código precisa alterar como segue:

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

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

  • Aplicando zoom para true, o comportamento de WYSISYG em relação ao contêiner.

  • Arraste/remoção e um formato personalizado da área de transferência.

  • Rolar a janela do contêiner como a seleção é alterado.

O exemplo de HIERSVR em MFC 3,0 também usa um design ligeiramente diferente para os itens do servidor. Isso ajuda a conservar a memória e torna os links mais flexíveis. Com a versão 2,0 de HIERSVR cada nó na árvore *AIA *COleServerItem. COleServerItem leva um pouco mais sobrecarga do que é estritamente necessária para cada um desses nós, mas COleServerItem é necessário para cada link ativo. Mas geralmente, há poucas links ativos em um determinado momento. Para fazer isso, o mais eficiente HIERSVR nesta versão MFC separa o nó de COleServerItem. Tem um CServerNode e uma classe de CServerItem . CServerItem (derivado de COleServerItem) é criado apenas conforme necessário. Uma vez que a parada do contêiner (ou contêineres) usando esse link específico desse nó específico, o objeto de CServerItem associado ao CServerNode é excluída. Esse design é mais eficiente e mais flexível. A flexibilidade na o tratar os vários links de seleção. Nenhuma dessas duas versões seleção de suporte de HIERSVR de mais, mas elas seriam muito mais fáceis de adicionar (e aos links de suporte a essas seleções) com a versão 3,0 de HIERSVR MFC, desde que COleServerItem está separado dos dados nativos.

Consulte também

Outros recursos

Observações técnicas por número

Observações técnicas por categoria