TN041: Migração do MFC/OLE1 para MFC/OLE 2
Observação: |
---|
A seguinte nota técnica não foi atualizada desde que foi incluída pela primeira vez na documentação online.sistema autônomo resultado, alguns procedimentos e tópicos podem estar desatualizado ou incorreto.Para obter informações mais recentes, é recomendável que você procurar o tópico de interesse no índice de documentação online. |
Problemas Geral relativas à migração
Um dos objetivos do projeto para as classes de OLE 2 no MFC 2.5 (e superiores) foi reter grande parte a mesma arquitetura de colocar no lugar no MFC 2.0 para suporte a OLE 1.0.sistema autônomo resultado, muitas das classes OLE mesmas no MFC 2.0 ainda existem nesta versão do MFC (COleDocument, COleServerDoc, COleClientItem, COleServerItem). Além disso, muitas das APIs nessas classes são exatamente os mesmos.No entanto, o OLE 2 é totalmente diferente do OLE 1.0 para que você pode esperar que alguns dos detalhes foram alterados.Se você estiver familiarizado com o suporte de OLE1 do MFC 2.0, você se sentirá em residência com o 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 problemas de alguns Geral que você pode encontrar durante a portagem de sua funcionalidade OLE1 para MFC/OLE 2 e, em seguida, discute problemas revelou ao portar dois aplicativos incluídos no MFC 2.0: o MFC OLE Exemplos OCLIENT and HIERSVR.
Arquitetura do MFC/visualização de documentos é importante
Se seu aplicativo não usar/Exibir documento arquitetura do MFC e você deseja adicionar suporte a OLE 2 ao seu aplicativo, agora é o time para ir para/visualização de documentos.Muitos dos benefícios do MFC OLE 2 classes são obtidos apenas depois que o aplicativo está usando a arquitetura interna e os componentes do MFC.
Implementar um servidor ou um contêiner sem usar a arquitetura do MFC é possível, mas não recomendado.
Use MFC implementação em vez de seu próprio
Classes MFC "gravados implementação", sistema autônomoCToolBar, CStatusBar, e CScrollView ter interno código caso especial para suporte a OLE 2. Portanto, se você pode usar essas classes em seu aplicativo você se beneficiará do esforço colocado em-las para torná-los OLE ciente.Novamente, é possível "roll-your-próprios" classes aqui para esses fins, mas não é recomendável.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 no local).
Examine o código de exemplo do MFC
Há um número de Exemplos MFC que inclui a funcionalidade OLE.Cada um desses aplicativos implementa OLE de um ângulo diferente:
HIERSVR Destinado principalmente para uso sistema autônomo um aplicativo do servidor.Ele foi incluído no MFC 2.0 sistema autônomo um aplicativo do MFC/OLE1 e tem foi portado para MFC/OLE 2 e, em seguida, estendido de modo que ele implementa vários recursos OLE disponível no OLE 2.
OCLIENT É um aplicativo de contêiner independente, destinado a demonstrar muitos dos recursos OLE de um ponto de vista do recipiente.Ele muito foi portado do MFC 2.0 e, em seguida, estendido para oferecer suporte a muitos dos recursos mais avançados do OLE, sistema autônomo formatos da á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 dentro da estrutura de um programa de desenho existente e orientada a objeto.Ele mostra como implementar suporte de contêiner OLE e integrá-lo em seu aplicativo existente.
SUPERPAD Este aplicativo, bem sistema autônomo sendo um aplicativo autônomo fino, também é um servidor OLE.O suporte de servidor implementa é bastante minimalista.De interesse particular é como ele usa serviços da área de transferência OLE para copiar dados para a área de transferência, mas usa a funcionalidade incorporada ao controle do Windows "edição" para implementar a funcionalidade de colar da área de transferência.Mostra uma combinação interessante de uso tradicional de API do Windows, além de integração com sistema autônomo novas OLE APIs.
Para obter mais informações sobre os aplicativos de exemplo, consulte a "MFC exemplo ajuda".
Estudo de caso: OCLIENT do MFC 2.0
Conforme mencionado acima, OCLIENT foi incluído no MFC 2.0 e implementado OLE com MFC/OLE1.As etapas através do qual 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 ilustrar melhor as classes do MFC/OLE.Esses recursos não serão tratados aqui; consultem o exemplo para obter mais informações sobre esses recursos avançados.
Observação: |
---|
O processo passo a passo e erros do compilador foi criado com Visual C++ 2.0.Mensagens de erro específicas e os locais podem ter alterado com o Visual C++ 4.0, mas as informações conceituais permanecem válidas. |
Obtendo IT para cima e em execução
A abordagem utilizada para a porta da amostra OCLIENT para MFC/OLE é começar a criá-lo e corrigir os erros de compilador óbvia resultante.Se você executar a amostra OCLIENT do MFC 2.0 e compilá-lo com esta versão do MFC, você encontrará o que não há tantos erros para resolver.Os 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
O primeiro erro está relacionada à COleClientItem::Draw. MFC/OLE1 demorou mais parâmetros que obtém a versão MFC/OLE.Parâmetros extras geralmente não eram necessários e normalmente nulo (sistema autônomo no exemplo).Esta versão do MFC pode determinar os valores para o lpWBounds automaticamente quando o CDC é desenhada é um metarquivo do controlador de domínio.Além disso, o parâmetro pFormatDC não é mais necessário, pois a estrutura criará um "atributo da DC" controlador de domínio primário passado.Portanto, para corrigir esse problema, remova simplesmente os dois extra nulo parâmetros para a telefonar 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 resultado do fato de que todos os COleClientItem::CreateXXXX funções no MFC/OLE1 necessário que um nome exclusivo ser passado para representar o item.Esse era um requisito da OLE API básica.Isso não é necessário no MFC/OLE 2 sistema autônomo OLE 2 não usar DDE sistema autônomo o mecanismo subjacente de comunicações (o nome foi usado em comunicações DDE).Para corrigir esse problema, você pode remover o CreateNewName função, bem sistema autônomo todas sistema autônomo referências a ele.É fácil de descobrir o que cada função MFC/OLE está esperando nesta versão simplesmente, colocando o cursor na telefonar e pressionando F1.
Outra área que é significativamente diferente é tratamento da área de transferência de OLE 2.Com OLE1, você usou a área de transferência do Windows que APIs interagem com a área de transferência.Com OLE 2, isso é concluído com um mecanismo diferente.As APIs do MFC/OLE1 adotada a área de transferência estava aberta antes de copiar um COleClientItem objeto 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 MFC/OLE falhe.Enquanto você edita o código para remover dependências de 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 do CMainView::OnInsertObject manipulador.Lidar com o comando "Inserir novo objeto" é outra área onde as coisas mudaram um bit.Nesse caso, é mais fácil simplesmente mesclagem a implementação original com fornecido pelo AppWizard para um novo aplicativo OLE contêiner.Na verdade, essa é uma técnica que você pode aplicar a portagem de outros aplicativos.No MFC/OLE1 exibido o diálogo "Insert objeto" chamandoAfxOleInsertDialog função.Nesta versão, você construir um COleInsertObject diálogo objeto e telefonar DoModal. Além disso, o OLE novos itens são criados com um CLSID em vez de uma seqüência de caracteres de nome de classe.O resultado participante deve ser semelhante à seguinte
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ção: |
---|
Inserir novo objeto pode ser diferente para seu aplicativo): |
Também é necessário incluir <afxodlgs.h>, que contém a declaração para oCOleInsertObject diálogo classe, bem sistema autônomo sistema autônomo 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 alteradas no OLE 2, embora o conceito estejam a mesma.Neste caso OLEVERB_PRIMARY alterado para OLEIVERB_PRIMARY. Em OLE1 e OLE 2, o verbo primário geralmente é executado por um contêiner de 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 somente é usado para implementar "Edição Visual" (ou ativação in-loco).Agora defina esse parâmetro como nulo, porque esse recurso não estiver implementando no momento.
Para certificar-se de que a estrutura nunca in-loco tenta ativar, você deve substituir COleClientItem::CanActivate sistema autônomo a seguir:
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 and SetBounds foram usadas para consultar e manipular a extensão de um item (o à esquerda and parte superior membros eram sempre zero).No MFC/OLE 2 isso mais diretamente é suportado por COleClientItem::GetExtent e SetExtent, que lidam com um dimensionar or CSize em vez disso.
O código para seu novo SetItemRectToServer, e procure UpdateItemRectFromServer chamadas como 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
MFC/OLE1 chamadas de API síncronas de um contêiner para um servidor eram simulada, porque OLE1 era inerentemente assíncrono em muitos casos.Foi necessário verificar uma telefonar assíncrono pendente em andamento antes de processar comandos do usuário.MFC/OLE1 fornecido o COleClientItem::InWaitForRelease função para fazê-lo.No MFC/OLE 2 isso não é necessário, para que você possa para remover a substituir 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 executadas que impedirá OCLIENT de execução, no entanto.É melhor corrigir esses problemas agora, em vez de mais tarde.
Primeiro, é necessário inicializar as bibliotecas OLE.Isso é concluído chamando AfxOleInit from InitInstance:
if (!AfxOleInit())
{
AfxMessageBox("Failed to initialize OLE libraries");
return FALSE;
}
Também é uma mercadoria idéia verificar se há funções virtual para alterações de lista de parâmetro.Uma função como é COleClientItem::OnChange, substituído em cada MFC/OLE aplicativo contêiner. Ao examinar a Ajuda online, você verá que 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 da COleClientDoc.No MFC/OLE 2 essa 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 to COleDocument para simplificar a portagem de aplicativos MFC/OLE1 para 2 MFC/OLE, tais sistema autônomo OCLIENT. Um dos recursos não fornecidos pela COleDocument que foram fornecida por COleClientDoc é as entradas de MAP de mensagem de comando padrão.Isso é concluído de forma que os aplicativos de servidor, que também usar COleDocument (indiretamente), não carregam com eles a sobrecarga desses manipuladores de comandos, a menos que seja um aplicativo contêiner/servidor. Você precisará adicionar as seguintes entradas ao MAP 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 é em COleDocument, que é a classe base para o seu documento.
Nesse 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 for implementado, os 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 é ativação in-loco (ou "Edição Visual").Esse recurso permite que o aplicativo do servidor assumir partes da interface do usuário do contêiner para fornecidos uma interface de edição mais perfeita ao usuário.Para implementar a ativação in-loco para OCLIENT, alguns recursos especiais precisam ser adicionado, bem sistema autônomo alguns códigos adicionais.Esses recursos e o código normalmente são fornecidas pelo AppWizard — na verdade, muito do código aqui foi emprestado diretamente de um aplicativo AppWizard recente 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 este recurso menu extra no Visual C++, copiando o recurso IDR_OCLITYPE e removendo todas menos a arquivo e janela pop-no-break.Duas barras de separação são inseridas entre os arquivos e janela pop-no-break para indicar a separação de grupos (ele deve ter a seguinte aparência: Arquivo || janela).Para obter mais informações sobre o que significam esses separadores e como os menus do servidor e o contêiner serão mesclados consulte "recursos e menus: Menu mesclagem"emOLE 2 classes.
Uma vez que você tenha esses menus criados, você precisa permitir que a estrutura terá conhecimento delas.Isso é concluído chamando CDocTemplate::SetContainerInfo para o modelo de documento antes de adicioná-lo à lista de modelos de documento no seu CWinAppEx. O novo código para registrar o modelo de documento fica assim:
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 ativação in-loco, existem algumas coisas que precisam para mudar o CView Classe derivada de (CMainView), bem sistema autônomo o COleClientItem classe derivada (CRectItem). Todas essas substituições são fornecidas pelo AppWizard e a maioria da implementação virá diretamente de um aplicativo de AppWizard padrão.
Na primeira etapa dessa porta, ativação in-loco foi desativado completamente substituindo COleClientItem::CanActivate. Essa substituir deve ser removida para permitir a ativação in-loco.Além disso, nulo foi passado para todas as chamadas de DoVerb (há dois deles) porque fornecer o modo de exibição somente era necessária para ativação in-loco. Para implementar completamente a ativação no local, é necessário passar o modo de exibição correto no DoVerb Chame. Uma dessas chamadas está em CMainView::OnInsertObject:
pItem->DoVerb(OLEIVERB_SHOW, this);
A outra é em CMainView::OnLButtonDblClk:
m_pSelection->DoVerb(OLEIVERB_PRIMARY, this);
É necessário substituir COleClientItem::OnGetItemPosition. Isso informa ao servidor onde colocar a janela em relação à janela do contêiner quando o item é ativado no local.Para OCLIENT, a implementação é trivial:
void CRectItem::OnGetItemPosition(CRect& rPosition)
{
rPosition = m_rect;
}
A maioria dos servidores também implementar o que é chamado "no local redimensionando." Isso permite que a janela do servidor para ser dimensionada e movidos enquanto o usuário edita o item.O contêiner deve participar nessa ação, como mover ou redimensionar a janela geralmente afeta a posição e dimensionar no próprio documento recipiente.A implementação de OCLIENT sincroniza o retângulo interno mantido pelo m_rect com a nova posição e dimensionar.
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 local e para 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 irão fornecer essa funcionalidade próprios cláusula manipulando o escape chave, é recomendável que os contêineres fornecem duas maneiras para desativar um item: (1) (2) clicando fora do item e, pressione a tecla ESCAPE.
Para a chave ESCAPE, adicionar um acelerador com o Visual C++ que mapeia a tecla VK_ESCAPE para um comando, ID_CANCEL_EDIT é adicionado aos recursos.O manipulador para este comando é:
// 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 em que o usuário clica fora do item, adicione o seguinte código para o início da 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.Para verificar que isso é o caso você manipular OnSetFocus para que o foco sempre é transferido para o item ativo quando o modo de 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 que o retângulo de recorte foi alterado.Para isso você deve fornecer 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 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 através do qual 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 ilustrar melhor as classes do MFC/OLE 2.Esses recursos não serão tratados aqui; consultem o exemplo para obter mais informações sobre esses recursos avançados.
Observação: |
---|
O processo passo a passo e erros do compilador foi criado com Visual C++ 2.0.Mensagens de erro específicas e os locais podem ter alterado com o Visual C++ 4.0, mas as informações conceituais permanecem válidas. |
Obtendo IT para cima e em execução
A abordagem utilizada para a porta da amostra HIERSVR para MFC/OLE é começar a criá-lo e corrigir os erros de compilador óbvia resultante.Se você executar a amostra HIERSVR do MFC 2.0 e compilá-lo com esta versão do MFC, você encontrará o que não há muitos erros para resolver (apesar de haver mais de com o exemplo OCLIENT).Os 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'
Este erro primeiro 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 ao seu aplicativo MFC/OLE1 para obtê-lo em execução.A melhor coisa a fazer é examinar o que cria AppWizard para um servidor OLE e modifique seu código sistema autônomo apropriado.Aqui estão alguns pontos a serem lembrados:
É necessário inicializar as bibliotecas OLE chamando AfxOleInit
Chamar SetServerInfo no objeto de modelo de documento para conjunto identificadores de recurso de servidor e informações de classe em tempo de execução que não pode ser definida com o CDocTemplate construtor.
Não mostra a janela principal do seu aplicativo se /Embedding estiver presente na linha de comando.
Você precisará de um GUID para o seu documento.Este é um identificador exclusivo para o tipo do seu 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" GUID desse aplicativo.Caso contrário, você pode usar o utilitário GUIDGEN.EXE no diretório BIN.
É necessário "conectar" seuCOleTemplateServer objeto para o modelo de documento, chamando COleTemplateServer::ConnectTemplate.
atualização Registro do sistema quando seu aplicativo é executado independente.Dessa forma, se o usuário move o .exe para seu aplicativo executá-lo de seu novo local atualizará o banco de dados do inscrição de sistema Windows para apontar para o novo local.
Depois de aplicar a todas essas alterações com base em qual AppWizard cria para InitInstance, o InitInstance (e relacionados à 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ê observará que o código acima se refere a uma nova ID de recurso IDR_HIERSVRTYPE_SRVR_EMB.Este é o recurso de menu a ser usado quando um documento que está incorporado em outro contêiner é editado.No MFC/OLE1 os itens de menu específicos para editar um item incorporado foram modificados dinamicamente.Usando uma estrutura de menu totalmente diferente ao editar um item incorporado em vez de editar um documento baseado em arquivo torna muito mais fácil fornecer interfaces de usuário diferente para esses dois modos separados.sistema autônomo você verá posteriormente, um recurso de menu totalmente separado será usado para editar um objeto incorporado no local.
Para criar esse recurso, carregar o script de recurso no Visual C++ e copie o recurso de menu IDR_HIERSVRTYPE existente.Renomeie o novo recurso para IDR_HIERSVRTYPE_SRVR_EMB (Esta é a mesma convenção de nomeclatura AppWizard usa).Em seguida alterar "Salvar arquivo" como "atualização File"; concede-ID de comandoID_FILE_UPDATE.Também, alterar "Arquivo Salvar sistema autônomo"para"arquivo Salvar cópia sistema autônomo"; atribua a ele o ID de comandoID_FILE_SAVE_COPY_AS.A estrutura oferece 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 de substituir de OnSetData, desde que ele está se referindo a OLESTATUS tipo.OLESTATUS era a forma que 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 substituir de OnSetData não é mais necessário, portanto, é o mais fácil fazer para removê-lo.
\hiersvr\svritem.cpp(30) : error C2660: 'COleServerItem::COleServerItem' : function does not take 1 parameters
The COleServerItem construtor utiliza um parâmetro extra 'BOOL'. Este sinalizar determina como o gerenciamento de memória é concluído a COleServerItem objetos. Por configuração ele para 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 sistema autônomo parte de seus dados nativo, portanto, você irá conjunto esse sinalizar sistema autônomo 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
sistema autônomo esses erros implicam, há algumas funções 'puras virtual' que não foram substituídas CServerItem.Provavelmente isso é causado pelo fato de que a lista de parâmetros do OnDraw foi alterado.Para corrigir este erro alterar CServerItem::OnDraw sistema autônomo segue (assim sistema autônomo a 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 dimensionar do desenho, se for conveniente.Esse dimensionar deve estar no HIMETRO.Nesse caso, não for conveniente preencher esse valor, portanto, a estrutura chama OnGetExtent para recuperar a extensão. Para que 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 dimensionar do item é convertido para HIMETRO e armazenados em m_rectBounds.O undocumented ' m_rectBounds' membro de COleServerItem não existe (foi parcialmente substituído por m_sizeExtent, mas no OLE 2 Este membro tem um uso um pouco diferente de m_rectBounds fez no OLE1).Em vez de definir o HIMETRO dimensionar para essa variável de membro irá retorná-lo.Esse valor retornado é usado em OnGetExtent, implementado 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 sobrescreveCOleServerItem::OnGetTextData.Esta função é obsoleta no MFC/OLE e é substituída por um mecanismo diferente.A versão de MFC 3.0 do MFC OLE amostra HIERSVR implementa essa funcionalidade, substituindo COleServerItem::OnRenderFileData. Essa funcionalidade não é importante para esta porta básica, portanto, você pode remover a substituir OnGetTextData.
Há muitos erros mais em svritem.cpp não foram resolvidos.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::CopyToClipboard não é mais compatível com o sinalizar 'bIncludeNative'. Os dados nativo (os dados gravados pela função do servidor do item serialize) é sempre copiados para 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. Altere o código para 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 de compilação da versão 2.0 do MFC do HIERSVR que existiam para a mesma versão do OCLIENT, havia realmente menos alterações.
Neste ponto HIERSVR irá compilar e vincular e funcionar sistema autônomo 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) a este aplicativo de servidor, há apenas algumas coisas que você deve ter cuidado de:
Você precisa de um recurso de menus especial a ser usado quando o item está ativo no local.
Este aplicativo tem uma barra de ferramentas, portanto, você precisará de uma barra de ferramentas com apenas um subconjunto da barra de ferramentas normal para coincidir com os comandos de menu disponível a partir do servidor (corresponde do recurso de menu mencionado acima).
Você precisa de uma nova classe derivada de COleIPFrameWnd que fornece a interface de usuário local (muito semelhante CMainFrame, derivado de CMDIFrameWndFornece a interface de usuário MDI).
Você precisa dizer a estrutura sobre essas classes e recursos especiais.
É fácil criar o recurso de menu.Execute o Visual C++, copie 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 edição e ajuda são deixados.Adicionar dois separadores ao menu entre os menus de edição e ajuda (ele deve ter a seguinte aparência: edição || Ajuda).Para obter mais informações sobre o que significam esses separadores e como os menus do servidor e o contêiner serão mesclados, consulte "recursos e menus: Menu mesclagem"emOLE 2 classes.
O bitmap para o subconjunto da barra de ferramentas pode ser criado com com facilidade copiando aquele de um aplicativo AppWizard gerado atualizado com a opção de "Servidor" marcada.Esse bitmap pode então ser importado para o Visual C++.Não esqueça de atribuir o bitmap de uma ID de IDR_HIERSVRTYPE_SRVR_IP.
O clsistema autônomos derivado de COleIPFrameWnd podem ser copiadas de um aplicativo AppWizard gerado com suporte do servidor sistema autônomo bem. Copie os dois arquivos, IPFRAME.CPP e IPFRAME.H e adicioná-las para o projeto.Certifique-se de que o LoadBitmap telefonar refere-se ao 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 para que a estrutura sabe sobre esses (e sabe que esse aplicativo agora suporte à edição in-loco).Isso é concluído 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 ser executado no local em qualquer contêiner também oferece suporte a ativação in-loco.Mas, há um bug pequeno à espreita ainda no código.HIERSVR oferece suporte a um menu de contexto, exibido quando o usuário pressiona botão do mouse direito do mouse.Esse menu funciona quando HIERSVR está totalmente aberta, mas não funciona ao editar um incorporação no local.O motivo pelo qual pode ser fixado para baixo 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 AfxGetApp()-> m_pMainWnd.Quando o servidor está ativado no local, ele tem uma janela principal e m_pMainWnd conjunto, mas é geralmente invisível.Além disso, essa janela refere-se ao principal janela do aplicativo, a janela de quadro MDI que aparece quando o servidor está totalmente em em aberto ou executar autônoma.Ele não faz referência à janela do quadro ativo — que ao local ativado é uma janela do quadro derivada de COleIPFrameWnd. Para obter a janela ativo correta, mesmo quando in-loco editando, esta versão do MFC adiciona uma nova função, AfxGetMainWnd. Em geral, você deve usar essa função em vez de AfxGetApp()-> m_pMainWnd.Este código precisa ser alterado sistema autônomo segue:
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 existem muitos recursos disponível com 2 MFC/OLE que não estavam disponível no MFC/OLE1.Consulte o exemplo HIERSVR para obter mais idéias sobre recursos que deseja implementar.Alguns dos recursos que implementa HIERSVR estão listados a seguir:
Aplicar zoom para true comportamento WYSISYG com relação ao contêiner.
Arrastar / soltar e um formato de área de transferência personalizada.
Rolagem da janela recipiente sistema autônomo a seleção é alterada.
O exemplo HIERSVR no MFC 3.0 também usa um design de um pouco diferentes para seus itens do servidor.Isso ajuda a conservar a memória e torna mais flexível, seus links.With the 2.0 version of HIERSVR each node in the tree is-a COleServerItem.COleServerItem executa um bit mais sobrecarga do que é estritamente necessário para cada um de nós, mas um COleServerItem é necessário para cada link ativo. Mas em sua maioria, existem muito poucos links ativo a qualquer momento.Para tornar mais eficiente, HIERSVR nesta versão do MFC separa o nó do COleServerItem. Ele tem um CServerNode e um CServerItem classe.The CServerItem (derivado de COleServerItem) é criado somente quando necessário. Depois que o contêiner (ou contêineres) parar de usar esse link específico para esse nó específico, o objeto 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 para oferecer suporte a links para essas seleções) com a versão 3.0 do MFC do HIERSVR, desde o COleServerItem é separado dos dados nativo.