TN041: migrazione MFC/OLE1 a MFC/OLE 2

[!NOTA]

La seguente nota tecnica non è stata aggiornata dalla prima volta che viene inclusa nella documentazione online.Di conseguenza, alcune procedure e argomenti potrebbero non essere aggiornati o errati.Per le informazioni più recenti, è consigliabile cercare l'argomento di interesseindice della documentazione online.

Esempi generali relativi a migrazione

Uno degli obiettivi di progettazione per le classi OLE 2 in MFC 2,5 (e superiore) è di mantenere gran parte della stessa architettura stabilita in MFC 2,0 per il supporto di OLE 1,0.Di conseguenza, molte delle stesse classi OLE di MFC 2,0 sono ancora inclusi in questa versione di MFC (COleDocument, COleServerDoc, COleClientItem, COleServerItem).Inoltre, molte delle API in queste classi sono identici.Tuttavia, OLE 2 è radicalmente diverso da OLE 1,0 in modo da poter prevedere che alcuni dettagli sono stati modificati.Se si ha dimestichezza con il supporto di MFC 2.0 " s OLE1, si sentirete da casa con il supporto di MFC 2,0.

Se si prevede di un'applicazione esistente MFC/OLE1 e si aggiunge funzionalità OLE 2 a, è consigliabile leggere questa nota per primo.Questa nota vengono illustrate alcune edizioni che generali possono verificarsi durante forniscono la funzionalità OLE1 a MFC/OLE 2 e quindi vengono illustrati i problemi come non gestito durante la comunicazione di due applicazioni incluse in MFC 2,0: OLE MFC campione OCLIENT e HIERSVR.

L'architettura documento/visualizzazione MFC è importante

Se l'applicazione non utilizza l'architettura documento/visualizzazione MFC e si desidera aggiungere il supporto di OLE 2 all'applicazione, ora è il momento di passare al documento/visualizzazione.Molti dei vantaggi offerti dalle classi OLE di MFC precedenti alla 2 vengono eseguite solo una volta che l'applicazione utilizza l'architettura e i componenti incorporate di MFC.

Implementazione di un server o un contenitore senza l'architettura di MFC è possibile, ma non consigliato.

Implementazione di utilizzo MFC anziché i propri

MFC “implementazione inscatolata„ classi come CToolBar, CStatusBare CScrollView dispone di codice incorporato di caso speciale per il supporto di OLE 2.Quindi, se è possibile utilizzare queste classi nell'applicazione saranno vantaggio per l'impegno inserito nei rispettivi per notificare loro OLE.Nuovamente, è possibile “a rotolo-vostro-proprie„ classe " qui per tali scopi, ma non è consigliato.Se è necessario implementare funzionalità simili, il codice sorgente MFC è un riferimento super per la gestione di alcuni punti più precisi OLE soprattutto quando si lavora di attivazione sul posto).

Esaminare il codice di esempio MFC

Esistono diversi esempi di MFC che includono funzionalità OLE.Ognuna di queste applicazioni implementa OLE da un angolo diverso:

  • HIERSVR è utile principalmente da utilizzare come applicazione server.È inclusa in MFC 2,0 come applicazione MFC/OLE1 ed è stato trasferito a MFC/OLE 2 e quindi esteso in modo che implementa numerose funzionalità OLE disponibili in OLE 2.

  • OCLIENT questo è un'applicazione contenitore autonoma, lo scopo di illustrare molte delle funzionalità OLE da un punto di vista del contenitore.Anche se è stato trasferito da MFC 2,0 e quindi esteso per supportare molte delle funzionalità più avanzate OLE, ad esempio i formati degli Appunti personalizzati e collegamenti a elementi incorporati.

  • DRAWCLI questa applicazione implementa il supporto del contenitore OLE in modo simile ad esempio OCLIENT affermativo, con la differenza che agisce nel contesto di un programma di disegno orientata a oggetti esistente.Viene illustrato come è possibile implementare il supporto del contenitore OLE e integrarlo nell'applicazione esistente.

  • In SUPERPAD questa applicazione nonché è un'applicazione autonoma express, è anche un server OLE.Il supporto di server implementa è sufficiente minimalista.Di particolare interesse è la modalità di utilizzo dei servizi degli Appunti OLE per copiare i dati negli Appunti, ma utilizza le funzionalità disponibili nel controllo di “modifica„ windows per implementare la funzionalità di copia negli Appunti.Viene mostrata una combinazione interessante di utilizzo tradizionale nonché di integrazione di API Windows con il nuovo OLE API.

Per ulteriori informazioni sulle applicazioni di esempio, vedere la guida dell'esempio MFC„.

Case study: OCLIENT da MFC 2,0

Come descritto in precedenza, OCLIENT è stato incluso in MFC 2,0 e ha implementato OLE con MFC/OLE1.I passaggi da cui l'applicazione inizialmente è stata convertita utilizzare le classi di MFC/OLE 2 vengono descritti di seguito.Numerose funzionalità sono stati aggiunti dopo che la porta iniziale è stata completata per illustrare le classi di MFC/OLE.Queste funzionalità non saranno trattate in questo argomento; fare riferimento all'esempio stesso per ulteriori informazioni sulle funzionalità avanzate.

[!NOTA]

Gli errori del compilatore e il processo dettagliato sono stati creati con Visual C++ 2,0.I messaggi di errore specifici e le posizioni possono modificare con Visual C++ 4,0, ma i concetti rimangano valide.

Nel servizio

Approccio adottato per trasferire l'esempio OCLIENT a MFC/OLE prevede compilandola e correzione degli errori del compilatore utili che darà luogo a.Se prelevate l'esempio OCLIENT da MFC 2,0 e compilarlo in questa versione di MFC, si scoprirà che non c " è che molti errori da risolvere.Errori nell'ordine in cui si sono verificati vengono descritti di seguito.

Compilare e correggere gli errori

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

Il primo errore influisce su COleClientItem::Draw.In MFC/OLE1 ha richiesto più parametri che accetta la versione di MFC/OLE.Parametri aggiuntivi non sono spesso necessari e in genere NULL (come in questo esempio).Questa versione di MFC può determinare automaticamente i valori per i lpWBounds quando il CDC che disegna è un controller di dominio del metafile.Inoltre, il parametro del pFormatDC non è più necessario poiché il framework compilerà uno “dal controller di dominio dell'attributo„ di pDC passato.Pertanto per risolvere il problema, rimuovere semplicemente i due parametri aggiuntivi NULL alla chiamata di disegno.

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

Gli errori sul risultato dal fatto che tutto viene eseguito in MFC/OLE1 necessario che un nome univoco viene passato per rappresentare l'elemento.Ciò è un requisito OLE sottostante API.Questa operazione non è necessaria in MFC/OLE 2 poiché OLE 2 non utilizza il DDE come meccanismo sottostante di comunicazioni (il nome è stato utilizzato nelle conversazioni di DDE).Per correggere questo problema, è possibile rimuovere la funzione di CreateNewName nonché tutti i riferimenti a.È più facile trovare in quanto ogni funzione di MFC/OLE previsto semplicemente nella versione corrente posizionando il cursore sulla chiamata e premendo F1.

Un'altra area che differisce significativamente è gestione degli Appunti OLE 2.Con OLE1, sono state utilizzate le finestre che gli Appunti API interagiscono con gli Appunti.Con OLE 2 questa operazione viene eseguita con un meccanismo diverso.Il MFC/OLE1 API di presuppone che gli Appunti sono aperti prima di copiare oggetto di COleClientItem negli Appunti.Questa operazione non è più necessaria e contiene tutte le operazioni relative agli Appunti di MFC/OLE non riesca.Durante la modifica del codice per rimuovere le dipendenze da CreateNewName, è necessario rimuovere anche il codice che consente di aprire e chiudere gli Appunti di 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'

Risultato di questi errori dal gestore di CMainView::OnInsertObject .Gestire “il comando nuovo oggetto inserisci„ è un'altra area in cui le operazioni sono stati modificati sufficiente un bit.In questo caso, è più facile unire semplicemente implementazione originale con quella fornita da AppWizard per una nuova applicazione contenitore OLE.Infatti, si tratta di una tecnica che è possibile applicare a trasportare altre applicazioni.In MFC/OLE1, viene visualizzata la finestra di dialogo “dell'oggetto inserisci„ chiamando la funzione di AfxOleInsertDialog .In questa versione si costruisce un oggetto finestra di dialogo di COleInsertObject e chiamate DoModal.Inoltre, i nuovi elementi OLE vengono creati con CLSID anziché una stringa di nomeclasse.Il risultato finale dovrebbe risultare simile al seguente

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

[!NOTA]

Il nuovo oggetto INSERT può essere diverso per l'applicazione):

È inoltre necessario includere <afxodlgs.h>, che contiene la dichiarazione della classe della finestra di dialogo di COleInsertObject nonché le altre finestre di dialogo standard fornite da MFC.

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

Questi errori sono causati dal fatto che costanti qualsiasi OLE1 sono stati modificati in OLE 2, anche se il concetto di sono uguali.In questo caso OLEVERB_PRIMARY ha modificato in OLEIVERB_PRIMARY.Sia in OLE1 che in OLE 2, il verbo primario in genere viene eseguito da un contenitore quando l'utente fa doppio clic su un elemento.

Inoltre, DoVerb ora accetta un parametro aggiuntivo a un puntatore a una visualizzazione (CView*).Questo parametro viene utilizzato solo per implementare “modifica visiva„ (o l'attivazione sul posto).Per ora si imposta il parametro FROM NULL, perché non si implementa questa funzionalità attualmente.

Per assicurarsi che il framework non venga mai sul posto attivare, è necessario eseguire l'override COleClientItem::CanActivate come 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

In MFC/OLE1, COleClientItem::GetBounds e SetBounds sono stati utilizzati per eseguire una query e modificare l'ambito di un elemento (i membri di top e di left sono sempre zero).In MFC/OLE 2 questo è più supportato direttamente da COleClientItem::GetExtent e da SetExtent, che si occupano di DIMENSIONI o di CSize anziché.

Il codice per il nuovo SetItemRectToServer e UpdateItemRectFromServer chiama in modo che abbia l'aspetto al seguente:

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

Nelle API chiamate sincrone MFC/OLE1 da un contenitore a un server sono stati simulati, poiché OLE1 è intrinsecamente asincrono in molti casi.È necessario verificare una chiamata asincrona costante in corso prima di elaborare i controlli dall'utente.MFC/OLE1 ha svolto la funzione di COleClientItem::InWaitForRelease per eseguire tale operazione.In MFC/OLE 2 questo non è necessario, pertanto è possibile rimuovere insieme l'override di OnCommand in CMainFrame tutte.

In questa fase OCLIENT verrà compilato e viene collegato.

Altre modifiche necessarie

Esistono poche operazioni che non vengono apportate per OCLIENT di esecuzione, tuttavia.È consigliabile correggere questi problemi anziché in un secondo momento.

Innanzitutto, è necessario inizializzare le librerie OLE.Questa operazione viene eseguita chiamando AfxOleInit da InitInstance:

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

È inoltre consigliabile verificare la presenza di funzioni virtuali per le modifiche dell'elenco di parametri.Una tale funzione è COleClientItem::OnChange, sottoposto a override in ogni applicazione contenitore di MFC/OLE.Esaminando la Guida, si noterà che un “dwParam DWORD„ aggiuntivo venga aggiunto.Il nuovo CRectItem::OnChange ha l'aspetto seguente:

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
}

In MFC/OLE1, applicazioni contenitori derivare la classe del documento da COleClientDoc.In MFC/OLE 2 questa classe è stata rimossa e stata sostituita da COleDocument (questa nuova organizzazione semplifica lo sviluppo di applicazioni contenitore/server).È #define che esegue il mapping COleClientDoc a COleDocument per semplificare il porting delle applicazioni MFC/OLE1 a MFC/OLE 2, ad esempio OCLIENT.Una delle funzionalità non fornite da COleDocument fornita da COleClientDoc è le voci della mappa standard del messaggio di comando.Questa operazione viene eseguita in modo che le applicazioni server, che utilizzano inoltre COleDocument (indirettamente), non portino con cui il sovraccarico di questi gestori comandi a meno che non siano un'applicazione contenitore/server.È necessario aggiungere le voci seguenti nella mappa messaggi di 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)

L'implementazione di tutti questi controlli è in COleDocument, che sono la classe di base per il documento.

In questa fase, OCLIENT viene illustrata un'applicazione funzionale del contenitore OLE.È possibile inserire gli elementi di qualsiasi tipo (OLE1 o OLE 2).Poiché il codice necessario per abilitare l'attivazione sul posto non viene implementato, gli elementi vengono modificati in una finestra separata in modo analogo a con OLE1.La sezione successiva vengono illustrate le modifiche necessarie per attivare la modifica sul posto (talvolta denominata “modifica visiva„).

Aggiunta di “modifica visiva„

Una delle funzionalità più interessanti OLE è attivazione sul posto (o “modifica visiva„).Questa funzionalità consente l'applicazione server assumere la direzione delle parti dell'interfaccia utente del contenitore viene fornita un'interfaccia più fine di semplificare le attività di modifica per l'utente.Per implementare l'attivazione sul posto a OCLIENT, alcune risorse speciali devono essere aggiunti nonché il codice aggiuntivo.Queste risorse e il codice vengono generalmente forniti da AppWizard — infatti, molto del codice è stato creato in loan direttamente da un'applicazione aggiornata di AppWizard con il supporto del contenitore„.

Innanzitutto, è necessario aggiungere una risorsa di menu da utilizzare quando è presente un elemento che è attivo sul posto.È possibile creare questa risorsa menu aggiuntive in Visual C++ copiando la risorsa di IDR_OCLITYPE e rimuovendo tutto tranne nel file e nella finestra popup.Due separatori vengono inseriti tra il file e la finestra popup per indicare la separazione di gruppi (dovrebbe risultare simile al seguente: file | | finestra).Per ulteriori informazioni sul significato di questi separatori e come i menu del contenitore e il server vengono uniti vedere “i menu e a risorse: Unione dei menu„ nelle classi OLE 2.

Una volta eseguita questi creare menu, è necessario lasciare il framework sapere su di essi.Questa operazione viene eseguita chiamando CDocTemplate::SetContainerInfo per il modello di documento prima che lo aggiungere all'elenco di modelli di documento nel InitInstance.Il nuovo codice per registrare gli aspetti del modello di documento è analogo al seguente:

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

La risorsa di IDR_OLECLITYPE_INPLACE rappresenta la risorsa sul posto speciale creata in Visual C++.

Per abilitare l'attivazione sul posto, esistono alcune operazioni che devono modificare sia nella classe derivata di CView (CMainView) e nella classe derivata di COleClientItem (CRectItem).Tutte queste override sono fornite da AppWizard e la maggior parte dell'implementazione avranno direttamente da un'applicazione predefinita di AppWizard.

Nel primo passaggio di questa porta, l'attivazione sul posto è stata disabilitata completamente eseguendo l'override di COleClientItem::CanActivate.Questo override devono essere rimosse per consentire l'attivazione sul posto.Inoltre, NULL è stato passato a tutte le chiamate a DoVerb (esistono due delle) perché fornire la visualizzazione è necessaria solo per l'attivazione sul posto.Per implementare completamente l'attivazione sul posto, è necessario passare la visualizzazione corretta nella chiamata di DoVerb .Una di queste chiamate è in CMainView::OnInsertObject:

pItem->DoVerb(OLEIVERB_SHOW, this);

Un altro è in CMainView::OnLButtonDblClk:

m_pSelection->DoVerb(OLEIVERB_PRIMARY, this);

È necessario eseguire l'override di COleClientItem::OnGetItemPosition.Ciò fornisce al server in cui inserire la finestra alla finestra contenitore quando l'elemento è disponibile attivato.Per OCLIENT, l'implementazione è semplice:

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

La maggior parte dei server implementano anche ciò che viene chiamato “ridimensionamento sul posto„. In questo modo la finestra server da graduare e spostare quando l'utente modifica l'elemento.Il contenitore deve prendere parte a questa azione, lo spostamento o da ridimensionare la finestra in genere impatto sulla posizione e le dimensioni nel documento contenitore stessa.L'implementazione di OCLIENT sincronizza il rettangolo interno gestito da m_rect con la nuova posizione e le dimensioni.

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

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

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

    return TRUE;
}

In questa fase, è sufficiente codice per consentire un elemento sia attivato sul posto e per gestire di ridimensionamento e di spostare l'elemento quando è attivo, ma nessun codice consentirà al termine della sessione di modifica.Sebbene alcuni server e forniscono questa funzionalità stessi mediante il tasto escape, è consigliato ai contenitori forniscono due modi per disattivare un elemento: (1) facendo clic all'esterno di e (2) premendo escape.

Per il tasto escape, aggiungere un tasto con Visual C++ che esegue il mapping della chiave VK_ESCAPE a un comando, ID_CANCEL_EDIT viene aggiunta alle risorse.Il gestore per questo comando seguente:

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

Per gestire il caso in cui l'utente fa clic all'esterno dell'elemento, aggiungere il codice seguente all'inizio di CMainView::SetSelection:

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

Quando un elemento è attivo sul posto, deve avere lo stato attivo.Per garantire questo caso la gestione OnSetFocus in modo da trasferire sempre lo stato attivo all'elemento attivo quando il punto di vista riceve lo stato attivo:

// 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 la visualizzazione viene ridimensionata, è necessario notificare all'elemento attivo che il rettangolo di ridimensionamento è stato modificato.A tale scopo si fornisce un gestore per 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();
}

Case study: HIERSVR da MFC 2,0

HIERSVR inoltre è stato incluso in MFC 2,0 e ha implementato OLE con MFC/OLE1.Questa nota una breve descrizione dei passaggi da cui l'applicazione inizialmente è stata convertita utilizzare le classi di MFC/OLE 2.Numerose funzionalità sono stati aggiunti dopo che la porta iniziale è stata completata per illustrare le classi di MFC/OLE 2.Queste funzionalità non saranno trattate in questo argomento; fare riferimento all'esempio stesso per ulteriori informazioni sulle funzionalità avanzate.

[!NOTA]

Gli errori del compilatore e il processo dettagliato sono stati creati con Visual C++ 2,0.I messaggi di errore specifici e le posizioni possono modificare con Visual C++ 4,0, ma i concetti rimangano valide.

Nel servizio

Approccio adottato per trasferire l'esempio HIERSVR a MFC/OLE prevede compilandola e correzione degli errori del compilatore utili che darà luogo a.Se prelevate l'esempio HIERSVR da MFC 2,0 e compilarlo in questa versione di MFC, si scoprirà che non sono molti errori vengano risolti sebbene vi siano più di con l'esempio OCLIENT).Errori nell'ordine in cui in genere si verificano vengono descritti di seguito.

Compilare e correggere gli errori

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

Nel primo errore indica un problema più grande con la funzione di InitInstance per i server.L'inizializzazione necessaria per un server OLE è probabilmente una di dimensioni maggiori modifiche che è necessario apportare all'applicazione MFC/OLE1 ottenerla in esecuzione.La scelta migliore da eseguire è possibile esaminare cui AppWizard crea per un server OLE e modificare il codice in base alle proprie esigenze.Di seguito sono riportati alcuni punti ricordare:

È necessario inizializzare le librerie OLE chiamando AfxOleInit

Chiamata SetServerInfo sull'oggetto del modello di documento all'handle della risorsa server impostate e alle informazioni sulla classe di runtime che non è possibile impostare con il costruttore di CDocTemplate .

Non mostrare la finestra principale dell'applicazione se /Embedding è presente nella riga di comando.

È necessario disporre di GUID per il documento.Si tratta di un identificatore univoco per il tipo di documento (128 bit).AppWizard creerà uno automaticamente — se pertanto si utilizza la tecnica descritta qui di copiare il codice nuovo da una nuova applicazione server generata AppWizard, è possibile “impossessarsi semplicemente„ il GUID dall'applicazione.In caso contrario, è possibile utilizzare l'utilità di GUIDGEN.EXE nella directory BIN.

È necessario “connette„ l'oggetto di COleTemplateServer al modello di documento chiamando COleTemplateServer::ConnectTemplate.

Aggiornare il Registro di sistema quando l'applicazione viene eseguito autonomamente.In questo modo, se l'utente sposta il file EXE per l'applicazione, della nuova percorso aggiorna il database di registrazione di sistema di Windows per indicare la nuova posizione.

Dopo avere applicato tutte queste modifiche basate su cui AppWizard crea per InitInstance, InitInstance (e GUID correlato) per HIERSVR necessario leggere come segue:

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

Si noterà che il codice precedente si riferisce a un nuovo ID di risorsa, IDR_HIERSVRTYPE_SRVR_EMB.Si tratta della risorsa menu da utilizzare quando un documento incorporato in un altro contenitore viene modificato.In MFC/OLE1 voci di menu specifiche di modificare un elemento incorporato sono immediatamente modificato.Mediante un menu completamente diverso per strutturare quando modificare un elemento incorporato anziché modificare un documento basato su file rende più facile fornire interfacce utente diversi per queste due modalità distinte.Come verrà illustrato più avanti, una risorsa menu completamente separata viene utilizzata quando si modifica un oggetto incorporato sul posto.

Per creare questa risorsa, caricare lo script di risorsa in Visual C++ e copiare la risorsa menu esistente di IDR_HIERSVRTYPE.Rinominare la nuova risorsa a IDR_HIERSVRTYPE_SRVR_EMB (si tratta della stessa convenzione di denominazione che utilizzi di AppWizard).“Salvataggio del file di modifica seguenti impostata su “update„ motivo "; fornire l'ID di comando ID_FILE_UPDATE.Anche modificare i “salvataggio di file come„ classificare la copia di salvataggio come„; fornire l'ID di comando ID_FILE_SAVE_COPY_AS.Il framework fornisce l'implementazione di entrambi i controlli.

\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

Esistono numerosi errori derivazione da un override di OnSetData, poiché sono riferiti al tipo di OLESTATUS .OLESTATUS era gli errori restituiti OLE1 la modalità.Ciò ha modificato in HRESULT in OLE 2, sebbene MFC in genere convertisse HRESULT in COleException che contiene l'errore.In questo caso specifico, l'override di OnSetData non sono più necessarie, pertanto la soluzione più semplice consiste di rimuoverlo.

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

Il costruttore di COleServerItem accetta un parametro aggiuntivo “di BOOL„.Questo flag determina quali la gestione della memoria viene eseguita sugli oggetti di COleServerItem .Impostando TRUE, il framework gestisce la gestione della memoria di questi oggetti — eliminarli quando non più necessari.HIERSVR utilizza oggetti di CServerItem (derivato da COleServerItem) come parte dei dati nativi, pertanto sarà necessario impostare questo flag valore FALSE.In questo modo HIERSVR determinare se ogni elemento server viene eliminato.

\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

Poiché questi errori stessi, esistono alcune funzioni “puro-virtuali„ che non sono state sottoposte a override in CServerItem.Di solito questo è causato dal fatto che l'elenco di parametri di OnDraw è stato modificato.Per correggere questo errore, CServerItem::OnDraw di modifica come riportato di seguito (nonché la dichiarazione in 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);
}

Il nuovo parametro è “rSize„.Ciò consente di compilare la dimensione di disegno, se appropriato.Questa dimensione deve essere in HIMETRIC.In questo caso, non è possibile riempire questo valore, pertanto il framework chiama OnGetExtent per recuperare l'ambito.Affinché questo caso, sarà necessario implementare 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 *'

Nella funzione di CServerItem::CalcNodeSize la dimensione elemento viene convertita in HIMETRIC e memorizzata in m_rectBounds.Il membro non documentato “dim_rectBounds„ di COleServerItem non esiste (parzialmente è stata sostituita da m_sizeExtent, ma in OLE 2 questo membro è un utilizzo leggermente diverso da m_rectBounds ha eseguito in OLE1).Anziché impostare la dimensione di HIMETRIC in questa variabile membro, la restituirà.Questo valore restituito viene utilizzato in OnGetExtent, implementato in precedenza.

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 esegue inoltre l'override COleServerItem::OnGetTextData.Questa funzione è obsoleta in MFC/OLE e viene sostituita da un meccanismo diverso.La versione di MFC 3,0 dell'esempio HIERSVR OLE MFC implementa questa funzionalità eseguendo l'override di COleServerItem::OnRenderFileData.Questa funzionalità non è importante per la porta di base, pertanto è possibile rimuovere un override di OnGetTextData.

Esistono molti altri errori in svritem.cpp che non sono stati risolti.Non presenti errori “reali„ — appena errori causati da errori precedenti.

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

COleServerItem::CopyToClipboard non supporta più il flag “bIncludeNative„.I dati nativi (i dati scritti dall'elemento server serializzano la funzione) vengono copiati sempre, pertanto si rimuove il primo parametro.Inoltre, CopyToClipboard genererà un'eccezione quando si verifica un errore anziché restituire FALSE.Modificare il codice per CServerView::OnEditCopy come 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   
}

Benché vi siano più errori derivandole dalla compilazione di rilascio MFC 2,0 HIERSVR che si è verificato per la stessa versione di OCLIENT, vi sono effettivamente meno modifiche.

In questa fase HIERSVR verrà compilato e viene collegato e funzionerà come server OLE, ma senza la funzionalità di modifica sul posto, che verrà distribuita dopo.

Aggiunta di “modifica visiva„

Per aggiungere “modifica visiva„ (o l'attivazione sul posto) all'applicazione server, sono disponibili solo alcune operazioni che è necessario eseguire la visualizzazione di:

  • È una risorsa menu speciale essere utilizzato quando l'elemento è attivo sul posto.

  • Questa applicazione è presente una barra degli strumenti, pertanto è necessario disporre di una barra degli strumenti con solo un sottoinsieme della barra degli strumenti normale di corrispondenza dei comandi di menu disponibili dal server (corrisponde alla risorsa menu indicate in precedenza).

  • È necessario disporre di una nuova classe derivata da COleIPFrameWnd che fornisce l'interfaccia utente sul posto (simile a l CMainFrame, derivato da CMDIFrameWnd, fornisce l'interfaccia utente MDI).

  • È necessario impostare il framework su queste risorse e classi speciali.

La risorsa menu è facile creare.Per eseguire Visual C++, copiare la risorsa menu IDR_HIERSVRTYPE a una risorsa menu chiamata IDR_HIERSVRTYPE_SRVR_IP.Modificare il menu in modo dai popup di menu? e vengono lasciati solo di modifica.Aggiungere due separatori al menu tra la modifica e i menu? (dovrebbe risultare simile al seguente: modifica | | Guida.Per ulteriori informazioni sul significato di questi separatori e come i menu del contenitore e il server vengono uniti, vedere “i menu e le risorse: Unione dei menu„ nelle classi OLE 2.

La bitmap per la barra degli strumenti del sottoinsieme può essere creata copiando quella da un'applicazione generata AppWizard aggiornata con un'opzione “server„ selezionata.Questa bitmap può quindi essere inclusa in Visual C++.Assicurarsi di assegnare alla bitmap un ID di IDR_HIERSVRTYPE_SRVR_IP.

La classe derivata da COleIPFrameWnd può essere copiato da un'applicazione generata AppWizard con supporto server anche.Copiare entrambi i file, IPFRAME.CPP e IPFRAME.H e aggiungerle al progetto.Assicurarsi che la chiamata di LoadBitmap fa riferimento a IDR_HIERSVRTYPE_SRVR_IP, la bitmap creata nel passaggio precedente.

Ora che tutte le nuove risorse e classi vengono create, aggiungere il codice necessario in modo che il framework sia su questi (e sa che questa applicazione supporta ora la modifica sul posto).Questa operazione viene eseguita mediante l'aggiunta di un certo numero di parametri della chiamata di SetServerInfo nella funzione di InitInstance :

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

È ora possibile eseguire sul posto nel contenitore che supporta anche l'attivazione sul posto.Tuttavia, esiste un bug secondario nuovamente che è apposta nel codice.HIERSVR supporta un menu di scelta rapida, viene visualizzato quando l'utente preme il pulsante destro del mouse.Funzionamento di questo menu quando HIERSVR è completamente aperto, ma non riesce quando modifica incorporare sul posto.Il motivo è possibile bloccarlo a questa singola riga di codice in CServerView::OnRButtonDown:

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

Si noti il riferimento a AfxGetApp()->m_pMainWnd.Quando il server è attivato sul posto, viene visualizzata una finestra principale e il m_pMainWnd è impostato, ma è in genere invisibile.Inoltre, questa finestra si riferisce alla finestra principale dell'applicazione, la finestra cornice MDI che viene visualizzato quando il server è completamente aperto o esecuzione autonomo.Non alla finestra cornice attiva — che una volta sul posto attivato sia una finestra cornice derivata da COleIPFrameWnd.Per ottenere la finestra attiva corretta anche quando la modifica sul posto, questa versione di MFC aggiunge una nuova funzione, AfxGetMainWnd.In genere, è consigliabile utilizzare questa funzione anziché AfxGetApp()->m_pMainWnd.Questo codice deve essere modificato come segue:

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

A questo punto si dispone di un server OLE come minimo abilitato per l'attivazione sul posto funzionale.Esistono tuttavia ancora molte funzionalità disponibili con MFC/OLE 2 che non erano disponibili in MFC/OLE1.Vedere l'esempio HIERSVR per ulteriori informazioni sulle funzionalità che è possibile implementare.Alcune delle funzionalità che implementa HIERSVR vengono elencate di seguito:

  • Eseguendo l'ingrandimento, per il vero comportamento di WYSISYG rispetto al contenitore.

  • Trascinamento della selezione e un formato degli Appunti personalizzato.

  • Scorrere la finestra contenitore come la selezione viene modificato.

L'esempio HIERSVR in MFC 3,0 utilizza una progettazione leggermente diverso per gli elementi server.Ciò consente di risparmiare memoria e rende i collegamenti più flessibili.Con la versione 2,0 HIERSVR ogni nodo nella struttura ad albero sottotipoCOleServerItem.COleServerItem porta un certo un maggiore sovraccarico esclusivamente per ognuno di questi nodi, ma COleServerItem è obbligatoria per ogni collegamento attivo.Ma in genere, sono disponibili collegamenti attivi poche in un determinato momento.A tale scopo più efficiente, il HIERSVR in questa versione di MFC separa il nodo da COleServerItem.Dispone sia di un CServerNode che una classe di CServerItem .CServerItem (derivato da COleServerItem) viene creato solo in base alle esigenze.Dopo l'interruzione contenitori (o del contenitore) utilizzando tale particolare collegamento a tale nodo specifico, l'oggetto di CServerItem associato al CServerNode viene eliminato.Questa progettazione è più efficiente e flessibile.La flessibilità è disponibile in quando si occupa dei collegamenti di selezione multipla.Non si verifica alcuna di queste due versioni della selezione multipla di supporto HIERSVR, ma sono molto più facili da aggiungere (e collegamenti di supporto per tali selezioni) con la versione MFC 3,0 HIERSVR, poiché COleServerItem è separato dai dati nativi.

Vedere anche

Altre risorse

Note tecniche del numero

Note tecniche per categoria