Questions générales relatives à la Migration
Un des objectifs de conception pour les classes OLE 2 dans MFC 2.5 (et plus) a été de conserver une grande partie de la même architecture mises en place en 2.0 MFC OLE 1.0 soutien. Par conséquent, bon nombre des mêmes classes OLE MFC 2.0 existent encore dans cette version de MFC (COleDocument COleServerDoc COleClientItem, COleServerItem). En outre, la plupart des API dans ces classes sont exactement les mêmes. Cependant, OLE 2 est radicalement différente de OLE 1.0, donc vous pouvez vous attendre que certains détails ont changé. Si vous êtes familiarisé avec l'aide MFC 2.0 OLE1, vous vous sentirez à la maison avec la prise en charge de MFC 2.0.
Si vous êtes prenant une application MFC/OLE1 existante et y ajouter des fonctionnalités OLE 2, vous devez tout d'abord lire cette note. Cette note porte sur certaines questions générales vous peuvent rencontrer tout en Portage de vos fonctionnalités OLE1 MFC/OLE 2 et aborde ensuite les problèmes découverts pendant le portage de deux applications incluses dans les MFC 2.0 : les exemples MFC OLE OCLIENT et HIERSVR.
L'Architecture Document/vue MFC est Important
Si votre application n'utilise pas l'architecture Document/vue MFC et que vous souhaitez ajouter un OLE 2 soutien à votre application, est maintenant le temps de passer au Document/vue. Bon nombre des avantages de classes MFC OLE 2 sont réalisées uniquement une fois que votre application utilise l'architecture intégrée et les composants des MFC.
Mise en oeuvre d'un serveur ou un conteneur sans utiliser l'architecture MFC est possible, mais pas recommandée.
Utiliser MFC mise en place de votre propre
MFC » conserve la mise en œuvre » des classes telles que CToolBar, CStatusBaret CScrollView ont intégré code cas spécial pour support OLE 2. Donc, si vous pouvez utiliser ces classes dans votre application vous profiterez de l'effort mis en eux pour les sensibiliser OLE. Encore une fois, il est possible de "roll-your-own" classes ici à ces fins, mais il n'est pas suggéré. Si vous avez besoin mettre en œuvre des fonctionnalités similaires, le code source MFC est une excellente référence pour traiter certains des points plus fines de OLE (surtout quand il s'agit de l'activation sur place).
Examinez l'exemple de Code MFC
Il y a un certain nombre d'exemples MFC qui incluent la fonctionnalité OLE. Chacune de ces applications implémente OLE sous un angle différent:
HIERSVR - destinées principalement à une application comme une application serveur. Il a été inclus dans MFC 2.0 comme une application MFC/OLE1 et a été porté sur MFC/OLE 2 et ensuite étendue telle qu'elle met en œuvre plusieurs fonctionnalités OLE de OLE 2.
OCLIENT - il s'agit d'une application conteneur autonome, destinée à démontrer les nombreuses fonctionnalités OLE d'un point de vue de conteneur. Il a été porté trop de MFC 2.0 et ensuite étendu à l'appui de nombreuses fonctionnalités OLE plus avancées, tels que les formats de presse-papiers personnalisé et des liens vers les éléments incorporés.
DRAWCLI - cette application implémente les soutien de conteneur OLE beaucoup comme OCLIENT, sauf qu'il le fait dans le cadre d'un programme de dessin orienté objet existant. Il vous montre comment vous pouvez mettre en œuvre le soutien de conteneur OLE et intégrer dans votre application existante.
SUPERPAD - cette demande, tout autant qu'une fine application autonome, est également un serveur OLE. Le support du serveur qu'elle implémente est assez minimaliste. Un intérêt particulier est comment il utilise les services OLE presse-papiers pour copier des données dans le presse-papiers, mais utilise la fonctionnalité intégrée dans les fenêtres « edit » contrôle à mettre en œuvre des fonctionnalités de pâte de presse-papiers. Cela montre un mélange intéressant de l'utilisation des API Windows traditionnelle ainsi que l'intégration avec les nouveaux OLE API.
Pour plus d'informations sur les exemples d'applications, voir « MFC exemple aider ».
Étude de cas : OCLIENT de MFC 2.0
Comme nous l'avons vu, OCLIENT figurait dans MFC 2.0 et mis en œuvre OLE avec MFC/OLE1. Les mesures par lequel cette demande a été initialement converti pour utiliser les classes MFC/OLE 2 sont décrites ci-dessous. Un certain nombre de fonctionnalités ont été ajouté après que le port initial a été réalisé afin de mieux illustrer les classes MFC/OLE. Ces caractéristiques ne seront pas couverts ici ; se référer à l'échantillon lui-même pour plus d'informations sur ces fonctionnalités avancées.
&Notenbsp ; Le processus étape par étape et les erreurs du compilateur a été créé avec Visual C++ 2.0. Emplacements et les messages d'erreur spécifiques peuvent avoir changé avec Visual C++ 4.0, mais les informations conceptuelles demeurent valides.
Il se lever et en cours d'exécution
L'approche adoptée pour l'exemple OCLIENT MFC/OLE de port est de commencer par construire et réparer les erreurs du compilateur évident qui entraîneront. Si vous prenez l'exemple OCLIENT de MFC 2.0 et le compilez sous cette version de MFC, vous trouverez qu'il n'y a pas que de nombreuses erreurs à résoudre. Les erreurs dans l'ordre dans lequel ils surviennent sont décrits ci-dessous.
Compilation et correction des erreurs
\oclient\mainview.cpp(104) : erreur C2660: « Dessiner »: fonction ne prend pas de 4 paramètres
La première erreur concerne COleClientItem::Draw. Dans MFC/OLE1, il a fallu plus de paramètres que prend la version MFC/OLE. Les paramètres supplémentaires sont souvent pas nécessaires et généralement NULL (comme dans cet exemple). Cette version de MFC peut déterminer automatiquement les valeurs de la lpWBounds lorsque le CDC qui est fixée à un métafichier. En outre, le paramètre pFormatDC n'est plus nécessaire étant donné que le cadre de la « attribut DC » du pDC en construira. Afin de résoudre ce problème, vous supprimez simplement les deux NULL supplémentaire des paramètres à l'appel de Draw.
\oclient\mainview.cpp(273) : erreur C2065: « OLE_MAXNAMESIZE »: identificateur non déclaré
\oclient\mainview.cpp(273) : erreur C2057 : attend une expression constante
\oclient\mainview.cpp(280) : erreur C2664: « CreateLinkFromClipboard »: Impossible de convertir le paramètre 1 de 'char [1]' en ' enum:: tagOLERENDER '
\oclient\mainview.cpp(286) : erreur C2664: « CreateFromClipboard »: Impossible de convertir le paramètre 1 de 'char [1]' en ' enum:: tagOLERENDER '
\oclient\mainview.cpp(288) : erreur C2664: « CreateStaticFromClipboard »: Impossible de convertir le paramètre 1 de 'char [1]' en ' enum:: tagOLERENDER '
Les erreurs ci-dessus résultent du fait que toutes les fonctions de COleClientItem::CreateXXXX dans MFC/OLE1 exige qu'un nom unique soit passé pour représenter l'élément. Il s'agissait d'une exigence de l'OLE API sous-jacente. Ce n'est pas nécessaire dans le MFC/OLE 2 car OLE 2 n'utilise pas DDE comme mécanisme de communication sous-jacent (le nom a été utilisé dans les conversations DDE). Pour résoudre ce problème, vous pouvez supprimer la fonction de CreateNewName ainsi que toutes les références à ce sujet. Il est facile de savoir ce que chaque fonction MFC/OLE attend dans cette version tout simplement en plaçant votre curseur sur l'appel en appuyant sur F1.
Un autre domaine qui est significativement différent est manipulation de presse-papiers OLE 2. Avec OLE1, vous avez utilisé le presse-papiers de Windows Qu'api interagissent avec le presse-papiers. Avec OLE 2, cela se fait avec un mécanisme différent. Les API MFC/OLE1 suppose que le presse-papiers est ouvert avant de copier un objet COleClientItem dans le presse-papiers. Ce n'est plus nécessaire et fera toutes les opérations de presse-papiers MFC/OLE à l'échec. Tandis que vous modifiez le code pour supprimer des dépendances sur CreateNewName, vous devez également supprimer le code qui ouvre et ferme le presse-papiers de Windows.
\oclient\mainview.cpp(332) : erreur C2065: « AfxOleInsertDialog »: identificateur non déclaré
\oclient\mainview.cpp(332) : erreur C2064 : terme n'évalue pas à une fonction
\oclient\mainview.cpp(344) : erreur C2057 : attend une expression constante
\oclient\mainview.cpp(347) : erreur C2039: « CreateNewObject »: n'est pas membre de « CRectItem »
Ces erreurs se produisent dans le gestionnaire d'événements CMainView::OnInsertObject . Traitement de la commande « Insérer un objet nouveau » est un autre domaine où les choses ont changé un peu. Dans ce cas, il est plus facile de fusionner tout simplement la mise en œuvre originale avec celles fournies par AppWizard pour une nouvelle application conteneur OLE. En fait, c'est une technique que vous pouvez appliquer à Portage d'autres applications. Dans MFC/OLE1, vous affiche la boîte de dialogue « Insérer un objet » en appelant la fonction AfxOleInsertDialog . Dans cette version, vous construisez un objet boîte de dialogue de COleInsertObject et appelez DoModal. En outre, de nouveaux éléments OLE sont créés avec un CLSID au lieu d'une chaîne de classname. Le résultat final doit être quelque chose comme ça
COleInsertDialog dlg ;
Si (dlg.DoModal()! = IDOK)
nbsp ; retour ;
BeginWaitCursor() ;
CRectItem * pItem = NULL ;
ESSAYEZ
{
/ / Créez d'abord l'objet C++
pItem = GetDocument() - > CreateItem() ;
ASSERT_VALID(pItem) ;
/ / Initialiser le point à partir des données de la boîte de dialogue.
Si (! dlg.CreateItem(pItem))
AfxThrowMemoryException() ;
/ / fera de toute exception
ASSERT_VALID(pItem) ;
/ / exécute l'objet, le cas échéant
Si (dlg.GetSelectionType() == COleInsertDialog::createNewItem)
pItem - > DoVerb(OLEIVERB_SHOW, this) ;
/ mise à jour immédiatement
pItem - > UpdateLink() ;
pItem - > UpdateItemRectFromServer() ;
/ / définir la sélection à l'élément nouvellement insérée
SetSelection(pItem) ;
pItem - > Invalidate() ;
}
CATCH (CException, e)
{/ / nettoyer les élément
Si (pItem! = NULL)
GetDocument() - > DeleteItem(pItem) ;
AfxMessageBox(IDP_FAILED_TO_CREATE) ;
}
END_CATCH
EndWaitCursor()
&Notenbsp ; Nouvel objet Insert peut être différent de votre application):
Il est également nécessaire d'inclure les lt;afxodlgs.h > qui contient la déclaration pour la classe de boîte de dialogue de COleInsertObject ainsi que les autres boîtes de dialogue standards fournies par MFC.
\oclient\mainview.cpp(367) : erreur C2065: « OLEVERB_PRIMARY »: identificateur non déclaré
\oclient\mainview.cpp(367) : erreur C2660: « DoVerb »: fonction ne prend pas de paramètres 1
Ces erreurs sont causées par le fait que certaines constantes OLE1 ont changé dans OLE 2, même si le concept, ce sont les mêmes. Dans ce cas OLEVERB_PRIMARY a changé OLEIVERB_PRIMARY. OLE1 et OLE 2, le verbe principal est habituellement exécuté par un conteneur lorsque l'utilisateur double-clique sur un élément.
En outre, DoVerb prend un paramètre supplémentaire — un pointeur vers un point de vue (CView*). Ce paramètre est uniquement utilisé pour mettre en œuvre « Édition visuelle » (ou l'activation sur place). Pour l'instant vous définissez ce paramètre à NULL, étant donné que vous implémentez pas cette fonctionnalité en ce moment.
Pour s'assurer que le cadre jamais tenté en place active, vous devez substituer des COleClientItem::CanActivate comme suit
BOOL CRectItem::Ca&nActivate()
{
nbsp ; Return FALSE ;
}
\oclient\rectitem.cpp(53) : erreur C2065: « GetBounds »: identificateur non déclaré
\oclient\rectitem.cpp(53) : erreur C2064 : terme n'évalue pas à une fonction
\oclient\rectitem.cpp(84) : erreur C2065: « SetBounds »: identificateur non déclaré
\oclient\rectitem.cpp(84) : erreur C2064 : terme n'évalue pas à une fonction
Dans MFC/OLE1, COleClientItem::GetBounds et SetBounds ont été utilisés pour interroger et manipuler l'étendue d'un élément (les membres de gauche et de haut étaient toujours zéro). Dans MFC/OLE 2 c'est plus directement soutenue par COleClientItem::GetExtent et SetExtent, qui portent sur une taille ou CSize plutôt.
Le code de votre nouvelle SetItemRectToServer, et les appels UpdateItemRectFromServer ressemblent à ceci:
BOOL CRectItem::UpdateItemRectFromServer()
{
nbsp ; Assert(m_bTrackServerSize) ;
CSize taille ;
if (!.GetExtent(&size))
Return FALSE ; / / blanc
/ / carte de HIMETRIC aux coordonnées d'écran
{
CClientDC screenDC(NULL) ;
screenDC.SetMapMode(MM_HIMETRIC) ;
screenDC.LPtoDP(&size) ;
}
/ / juste définir la taille de l'élément
Si (m_rect.Size()! = taille)
{
/ / invalider la vieille taille/position
Invalidate() ;
m_rect.Right = m_rect.left + size.cx ;
m_rect.Bottom = m_rect.top + size.cy ;
/ / ainsi que la nouvelle taille et position
Invalidate() ;
}
Return TRUE ;
}
BOOL CRectItem::SetItemRectToServer()
{
/ / définir les limites officielles pour l'élément incorporé
CSize taille = m_rect.Size() ;
{
CClientDC screenDC(NULL) ;
screenDC.SetMapMode(MM_HIMETRIC) ;
screenDC.DPtoLP(&size) ;
}
ESSAYEZ
{
SetExtent(size) ; / / peut faire une attente
}
CATCH (CException, e)
{
Return FALSE ; / / Liens permettra pas SetBounds
}
END_CATCH
Return TRUE ;
}
\oclient\frame.cpp(50) : erreur C2039: « InWaitForRelease »: n'est pas membre de « COleClientItem »
\oclient\frame.cpp(50) : erreur C2065: « InWaitForRelease »: identificateur non déclaré
\oclient\frame.cpp(50) : erreur C2064 : terme n'évalue pas à une fonction
Dans MFC/OLE1 synchrones appels API d'un conteneur à un serveur ont été simulés, OLE1 étant intrinsèquement asynchrone dans de nombreux cas. Il était nécessaire de rechercher un appel asynchrone exceptionnel en cours avant de traiter les commandes de l'utilisateur. MFC/OLE1 fourni la fonction COleClientItem::InWaitForRelease pour le faire. Dans MFC/OLE 2 ce n'est pas nécessaire, donc vous pouvez supprimer la substitution de OnCommand dans CMainFrame tous ensemble.
À ce stade OCLIENT va compiler et lier.
Autres changements nécessaires
Il y a quelques choses qui ne sont pas faites que gardera OCLIENT de s'exécuter, cependant. Il est préférable de régler ces problèmes maintenant au lieu de plus tard.
Tout d'abord, il est nécessaire d'initialiser les bibliothèques OLE. Cela se fait en appelant AfxOleInit de InitInstance:
if (!.AfxOleInit())
{
AfxMessageBox ("Impossible d'initialiser les bibliothèques OLE ») ;
Return FALSE ;
}
C'est aussi une bonne idée de vérifier les fonctions virtuelles pour les modifications de liste de paramètre. Une telle fonction est COleClientItem::OnChange, elle est substituée dans chaque application de conteneur MFC/OLE. En regardant les aide en ligne, vous verrez qu'un extra « DWORD dwParam » a été ajouté. Le nouveau CRectItem::OnChange regarde comme suit
vOID CRectItem::OnChange (OLE_NOTIFICATION wNotification, DWORD dwParam)
{
Si (m_bTrackServerSize amp ; &
!UpdateItemRectFromServer())
{
/ / Objet en blanc
Si (wNotification == OLE_CLOSED)
{
/ / aucune données reçues de l'objet - ne détruire
FAIRE VALOIR (!.IsVisible()) ;
GetDocument() - > DeleteItem(this) ;
retour ; / / aucune mise à jour (élément est disparu aujourd'hui)
}
}
Si (wNotification! = OLE_CLOSED)
Dirty() ;
Invalidate() ; / / tout changement provoquera un retraçage
}
Dans MFC/OLE1, demandes de conteneur dérivé la classe de document COleClientDoc. Dans MFC/OLE 2 cette classe a été supprimée et remplacée par COleDocument (cette nouvelle organisation rend plus facile de construire des applications conteneur/serveur). Il y a un # define qui mappe COleClientDoc COleDocument afin de simplifier le portage des applications MFC/OLE1 MFC/OLE 2, comme par exemple OCLIENT. Une des fonctionnalités non fournies par COleDocument qui ont été fournis par COleClientDoc est les entrées carte message de commande standard. Cela est fait pour que les applications serveur qui utilisent également COleDocument (indirectement), ne portent pas avec eux les frais généraux de ces gestionnaires de commande sauf si elles sont une application conteneur et serveur. Ainsi, vous devez ajouter les entrées suivantes à la carte de message 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)
La mise en œuvre de l'ensemble de ces commandes est dans COleDocument, qui est la classe de base pour votre document.
À ce stade, OCLIENT est une application fonctionnelle du conteneur OLE. Il est possible d'insérer des éléments de tout type (OLE1 ou OLE 2). Étant donné que le code nécessaire pour permettre l'activation sur place n'est pas mis en œuvre, les éléments sont édités dans une fenêtre séparée comme avec OLE1. La section suivante décrit les modifications nécessaires pour permettre la modification sur place (parfois appelée « Édition visuelle »).
Ajout de « Édition visuelle »
Une des caractéristiques plus intéressantes de OLE est l'activation in-place (ou « Édition visuelle »). Cette fonctionnalité permet à l'application serveur à prendre en charge certaines parties de l'interface du conteneur utilisateur fourni une interface d'édition plus transparente pour l'utilisateur. Pour mettre en œuvre l'activation sur place d'OCLIENT, certaines ressources spéciales doivent être ajoutés, ainsi que certains code supplémentaire. Ces ressources et le code sont normalement fournis par AppWizard — en fait, une grande partie du code ici a été empruntée directement depuis une application AppWizard fraîche avec le soutien de « Conteneur ».
Tout d'abord, il est nécessaire d'ajouter une ressource menu à utiliser lorsqu'il y a un élément qui est active sur place. Vous pouvez créer cette ressource supplémentaire menu dans Visual C++ en copiant la ressource IDR_OCLITYPE et en enlevant tout sauf les fichier et fenêtre popups. Deux barres de séparation sont insérés entre les fichier et fenêtre popups pour indiquer la séparation des groupes (ça devrait ressembler : fichier || Fenêtre). Pour plus d'informations sur ce que signifient ces séparateurs et comment les menus de serveur et de conteneur sont fusionnés voir « Menus et ressources : fusion de menus » dans Les Classes OLE 2.
Une fois que vous avez ces menus créés, vous devez faire savoir à leur sujet le cadre. Cela se fait en appelant CDocTemplate::SetContainerInfo pour le modèle de document avant de vous ajouter à la liste de modèle de document dans votre InitInstance. Le nouveau code pour enregistrer le modèle de document ressemble à ceci
CDocTemplate * pTemplate = nouveau CMultiDocTemplate (
nbsp ; IDR_OLECLITYPE,
RUNTIME_CLASS(CMainDoc),
RUNTIME_CLASS(CMDIChildWnd), / / norme cadre d'enfant MDI
RUNTIME_CLASS(CMainView)) ;
pTemplate - > SetContainerInfo(IDR_OLECLITYPE_INPLACE) ;
AddDocTemplate(pTemplate)
La ressource IDR_OLECLITYPE_INPLACE est la ressource en place spéciale créée dans Visual C++.
Afin de permettre l'activation sur place, il y a certaines choses qui doivent changer en deux la classe CView (CMainView) dérivé de la classe ainsi que la classe de COleClientItem dérivée (CRectItem). Tous ces remplacements sont fournis par AppWizard et la plupart de la mise en œuvre viendra directement de l'application AppWizard par défaut.
Dans la première étape de ce port, l'activation sur place a été désactivée par substitution de COleClientItem::CanActivate. Cette dérogation devrait être enlevée pour permettre l'activation sur place. En outre, NULL a été adoptée pour tous les appels de DoVerb (il en existe deux d'entre eux) parce que fournir que le point de vue n'était nécessaire pour l'activation sur place. Pour mettre en oeuvre l'activation sur place, il est nécessaire de passer l'affichage correct dans l'appel de DoVerb . Un de ces appels est en CMainView::OnInsertObject
pItem-> ; DoVerb(OLEIVERB_SHOW, this)
Un autre est en CMainView::OnLButtonDblClk
m_pSelection-> ; DoVerb(OLEIVERB_PRIMARY, this)
Il est nécessaire de substituer COleClientItem::OnGetItemPosition. Cela indique au serveur où mettre sa fenêtre par rapport à la fenêtre du conteneur lorsque l'élément est activé en place. OCLIENT, la mise en œuvre est trivial
vOID CRectItem::OnGetItemPosition (CRect& ; rPosition)
{
rPosition = m_rect ;
}
La plupart des serveurs également implémentent ce qu'on appelle « place redimensionnement. » Cela permet à la fenêtre serveur d'être de taille et s'installe alors que l'utilisateur est modifier l'élément. Le conteneur doit participer à cette action, car déplaçant ou en redimensionnant la fenêtre habituellement affecte la position et la taille dans le document conteneur lui-même. La mise en œuvre d'OCLIENT synchronise le rectangle intérieur maintenu par m_rect avec la nouvelle position et la taille.
BOOL CRectItem::OnChangeItemPosition(const CRectamp; rectPos)
{
ASSERT_VALID(this) ;
if (!.COleClientItem::OnChangeItemPosition(rectPos))
Return FALSE ;
Invalidate() ;
m_rect = rectPos ;
Invalidate() ;
GetDocument() - > SetModifiedFlag() ;
Return TRUE ;
}
À ce stade, il y a suffisamment de code pour permettre à un élément d'être activé en place et s'occuper de dimensionnement et de déplacement de l'élément lorsqu'il est actif, mais il n'y a aucun code qui permettra à l'utilisateur de quitter la session d'édition. Bien que certains serveurs offrira cette fonctionnalité eux-mêmes en manipulant la touche d'échappement, il est suggéré que conteneurs permettent de désactiver un élément de deux façons: (1) en cliquant à l'extérieur de l'élément et (2) en appuyant sur la touche Echap.
Pour l'évasion clé ajouter un accélérateur avec Visual C++ qui mappe les clés VK_ESCAPE pour une commande, ID_CANCEL_EDIT est ajoutée aux ressources. Le gestionnaire d'événements pour cette commande suit:
/ / Le gestionnaire de commandes suivante fournit la norme
/ / clavier d'interface utilisateur pour annuler un place
/ / montage session.void CMainView::OnCancelEdit()
{
nbsp ; / / Fermer tout élément actif en place sur ce point de vue.
COleClientItem * pActiveItem = GetDocument() - > GetInPlaceActiveItem(this) ;
Si (pActiveItem! = NULL)
pActiveItem - > Close() ;
Assert(GetDocument()-> GetInPlaceActiveItem(this) == NULL) ;
}
Pour gérer le cas où l'utilisateur clique à l'extérieur de l'élément, vous ajoutez le code suivant au début de CMainView::SetSelection
si (pNewSel! = m_pSelection || pNewSel == NULL)
{
nbsp ; COleClientItem * pActiveItem = GetDocument() - > GetInPlaceActiveItem(this) ;
Si (pActiveItem! = NULL & & pActiveItem! = pNewSel)
pActiveItem - > Close() ;
}
& nbsp
Lorsqu'un élément est actif sur place, il devrait avoir le focus. Pour vous assurer que c'est le cas, vous gérer OnSetFocus afin que l'accent est toujours transféré vers l'élément actif lorsque votre point de vue reçoit le focus:
/ / Spéciale de OnSetFocus et OnSize sont requis / / quand un objet est modifié sur place.
VOID CMainView::OnSetFocus (CWnd * pOldWnd)
{
nbsp ; COleClientItem * pActiveItem = GetDocument() - > GetInPlaceActiveItem(this) ;
Si (pActiveItem! = NULL & &
pActiveItem - > GetItemState() == COleClientItem::activeUIState)
{
/ / nécessaire définir le focus à ce point si c'est le même point de vue
PWnd CWnd * = pActiveItem - > GetInPlaceWindow() ;
Si (pWnd! = NULL)
{
pWnd - > SetFocus() ; / / ne pas appeler la classe de base
retour ;
}
}
CView::OnSetFocus(pOldWnd) ;
}
Quand la vue est redimensionnée, vous devez aviser l'élément actif que le rectangle de découpage a été modifié. Pour ce faire, vous fournissez un gestionnaire d'événements pour OnSize:
vOID CMainView::OnSize (UINT nType, cx int, int cy)
{
nbsp ; CView::OnSize (nType, cx, cy) ;
COleClientItem * pActiveItem = GetDocument() - > GetInPlaceActiveItem(this) ;
Si (pActiveItem! = NULL)
pActiveItem - > SetItemRects() ;
}
Étude de cas : HIERSVR de MFC 2.0
HIERSVR a également été incluse dans MFC 2.0 et mis en œuvre OLE avec MFC/OLE1. Cette note décrit brièvement les étapes par laquelle cette demande a été initialement converti pour utiliser les classes MFC/OLE 2. Un certain nombre de fonctionnalités ont été ajouté après que le port initial a été réalisé afin de mieux illustrer les classes MFC/OLE 2. Ces caractéristiques ne seront pas couverts ici ; se référer à l'échantillon lui-même pour plus d'informations sur ces fonctionnalités avancées.
&Notenbsp ; Le processus étape par étape et les erreurs du compilateur a été créé avec Visual C++ 2.0. Emplacements et les messages d'erreur spécifiques peuvent avoir changé avec Visual C++ 4.0, mais les informations conceptuelles demeurent valides.
Il se lever et en cours d'exécution
L'approche adoptée pour l'exemple HIERSVR à MFC/OLE de port est de commencer par construire et réparer les erreurs du compilateur évident qui entraîneront. Si vous prenez l'exemple HIERSVR de MFC 2.0 et le compilez sous cette version de MFC, vous trouverez qu'il n'y a pas beaucoup d'erreurs pour résoudre (même s'il y a plus d'avec l'exemple OCLIENT). Les erreurs dans l'ordre dans lequel ils surviennent habituellement sont décrits ci-dessous.
Compilation et correction des erreurs
\hiersvr\hiersvr.cpp(83) : erreur C2039: « RunEmbedded »: n'est pas membre de « COleTemplateServer »
Cette première erreur signale un problème beaucoup plus grand avec la fonction InitInstance de serveurs. L'initialisation requise pour un serveur OLE est probablement l'un des plus grands changements que vous devrez apporter à votre application MFC/OLE1 pour qu'il exécute. La meilleure chose à faire est de regarder ce que AppWizard crée un serveur OLE et modifier votre code comme il convient. Voici quelques points à garder à l'esprit:
Il est nécessaire d'initialiser les bibliothèques OLE en appelant AfxOleInit
Appelez SetServerInfo sur l'objet de modèle de document pour définir les poignées de ressource de serveur et les informations de la classe runtime que vous ne pouvez pas définir avec le constructeur CDocTemplate.
Ne pas afficher la fenêtre principale de votre application si /Embedding est présent sur la ligne de commande.
Vous aurez besoin d'un GUID pour votre document. Il s'agit d'un identificateur unique pour le type de votre document (128 bits). AppWizard va créer un pour vous, alors, si vous utilisez la technique décrite ici de copier le nouveau code d'une application serveur AppWizard générée, vous pouvez simplement « voler » le GUID de l'application. Si ce n'est pas le cas, vous pouvez utiliser le GUIDGEN.Utilité de l'EXE dans le répertoire BIN.
Il est nécessaire de « brancher » votre objet COleTemplateServer pour le modèle de document en appelant COleTemplateServer::ConnectTemplate.
Mise à jour le registre du système lorsque votre application est exécutée en autonome. De cette façon, si l'utilisateur déplace le.EXE de votre application, il exécute à son nouvel emplacement mettra à jour la base de données d'enregistrement de système de Windows pour pointer vers le nouvel emplacement.
Après l'application de tous ces changements basés sur ce qui crée des AppWizard pour InitInstance, InitInstance (et les GUID associé) de HIERSVR comme suit:
// 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 se réfère à un nouvel ID de ressource, IDR_HIERSVRTYPE_SRVR_EMB. C'est la ressource de menu à utiliser lorsqu'un document qui est incorporé dans un autre récipient est modifié. Dans MFC/OLE1, les éléments de menu spécifiques à un élément incorporé d'édition ont été modifiées à la volée. À l'aide d'une structure de menu tout à fait différente lorsque vous modifiez un élément incorporé au lieu d'éditer un document basé sur fichier rend beaucoup plus facile de fournir des interfaces utilisateur différentes pour ces deux modes distincts. Comme vous le verrez plus tard, une ressource menu entièrement distinct est utilisée pour modifier un objet incorporé en place.
Pour créer cette ressource, charger le script de ressources dans Visual C++ et copier la ressource existante du menu IDR_HIERSVRTYPE. Renommez la nouvelle ressource en IDR_HIERSVRTYPE_SRVR_EMB (c'est la même convention de nommage qui utilise AppWizard). Ensuite changer « Fichier enregistrer » « Fichier de mise à jour » ; Il donne les ID de commande ID_FILE_UPDATE. Également changer « Fichier enregistrer sous » pour « Fichier enregistrer la copie sous » ; Il donne les ID de commande ID_FILE_SAVE_COPY_AS. Le cadre prévoit la mise en œuvre de deux de ces commandes.
\hiersvr\svritem.h(60) : erreur C2433: « OLESTATUS »: « virtuelle » non autorisées sur les déclarations des données
\hiersvr\svritem.h(60) : erreur C2501: « OLESTATUS »: manquantes à spécificateurs de decl
\hiersvr\svritem.h(60) : erreur C2146 : erreur de syntaxe : portés disparus «; » avant l'identificateur « OnSetData »
\hiersvr\svritem.h(60) : erreur C2061 : erreur de syntaxe : identificateur « OLECLIPFORMAT »
\hiersvr\svritem.h(60) : erreur C2501: « OnSetData »: manquantes à spécificateurs de decl
Il y a un certain nombre d'erreurs résultant de la substitution OnSetData, puisqu'il fait référence au type OLESTATUS . OLESTATUS était la façon qu'ole1 retourne des erreurs. HRESULT OLE 2, cela a changé bien que MFC convertit habituellement un HRESULT dans un COleException contenant l'erreur. Dans ce cas particulier, la substitution de OnSetData n'est plus nécessaire, donc la chose la plus facile à faire pour le supprimer.
\hiersvr\svritem.cpp(30) : erreur C2660: « COleServerItem::COleServerItem »: fonction ne prend pas de paramètres 1
Le constructeur de COleServerItem prend un paramètre supplémentaire de 'BOOL'. Cet indicateur détermine comment la gestion de la mémoire se faite sur les objets de COleServerItem . En affectant TRUE, le framework gère la gestion de la mémoire de ces objets — supprimer lorsqu'ils ne sont plus nécessaires. HIERSVR utilise des objets CServerItem (dérivée de COleServerItem) dans le cadre de ses données natif, donc vous allez définir cet indicateur sur FALSE. Cela permet de HIERSVR déterminer quand chaque élément de serveur est supprimé.
\hiersvr\svritem.cpp(44) : erreur C2259: « CServerItem »: tentative illégale d'instancier la classe abstraite
\hiersvr\svritem.cpp(44) : erreur C2259: « CServerItem »: tentative illégale d'instancier la classe abstraite
Comme ces erreurs, il y a certaines fonctions « pures virtuelles » qui n'ont pas été substituées dans CServerItem. Cela est probablement causé par le fait que la liste des paramètres de OnDraw a changé. Pour corriger cette erreur, modifiez comme suit CServerItem::OnDraw (ainsi que la déclaration de svritem.h)
BOOL CServerItem::OnDraw(CDC* pDC, CSizeamp; rSize)
{
/ demande de OLE pour dessiner le nœud
pDC - > SetMapMode(MM_TEXT) ; / / toujours en pixels
Return DoDraw (pDC, CPoint(0,0), FALSE) ;
}
Le nouveau paramètre est « rSize ». Cela vous permet de remplir la taille du dessin, si commode. Cette taille doit être en dixièmes de millimètres. Dans ce cas, il n'est pas commode remplir cette valeur, donc le framework appelle OnGetExtent pour récupérer l'étendue. Pour cela à travailler, vous devrez implémenter OnGetExtent:
BOOL CServerItem::OnGetExtent(DV&ASPECT dwDrawAspect, CSizeamp; rSize)
{
Si (dwDrawAspect! = DVASPECT_CONTENT)
Return COleServerItem::OnGetExtent (dwDrawAspect, rSize) ;
rSize = CalcNodeSize() ;
Return TRUE ;
}
\hiersvr\svritem.cpp(104) : erreur C2065: « m_rectBounds »: identificateur non déclaré
\hiersvr\svritem.cpp(104) : erreur C2228 : gauche de '.SetRect' doit être de type class/struct/union
\hiersvr\svritem.cpp(106) : erreur C2664: ' nul __pascal __far DPtoLP (struct:: tagPOINT __far *, int) __far const ': Impossible de convertir le paramètre 1 de ' int __far *' à ' struct:: tagPOINT __far *'
Dans la CServerItem::CalcNodeSize fonction de la taille de l'élément est convertie en dixièmes de millimètres et stockée dans m_rectBounds. Les membres de sans papiers 'm_rectBounds' de COleServerItem n'existe pas (il a été partiellement remplacé par m_sizeExtent, mais en OLE 2, ce membre a un usage un peu différent que m_rectBounds en OLE1). Au lieu de définir la taille HIMETRIC dans cette variable de membre, vous aurez le retour. Cette valeur de retour est utilisée dans OnGetExtent, mis en œuvre précédemment.
CSize CServerItem::CalcNodeSize()
{
nbsp ; CClientDC dcScreen(NULL) ;
m_sizeNode = dcScreen.GetTextExtent (m_strDescription,
m_strDescription.GetLength()) ;
m_sizeNode += CSize (CX_INSET * CY_INSET 2, * 2) ;
/ / définir la taille de HIMETRIC suggérée
CSize taille (m_sizeNode.cx, m_sizeNode.cy) ;
dcScreen.SetMapMode(MM_HIMETRIC) ;
dcScreen.DPtoLP(&size) ;
retour de taille ;
}
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 de MFC 3.0 de l'exemple MFC OLE HIERSVR implémente cette fonctionnalité par substitution de COleServerItem::OnRenderFileData. Cette fonctionnalité n'est pas importante pour ce port base, donc vous pouvez supprimer la substitution OnGetTextData.
Il y a beaucoup plus d'erreurs dans la svritem.cpp qui n'ont pas été abordés. Ils ne sont pas des erreurs de « vrais » — quelques erreurs causées par les erreurs passées.
\hiersvr\svrview.cpp(325) : erreur C2660: « CopyToClipboard »: fonction ne prend pas de 2 paramètres
COleServerItem::CopyToClipboard soutient n'est plus le drapeau de « bIncludeNative ». Les données natives (les données écrites par la fonction Serialize de l'élément serveur) sont toujours copiées, afin de supprimer 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()
{
nbsp ; Si (m_pSelectedNode == NULL)
AfxThrowNotSupportedException() ;
ESSAYEZ
{
m_pSelectedNode - > CopyToClipboard(TRUE) ;
}
CATCH_ALL(e)
{
AfxMessageBox ("copie au presse-papiers n'a pas") ;
}
END_CATCH_ALL}
Bien qu'il n'y a plus d'erreurs résultant de la compilation de la version 2.0 de MFC de HIERSVR, qu'il y avait de la même version de OCLIENT, il y a effectivement moins des changements.
À ce stade HIERSVR compiler et lier et fonctionner comme un serveur OLE, mais sans la fonction d'édition en place, qui sera ensuite appliquée.
Ajout de « Édition visuelle »
Pour ajouter « Édition visuelle » (ou l'activation sur place) à cette application de serveur, il y a seulement quelques choses que vous devez prendre soin de:
Les ressources de menu sont facile à créer. Exécutez Visual C++, copier la ressource menu IDR_HIERSVRTYPE à une ressource menu appelée IDR_HIERSVRTYPE_SRVR_IP. Modifier le menu de sorte que seulement les popups de menu Edit et aide sont laissés. Ajouter deux séparateurs au menu entre les menus modifier et aide (ça devrait ressembler : Edit || Aide). Pour plus d'informations sur ce que signifient ces séparateurs et comment les menus de serveur et de conteneur sont fusionnés, voir « Menus et ressources : fusion de menus » dans Les Classes OLE 2.
L'image bitmap pour la barre d'outils du sous-ensemble peut être facilement créé par copie celui d'une nouvelle demande de AppWizard généré avec l'option « Serveur » cochée. Ce bitmap peut ensuite être importé dans Visual C++. N'oubliez pas de donner l'image bitmap, un ID de IDR_HIERSVRTYPE_SRVR_IP.
La classe dérivée COleIPFrameWnd peut être copiée à partir d'une application AppWizard générée avec l'appui de serveur ainsi. Copiez les deux fichiers, IPFRAME.RPC et IPFRAME.H et les ajouter au projet. Assurez-vous que l'appel LoadBitmap fait référence à IDR_HIERSVRTYPE_SRVR_IP, l'image bitmap créé à l'étape précédente.
Maintenant que toutes les classes et nouvelles ressources sont créées, ajoutez le code nécessaire pour que le cadre connaît ces (et il sait que maintenant, cette application prend en charge la modification sur place). Ceci est accompli en ajoutant que certains paramètres plus à la SetServerInfo appellent à la fonction InitInstance:
pDocTemplate-> ;SetServerInfo (IDR_HIERSVRTYPE_SRVR_EMB,
IDR_HIERSVRTYPE_SRVR_IP, RUNTIME_CLASS(CInPlaceFrame))
Il est maintenant prêt à exécuter sur place dans un conteneur qui prend également en charge l'activation sur place. Mais il y a un bug mineur toujours rôdant dans le code. HIERSVR prend en charge un menu contextuel, s'affiché lorsque l'utilisateur appuie sur le bouton droit de la souris. Ce menu fonctionne lorsque HIERSVR est complètement ouvert, mais ne fonctionne pas lorsque vous modifiez un enrobage en place. La raison peut être coincée à cette seule ligne de code dans CServerView::OnRButtonDown
pMenu-gt ;TrackPopupMenu(TPM_CENTERALIGN | TPM_RIGHTBUTTON,
point.x, point.y, AfxGetApp() - > m_pMainWnd)
Notez la référence à AfxGetApp ()-gt ; m_pMainWnd. Lorsque le serveur est activé en place, elle a une fenêtre principale et m_pMainWnd est défini, mais il est généralement invisible. En outre, cette fenêtre se réfère à la fenêtre principale de l'application, la fenêtre frame MDI qui s'affiche lorsque le serveur est complètement ouvert ou exécutez autonome. Il ne fait pas référence à la fenêtre frame actif — qui place activé est une fenêtre frame dérivée COleIPFrameWnd. Pour obtenir la bonne fenêtre active même lorsque la place d'édition, cette version de MFC ajoute une nouvelle fonction, AfxGetMainWnd. En règle générale, vous devez utiliser cette fonction au lieu de AfxGetApp() - > m_pMainWnd. Ce code doit changer comme suit:
pMenu-gt ;TrackPopupMenu(TPM_CENTERALI&GN | TPM_RIGHTBUTTON,
point.x, point.y, AfxGetMainWnd())
Vous avez maintenant un serveur OLE activé minimale pour l'activation in-place fonctionnelle. Mais il existe encore des nombreuses fonctionnalités disponibles avec 2 MFC/OLE qui n'étaient pas disponibles dans MFC/OLE1. Voir l'exemple HIERSVR pour plus d'idées sur les fonctionnalités, que vous pouvez mettre en œuvre. Certaines fonctionnalités qui implémente HIERSVR sont énumérés ci-dessous:
L'exemple HIERSVR dans MFC 3.0 utilise également une conception légèrement différente pour les éléments de son serveur. Cela aide à conserver la mémoire et rend vos liens plus flexible. Avec la version 2.0 de HIERSVR chaque nœud de l'arborescence est un COleServerItem. COleServerItem comporte un peu plus frais que ce qui est strictement nécessaire pour chacun de ces nœuds, mais un COleServerItem est requise pour chaque lien actif. Mais pour l'essentiel, il y a très peu de liens actifs à un moment donné. Pour rendre cela plus efficace, la HIERSVR dans cette version de MFC sépare le nœud de la COleServerItem. Elle a la fois une CServerNode et une classe CServerItem . Les CServerItem (dérivé de COleServerItem) n'est créé que si nécessaire. Une fois le contenant (ou conteneurs) cesser d'utiliser ce lien particulier de ce nœud particulier, l'objet CServerItem associé à la CServerNode est supprimé. Cette conception est plus efficace et plus souple. Sa flexibilité vient lorsqu'il s'agit des liens de sélection multiple. Aucune de ces deux versions de HIERSVR prend en charge la sélection multiple, mais il serait beaucoup plus facile d'ajouter (et de soutenir les liens vers ces sélections) avec la version de MFC 3.0 de HIERSVR, puisque le COleServerItem est séparé des données natives.
&Notes techniques par le numéro |nbsp ; Notes techniques par catégorie