TN041: MFC/OLE1-Migration zu MFC/OLE 2
Hinweis
Der folgende technische Hinweis wurde seit dem ersten Erscheinen in der Onlinedokumentation nicht aktualisiert.Daher können einige Verfahren und Themen veraltet oder falsch sein.Um aktuelle Informationen zu erhalten, wird empfohlen, das gewünschte Thema im Index der Onlinedokumentation zu suchen.
Allgemeine Probleme hinsichtlich Migration
Eines der Entwurfsziele für die OLE 2 Klassen in MFC 2.5 (und höher), viel derselben Architektur beizubehalten, die in MFC 2.0 für die OLE 1.0 eingeführt wurde. Daher sind viele der gleichen OLE-Klassen in MFC 2.0 weiterhin in dieser Version von MFC (COleDocument, COleServerDoc, COleClientItem, COleServerItem). Außerdem wurden viele der APIs in diesen Klassen genau gleich. Allerdings ist OLE 2 zu OLE 1.0 deutlich anders, sodass Sie erwarten, dass einige der Details geändert haben. Wenn Sie mit von MFC 2.0 OLE1-Unterstützung vertraut sind, fühlen sich Sie zu Hause mit Unterstützung MFC 2.0.
Wenn Sie eine vorhandene Anwendung MFC/OLE1 erstellen und Funktionen OLE 2 hinzufügen, können Sie den Hinweis vertraut machen. Dieser Hinweis werden einige allgemeine Probleme, die möglicherweise auftreten, beim Portieren der Funktionalität OLE1 zu MFC/OLE 2 und die Probleme erläutert, die aufgedeckt werden, beim Portieren von zwei Anwendungen enthalten in MFC 2.0: MFC-OLE das überprüft OCLIENT und HIERSVR.
MFC-Dokument-/Ansichtsarchitektur ist wichtig
Wenn die Anwendung keine Dokument-/Ansichtarchitektur MFC verwendet und Sie Unterstützung OLE 2 der Anwendung hinzufügen möchten, ist nun die Zeit, in die Dokument/Ansicht zu bewegen. Viele der Vorteile von OLE 2-Klassen MFC werden nur realisiert, sobald die Anwendung integrierte die Architektur und die Komponenten von MFC verwendet.
Ein Server oder einem Container, ohne die MFC-Architektur verwenden, zu implementieren ist möglich, jedoch nicht empfohlen.
Implementierung der Verwendungs-MFC statt der Auch
MFC eingemachte "Implementierung" Klassen wie CToolBar, CStatusBar, und CScrollView sind integrierte Sonderfallcode für die OLE 2. Wenn Sie diese Klassen in der Anwendung verwenden können, profitieren Sie von dem Aufwand, in der sie eingefügt wird, um die OLE aufmerksam zu machen. Auch hier ist stattdessen "Rolle-Ihr-eigenen" Klassen hier zu diesen Zwecken möglich, jedoch nicht empfohlen. Wenn Sie ähnliche Funktionen implementieren müssen, ist der MFC-Quellcode ein ausgezeichneter Verweis zur Handhabung von mehrere der Feinheiten von OLE (insbesondere wenn es um direkte Aktivierung wird).
Überprüfen Sie den MFC-Beispielcode
Es gibt mehrere MFC-Beispiele, die OLE-Funktionalität einschließen. Jede dieser Anwendungen implementiert OLE von einem anderen Winkel:
HIERSVR bedeutete hauptsächlich zur Verwendung als Serveranwendung. Sie wurde in MFC 2.0 als Anwendung MFC/OLE1 enthalten und ist mit MFC/2 OLE portiert wurde und erweiterte dann so, dass diese viele OLE-Funktionen implementiert, die in OLE. 2 verfügbar sind.
OCLIENT Dieses ist eine eigenständige Containeranwendung, bedeutet, um zahlreiche OLE-Funktionen von einem Containerstandpunkt zu veranschaulichen. Außerdem wurde von MFC 2.0 Ports und erweiterte sich dann, um viele der erweiterte OLE-Funktionen, wie benutzerdefinierte Zwischenablageformate und Links zu eingebetteten Elementen zu unterstützen.
DRAWCLI Diese Anwendung implementiert OLE-Containerunterstützung ähnlich OCLIENT wird, außer dass dies jetzt im Rahmen eines vorhandenen objektorientierten Zeichnungsprogramms. Es zeigt, wie Sie möglicherweise OLE-Containerunterstützung implementieren und sie in die vorhandene Anwendung integrierten.
SUPERPAD Diese Anwendung sowie davon eine feine eigenständige Anwendung, ist ebenfalls ein OLE-Server. Die Serverunterstützung, die sie implementiert, ist recht unwichtig. Besonders interessant ist, wie es OLE-Zwischenablagedienstleistungen verwendet, um Daten in die Zwischenablage zu kopieren, verwendet jedoch die Funktionen, die in das Windows-" Bearbeitungs" Steuerelement erstellt wird, um Zwischenablagepastenfunktionalität zu implementieren. Dies zeigt eine interessante Mischung der herkömmlichen Windows-API-Verwendung sowie -Integration mit den neuen OLE-APIs an.
Weitere Informationen zur Beispielanwendungen, finden Sie in "MFC-Beispielhilfe".
Fallstudie: OCLIENT von MFC 2.0
Wie oben erläutert, wurde OCLIENT in MFC 2.0 enthalten und OLE mit MFC/OLE1 implementierte. Die Schritte, durch die Anwendung zuerst, die MFC Klassen/OLE 2 verwenden konvertiert wurde, sind unten beschrieben. Einige Funktionen hinzugefügt wurden, nachdem der ursprüngliche Port abgeschlossen wurde, die MFC/OLE-Klassen besser zu veranschaulichen. Diese Funktionen sind hier nicht behandelt; finden Sie das Beispiel auch weitere Informationen zu diesen erweiterten Funktionen an.
Hinweis
Die schrittweise Compilerfehler und der Prozess wurden mit Visual C++ 2.0.Bestimmte Fehlermeldungen und geändert mit Visual C++ 4.0, aber die grundlegende Informationen gültig bleiben.
Es abrufen ausgeführt
Der Ansatz, der gewählt wird, um das OCLIENT-Beispiel zu MFC/OLE zu portieren, ist, zu starten, indem er sie erstellt und der offensichtlichen Compilerfehler behoben, die sich ergeben. Wenn Sie das OCLIENT-Beispiel von MFC 2.0 entnehmen und es unter dieser Version von MFC kompilieren, suchen Sie, dass es nicht dass viele Fehler gibt, um das Problem zu beheben. Die Fehler in der Reihenfolge, in der sie aufgetreten sind, sind unten beschrieben.
Kompilieren und beheben Sie Fehler
\oclient\mainview.cpp(104) : error C2660: 'Draw' : function does not take 4 parameters
Der erste Fehler bezieht COleClientItem::Draw. In MFC/OLE1 dauerte die weitere Parameter als MFC/OLE-Versionsnehmen. Die zusätzlichen Parameter wurden häufig nicht notwendig und normalerweise NULL (wie in diesem Beispiel). Diese Version von MFC können die Werte für die lpWBounds automatisch feststellen, wenn das CDC, dem gezeichnet wird, ein Metadatei-DC ist. Außerdem ist der pFormatDC Parameter nicht mehr notwendig, da das Framework ein vom Domänencontroller "Attribut" pDC erstellt, das übergeben wird. , dieses Problem möglichst zu korrigieren, entfernen Sie einfach die zwei zusätzlichen fehlenden Parameter dem Zeichnen-Aufruf.
\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 '
Das obige Ergebnis der Fehler vom Tatsache dieses alle COleClientItem::CreateXXXX funktioniert in Erstellung, MFC/OLE1 dass ein eindeutiger Name übergeben wird, um das Element darzustellen. Dies war eine Anforderung der zugrunde liegenden OLE-API. Dies ist in MFC/2 OLE notwendig, da 2 OLE DDE nicht als zugrunde liegender Kommunikationsmechanismus verwendet (der Name wurde in DDE-Konversationen verwendet). Um dieses Problem zu beheben, können Sie die Funktion CreateNewName entfernen sowie alle Verweise darauf. Herauszufinden ist einfach, was eine MFC/OLE-Funktion in dieser Version erwartet einfach, indem Sie den Cursor auf dem Aufruf platziert und F1 drückt.
Ein anderer Bereich, der deutlich anders ist, Behandlung der Zwischenablage OLE 2. Mit OLE1 haben Sie die Windows-Zwischenablage APIs Interaktion mit der Zwischenablage. Bei OLE 2 wird dieses mit einem weiteren Mechanismus verwendet. Die APIs MFC/OLE1 haben an, dass die Zwischenablage geöffnet war, bevor sie ein COleClientItem-Objekt in die Zwischenablage kopierte. Dies ist nicht mehr notwendig und wird alle MFC/OLE-Zwischenablagevorgänge verursachen. Während Sie den Code bearbeiten, um Abhängigkeiten auf CreateNewName entfernen, sollten Sie den Code auch entfernen, der die Windows-Zwischenablage öffnet und schließt.
\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'
Ergebnis dieser Fehler vom CMainView::OnInsertObject-Handler. Der "Befehl" den neuen Objekts zu behandeln ist ein anderer Bereich, in dem Aufgaben über ein Bit geändert haben. In diesem Fall ist es am einfachsten, die Anfangsimplementierung mit dem einfach zusammenzuführen, das vom Anwendungs-Assistenten für eine neue OLE-Containeranwendung bereitgestellt wird. Tatsächlich stellt dieses eine Technik, die Sie zum Portieren anderer Anwendungen anwenden können. In MFC/OLE1 können Sie das "EINFG-Objekt" Dialogfeld an, indem Sie AfxOleInsertDialog-Funktion aufgerufen haben. In dieser Version erstellen Sie ein COleInsertObject Dialogfeldobjekt und rufen DoModal auf. Außerdem werden neue OLE-Elemente mit CLSID anstelle einer Klassennamenzeichenfolge erstellt. Das Endergebnis sollte etwa folgendermaßen aussehen
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();
Hinweis
Neues Objekt Einfüge- kann für die Anwendung) unterscheiden:
Außerdem muss, afxodlgs.h einschließen <, da>die Deklaration für die Dialogfeldklasse COleInsertObject sowie die anderen Standarddialogfelder enthält, die von MFC bereitgestellte werden.
\oclient\mainview.cpp(367) : error C2065: 'OLEVERB_PRIMARY' : undeclared identifier
\oclient\mainview.cpp(367) : error C2660: 'DoVerb' : function does not take 1 parameters
Diese Fehler werden durch die Tatsache verursacht, Konstanten ein OLE1 in OLE 2 geändert haben, obwohl im Konzept sie gleich sind. In diesem Fall hat OLEVERB_PRIMARY in OLEIVERB_PRIMARY geändert. in OLE1 und in OLE 2 wird der primäre Verb normalerweise durch einen Container ausgeführt, wenn der Benutzer auf ein Element doppelklicken.
Zudem nimmt DoVerb jetzt einen zusätzlichen Parameter - einen Zeiger auf eine Ansicht (CView*). Dieser Parameter wird nur verwendet, um "visuelle Bearbeitung" (bzw direkte Aktivierung) zu implementieren. Wichtig Sie dieses Parameter auf NULL, da Sie diese Funktion derzeit nicht implementieren.
Um sicherzustellen dass das Framework nie an direktem versucht Sie aktivieren, sollten Sie COleClientItem::CanActivate überschreiben wie folgt:
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 wurden COleClientItem::GetBounds und SetBounds verwendet, um den Umfang eines Elements Abfragen und Bearbeiten (die links und oben-Member waren immer null). In MFC/OLE 2 wird dieses direkt von COleClientItem::GetExtent und SetExtent unterstützt, die GRÖSSE oder stattdessen CSize verarbeiten.
Der Code für das neue SetItemRectToServer und UpdateItemRectFromServer-Aufrufe sehen wie folgt aus:
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
In den synchronen API-Aufrufen MFC/OLE1 von einem Container zu einem Server wurden simuliert, da OLE1 in vielen Fällen grundsätzlich asynchron war. So überprüfen war erforderlich, für einen ausstehenden asynchronen Aufruf, der vor Befehlen vom Benutzer ausgeführt wird, verarbeitet hat. MFC/OLE1 enthält die Funktion COleClientItem::InWaitForRelease hierfür bereit. In MFC/OLE 2 ist dies nicht erforderlich, sodass Sie die Überschreibung von OnCommand in CMainFrame alle zusammen entfernen.
Nun kompiliert OCLIENT und Links.
Andere notwendige Änderungen
Es gibt einige Aufgaben, die nicht vom OCLIENT, das ausgeführt wird, jedoch ausgeführt werden. Es ist besser, statt diese Probleme später jetzt zu korrigieren.
Zunächst ist es notwendig, die OLE-Bibliotheken zu initialisieren. Dies geschieht, indem AfxOleInit von InitInstance aufgerufen wird:
if (!AfxOleInit())
{
AfxMessageBox("Failed to initialize OLE libraries");
return FALSE;
}
Außerdem wird empfohlen, für virtuelle Funktionen für Parameterlistenänderungen zu überprüfen. Eine solche Funktion ist COleClientItem::OnChange überschrieben, in jeder MFC/OLE-Containeranwendung. Wenn Sie Online-Hilfe anschauen, sehen Sie, dass ein zusätzliches "DWORD dwParam" hinzugefügt wurde. Das neue CRectItem::OnChange sieht, wie folgt:
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 berechneten Containeranwendungen die Dokumentklasse von COleClientDoc. In MFC/OLE 2 ist diese Klasse entfernt und durch COleDocument ersetzt wurde (diese neue Organisation leichter, Container/Server-Anwendungen zu erstellen). Sie gibt #define, das COleClientDoc werden COleDocument zugeordnet, um Portieren von Anwendungen MFC/OLE1 zu MFC/2 OLE zu vereinfachen, z OCLIENT. Eine der Funktionen, die nicht von COleDocument, angegeben werden, die von COleClientDoc bereitgestellt wurde, ist die Standardbefehlsmeldungszuordnungseinträge. Dies geschieht, sodass Serveranwendungen, die auch COleDocument (indirekt), nicht mit diesen Mehraufwand dieser Befehlshandler tragen, es sei denn, sie eine bin/Server-Anwendung sind. Sie müssen die folgenden Einträge der CMainDoc-Meldungszuordnung hinzufügen:
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)
Die Implementierung aller Befehle diese ist in COleDocument, die die Basisklasse für das Dokument ist.
An diesem Punkt wurde OCLIENT eine funktionale OLE-Containeranwendung. Es ist möglich, Elemente beliebigen Typ einzufügen (OLE1 oder OLE 2). Da der notwendige Code, um von direkter Aktivierung zu aktivieren nicht implementiert wird, werden Elemente in einem separaten Fenster ähnlich wie mit OLE1 bearbeitet. Der nächste Abschnitt erläutert die erforderlichen Änderungen, um die direkte Bearbeitung zu aktivieren (manchmal als "visuelle Bearbeiten").
Visueller "Bearbeiten" hinzufügen
Eines der interessantesten Features von OLE ist direkte Aktivierung (oder "visuelle Bearbeiten"). Diese Funktion kann die Serveranwendung, Teile der Benutzeroberfläche des Containers anzuwenden bereitgestellte eine nahtlosere Bearbeitungsschnittstelle für den Benutzer. Um direkte Aktivierung eingeschlossen OCLIENT zu implementieren, müssen mehrere spezielle Ressourcen hinzugefügt werden sowie jeder zusätzlichen Code. Diese Ressourcen und die Code werden normalerweise vom Anwendungs-Assistenten bereitgestellt - tatsächlich wurde ein Großteil des Codes hier direkt von einer neuen Anwendungs-Assistenten-Anwendung mit "Container" Unterstützung ausgeborgt.
Zunächst ist es notwendig, eine zu verwendende Menüressource hinzuzufügen, wenn ein Element gibt, das direkt aktiv ist. Sie können diese zusätzlichen Menüressource in Visual C++ erstellen, indem Sie nur die IDR_OCLITYPE-Ressource kopieren und die Datei- und Fensterpopups entfernen. Zwei Trennlinien werden zwischen den Datei- und Fensterpopups eingefügt, um der Trennung von Gruppen angeben (sollte in etwa so aussehen: Datei | | Fenster). Weitere Informationen darüber, was diese Trennzeichen bedeuten und wie die Server- und Containermenüs zusammengeführt werden, finden Sie unter "Menüs und Ressourcen: Menüzusammenführung" in OLE 2-Klassen.
Sobald diese Menüs erstellen haben, müssen Sie das Framework informieren darüber bereit. Dies geschieht, indem CDocTemplate::SetContainerInfo für die Normal-Vorlage aufruft, bevor Sie sie der Dokumentvorlagenliste im InitInstance hinzufügen. Der neue Code, z der Normal-Vorlage zu registrieren sieht wie folgt aus:
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);
Die IDR_OLECLITYPE_INPLACE-Ressource ist die spezielle direkte Ressource, die in Visual C++ erstellt wurde.
Um direkte Aktivierung zu aktivieren, gibt es einige Punkte die in der abgeleiteten Klasse CView (CMainView) sowie in der abgeleiteten COleClientItem-Klasse (CRectItem) ändern müssen. Diese Überschreibungen werden vom Anwendungs-Assistenten bereitgestellt und die meisten Implementierungsfunktionen stammen direkt von einer Standard Anwendungs-Assistenten-Anwendung.
Im ersten Schritt dieser Ports, direkte Aktivierung wurde vollständig deaktiviert, indem COleClientItem::CanActivate hat. Diese Überschreibung muss entfernt werden, um die direkte Aktivierung zuzulassen. Zusätzlich wurden NULL an alle Aufrufe an DoVerb übergeben (es gibt zwei davon), da, die Ansicht bereitzustellen für direkte Aktivierung nur erforderlich war. Um direkte Aktivierung vollständig zu implementieren, ist es notwendig die ordnungsgemäße Ansicht im DoVerb - Aufruf übergeben. Einer dieser Aufrufe ist in CMainView::OnInsertObject:
pItem->DoVerb(OLEIVERB_SHOW, this);
Anders ist in CMainView::OnLButtonDblClk:
m_pSelection->DoVerb(OLEIVERB_PRIMARY, this);
Es ist erforderlich, COleClientItem::OnGetItemPosition zu überschreiben. Dies weist dem Server mit, wo der Fenster an der des Containers wird, wenn das Element aktiviert direkt ist. Für OCLIENT ist die Implementierung angezeigt:
void CRectItem::OnGetItemPosition(CRect& rPosition)
{
rPosition = m_rect;
}
Die meisten Server implementieren auch, was wird aufgerufen "direkte Größenanpassung." Dadurch können das Skalieren und verschoben wurde, Serverfenster, während der Benutzer das Element bearbeitet. Der Container muss an dieser Aktion teilnehmen, seit das Verschieben oder das Ändern der Größe des Fensters, beeinflusst normalerweise die Position und die Größe innerhalb des Containerdokuments selbst. Die Implementierung für OCLIENT synchronisiert das interne Rechteck, das durch m_rect mit der neuen Größe und Position beibehalten.
BOOL CRectItem::OnChangeItemPosition(const CRect& rectPos)
{
ASSERT_VALID(this);
if (!COleClientItem::OnChangeItemPosition(rectPos))
return FALSE;
Invalidate();
m_rect = rectPos;
Invalidate();
GetDocument()->SetModifiedFlag();
return TRUE;
}
An diesem Punkt, gibt es ausreichend Code, ein Element ermöglichen, direktes zu aktivierenden und Größenanpassung und das Verschieben des Elements zu begegnen, wenn er aktiv ist, jedoch kein Code ermöglicht dem Benutzer, die Bearbeitung beenden. Obwohl mehrere Server diese Funktion selbst vom Behandeln der Escape-Taste bereitstellen, wird es vorgeschlagen, dass Container bieten zwei Möglichkeiten, ein Element zu deaktivieren: (1) durch Klicken außerhalb des Elements und (2) durch das Drücken der ESC-TASTE.
Für die ESC-TASTE fügen Sie eine Zugriffstaste in Visual C++, die der VK_ESCAPE-Taste zu einem Befehl zugeordnet ist, ID_CANCEL_EDIT wird hinzugefügt Ressourcen hinzu. Der Handler für diesen Befehl aus:
// 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);
}
Um den Fall zu behandeln in dem der Benutzer außerhalb des Elements klicken, fügen Sie folgenden Code am Anfang von CMainView::SetSelection hinzu:
if (pNewSel != m_pSelection || pNewSel == NULL)
{
COleClientItem* pActiveItem =
GetDocument()->GetInPlaceActiveItem(this);
if (pActiveItem != NULL && pActiveItem != pNewSel)
pActiveItem->Close();
}
Wenn ein Element direkt aktiv ist, sollte es den Fokus besitzt. Um dies sicherzustellen ist der Fall, den Sie OnSetFocus behandeln damit Fokus immer zum aktiven Element übertragen wird wenn die Ansicht den Fokus erhält:
// 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);
}
Wenn die Ansicht Größe geändert wird, muss das aktive Element benachrichtigen, dass das Ausschneiderechteck geändert hat. Hierzu erstellen Sie einen Handler für OnSize bereit:
void CMainView::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
COleClientItem* pActiveItem =
GetDocument()->GetInPlaceActiveItem(this);
if (pActiveItem != NULL)
pActiveItem->SetItemRects();
}
Fallstudie: HIERSVR von MFC 2.0
HIERSVR wurde auch MFC 2.0 enthalten und OLE mit MFC/OLE1 implementierte. Dieser Hinweis wird kurz die Schritte, durch die Anwendung zuerst, die MFC Klassen/OLE 2 verwenden konvertiert wurde. Einige Funktionen hinzugefügt wurden, nachdem der ursprüngliche Port abgeschlossen wurde, um die Klassen von MFC 2/OLE besser zu veranschaulichen. Diese Funktionen sind hier nicht behandelt; finden Sie das Beispiel auch weitere Informationen zu diesen erweiterten Funktionen an.
Hinweis
Die schrittweise Compilerfehler und der Prozess wurden mit Visual C++ 2.0.Bestimmte Fehlermeldungen und geändert mit Visual C++ 4.0, aber die grundlegende Informationen gültig bleiben.
Es abrufen ausgeführt
Der Ansatz, der gewählt wird, um das HIERSVR-Beispiel zu MFC/OLE zu portieren, ist, zu starten, indem er sie erstellt und der offensichtlichen Compilerfehler behoben, die sich ergeben. Wenn Sie das HIERSVR-Beispiel von MFC 2.0 entnehmen und es unter dieser Version von MFC kompilieren, suchen Sie, dass es nicht viele Fehler gibt, z zu beheben (obwohl es mehrere mit dem OCLIENT-Beispiel vorhanden ist). Die Fehler in der Reihenfolge, in der sie normalerweise vorkommen, sind unten beschrieben.
Kompilieren und beheben Sie Fehler
\hiersvr\hiersvr.cpp(83) : error C2039: 'RunEmbedded' : is not a member of 'COleTemplateServer'
Dieser erste Fehler zeigt auf ein viel größer Problem mit der Funktion InitInstance für Server. Die Initialisierung, die für einen OLE-Server erforderlich ist, ist wahrscheinlich eine der größten Änderungen, die Sie der Anwendung MFC/OLE1 vornehmen müssen, sie abzurufen ausgeführt. Am besten legen zu erreichen, zu überlegen, was Anwendungs-Assistent für einen OLE-Server erstellt und ändert den Code entsprechend. sprechen, Punkte zu beachten:
Es ist erforderlich, die OLE-Bibliotheken zu initialisieren, indem AfxOleInit aufruft
Aufruf SetServerInfo auf dem Dokumentvorlagenobjekt zu den festgelegten Serverressourcenhandles und den Ablaufklasseninformationen, die Sie mit dem CDocTemplate-Konstruktor festlegen können.
Anzeigen nicht das Hauptfenster der Anwendung, wenn /Embedding auf der Befehlszeile vorhanden ist.
Sie erfordern Guid für das Dokument. Dies ist ein eindeutiger Bezeichner für die Eingaben des Dokuments (128 Bits). Der Anwendungs-Assistent erstellt ein für Sie - so, wenn Sie die Methode verwenden, die hier dem Kopieren des neuen Codeblöcke einer neuer Anwendungs-Assistent generierten Serveranwendung beschrieben wird, können Sie die GUID aus der Anwendung "einfach stehlen". Wenn nicht, können Sie das GUIDGEN.EXE-Hilfsprogramm im Verzeichnis Bin verwenden.
Es ist "verbunden" das COleTemplateServer-Objekt der Normal-Vorlage erforderlich, indem COleTemplateServer::ConnectTemplate aufgerufen wird.
Aktualisieren Sie die Systemregistrierung, wenn die Anwendung ausgeführt eigenständiges ist. Auf diese Weise, wenn der Benutzer die EXE-Datei der Anwendung verschoben, von seiner neuen Position ausgeführt aktualisiert die Windows-Systemregistrierungsdatenbank, um es auf den neuen Speicherort angepasst wird.
Nach dem Anwenden alle diese, wird entsprechend, was Anwendungs-Assistent für InitInstance, InitInstance (und verwandte GUID) für HIERSVR sollte lesen erstellt, wie folgt:
// 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;
}
Beachten Sie, dass der Code oben eine neue Ressourcen-ID verweist, IDR_HIERSVRTYPE_SRVR_EMB. Dies ist die zu verwendende Menüressource, wenn ein Dokument, das in einem anderen Container eingebettet wird, bearbeitet wird. In MFC/OLE1 wurden die Menüelemente, die z eingebettetes Elements bestimmt sind, selbst geändert. Verwenden eines vollständig anderen Menüs Struktur, wann das eingebettetes Elements, anstatt, ein Basisdokument zu bearbeiten, ist es viel einfacher, verschiedenen Schnittstellen des Benutzers für diese beiden Modi separaten bereitzustellen. Wenn Sie später finden, wird eine Menüressource separate vollständig verwendet, wenn Sie ein direktes eingebettetes Objekt bearbeitet.
Um diese Ressource zu erstellen, können Sie das Ressourcenskript in Visual C++ und kopieren Sie die vorhandene IDR_HIERSVRTYPE-Menüressource. Nennen Sie die neue Ressource auf IDR_HIERSVRTYPE_SRVR_EMB um (dies ist die gleiche Namenskonvention, die Anwendungs-Assistenten-Verwendung). Als Nächstes ändern Sie "Datei speichern" in "Datei aktualisieren;" geben Sie ihm Befehls-ID ID_FILE_UPDATE. Ändern Sie außerdem "Datei speichern unter" in "Dateikopie speichern unter"; geben Sie ihm Befehls-ID ID_FILE_SAVE_COPY_AS. Das Framework stellt die Implementierung beider Befehle bereit.
\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
Es gibt mehrere Fehler, erstellten der Überschreibung von OnSetData, da sie den OLESTATUS-Typ. OLESTATUS war der zurückgegebenen Fehler der Methode OLE1. Dies hat zu HRESULT in OLE 2 geändert, obwohl MFC normalerweise HRESULT in COleException konvertiert, das den Fehler enthält. In diesem besonderen Fall wird die Überschreibung von OnSetData nicht mehr erforderlich, sodass es das Einfachste, sie zu entfernen.
\hiersvr\svritem.cpp(30) : error C2660: 'COleServerItem::COleServerItem' : function does not take 1 parameters
Der COleServerItem-Konstruktor akzeptiert einen zusätzlichen "BOOL-" Parameter. Dieses Flag bestimmt, wie Speicherverwaltung in den COleServerItem-Objekten ausgeführt. Wenn es auf True festlegen, behandelt das Framework die Speicherverwaltung dieser Objekte - sie löschen, wenn sie nicht mehr notwendig sind. HIERSVR verwendet Objekte CServerItem (abgeleitet von COleServerItem) als Teil der systemeigene Daten, sodass Legen Sie dieses Flag auf FALSE fest. Dadurch können HIERSVR bestimmen, wenn jedes Serverelement gelöscht wird.
\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
Da diese Fehler bedeutet, sind mehrere "rein-virtuelle" Funktionen, die nicht in CServerItem überschrieben wurden. Höchstwahrscheinlich wird dies mit der Tatsache verursacht, den OnDraws Parameterliste geändert hat. Um diesen Fehler zu beheben, ändern Sie CServerItem::OnDraw wie folgt (sowie die Deklaration 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);
}
Der neue Parameter ist "rSize". Dies ermöglicht es Ihnen, die Größe der Zeichnung zu füllen, wenn zweckmäßig. Diese Größe muss in HIMETRIC sein. In diesem Fall ist es nicht sinnvoll, diesen Wert auszufüllen, sodass ruft das Framework OnGetExtent auf, um das Ausmaß abzurufen. Damit das funktioniert, müssen Sie OnGetExtent implementieren:
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 *'
In der CServerItem::CalcNodeSize-Funktion wird die Elementgröße HIMETRIC ergibt und gespeichert in m_rectBounds. Der nicht dokumentierten 'm_rectBounds' Member von COleServerItem ist nicht vorhanden (diese ist teilweise durch m_sizeExtent ersetzt, jedoch in OLE 2 verfügt dieser Member eine etwas andere Verwendungen, als m_rectBounds in OLE1 hat). Anstatt, die HIMETRIC-Membervariable Größe in dieser zurück festzulegen, geben Sie sie. Dieser Rückgabewert wird in OnGetExtent verwendet, bereits implementiert.
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 überschreibt auch COleServerItem::OnGetTextData. Diese Funktion ist in MFC/OLE veraltet und wird durch einen anderen Mechanismus ersetzt. Die MFC 3.0-Version des Beispiels HIERSVR MFC-OLE implementiert diese Funktionalität, indem COleServerItem::OnRenderFileData überschreibt. Diese Funktionalität ist nicht für diesen grundlegenden Port wichtig, damit Sie die OnGetTextData-Überschreibung entfernen.
Es gibt noch zahlreiche Fehler in svritem.cpp, die nicht behandelt wurden. Sie sind nicht "echten" Fehler - nur die Fehler, die durch vorherige Fehler auftreten.
\hiersvr\svrview.cpp(325) : error C2660: 'CopyToClipboard' : function does not take 2 parameters
COleServerItem::CopyToClipboard unterstützt nicht mehr das "bIncludeNative" Flag. Die systemeigene Daten (die Daten, die vom Serverelement ausgeschrieben werden, Serialisierung Funktion), werden immer, das Sie entfernen den ersten Parameter kopiert. Außerdem löst CopyToClipboard eine Ausnahme aus, wenn ein Fehler passiert, anstatt, FALSE zurückzugeben. Ändern Sie den Code für CServerView::OnEditCopy, wie folgt:
void CServerView::OnEditCopy()
{
if (m_pSelectedNode == NULL)
AfxThrowNotSupportedException();
TRY
{
m_pSelectedNode->CopyToClipboard(TRUE);
}
CATCH_ALL(e)
{
AfxMessageBox("Copy to clipboard failed");
}
END_CATCH_ALL
}
Obwohl es mehr Fehler hat, erstellten von der Kompilierung der MFC 2.0-Version von HIERSVR, als er für die gleiche Version von OCLIENT gab, gab es tatsächlich weniger Änderungen.
Nun kompiliert HIERSVR und Links und fungiert als OLE-Server, aber ohne die direkten Bearbeiten, die als Nächstes implementiert wird.
Visueller "Bearbeiten" hinzufügen
Um "visueller Bearbeitung" (bzw direkter Aktivierung) dieser Serveranwendung hinzuzufügen, sind nur einige Punkte aufgeführt, die Sie um zu kümmern müssen:
Sie benötigen eine spezielle verwendet werden Menüressource, wenn das Element direkt aktiv ist.
Diese Anwendung verfügt über eine Symbolleiste, folglich benötigen Sie eine Symbolleiste mit nur eine Teilmenge der normalen Symbolleiste, um die Menübefehle entsprechen, die vom Server verfügbar sind (Entspricht die Menüressource oben).
Sie benötigen eine neue Klasse, die von COleIPFrameWnd abgeleitet wird, die beim direkten Benutzeroberfläche bereitstellt (ähnlich wie CMainFrame, abgeleitet von CMDIFrameWnd, stellt die MDI-Benutzeroberfläche bereit).
Sie müssen das Framework über diese besondere Ressourcen und Klassen finden.
Die Menüressource ist leicht erstellen. Führen Sie Visual C++ aus, kopieren Sie die Menüressource IDR_HIERSVRTYPE an eine Menüressource, die IDR_HIERSVRTYPE_SRVR_IP aufgerufen wird. Ändern Sie das Menü, damit nur die Bearbeitungs- und Menü Hilfe-Popups verbleiben. Fügen Sie zwei Trennzeichen Menü zwischen der Bearbeitung und den Menü Hilfe hinzu (sollte in etwa so aussehen: Bearbeiten | | Hilfe). Weitere Informationen darüber, was diese Trennzeichen bedeuten und wie die Server- und Containermenüs zusammengeführt werden, finden Sie unter "Menüs und Ressourcen: Menüzusammenführung" in OLE 2-Klassen.
Die Bitmap für die Teilmengensymbolleiste kann leicht erstellt werden, indem das einer neuer Anwendungs-Assistent generierten Anwendung mit einer überprüften "wurde Server" Option kopiert. Diese Bitmap kann in Visual C++ anschließend importiert werden. Stellen Sie sicher, der Bitmap eine ID von IDR_HIERSVRTYPE_SRVR_IP zu geben.
Die Klasse, die von COleIPFrameWnd abgeleitet ist, kann von einer Anwendung generierten Anwendungs-Assistent mit Serverunterstützung auch kopiert werden. Kopieren Sie beide Dateien, IPFRAME.CPP und IPFRAME.H und fügen Sie sie dem Projekt hinzu. Stellen Sie sicher IDR_HIERSVRTYPE_SRVR_IP Aufruf der LoadBitmap verweist, um die Bitmap erstellt im vorherigen Schritt.
Nachdem alle neuen Ressourcen und Klassen erstellt werden, fügen Sie den erforderlichen Code hinzu, sodass das Framework in diesen auskennt (und weiß, dass die Anwendung jetzt direkte Bearbeiten unterstützt). Dies geschieht, indem mehr Parameter auf SetServerInfo Aufruf in der InitInstance-Funktion:
pDocTemplate->SetServerInfo(IDR_HIERSVRTYPE_SRVR_EMB,
IDR_HIERSVRTYPE_SRVR_IP, RUNTIME_CLASS(CInPlaceFrame));
Es ist nun, direktes in einem Container ausgeführt, der auch direkte Aktivierung unterstützt. Aber, es gibt einen kleinen Fehler, der weiterhin im Code lauert. HIERSVR unterstützt ein Kontextmenü, angezeigt, wenn der Benutzer die rechte Maustaste drückt. Dieses Menü funktioniert, wenn HIERSVR vollständig geöffnet ist, aber funktioniert nicht, wenn es eine direkte einbettende bearbeitet. Der Grund kann dieser einzelnen Codezeile in CServerView::OnRButtonDown unten angeheftet werden:
pMenu->TrackPopupMenu(TPM_CENTERALIGN | TPM_RIGHTBUTTON,
point.x, point.y, AfxGetApp()->m_pMainWnd);
Beachten Sie den Verweis auf AfxGetApp()->m_pMainWnd. Wenn der Server direktes aktiviert ist, verfügt sie ein Hauptfenster und m_pMainWnd wird festgelegt, aber es ist normalerweise nicht sichtbar. Außerdem verweist auf dieses Fenster das Hauptfenster der Anwendung, das MDI-Rahmenfenster dargestellt, die angezeigt wird, wenn der die eigenständig Server vollständig geöffnet oder Ausführung ist. Es werden nicht das aktive Rahmenfenster an - das, wenn direkt aktiv, ein Rahmenfenster ist, das von COleIPFrameWnd abgeleitet wird. Um das korrekte aktuelle Fenster, wenn direkte Bearbeiten, diese Version von MFC, eine neue Funktion hinzufügt AfxGetMainWnd abrufen. Im Allgemeinen sollten Sie diese Funktion anstelle AfxGetApp()->m_pMainWnd verwenden. Dieser Code muss ändern, wie folgt:
pMenu->TrackPopupMenu(TPM_CENTERALIGN | TPM_RIGHTBUTTON,
point.x, point.y, AfxGetMainWnd());
Sie haben einen OLE-Server, der nur für funktionale direkte Aktivierung aktiviert ist. Es gibt noch viele Funktionen, die mit MFC/OLE 2 verfügbar sind, die nicht in MFC/OLE1 verfügbar waren. Siehe das HIERSVR-Beispiel für mehr Ideen auf Funktionen, die Sie implementieren. Einige der Funktionen, die implementiert HIERSVR, sind nachfolgend aufgeführt:
Bei true, WYSISYG-Verhalten in Bezug auf den Container vergrößern.
Drag & Drop-Datenbindung und ein benutzerdefiniertes Zwischenablageformat.
Beim Scrollen des Containerfensters als die Auswahl geändert wird.
HIERSVR in MFC 3.0 verwendet auch einen etwas anderen Entwurf für seine Serverelemente. Dadurch wird Speicherplatz gespart, und werden die Links flexibler. Bei der Version 2.0 von HIERSVR *ist-ein * jeder Knoten in der Struktur COleServerItem. COleServerItem wirkt ein bisschen mehr Aufwand, als zwingend erforderlich für jeden dieser Knoten ist, COleServerItem ist jedoch für jeden aktiven Link erforderlich. In den meisten Fällen, gibt es sehr wenige aktive Verbindung zu einem beliebigen Zeitpunkt. Um dieses effizientere auszuführen, trennt das HIERSVR in dieser Version von MFC den Knoten von COleServerItem. Es verfügt ein CServerNode und eine CServerItem-Klasse. CServerItem (abgeleitet von COleServerItem) ist nur bei Bedarf erstellt. Sobald der Unterbrechung des Containers (oder Container) mithilfe dieses bestimmten Links zu diesem bestimmten Knoten, das CServerItem-Objekt, das dem CServerNode zugeordnet ist, deaktiviert ist. Dieser Entwurf ist effizienter und flexibler. Seine Flexibilität erreichen bei der Behandlung von Mehrfachauswahllinks. Keine dieser zwei Versionen der HIERSVR-Stützmehrfachauswahl, jedoch werden viel einfacher, (und den Links zu dieser Auswahl) mit der 3.0-Version MFC von HIERSVR hinzuzufügen umfassen, da COleServerItem aus systemeigenen Daten getrennt wird.