Questões gerais relacionadas com a migração
Uma das metas de design para as classes de OLE 2 no MFC 2.5 (e superior) foi reter grande parte da arquitetura do mesma posta em prática 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, COleClientIteme COleServerItem). Além disso, muitas das APIs nessas classes são exatamente o mesmo. No entanto, OLE 2 é dràstica diferente do OLE 1.0 para que você possa esperar que alguns detalhes foram alterados. Se você estiver familiarizado com suporte OLE1 a MFC 2.0, você vai se sentir em casa com suporte 2.0 do MFC.
Se você está tendo um aplicativo MFC/OLE1 existente e acrescentando-lhe a funcionalidade OLE 2, você deve ler esta nota em primeiro lugar. A presente nota aborda algumas questões gerais, você pode encontrar enquanto portando sua funcionalidade OLE1 a MFC/OLE 2 e, em seguida, discute os problemas descobertos enquanto portando duas aplicações incluídas no MFC 2.0: o MFC OLE exemplos OCLIENT e HIERSVR.
Arquitetura de documento/Exibir do MFC É importante
Se seu aplicativo não usar arquitetura de documento/Exibir do MFC e você deseja adicionar suporte OLE 2 para seu aplicativo, agora é a hora de mover para documento/Exibir. Muitos dos benefícios de classes do MFC OLE 2 só são percebidos depois que seu aplicativo está usando o built-in arquitetura e componentes do MFC.
A implementação de um servidor ou contêiner sem usar a arquitetura do MFC é possível, mas não recomendado.
Usar MFC implementação em vez de seu próprio
MFC "enlatado implementação" classes, como CToolBar, CStatusBare CScrollView tem built-in código maiúscminúsc especial para suporte OLE 2. Então, se você pode usar essas classes em seu aplicativo poderá beneficiar do esforço colocar para torná-los OLE consciente. Novamente, é possível "rolo seu próprio" classes aqui para estes fins, mas não é sugerido. Se você precisar implementar uma funcionalidade semelhante, o código-origem MFC é uma excelente referência para lidar com alguns dos melhores pontos de OLE (especialmente quando se trata de ativação in-loco).
Examine o código de exemplo MFC
Há uma série de exemplos do MFC que incluem funcionalidade OLE. Cada um desses aplicativos implementa OLE de um ângulo diferente:
HIERSVR - destinado principalmente para uso como um aplicativo de servidor. Foi incluído no MFC 2.0 como um aplicativo MFC/OLE1 e foi portado para MFC/OLE 2 e, em seguida, estendido tal que ele implementa vários recursos OLE disponíveis no OLE 2.
OCLIENT - este é um aplicativo de contêiner autônomo, destinado a demonstrar muitos dos recursos OLE do ponto de vista de contêiner. Ele também foi portado do MFC 2.0 e, em seguida, estendido para oferecer suporte a muitos dos mais avançados recursos OLE, como formatos área de transferência personalizada e links para itens incorporados.
DRAWCLI - este aplicativo implementa suporte de contêiner OLE muito como OCLIENT faz, exceto que ele faz isso no âmbito de um programa existente de desenho orientado a objeto. Mostra-lhe como você pode implementar suporte de contêiner OLE e integrá-lo em seu aplicativo.
SUPERPAD - este aplicativo, além de ser um aplicativo autônomo fino, é também um servidor OLE. O suporte de servidor que implementa é bastante minimalista. De particular interesse é como ele usa serviços de área de transferência OLE para copiar dados para a área de transferência, mas usa a funcionalidade incorporada do Windows "Editar" controle para implementar a funcionalidade de colar da área de transferência. Isso mostra uma mistura interessante de tradicional uso da API do Windows, bem como integração com o novo OLE APIs.
Para obter mais informações sobre os aplicativos de exemplo, consulte a "MFC exemplo ajuda".
Estudo de caso: OCLIENT de 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 que a porta inicial foi concluída para melhor ilustrar as classes MFC/OLE. Esses recursos não serão cobertos aqui; consulte o exemplo em si mesmo para obter mais informações sobre esses recursos avançados.
&Notanbsp; Os erros do compilador e o processo passo a passo foi criado com o Visual C++ 2.0. Locais e mensagens de erro específicas podem ter mudado com o Visual C++ 4.0, mas a informação conceitual permanece válida.
Começá-lo e funcionando
A abordagem adoptada para portar o exemplo OCLIENT para MFC/OLE é começar a construí-la e corrigindo os erros de compilador óbvio que resultarão. Se você tomar o exemplo OCLIENT do MFC 2.0 e compilá-lo com esta versão do MFC, você encontrará que não há que muitos erros para resolver. Os erros na ordem em que eles ocorreram estão descritos abaixo.
Compilar e corrigir erros
\oclient\mainview.cpp(104): erro C2660: 'Desenhar': função não aceita 4 parâmetros
O primeiro erro refere-se COleClientItem::Draw. Em MFC/OLE1 demorou mais parâmetros do que a versão do MFC/OLE leva. Os parâmetros extras muitas vezes não eram necessárias e normalmente nula (como no exemplo). Esta versão do MFC pode determinar automaticamente os valores para o lpWBounds quando o CDC que está sendo desenhado para é um metarquivo DC. Além disso, o parâmetro pFormatDC não é mais necessário já que o quadro vai construir um de "atributo DC" do pDC passado em. Para corrigir esse problema, você simplesmente remove os dois extra NULL parâmetros para a chamada de desenho.
\oclient\mainview.cpp(273): erro C2065: 'OLE_MAXNAMESIZE': identificador não declarada
\oclient\mainview.cpp(273): erro C2057: esperado expressão constante
\oclient\mainview.cpp(280): erro C2664: 'CreateLinkFromClipboard': não é possível converter parâmetro 1 de 'char [1]' para ' enum:: tagOLERENDER '
\oclient\mainview.cpp(286): erro C2664: 'CreateFromClipboard': não é possível converter parâmetro 1 de 'char [1]' para ' enum:: tagOLERENDER '
\oclient\mainview.cpp(288): erro C2664: 'CreateStaticFromClipboard': não é possível converter parâmetro 1 de 'char [1]' para ' enum:: tagOLERENDER '
Os erros acima resultam do fato de que todas as funções de COleClientItem::CreateXXXX no MFC/OLE1 exigido que um nome exclusivo ser passado para representar o item. Esta foi uma exigência do OLE API subjacente. Isso não é necessário no MFC/OLE 2 desde que OLE 2 não usar DDE como o mecanismo subjacente de comunicações (o nome foi usado em conversas DDE). Para corrigir esse problema, você pode remover a função de CreateNewName , bem como todas as referências a ele. É fácil de descobrir o que cada função do MFC/OLE está esperando nesta versão simplesmente, colocando o cursor sobre a chamada e pressionando F1.
Outra área que é significativamente diferente é manipulação de área de transferência OLE 2. Com OLE1, você usou a prancheta de Windows que APIs interagem com a área de transferência. Com OLE 2 isso é feito com um mecanismo diferente. As APIs do MFC/OLE1 pressupõe-se que a área de transferência foi aberta antes de copiar um objeto COleClientItem para a área de transferência. Isso não é mais necessário e fará com que todas as operações de área 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): erro C2065: 'AfxOleInsertDialog': identificador não declarada
\oclient\mainview.cpp(332): erro C2064: expressão não é avaliada como uma função
\oclient\mainview.cpp(344): erro C2057: esperado expressão constante
\oclient\mainview.cpp(347): Erro C2039: 'CreateNewObject': não é um membro de 'CRectItem'
Esses erros resultam do manipulador de CMainView::OnInsertObject . Manipular o comando "Inserir novo objeto" é outra área onde as coisas mudaram um pouco. Neste caso, é mais fácil simplesmente mesclar a implementação original com que fornecida pelo AppWizard para um novo aplicativo de contêiner OLE. Na verdade, esta é uma técnica que você pode aplicar para outros aplicativos. No MFC/OLE1, exibida a caixa de diálogo "Inserir objeto", chamado de AfxOleInsertDialog função. Nesta versão você construir um objeto de caixa de diálogo COleInsertObject e chame DoModal. Além disso, são criados novos itens OLE com um CLSID em vez de uma Cadeia de caracteres de nome de classe. O resultado final deve ser algo como isto
COleInsertDialog dlg;
se (dlg.DoModal()! = IDOK)
nbsp; retornar;
BeginWaitCursor();
CRectItem * pItem = NULL;
TENTE
{
/ / Primeiro criar o objeto C++
pItem = GetDocument() - > CreateItem();
ASSERT_VALID(pItem);
/ / Inicializar o item de dados de caixa de diálogo.
se (! dlg.CreateItem(pItem))
AfxThrowMemoryException();
/ / qualquer exceção vai fazer
ASSERT_VALID(pItem);
/ / executa o objeto, se for caso disso
se (dlg.GetSelectionType() = = COleInsertDialog:: CreateNewItem)
pItem - > DoVerb(OLEIVERB_SHOW, this);
/ / atualizar imediatamente
pItem - > UpdateLink();
pItem - > UpdateItemRectFromServer();
/ / Definir como item recentemente inserido
SetSelection(pItem);
pItem - > Invalidate();
}
CATCH (CException, e)
{/ / Apagar item
se (pItem! = NULL)
GetDocument() - > DeleteItem(pItem);
AfxMessageBox(IDP_FAILED_TO_CREATE);
}
END_CATCH
EndWaitCursor()
&Notanbsp; Inserir novo objeto pode ser diferente para seu aplicativo):
É igualmente necessário incluir lt;afxodlgs.h > que contém a declaração para a classe de caixa de diálogo COleInsertObject , bem como as outras caixas de diálogo padrão fornecidas pelo MFC.
\oclient\mainview.cpp(367): erro C2065: 'OLEVERB_PRIMARY': identificador não declarada
\oclient\mainview.cpp(367): erro C2660: 'DoVerb': função não aceita parâmetros 1
Esses erros são causados pelo fato de que algumas constantes OLE1 mudaram em OLE 2, mesmo que no conceito são os mesmos. Neste caso, OLEVERB_PRIMARY mudou para OLEIVERB_PRIMARY. OLE1 tanto OLE 2, o verbo principal é geralmente feito por um contêiner quando o usuário clica Duplo em um item.
Além disso, DoVerb agora leva um parâmetro extra — um ponteiro para um modo de exibição (CView*). Esse parâmetro só é usado para implementar "Edição Visual" (ou ativação in-loco). Por agora você definir esse parâmetro para NULL, uma vez que não estão a aplicar esse recurso neste momento.
Para certificar-se de que o quadro nunca tenta no local ativar, você deve substituir COleClientItem:: CanActivate como segue
BOOL CRectItem::Ca&nActivate()
{
nbsp; retornar FALSE;
}
\oclient\rectitem.cpp(53): erro C2065: 'GetBounds': identificador não declarada
\oclient\rectitem.cpp(53): erro C2064: expressão não é avaliada como uma função
\oclient\rectitem.cpp(84): erro C2065: 'SetBounds': identificador não declarada
\oclient\rectitem.cpp(84): erro C2064: expressão não é avaliada como uma função
No MFC/OLE1, COleClientItem::GetBounds e SetBounds foram usados para consultar e manipular a extensão de um item (os esquerdo e parte superior Membros foram sempre zero). No MFC/OLE 2 isso é mais diretamente suportado pelo COleClientItem::GetExtent e SetExtent, que lidar com um SIZE ou CSize em vez disso.
O código para o seu novo SetItemRectToServer, e UpdateItemRectFromServer chamadas esta aparência:
BOOL CRectItem::UpdateItemRectFromServer()
{
nbsp; Assert(m_bTrackServerSize);
Tamanho de CSize;
if (!.GetExtent(&size))
retornar FALSE; / / em branco
/ / mapa de HIMETRIC para coordenadas tela
{
CClientDC screenDC(NULL);
screenDC.SetMapMode(MM_HIMETRIC);
screenDC.LPtoDP(&size);
}
/ / apenas definir o tamanho do item
se (m_rect.Size()! = tamanho)
{
/ / invalidar a tamanho/posição antiga
Invalidate();
m_rect.Right = m_rect.left + size.cx;
m_rect.Bottom = m_rect.top + size.cy;
/ / assim como o novo tamanho/posição
Invalidate();
}
retornar TRUE;
}
BOOL CRectItem::SetItemRectToServer()
{
/ / definir os limites oficiais para o item incorporado
Tamanho de CSize = m_rect.Size();
{
CClientDC screenDC(NULL);
screenDC.SetMapMode(MM_HIMETRIC);
screenDC.DPtoLP(&size);
}
TENTE
{
SetExtent(size); / / pode fazer uma espera
}
CATCH (CException, e)
{
retornar FALSE; / / ligações não permitirá SetBounds
}
END_CATCH
retornar TRUE;
}
\oclient\frame.cpp(50): Erro C2039: 'InWaitForRelease': não é um membro de 'COleClientItem'
\oclient\frame.cpp(50): erro C2065: 'InWaitForRelease': identificador não declarada
\oclient\frame.cpp(50): erro C2064: expressão não é avaliada como uma função
No MFC/OLE1 síncronas chamadas de API de um contêiner para um servidor foram simulados, desde OLE1 era inerentemente assíncrona em muitos casos. Era necessário verificar se há uma chamada assíncrona pendente em andamento antes de processar comandos do usuário. MFC/OLE1 fornecido a função COleClientItem::InWaitForRelease para fazê-lo. No MFC/OLE 2 isso não é necessário, para que você possa remover a substituir de OnCommand em CMainFrame todos juntos.
Neste momento OCLIENT irá compilar e vincular.
Outras alterações necessárias
Existem algumas coisas que não são feitas que manterão OCLIENT seja executado, no entanto. É melhor corrigir esses problemas agora em vez de mais tarde.
Em primeiro lugar é necessário inicializar as bibliotecas OLE. Isso é feito chamando AfxOleInit de InitInstance:
if (!.AfxOleInit())
{
AfxMessageBox ("falhado ao inicializar as bibliotecas OLE");
retornar FALSE;
}
Também é uma boa idéia para verificar se há 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. Olhando para a ajuda online, você vai ver que foi adicionado um extra 'dwParam DWORD'. A nova CRectItem::OnChange tem a seguinte aparência
void CRectItem::OnChange (OLE_NOTIFICATION wNotification, DWORD dwParam)
{
se (m_bTrackServerSize amp; &
!UpdateItemRectFromServer())
{
/ / Blank objeto
se (wNotification = = OLE_CLOSED)
{
/ / não há dados recebidos para o objeto - destruí-lo
FAZER VALER (!.IsVisible());
GetDocument() - > DeleteItem(this);
retornar; / / nenhuma atualização (item é ido agora)
}
}
se (wNotification! = OLE_CLOSED)
Dirty();
Invalidate(); / / qualquer alteração causará um redesenho
}
No MFC/OLE1, aplicativos de Contêiner derivado o classe de documento COleClientDoc. No MFC/OLE 2 essa classe tem sido removida e substituída pelo COleDocument (esta nova organização torna mais fácil criar aplicativos de Contêiner/servidor). Existe um # define que mapeia COleClientDoc para COleDocument para simplificar operações de portabilidade dos aplicativos MFC/OLE1 a MFC/OLE 2, tais como OCLIENT. Uma das características não fornecidas pelo COleDocument que foi fornecida pelo COleClientDoc é as entradas de mapa de mensagem de comando padrão. Isso é feito para que os aplicativos de servidor, que também usam COleDocument (indiretamente), não carregam com eles a sobrecarga desses manipuladores de comando a menos que um aplicativo de Contêiner/servidor. Assim, 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 aplicação de todos estes comandos é no COleDocument, que é a classe base para o seu documento.
Neste ponto, OCLIENT é um aplicativo de contêiner OLE funcional. É possível inserir itens de qualquer tipo (OLE1 ou OLE 2). Uma vez que o código necessário para permitir a ativação in-loco não está implementado, itens são editados em uma janela separada, muito parecido com OLE1. A próxima seção discute as alterações necessárias para ativar a edição in-loco (às vezes chamado de "Edição Visual").
Adicionando "Edição Visual"
Uma das características mais interessantes do OLE é ativação in-loco (ou "Edição Visual"). Esse recurso permite que o aplicativo de servidor tomar sobre partes da interface de usuário do contêiner para forneciam uma interface de edição mais transparente para o usuário. Para implementar a ativação in-loco para OCLIENT, alguns recursos especiais precisam ser adicionados, bem como alguns código adicional. Esses recursos e o código são normalmente fornecidos pelo AppWizard — na verdade, grande parte do código aqui foi tomado emprestado diretamente de um aplicativo de AppWizard fresco com suporte de "Contêiner".
Em primeiro lugar, é 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, mas os arquivo e janela de pop-ups. Duas barras do separador são inseridas entre os arquivo e janela pop-ups para indicar a separação de grupos (deve ter o seguinte: arquivo | | Janela). Para obter mais informações sobre o que significam esses separadores e como os servidor e o contêiner menus são mesclados, consulte "Menus e recursos: Menu mesclagem" em Classes OLE 2.
Uma vez que estes menus criados, você precisará permitir que a estrutura de conhecê-los. Isso é feito chamando CDocTemplate::SetContainerInfo para o modelo do documento antes de adicionar a lista de modelo de documento em seu InitInstance. O novo código para registrar o modelo de documento tiver esta aparência
CDocTemplate * pTemplate = novo (CMultiDocTemplate
nbsp; IDR_OLECLITYPE,
RUNTIME_CLASS(CMainDoc),
RUNTIME_CLASS(CMDIChildWnd), / / padrão quadro de filho MDI
RUNTIME_CLASS(CMainView));
pTemplate - > SetContainerInfo(IDR_OLECLITYPE_INPLACE);
AddDocTemplate(pTemplate)
O recurso IDR_OLECLITYPE_INPLACE é o recurso especial no local criado no Visual C++.
Para habilitar a ativação in-loco, há algumas coisas que precisam mudar em ambos o CView (CMainView) derivado classe como a classe de COleClientItem derivado (CRectItem). Todas estas substituições são fornecidas pelo AppWizard e a maioria da execução virá diretamente de um aplicativo de AppWizard padrão.
Na primeira etapa deste Port, ativação in-loco foi desativada inteiramente por substituir COleClientItem:: CanActivate. Essa substituição deve ser removida para permitir a ativação in-loco. Além disso, NULL foi passado para todas as chamadas para DoVerb (existem dois deles) porque fornecendo que a visão só era necessária para a ativação in-loco. Para implementar plenamente a ativação in-loco, é necessário passar do modo de exibição correto na chamada DoVerb . Uma dessas chamadas está em CMainView::OnInsertObject
pItem-> DoVerb(OLEIVERB_SHOW, this)
Outra é em CMainView::OnLButtonDblClk
m_pSelection-> DoVerb(OLEIVERB_PRIMARY, this)
É necessário substituir COleClientItem::OnGetItemPosition. Isso informa ao servidor onde colocar sua janela relativo para a janela do contêiner quando o item é ativado no local. Para OCLIENT, a implementação é trivial
priv&atevoid CRectItem::OnGetItemPosition (CRectamp; rPosition)
{
rPosition = m_rect;
}
A maioria dos servidores também implementar o que é chamado "in loco redimensionamento." Isso permite que a janela do servidor sejam dimensionados e mudou-se enquanto o usuário edita o item. O contêiner deve participar nesta acção, como mover ou redimensionar a janela geralmente afeta a posição e tamanho dentro do documento recipiente próprio. A implementação para OCLIENT sincroniza o retângulo interno mantido por m_rect com a nova posição e o tamanho.
BOOL CRectItem::OnChangeItemPosition(const CRectamp; rectPos)
{
ASSERT_VALID(this);
if (!.COleClientItem::OnChangeItemPosition(rectPos))
retornar FALSE;
Invalidate();
m_rect = rectPos;
Invalidate();
GetDocument() - > SetModifiedFlag();
retornar TRUE;
}
Neste momento, não há suficiente código para permitir que um item para ser ativado no local e para lidar com o dimensionamento e mover o item quando ele está ativo, mas não existe nenhum código que permitirá que o usuário sair a sessão de edição. Embora alguns servidores fornecerá esta funcionalidade si, manipulando a tecla escape, recomenda-se que recipientes fornecem duas maneiras para desativar um item: (1) clicando fora do item e (2) pressionando a tecla escape.
Para o escape chave adicionar um acelerador com Visual C++ que mapeia a chave VK_ESCAPE para um comando, ID_CANCEL_EDIT é adicionado aos recursos. O manipulador para este comando segue:
/ / O seguinte manipulador de comando fornece o padrão
/ / teclado do interface do usuário para cancelar um lugar
/ / edição session.void CMainView::OnCancelEdit()
{
nbsp; / / Fechar qualquer in loco item ativo nesta exibição.
COleClientItem * pActiveItem = GetDocument() - > GetInPlaceActiveItem(this);
se (pActiveItem! = NULL)
pActiveItem - > Close ();
Assert(GetDocument()-> GetInPlaceActiveItem(this) = = NULL);
}
Para manipular o caso onde o usuário clica fora do item, você adicionar o seguinte código para o início da CMainView::SetSelection
se (pNewSel! = m_pSelection | | pNewSel = = NULL)
{
nbsp; COleClientItem * pActiveItem = GetDocument() - > GetInPlaceActiveItem(this);
se (pActiveItem! = NULL & & pActiveItem! = pNewSel)
pActiveItem - > Close ();
}
& nbsp
Quando um item está ativo no local, ele deve ter o foco. Para certificar-se de que este é o caso você manipular OnSetFocus para que o foco sempre é transferida para o item activo quando o Exibir recebe o foco:
/ / Manipulação especial dos OnSetFocus e OnSize são necessários / / quando um objeto está sendo editado no local.
privatevoid CMainView::OnSetFocus (CWnd pOldWnd)
{
nbsp; COleClientItem * pActiveItem = GetDocument() - > GetInPlaceActiveItem(this);
se (pActiveItem! = NULL & &
pActiveItem - > GetItemState() = = COleClientItem::activeUIState)
{
/ / precisa definir o foco para este item se ele for mesmo modo de exibição
PWnd CWnd * = pActiveItem - > GetInPlaceWindow();
se (pWnd! = NULL)
{
pWnd - > SetFocus (); / / não chamar a classe base
retornar;
}
}
CView::OnSetFocus(pOldWnd);
}
Quando a exibição é redimensionada, você precisa notificar o item ativo que mudou o Retangular de recorte. Para fazer isso, você fornece um manipulador de OnSize:
void CMainView::OnSize (UINT nType, int cx, int cy)
{
nbsp; CView::OnSize (nType, cx, cy);
COleClientItem * pActiveItem = GetDocument() - > GetInPlaceActiveItem(this);
se (pActiveItem! = NULL)
pActiveItem - > SetItemRects();
}
Estudo de caso: HIERSVR de MFC 2.0
HIERSVR também foi incluído no MFC 2.0 e implementado OLE com MFC/OLE1. Esta anotação 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 que a porta inicial foi concluída para melhor ilustrar as classes do MFC/OLE 2. Esses recursos não serão cobertos aqui; consulte o exemplo em si mesmo para obter mais informações sobre esses recursos avançados.
&Notanbsp; Os erros do compilador e o processo passo a passo foi criado com o Visual C++ 2.0. Locais e mensagens de erro específicas podem ter mudado com o Visual C++ 4.0, mas a informação conceitual permanece válida.
Começá-lo e funcionando
A abordagem adoptada para portar o exemplo HIERSVR para MFC/OLE é começar a construí-la e corrigindo os erros de compilador óbvio que resultarão. Se você tomar o exemplo HIERSVR do MFC 2.0 e compilá-lo com esta versão do MFC, você encontrará que não existem muitos erros para resolver (apesar de haver mais do que com o exemplo OCLIENT). Os erros na ordem em que ocorrem geralmente são descritos a seguir.
Compilar e corrigir erros
\hiersvr\hiersvr.cpp(83): Erro C2039: 'RunEmbedded': não é um membro de 'COleTemplateServer'
Este primeiro erro indica um problema muito maior com a função de InitInstance para servidores. A inicialização necessária para um servidor OLE é provavelmente uma das maiores mudanças que você terá que fazer para seu aplicativo do MFC/OLE1 para obtê-lo correr. A melhor coisa a fazer é olhar para o que AppWizard cria para um servidor OLE e modificar seu código conforme apropriado. Aqui estão alguns pontos a ter em mente:
É necessário inicializar as bibliotecas OLE chamando AfxOleInit
Chamar SetServerInfo no objeto de modelo de documento para definir identificadores de recurso de servidor e informações de classe de tempo de execução que não pode ser definida com o Construtor CDocTemplate.
Não mostrar a janela principal do seu aplicativo se /Embedding estiver presente na linha de comando.
Você precisará um GUID para seu documento. Este é um identificador exclusivo para o seu tipo de documento (128 bits). AppWizard cria um para você — por isso, se você usar a técnica descrita aqui de copiar o novo código de um novo aplicativo de servidor de AppWizard gerado, você pode simplesmente "roubar" o GUID do aplicativo. Se não, você pode usar o GUIDGEN.Utilitário EXE no diretório BIN.
É necessário para "conectar" seu COleTemplateServer objeto para o modelo de documento chamando COleTemplateServer::ConnectTemplate.
Atualize o registro do sistema quando seu aplicativo é executado autônomo. Desta 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 de Windows para apontar para o novo local.
Depois de aplicar todas estas alterações com base no qual AppWizard cria para InitInstance, o InitInstance (e relacionado GUID) para o HIERSVR devem ler-se:
// 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ê vai 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 que está incorporado em outro recipiente é editado. Em MFC/OLE1 os itens de menu específicos para editar um item incorporado foram alterados em tempo real. Usando uma estrutura de menu inteiramente diferente ao editar um item incorporado em vez de editar um documento file-based torna muito mais fácil para fornecer interfaces de usuário diferentes para estes dois modos separados. Como você verá posteriormente, um recurso de menu inteiramente separado é usado ao editar um objeto incorporado no local.
Para criar este recurso, carregar o recurso script no Visual C++ e copiar o recurso de menu existente do IDR_HIERSVRTYPE. Renomeie o novo recurso para IDR_HIERSVRTYPE_SRVR_EMB (Esta é a mesma Convenção de nomeação que AppWizard usa). Em seguida altere o "Salvar arquivo" para "Atualização"; dar-lhe o ID de comando ID_FILE_UPDATE. Também alterar "Arquivo Salvar como" para "Arquivo Salvar cópia como"; dar-lhe o ID de comando ID_FILE_SAVE_COPY_AS. O framework fornece a implementação de ambos esses comandos.
\hiersvr\svritem.h(60): erro C2433: 'OLESTATUS': 'virtual' não permitido em declarações de dados
\hiersvr\svritem.h(60): erro C2501: 'OLESTATUS': especificadores de decl em falta
\hiersvr\svritem.h(60): erro C2146: erro de sintaxe: ausente ';' antes de identificador 'OnSetData'
\hiersvr\svritem.h(60): erro C2061: erro de sintaxe: identificador 'OLECLIPFORMAT'
\hiersvr\svritem.h(60): erro C2501: 'OnSetData': especificadores de decl em falta
Há uma série de erros resultantes da substituição de OnSetData, já que ele está se referindo ao tipo de OLESTATUS . OLESTATUS foi a maneira que OLE1 retornado erros. Isso foi alterado para HRESULT no OLE 2, embora o MFC geralmente converte um HRESULT em um COleException que contém o erro. Neste caso específico, a substituir do OnSetData não é mais necessária, portanto, a melhor coisa a fazer é para removê-lo.
\hiersvr\svritem.cpp(30): erro C2660: 'COleServerItem::COleServerItem': função não aceita parâmetros 1
O Construtor de COleServerItem utiliza um parâmetro extra 'BOOL'. Este sinalizador determina como gerenciamento de memória é feito sobre os objetos de COleServerItem . Por defini-lo 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 objetos de CServerItem (derivada de COleServerItem) como parte de seus dados nativos, por isso vou definir esse sinalizador como FALSE. Isso permite que HIERSVR determinar quando cada item do servidor é excluído.
\hiersvr\svritem.cpp(44): erro C2259: 'CServerItem': ilegal tentar instanciar a classe abstrata
\hiersvr\svritem.cpp(44): erro C2259: 'CServerItem': ilegal tentar instanciar a classe abstrata
Como esses erros implicam, existem algumas funções 'puras-virtuais' que não foram substituídas em CServerItem. Provavelmente isto é causado pelo fato de que a lista de parâmetros de OnDraw mudou. Para corrigir esse erro, altere CServerItem::OnDraw da seguinte forma (bem como a declaração de svritem.h)
BOOL CServerItem::OnDraw(CDC* pDC, CSizeamp; rSize)
{
/ / pedido de OLE para desenhar nó
pDC - > SetMapMode(MM_TEXT); / / sempre em pixels
retornar DoDraw (pDC, CPoint(0,0), FALSE);
}
O novo parâmetro é 'rSize'. Isso permite que você preencha o tamanho do desenho, se conveniente. Esse tamanho deve ser em HIMETRIC. Neste 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(DV&ASPECT dwDrawAspect, CSizeamp; rSize)
{
se (dwDrawAspect! = DVASPECT_CONTENT)
retornar COleServerItem::OnGetExtent (dwDrawAspect, rSize);
rSize = CalcNodeSize();
retornar TRUE;
}
\hiersvr\svritem.cpp(104): erro C2065: 'm_rectBounds': identificador não declarada
\hiersvr\svritem.cpp(104): erro C2228: à esquerda do '.SetRect' deve ter tipo de classe/struct/união
\hiersvr\svritem.cpp(106): erro C2664: ' void __pascal DPtoLP __far (struct:: tagPOINT __far *, int) __far const ': não é possível converter parâmetro 1 de ' int __far *' para ' struct:: tagPOINT __far *'
No CServerItem::CalcNodeSize função do tamanho do item é convertida em HIMETRIC e armazenada em m_rectBounds. O membro em situação irregular 'm_rectBounds' de COleServerItem não existir (foi parcialmente substituído por m_sizeExtent, mas no OLE 2 Este membro tem um uso ligeiramente diferentes do que m_rectBounds fez em OLE1). Em vez de definir o tamanho HIMETRIC para essa variável de membro, você poderá devolvê-lo. Esse valor de retorno é usado em OnGetExtent, implementada anteriormente.
CServerItem::CalcNodeSize() de CSize
{
nbsp; CClientDC dcScreen(NULL);
m_sizeNode = dcScreen.GetTextExtent (m_strDescription,
m_strDescription.GetLength());
m_sizeNode + = CSize (CX_INSET * CY_INSET 2, * 2);
/ / Definir tamanho HIMETRIC sugerido
Tamanho de CSize (m_sizeNode.cx, m_sizeNode.cy);
dcScreen.SetMapMode(MM_HIMETRIC);
dcScreen.DPtoLP(&size);
retornar o tamanho;
}
CServerItem também substitui 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 substituir OnGetTextData.
Existem muitos mais erros em svritem.cpp que ainda não foram abordados. Eles não são erros de "reais" — apenas erros causados por erros anteriores.
\hiersvr\svrview.cpp(325): erro C2660: 'CopyToClipboard': função não aceita 2 parâmetros
COleServerItem::CopyToClipboard não suporta o sinalizador 'bIncludeNative'. Os dados nativos (os dados gravados pela função de Serialize do item de servidor) é sempre copiados, assim que você remove o primeiro parâmetro. Além disso, CopyToClipboard lançará uma exceção quando ocorre um erro em vez de retornar FALSE. Alterar o código de CServerView::OnEditCopy da seguinte forma
privatevoid CServerView::OnEditCopy()
{
nbsp; se (m_pSelectedNode = = NULL)
AfxThrowNotSupportedException();
TENTE
{
m_pSelectedNode - > CopyToClipboard(TRUE);
}
CATCH_ALL(e)
{
AfxMessageBox ("copiar para área de transferência falhada");
}
END_CATCH_ALL}
Embora houvesse mais erros resultantes da compilação da versão 2.0 do MFC do HIERSVR do que havia para a mesma versão do OCLIENT, eram na verdade menos alterações.
Neste momento HIERSVR irá compilar e vincular e funcionar como um servidor OLE, mas sem o recurso de edição in-loco, que 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 que você deve tomar cuidado de:
O recurso de menu é fácil de criar. Executar o Visual C++, com cópia para o recurso de menu IDR_HIERSVRTYPE para um recurso de menu chamado IDR_HIERSVRTYPE_SRVR_IP. Modificar o menu de modo que somente os popups de menu Editar e a ajuda são deixados. Adicionar dois separadores de menu entre menus editar e ajuda (deve olhar como: Editar | | Ajuda). Para obter mais informações sobre o que significam esses separadores e como os servidor e o contêiner menus são mesclados, consulte "Menus e recursos: Menu mesclagem" em Classes OLE 2.
O bitmap para a barra de ferramentas do subconjunto pode ser facilmente criado, copiando de um novo pedido de AppWizard gerado com uma opção de "Servidor" marcada. Este bitmap pode ser importado para Visual C++. Certifique-se de dar o bitmap de um ID de IDR_HIERSVRTYPE_SRVR_IP.
A classe derivada de COleIPFrameWnd pode ser copiada de um aplicativo de AppWizard gerado com suporte do servidor. Copie ambos os arquivos, IPFRAME.CPP e IPFRAME.H e adicioná-los ao projeto. Certifique-se de que a chamada LoadBitmap 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 quadro sabe sobre estes (e sabe que este aplicativo agora oferece suporte a edição in-loco). Isso é feito pela adição de que mais alguns parâmetros para SetServerInfo chamam na função InitInstance:
pDocTemplate->SetServerInfo (IDR_HIERSVRTYPE_SRVR_EMB,
IDR_HIERSVRTYPE_SRVR_IP, RUNTIME_CLASS(CInPlaceFrame))
Agora está pronto para ser executado no local em qualquer recipiente que também oferece suporte a ativação in-loco. Mas, há um pequeno bug ainda espreitando no código. HIERSVR oferece suporte a um menu de contexto exibido quando o usuário pressiona o botão direito do mouse. Este menu funciona quando HIERSVR é totalmente aberto, mas não funciona durante a edição de um incorporação no local. O motivo pode ser fixado para baixo para essa única linha de código em CServerView::OnRButtonDown
pMenu-gt;TrackPopupMenu(TPM_CENTERALIGN | TPM_RIGHTBUTTON,
Point, Point. y, AfxGetApp() - > m_pMainWnd)
Observe a referência a AfxGetApp ()-gt; m_pMainWnd. Quando o servidor está ativado no local, ele tem uma janela principal e m_pMainWnd é definido, mas é geralmente invisível. Além disso, esta janela refere-se à janela principal do aplicativo, a janela do quadro MDI que aparece quando o servidor está totalmente aberto ou executado autônomo. Ele não faz referência à janela do quadro ativo — que quando in loco ativado é uma janela de quadro derivada de COleIPFrameWnd. Para obter a janela ativa correta mesmo quando na edição local, 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-gt;TrackPopupMenu(TPM_CENTERALI&GN | TPM_RIGHTBUTTON,
Point, Point, 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 mais idéias sobre os recursos que você pode querer implementar. Alguns dos recursos que implementa HIERSVR estão listados abaixo:
O exemplo HIERSVR no MFC 3.0 também usa um design ligeiramente diferente para seus itens do servidor. Isso ajuda a conservar a memória e faz suas ligações mais flexíveis. Com a versão 2.0 do HIERSVR cada nó na árvore é um COleServerItem. COleServerItem carrega um pouco mais sobrecarga do que o estritamente necessário para cada um de nós, mas um COleServerItem é necessária para cada link ativo. Mas na maior parte, há muito poucos links ativos a qualquer momento. Para tornar isso mais eficiente, o HIERSVR nesta versão do MFC separa o nó de COleServerItem. Tem um CServerNode e uma CServerItem classe. CServerItem (derivada de COleServerItem) só é criado, se necessário. Depois que o recipiente (ou recipientes) parar de usar esse link especial para esse determinado nó, o objeto de CServerItem associado a CServerNode é excluído. Este projeto é mais eficiente e mais flexível. Sua flexibilidade é quando lidando com vários links de seleção. Nenhuma destas duas versões do HIERSVR suporte a seleção múltipla, mas seria muito mais fácil para adicionar (e para oferecer suporte a links para essas seleções) com a versão MFC 3.0 do HIERSVR, uma vez que o COleServerItem é separado de dados nativos.
Técnico anotações por número |nbsp; &Notas técnicas por categoria