Partager via


TN041 : Migration MFC/OLE1 aux MFC/OLE 2

[!REMARQUE]

La note technique suivante n'a pas été modifiée depuis si c'était première inclus dans la documentation en ligne.Par conséquent, certaines procédures et rubriques peuvent être obsolètes ou incorrects.Pour obtenir les informations les plus récentes, il est recommandé que vous trouviez la rubrique d'intérêt dans l'index de la documentation en ligne.

Problèmes généraux relatifs au transfert

L'un des objectifs de design pour OLE 2 classes dans MFC 2,5 (et supérieur) était de conserver une grande partie de la même architecture insertion dans MFC 2,0 pour la prise en charge d'OLE 1,0.Par conséquent, la plupart des mêmes OLE classes dans MFC 2,0 sont toujours présentes dans cette version MFC (COleDocument, COleServerDoc, COleClientItem, COleServerItem).En outre, la plupart des API dans ces classes sont exactement les mêmes.Toutefois, OLE 2 est totalement différent OLE 1,0 vous pouvez vous attendre à ce que certains détails ont changé.Si vous êtes familiarisé avec la prise en charge MFC 2.0 ' s OLE1, vous serez à l'aise avec la prise en charge MFC 2,0.

Si vous choisissez une application MFC/OLE1 existante et ajoutez la fonctionnalité OLE 2 à elle, vous devez lire cette remarque en premier.Cette remarque fournit des problèmes généraux que vous pouvez rencontrer lors de le déplacement de votre fonctionnalité OLE1 aux MFC/OLE 2 et décrit les problèmes découverts tout en déplaçant deux applications incluses dans les MFC 2,0 : les exemples de liaison et incorporation d'objets MFC OCLIENT et HIERSVR.

L'architecture Document/Vue MFC est importante

Si votre application n'utilise pas l'architecture Document/Vue MFC et vous souhaitez ajouter la prise en charge d'OLE 2 à votre application, est maintenant l'heure de passer au document/vue.Nombre des avantages OLE MFC 2 classes sont exécutés uniquement une fois que votre application utilise l'architecture et les composants prédéfinis MFC.

Implémenter un serveur ou un conteneur sans utiliser l'architecture des MFC est possible, mais pas recommandé.

Implémentation MFC de utilisation au lieu de votre propre

Les classes de « d'implémentation de boîte » MFC comme CToolBar, CStatusBar, et CScrollView ont le code prédéfini de cas spécial pour la prise en charge d'OLE 2.Par conséquent, si vous pouvez utiliser ces classes de votre application vous seront aussi les efforts déployés dans elles pour informer leur OLE.Là encore, il est possible de « roulette-votre-propres » classe " ici à ces fins, mais elle n'est pas suggérée.Si vous devez implémenter des fonctionnalités semblables, le code source MFC est une excellente référence pour traiter certains des points plus fins OLE (surtout lorsqu'il s'agit d'activation sur place).

Examinez le code d'exemple MFC

Il existe un certain nombre d'exemples MFC qui incluent la notion de fonctionnalité.Chacune de ces applications implémente OLE d'un angle différent :

  • HIERSVR signifiait principalement pour une utilisation en tant qu'application serveur.Elle a été incluse dans MFC 2,0 comme une application MFC/OLE1 et a été déplacée à MFC/OLE 2 puis étendu de sorte qu'elle implémente plusieurs OLE fonctionnalités disponibles dans OLE 2.

  • OCLIENT cela est une application conteneur autonome, destinée à afficher plusieurs OLE fonctionnalités d'un point de conteneur.Elle aussi a été déplacée MFC 2,0, et possède alors étendu pour prendre en charge plusieurs OLE fonctionnalités avancées, telles que les formats de presse-papiers personnalisés et des liens vers les éléments incorporés.

  • DRAWCLI cette application implémente la prise en charge des conteneurs OLE comme OCLIENT fait, mais il fait dans le cadre d'un programme de dessin orienté objet existant.Elle vous montre comment vous pouvez implémenter la prise en charge des conteneurs OLE et l'intégrer dans votre application existante.

  • SUPERPAD cette application, ainsi qu'une application autonome fine, est également OLE serveur.La prise en charge du serveur qu'elle implémente est assez minimaliste.Un intérêt particulier est comment il utilise des services de presse-papiers OLE pour copier des données au presse-papiers, mais utilise des fonctionnalités intégrées au contrôle « edit » windows pour implémenter la fonctionnalité de collage de presse-papiers.Cela montre une combinaison utile d'utilisation et d'intégration traditionnelles d'API Windows avec les nouvelles OLE API.

Pour plus d'informations sur les exemples d'applications, consultez « utilisation d'exemple MFC ».

Étude de cas : OCLIENT MFC 2,0

Comme décrit ci-dessus, OCLIENT a été incluses dans les MFC 2,0 et a implémenté OLE avec MFC/OLE1.Les étapes selon lesquelles cette application a été initialement convertie d'utiliser MFC/OLE 2 classes sont décrites ci-dessous.Plusieurs fonctionnalités ont été ajoutées après que le port initial a été effectué pour mieux illustrer les classes MFC/OLE.Ces fonctionnalités ne sont pas traitées ici ; reportez -vous à l'exemple lui-même pour plus d'informations sur ces fonctionnalités avancées.

[!REMARQUE]

Les erreurs du compilateur et le processus pas - à - pas créés avec Visual C++ 2,0.Les messages d'erreur spécifiques et les emplacements peuvent avoir changé avec Visual C++ 4,0, mais les informations conceptuelles restent valides.

Le récupérant en service

L'approche adoptée pour déplacer l'exemple OCLIENT aux MFC/OLE consiste à partir de le générer et de la résolution des erreurs de compilation explicite qui sont générées.Si vous prélevez l'exemple OCLIENT sur MFC 2,0 et le compiler sous cette version MFC, vous constaterez qu'il n'y a pas de nombreuses erreurs à les résoudre.Les erreurs dans l'ordre dans lequel ils se sont produits sont décrites ci-dessous.

Compilez et corrigez les erreurs

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

La première erreur concerne COleClientItem::Draw.Dans MFC/OLE1 il a pris plus de paramètres qui prend de version MFC/OLE.Les paramètres supplémentaires n'est souvent pas nécessaires et généralement NULL (comme dans cet exemple).Cette version MFC peut déterminer automatiquement les valeurs des lpWBounds lorsque la CDC à laquelle est dessiné est un contexte de périphérique de métafichier.En outre, le paramètre de pFormatDC n'est plus nécessaire car l'infrastructure génère un de « attribut contrôleur de domaine » du pDC passé.Résoudre ce problème, vous supprimez simplement les deux paramètres NULL supplémentaires à l'appel de Méthode.

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

Le résultat ci-dessus des erreurs du fait que toutes les s'exécute dans MFC/OLE1 requiert qu'un nom unique est passé pour représenter l'élément.C'est une spécification d'OLE API sous-jacente.Ce n'est pas nécessaire dans MFC/OLE 2 comme OLE 2 n'utilise pas DDE comme mécanisme sous-jacent de communication (le nom a été utilisé dans les appels DDE).Pour résoudre ce problème, vous pouvez supprimer la fonction de CreateNewName ainsi que toutes les références à celui-ci.Il est facile de savoir ce que chaque fonction MFC/OLE attend dans cette version simplement en plaçant le curseur sur l'appel et en appuyant sur F1.

Une autre zone qui est très différente est la gestion du presse-papiers OLE 2.Avec OLE1, vous avez utilisé des fenêtres que les API du presse-papiers interagissent avec le presse-papiers.Avec OLE 2 cela s'effectue avec un mécanisme différent.Les API MFC/OLE1 ont supposé que le presse-papiers était ouvert avant de copier un objet d' COleClientItem dans le presse-papiers.Ce n'est plus nécessaire et entraîne toutes les opérations de presse-papiers MFC/OLE son échec.Lorsque vous modifiez du code pour supprimer des dépendances sur CreateNewName, vous devez également supprimer le code qui ouvre et ferme le presse-papiers 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'

Résultat de ces erreurs du gestionnaire de CMainView::OnInsertObject .Gérer la commande de nouvel objet « insert » est une autre zone dans laquelle les éléments ont modifié un bit assez.Dans ce cas, il est plus facile de fusionner simplement l'implémentation d'origine et celui fourni par AppWizard pour une nouvelle application conteneur OLE.En fait, c'est une technique que vous pouvez appliquer à déplacer d'autres applications.Dans MFC/OLE1, vous avez affiché la boîte de dialogue « d'objet d'insertion » en appelant la fonction de AfxOleInsertDialog .Dans cette version vous construisez un objet dialog de COleInsertObject et appelez DoModal.En outre, de nouveaux OLE éléments sont créés avec CLSID au lieu d'une chaîne de nom de classe.Le résultat final doit se présenter de la manière suivante

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

[!REMARQUE]

Le nouvel objet d'insertion peut être différent pour votre application) :

Il est également nécessaire de comprendre <afxodlgs.h>, qui contient la déclaration de la classe de boîte de dialogue de COleInsertObject ainsi que les autres boîtes de dialogue standard fournis par les MFC.

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

Ces erreurs sont provoquées par le fait que les constantes un certain OLE1 modifiées dans OLE 2, bien que dans le concept elles soient les mêmes.Dans ce cas OLEVERB_PRIMARY a changé dans OLEIVERB_PRIMARY.Dans OLE1 et OLE 2, le verbe principal est généralement exécuté par un conteneur lorsque l'utilisateur double-clique sur un élément.

En outre, DoVerb prend maintenant un paramètre supplémentaire — pointeur vers une vue (CView*).Ce paramètre est uniquement utilisé pour implémenter la « modification sur place » (ou l'activation sur place).Pour le moment vous définissez ce paramètre DE valeur NULL, parce que vous n'implémentez pas cette fonctionnalité à ce stade.

Pour vous assurer que l'infrastructure à visuelle ne permettent pas, vous devez substituer COleClientItem::CanActivate comme suit :

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

Dans MFC/OLE1, COleClientItem::GetBounds et SetBounds ont été utilisés pour interroger et de manipuler l'étendue d'un élément (les membres de left et de top sont toujours zéro).Dans MFC/OLE 2 cela plus directement est pris en charge par COleClientItem::GetExtent et SetExtent, qui traitent SIZE ou CSize à la place.

Le code de votre nouveau SetItemRectToServer, et UpdateItemRectFromServer appelle le ressembler à ceci :

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

Dans les appels d'API MFC/OLE1 synchrones d'un conteneur à un serveur ont été simulés, car OLE1 était par nature asynchrone dans de nombreux cas.Il était nécessaire de vérifier un appel asynchrone en attente en cours avant de traiter des commandes de l'utilisateur.MFC/OLE1 fournissait une fonction de COleClientItem::InWaitForRelease pour ce faire.Dans MFC/OLE 2 ce n'est pas nécessaire, vous pouvez supprimer toutes la substitution d'OnCommand dans CMainFrame ensemble.

À ce stade OCLIENT compile et le crée.

D'autres modifications nécessaires

Il existe peu d'éléments qui ne sont pas effectuées qui conserve OCLIENT de s'exécuter, toutefois.Il est préférable de résoudre ces problèmes maintenant au lieu de version ultérieure.

Tout d'abord, il est nécessaire d'initialiser OLE les bibliothèques.Cela est fait en appelant AfxOleInit d' InitInstance:

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

Il est également recommandé de vérifier les fonctions virtuelles pour les modifications de liste de paramètres.Une telle fonction est COleClientItem::OnChange, substitué dans chaque application conteneur MFC/OLE.En examinant l'aide en ligne, vous verrez que des frais supplémentaires « dwParam DWORD » ont été ajoutés.Le nouveau CRectItem::OnChange se présente ainsi :

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
}

Dans MFC/OLE1, les applications conteneur dérivées la classe de document de COleClientDoc.Dans MFC/OLE 2 cette classe a été supprimée et remplacée par COleDocument (cette nouvelle organisation facilite générer des applications conteneur/serveur).Il existe #define qui mappe COleClientDoc à COleDocument pour simplifier le portage d'applications MFC/OLE1 aux MFC/OLE 2, par exemple OCLIENT.L'une des fonctionnalités non fournies par COleDocument fournies par COleClientDoc est les entrées de la table des messages standard de commande.Cela est fait afin que les applications serveur, qui utilisent également COleDocument (indirectement), n'ont pas avec eux la charge mémoire de ces gestionnaires de commandes à moins qu'elles soient une application conteneur/serveur.Vous devez ajouter les entrées suivantes dans la table des messages de CMainDoc :

ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE, OnUpdatePasteMenu)
ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE_LINK, OnUpdatePasteLinkMenu)
ON_UPDATE_COMMAND_UI(ID_OLE_EDIT_LINKS, OnUpdateEditLinksMenu)
ON_COMMAND(ID_OLE_EDIT_LINKS, COleDocument::OnEditLinks)
ON_UPDATE_COMMAND_UI(ID_OLE_VERB_FIRST, OnUpdateObjectVerbMenu)
ON_UPDATE_COMMAND_UI(ID_OLE_EDIT_CONVERT, OnUpdateObjectVerbMenu)
ON_COMMAND(ID_OLE_EDIT_CONVERT, OnEditConvert)

L'implémentation de toutes ces commandes est dans COleDocument, ce qui est la classe de base pour votre document.

À ce stade, OCLIENT est une application conteneur OLE fonctionnelle.Il est possible d'insérer des éléments de tout type (OLE1 ou OLE 2).Étant donné que le code nécessaire pour activer l'activation sur place n'est pas implémenté, les éléments sont modifiés dans une fenêtre séparée tout comme avec OLE1.La section traite les modifications nécessaires pour activer l'édition sur place (parfois appelée « modification sur place »).

Ajoutant la « modification sur place »

L'une des fonctions les plus intéressantes OLE est activation sur place (ou « modification sur place »).Cette fonctionnalité permet à l'application serveur pour fournir des parties de l'interface utilisateur du conteneur a fourni une interface est plus transparente de modification pour l'utilisateur.Pour implémenter l'activation sur place à OCLIENT, certaines ressources particulières doivent être ajoutées, ainsi qu'un code supplémentaire.Ces ressources et code sont normalement fournis par AppWizard — en fait, la majeure partie du code présenté ici a été emprunté directement à une application d'AppWizard avec la prise en charge de « conteneur ».

Tout d'abord, il est nécessaire d'ajouter une ressource menu à utiliser lorsqu'un élément qui est actif sur place.Vous pouvez créer cette ressource menu supplémentaire dans Visual C++ en copiant la ressource de IDR_OCLITYPE et en supprimant tous sauf dans le fichier et la fenêtre fenêtres contextuelles.Deux barres de séparation sont insérées entre le fichier et la fenêtre menus contextuels pour indiquer la séparation des groupes (elle doit se présenter : fichier | | fenêtre).Pour plus d'informations sur ce que le déploiement de ces délimiteurs et comment les menus du serveur et du conteneur sont fusionnés consultez à « menus et aux ressources : fusion de menus » dans OLE 2 classes.

Une fois que vous créez ces menus, vous devez effectuer l'infrastructure savoir eux.Cela est fait en appelant CDocTemplate::SetContainerInfo pour le modèle de document avant de l'ajouter à la liste des modèles de document de votre InitInstance.Le nouveau code pour stocker le modèle de document se présente comme suit :

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 ressource de IDR_OLECLITYPE_INPLACE est la ressource sur place spéciale créé dans Visual C++.

Pour activer l'activation sur place, il existe certaines opérations qui doivent changer dans la classe dérivée d' CView (CMainView) ainsi que la classe dérivée d' COleClientItem (CRectItem).Toutes ces substitutions sont fournies par AppWizard et la majeure partie de l'implémentation viendra directement d'une application par défaut d'AppWizard.

Dans la première étape de ce port, l'activation sur place a été désactivée entièrement en substituant COleClientItem::CanActivate.Cette substitution doit être supprimée pour permettre l'activation sur place.En outre, nulle a été passé à tous les appels à DoVerb (il existe deux d'entre eux) car la fourniture de la vue est uniquement requis pour l'activation sur place.Pour implémenter intégralement l'activation sur place, il est nécessaire de passer la vue correcte dans l'appel d' DoVerb .Un de ces appels est dans CMainView::OnInsertObject:

pItem->DoVerb(OLEIVERB_SHOW, this);

Un autre est dans CMainView::OnLButtonDblClk:

m_pSelection->DoVerb(OLEIVERB_PRIMARY, this);

Il est nécessaire de substituer COleClientItem::OnGetItemPosition.Cela indique au serveur où placer sa fenêtre par rapport à la fenêtre de conteneur lorsque l'élément est sur place activé.Pour OCLIENT, l'implémentation est triviale :

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

La plupart des serveurs implémentent également ce qui est appelé « redimensionnement sur place ». Cela permet la fenêtre de serveur classer et déplacée lorsque l'utilisateur modifie l'élément.Le conteneur doit participer à cette action, puisque déplacer ou redimensionner la fenêtre affecte généralement la position et la taille dans le document conteneur elle-même.L'implémentation pour OCLIENT synchronise le rectangle interne gérée par le m_rect avec la nouvelle position et taille.

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

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

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

    return TRUE;
}

À ce stade, il est suffisante pour le code pour permettre à un élément pour être activé sur place et traiter le dimensionnement et déplacer l'élément lorsqu'il est active, mais aucun code ne permettra à l'utilisateur de quitter la session d'édition.Bien que certains serveurs fournissent cette fonctionnalité eux-mêmes en gérant la touche ESCAPE, on suggère que les conteneurs proposent deux façons de désactiver un élément : (1) en cliquant à l'extérieur de l'élément, et (2) en appuyant sur la touche ESCAPE.

Pour la touche ESCAPE, ajoutez un accélérateur avec Visual C++ qui mappe la clé de VK_ESCAPE à une commande, ID_CANCEL_EDIT est ajouté aux ressources.Le gestionnaire de cette commande suivante :

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

Pour gérer le cas où l'utilisateur clique en dehors de l'élément, ajoutez le code suivant au début de CMainView::SetSelection:

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

Lorsqu'un élément est actif sur place, il doit avoir le focus.Pour garantir cela est le cas que vous gérez OnSetFocus afin que le focus passe toujours transféré à l'élément actif lorsque votre vue reçoit le focus :

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

Lorsque la vue est redimensionnée, vous devez informer l'élément actif que le rectangle de découpage a changé.Pour ce faire vous fournissez un gestionnaire pour 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();
}

Étude de cas : HIERSVR MFC 2,0

HIERSVR a également été incluses dans les MFC 2,0 et a implémenté OLE avec MFC/OLE1.Cette remarque décrit brièvement les étapes selon lesquelles cette application a été initialement convertie d'utiliser MFC/OLE 2 classes.Plusieurs fonctionnalités ont été ajoutées après que le port initial a été effectué pour mieux illustrer MFC/OLE 2 classes.Ces fonctionnalités ne sont pas traitées ici ; reportez -vous à l'exemple lui-même pour plus d'informations sur ces fonctionnalités avancées.

[!REMARQUE]

Les erreurs du compilateur et le processus pas - à - pas créés avec Visual C++ 2,0.Les messages d'erreur spécifiques et les emplacements peuvent avoir changé avec Visual C++ 4,0, mais les informations conceptuelles restent valides.

Le récupérant en service

L'approche adoptée pour déplacer l'exemple HIERSVR aux MFC/OLE consiste à partir de le générer et de la résolution des erreurs de compilation explicite qui sont générées.Si vous prélevez l'exemple HIERSVR sur MFC 2,0 et le compiler sous cette version MFC, vous constaterez qu'il n'y a pas de nombreuses erreurs à les résoudre (bien qu'il y a plus qu'à l'exemple OCLIENT).Les erreurs dans l'ordre dans lequel elles se produisent généralement sont décrites ci-dessous.

Compilez et corrigez les erreurs

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

Cette première erreur signale un problème beaucoup plus grand avec la fonction d' InitInstance pour les serveurs.L'initialisation requise pour OLE serveur est probablement l'une des plus importantes modifications que vous devez apporter à votre application MFC/OLE1 de l'obtenir l'exécution.La meilleure chose à faire est de surveiller ce qu'AppWizard crée pour OLE serveur et modifier votre code si nécessaire.Voici quelques points à l'esprit :

Il est nécessaire d'initialiser les OLE bibliothèques en appelant AfxOleInit

Appelez SetServerInfo à l'objet de modèle de document aux informations définies de handles et de classe d'exécution de ressources du serveur que vous ne pouvez pas définir avec le constructeur d' CDocTemplate .

Ne pas afficher la fenêtre principale de votre application si /Embedding est présent sur la ligne de commande.

Vous aurez besoin de GUID pour votre document.c'est un identificateur unique pour votre type de document (128 bits).AppWizard crée un pour vous — si vous utilisez la technique décrite ici pour copier un nouveau code d'une application serveur générée par AppWizard, vous pouvez simplement « voler » GUID de cette application.Sinon, vous pouvez utiliser l'utilitaire de GUIDGEN.EXE dans le répertoire bin.

Il est nécessaire « ou » votre objet d' COleTemplateServer au modèle de document en appelant COleTemplateServer::ConnectTemplate.

Mettez à jour la base de registres lorsque votre application est autonome exécuté.De cette façon, si l'utilisateur déplace le projet .EXE pour votre application, l'exécution de son nouvel emplacement met à jour la base de données d'inscription du système Windows pour pointer vers le nouvel emplacement.

Après avoir appliqué tous ces éléments change en fonction de ce qu'AppWizard crée pour InitInstance, InitInstance (et GUID connexe) pour HIERSVR doit obtenir la directive suivante :

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

Vous remarquerez que le code ci-dessus fait référence à un nouvel ID de ressource, IDR_HIERSVRTYPE_SRVR_EMB.Il s'agit de la ressource menu à utiliser lorsqu'un document qui est incorporé dans un autre conteneur est modifié.Dans MFC/OLE1 les éléments de menu spécifiques à modifier un élément incorporé ont été modifiés à la volée.À l'aide d'un menu complètement différent structurez lorsque vous modifiez un élément incorporé au lieu de modifier un document basé sur des fichiers facilite grandement fournir des interfaces utilisateur pour ces deux modes distincts.Comme vous le verrez ultérieurement, une ressource menu complètement séparée est utilisée en modifiant un objet incorporé sur place.

Pour créer cette ressource, chargez le script de ressources dans Visual C++ et copiez la ressource menu existante en IDR_HIERSVRTYPE.Renommez la nouvelle ressource à IDR_HIERSVRTYPE_SRVR_EMB (c'est la même convention d'affectation des noms qui utilise d'AppWizard).Modification suivante « sauvegarde de fichier » à « mise à jour de fichiers » ; donnez-lui l'ID de commande ID_FILE_UPDATE.Également modifier « sauvegarde de fichier comme "" à la sauvegarde de fichier comme » ; donnez-lui l'ID de commande ID_FILE_SAVE_COPY_AS.L'infrastructure fournit l'implémentation des deux commandes.

\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

Il existe un certain nombre d'erreurs émanant de la substitution d' OnSetData, puisqu'il fait référence au type d' OLESTATUS .OLESTATUS était les erreurs retournées par OLE1 de façon.Cela a changé dans HRESULT dans OLE 2, bien que MFC convertisse généralement HRESULT dans COleException contenant l'erreur.Dans ce cas particulier, la substitution d' OnSetData n'est plus nécessaire, donc le plus simple est de le supprimer.

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

Le constructeur d' COleServerItem accepte un paramètre supplémentaire « BOOL ».Cette balise détermine comment la gestion de la mémoire est effectuée sur les objets d' COleServerItem .En lui attribuant la valeur TRUE, l'infrastructure gère la gestion de la mémoire de ces objets — les supprimer lorsqu'ils ne sont plus nécessaires.HIERSVR utilise des objets de CServerItem (dérivé d' COleServerItem) dans le cadre de ses données natives, vous définirez cette balise à FALSE.Cela permet de HIERSVR déterminer quand chaque élément du serveur est supprimé.

\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

Comme ces erreurs l'indique, il existe certaines fonctions « pur-virtuelles » qui n'ont pas été remplacées dans CServerItem.Très probablement cela s'explique par le fait que la liste de paramètres d'OnDraw a modifié.Pour corriger cette erreur, modifiez CServerItem::OnDraw manière suivante (ainsi que la déclaration dans 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);
}

Le nouveau paramètre est « rSize ».Cela vous permet de terminer la taille du dessin, si possible.Cette taille doit être dans HIMETRIC.Dans ce cas, il n'est pas pratique d'effectuer cette valeur, l'infrastructure appelle OnGetExtent pour récupérer l'étendue.Pour que cela fonctionne, vous devez implémenter 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 *'

Dans la fonction de CServerItem::CalcNodeSize la taille d'élément est convertie en HIMETRIC et stockées dans m_rectBounds.Le membre non documenté « m_rectBounds » d' COleServerItem n'existe pas (il a été partiellement remplacé par m_sizeExtent, mais dans OLE 2 ce membre a une utilisation légèrement différente m_rectBounds est dans OLE1).Au lieu de définir la taille de HIMETRIC dans cette variable membre, vous la retournerez.Cette valeur de retour est utilisée dans OnGetExtent, implémenté précédemment.

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 substitue également COleServerItem::OnGetTextData.Cette fonction est obsolète dans MFC/OLE et est remplacée par un mécanisme différent.La version MFC 3,0 de l'exemple de liaison et incorporation d'objets MFC HIERSVR implémente cette fonctionnalité en substituant COleServerItem::OnRenderFileData.Cette fonctionnalité n'est pas importante pour ce port de base, vous pouvez supprimer la substitution d'OnGetTextData.

Il existe de nombreuses autres erreurs dans svritem.cpp qui n'ont pas été traités.Ils ne sont pas des « true » erreurs — simplement erreurs provoquées par des erreurs précédentes.

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

COleServerItem::CopyToClipboard ne prend plus en charge la balise « bIncludeNative ».Les données natives (les données entrées par l'élément du serveur sérialisent la fonction) est systématiquement copiée, vous supprime le premier paramètre.En outre, CopyToClipboard lève une exception lorsqu'une erreur se produit au lieu de retourner FALSE.Modifiez le code pour CServerView::OnEditCopy comme suit :

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

Bien qu'il y ait eu plus d'erreur provenant de la compilation de la version MFC 2,0 HIERSVR qu'il s'agisse de la même version d'OCLIENT, il y avait réellement moins modifications.

À ce stade HIERSVR compile et le crée et fonctionnera comme OLE serveur, mais sans fonctionnalité d'édition visuelle, suivante qui sera implémenté.

Ajoutant la « modification sur place »

Pour ajouter la « modification sur place » (ou l'activation sur place) à cette application serveur, il existe quelques aspects que vous devez prendre soin de :

  • Vous avez besoin d'une ressource menu spéciale pour être utilisée lorsque l'élément est actif sur place.

  • Cette application comprend une barre d'outils, vous aurez besoin d'une barre d'outils avec un seul sous-ensemble de la barre d'outils normale pour faire correspondre les commandes de menu disponibles dans le serveur (correspond à la ressource menu mentionnées ci-dessus).

  • Vous avez besoin d'une nouvelle classe dérivée d' COleIPFrameWnd qui fournit l'interface utilisateur visuelle (comme CMainFrame, dérivé d' CMDIFrameWnd, fournit l'interface utilisateur MDI).

  • Vous devez indiquer l'infrastructure sur ces ressources et des classes spéciales.

Il est de créer facilement la ressource menu.Exécutez Visual C++, copiez la ressource menu IDR_HIERSVRTYPE à une ressource menu appelée IDR_HIERSVRTYPE_SRVR_IP.Modifiez le menu afin que les messages uniquement de modification et de menu ? soient conservés.Ajoutez deux séparateurs au menu entre la modification et les menus d'aide (elle doit se présenter : modification | | aide).Pour plus d'informations sur ce que le déploiement de ces délimiteurs et comment les menus du serveur et du conteneur sont fusionnés, consultez des « menus et les ressources : fusion de menus » dans OLE 2 classes.

La bitmap pour la barre d'outils du sous-ensemble peut être facilement créées en copiant celui d'une application générée par AppWizard avec une option « server » activée.Elle peut ensuite être importée dans Visual C++.Veillez à attribuer à la bitmap un ID d'IDR_HIERSVRTYPE_SRVR_IP.

La classe dérivée d' COleIPFrameWnd peut également être copiée d'une application générée par AppWizard avec la prise en charge du serveur.Copiez les deux fichiers, IPFRAME.CPP et IPFRAME.H et ajoutez -la au projet.Assurez -vous que l'appel d' LoadBitmap fait référence à IDR_HIERSVRTYPE_SRVR_IP, la bitmap créée à l'étape précédente.

Maintenant que toutes les nouvelles ressources et des classes sont créées, ajoutez le code nécessaire pour que l'infrastructure sache ces derniers (et de savoir que cette application prend désormais en charge la modification sur place).Cette opération est effectuée en ajoutant encore plus de paramètres à l'appel d' SetServerInfo dans la fonction d' InitInstance :

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

Il est désormais sur place prêt à exécuter dans tout conteneur qui prend en charge également l'activation sur place.Toutefois, il existe un bogue secondaire flânant toujours dans le code.HIERSVR prend en charge un menu contextuel, s'affiche lorsque l'utilisateur appuie sur le bouton de la souris.Ce menu s'exécute lorsque HIERSVR est entièrement ouvert, mais ne fonctionne pas en modifiant une incorporation sur place.La raison peut être épinglée jusqu'à cette ligne de code unique dans CServerView::OnRButtonDown :

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

Notez la référence à AfxGetApp()->m_pMainWnd.Lorsque le serveur est activé sur place, il possède une fenêtre principale et le m_pMainWnd est défini, mais il est généralement invisible.En outre, cette fenêtre fait référence à la fenêtre principale de l'application, la fenêtre frame MDI qui s'affiche lorsque le serveur est entièrement ouvert ou exécuté autonome.Il ne fait pas référence à la fenêtre frame actif )qui si sur place checked est une fenêtre frame dérivée d' COleIPFrameWnd.Pour obtenir la fenêtre active correcte même lorsque la modification sur place, cette version MFC ajoute une nouvelle fonction, AfxGetMainWnd.En général, vous devez utiliser cette fonction au lieu d' AfxGetApp()->m_pMainWnd.Ce code doit changer comme suit :

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

Vous avez maintenant OLE serveur de façon minimum activé pour l'activation sur place fonctionnelle.Mais il reste de nombreuses fonctionnalités disponibles avec MFC/OLE 2 qui n'étaient pas disponibles dans MFC/OLE1.Consultez l'exemple HIERSVR pour plus d'informations sur les fonctionnalités que vous êtes susceptible d'implémenter.Certaines fonctionnalités que HIERSVR implémente sont répertoriées ci-dessous :

  • Zoom, pour un vrai comportement de WYSISYG par rapport à le conteneur.

  • Glisser-déplacer/déplacement et un format de presse-papiers personnalisé.

  • Faire défiler la fenêtre de conteneur comme sélection est modifié.

L'exemple HIERSVR dans MFC 3,0 utilise également une conception légèrement différente pour ses éléments du serveur.Cela permet d'économiser de la mémoire et rend votre liens plus flexibles.Avec la version 2,0 de HIERSVR chaque nœud de l'arborescence AISCOleServerItem.COleServerItem comporte un peu plus de charge que est strictement nécessaire pour chacun de ces nœuds, mais COleServerItem est requis pour chaque lien actif.Mais pour la plupart, il existe très peu de liens actifs à un moment donné.Pour que cet plus efficace, le HIERSVR dans cette version MFC sépare le nœud d' COleServerItem.Il possède un CServerNode et une classe de CServerItem .CServerItem (dérivé d' COleServerItem) n'est créé selon vos besoins.Une fois que l'arrêt du conteneur (ou conteneurs) à l'aide de ce lien particulier vers ce nœud particulier, l'objet de CServerItem associé au CServerNode est supprimé.Cette conception est plus efficace et plus flexible.Sa souplesse apparaît dans le traitement des liens de sélection multiple.Il serait beaucoup plus simple d'ajouter ni l'un ni l'autre de ces deux versions de sélection multiple de support de HIERSVR, mais il (et sur des médias à ces sélections) à la version MFC 3,0 de HIERSVR, car COleServerItem est séparé de données natives.

Voir aussi

Autres ressources

Notes techniques de nombres

Notes techniques de catégorie