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 im Zusammenhang mit der Migration
Eines der Entwurfsziele für die OLE 2-Klassen in MFC 2.5 (und höher) bestand darin, einen Großteil der gleichen Architektur beizubehalten, die in MFC 2.0 für die OLE 1.0-Unterstützung bereitgestellt wurde. Daher sind viele der gleichen OLE-Klassen in MFC 2.0 in dieser Version von MFC (COleDocument
, COleServerDoc
, COleClientItem
, COleServerItem
) noch vorhanden. Darüber hinaus sind viele der APIs in diesen Klassen identisch. OLE 2 unterscheidet sich jedoch drastisch von OLE 1.0, sodass Sie davon ausgehen können, dass sich einige details geändert haben. Wenn Sie mit der OLE1-Unterstützung von MFC 2.0 vertraut sind, fühlen Sie sich mit der Unterstützung von MFC 2.0 zu Hause.
Wenn Sie eine vorhandene MFC/OLE1-Anwendung verwenden und ihr OLE 2-Funktionen hinzufügen, sollten Sie diese Notiz zuerst lesen. In diesem Hinweis werden einige allgemeine Probleme behandelt, die beim Portieren Ihrer OLE1-Funktionalität zu MFC/OLE 2 auftreten können, und dann werden die Probleme erläutert, die beim Portieren von zwei Anwendungen in MFC 2.0 auftreten: die MFC OLE-Beispiele OCLIENT und HIERSVR.
MFC-Dokument-/Ansichtsarchitektur ist wichtig
Wenn Ihre Anwendung die Dokument-/Ansichtsarchitektur von MFC nicht verwendet und Sie ihrer Anwendung OLE 2-Unterstützung hinzufügen möchten, ist jetzt der Zeitpunkt, um zu Dokument/Ansicht zu wechseln. Viele der Vorteile der OLE 2-Klassen von MFC werden erst realisiert, wenn Ihre Anwendung die integrierte Architektur und Komponenten von MFC verwendet.
Das Implementieren eines Servers oder Containers ohne Verwendung der MFC-Architektur ist möglich, wird jedoch nicht empfohlen.
Verwenden sie die MFC-Implementierung anstelle Ihrer eigenen
MFC-Klassen "canned implementation" wie CToolBar
, CStatusBar
, und CScrollView
verfügen über integrierte Sonderfallcode für DIE OLE 2-Unterstützung. Wenn Sie diese Klassen also in Ihrer Anwendung verwenden können, profitieren Sie von dem Aufwand, den Sie in diese Anwendungen umsetzen, um sie auf OLE aufmerksam zu machen. Auch hier ist es möglich, für diese Zwecke "eigene" Klassen zu "rollieren", aber es wird nicht vorgeschlagen. Wenn Sie ähnliche Funktionen implementieren müssen, ist der MFC-Quellcode ein hervorragender Verweis für einige der feineren OLE-Punkte (insbesondere bei der direkten Aktivierung).
Untersuchen des MFC-Beispielcodes
Es gibt eine Reihe von MFC-Beispielen, die OLE-Funktionen enthalten. Jede dieser Anwendungen implementiert OLE aus einem anderen Blickwinkel:
HIERSVR Ist hauptsächlich für die Verwendung als Serveranwendung vorgesehen. Es wurde in MFC 2.0 als MFC/OLE1-Anwendung enthalten und wurde zu MFC/OLE 2 portiert und dann so erweitert, dass es viele in OLE 2 verfügbare OLE-Features implementiert.
OCLIENT Dies ist eine eigenständige Containeranwendung, die viele der OLE-Features aus Containersicht demonstrieren soll. Es wurde ebenfalls von MFC 2.0 portiert und dann erweitert, um viele der erweiterten OLE-Features wie benutzerdefinierte Zwischenablageformate und Links zu eingebetteten Elementen zu unterstützen.
DRAWCLI Diese Anwendung implementiert OLE-Containerunterstützung ähnlich wie OCLIENT, mit der Ausnahme, dass dies im Rahmen eines vorhandenen objektorientierten Zeichnungsprogramms der Fall ist. Es zeigt Ihnen, wie Sie die OLE-Containerunterstützung implementieren und in Ihre vorhandene Anwendung integrieren können.
SUPERPAD Diese Anwendung ist auch ein OLE-Server und eine feine eigenständige Anwendung. Die Serverunterstützung, die implementiert wird, ist ziemlich minimalistisch. Besonders interessant ist, wie OLE-Zwischenablagedienste zum Kopieren von Daten in die Zwischenablage verwendet werden, aber die in das Windows-Steuerelement "Bearbeiten" integrierten Funktionen zum Einfügen von Zwischenablage verwenden. Dies zeigt eine interessante Mischung aus herkömmlicher Windows-API-Verwendung sowie Integration in die neuen OLE-APIs.
Weitere Informationen zu den Beispielanwendungen finden Sie in der "MFC-Beispielhilfe".
Fallstudie: OCLIENT von MFC 2.0
Wie oben beschrieben, wurde OCLIENT in MFC 2.0 enthalten und OLE mit MFC/OLE1 implementiert. Die Schritte, mit denen diese Anwendung anfänglich in die Verwendung der MFC/OLE 2-Klassen konvertiert wurde, werden unten beschrieben. Eine Reihe von Features wurden hinzugefügt, nachdem der erste Port abgeschlossen wurde, um die MFC/OLE-Klassen besser zu veranschaulichen. Diese Features werden hier nicht behandelt; weitere Informationen zu diesen erweiterten Features finden Sie im Beispiel selbst.
Hinweis
Die Compilerfehler und der schrittweise Prozess wurden mit Visual C++ 2.0 erstellt. Bestimmte Fehlermeldungen und Speicherorte haben sich möglicherweise mit Visual C++ 4.0 geändert, aber die konzeptionellen Informationen sind gültig Standard.
Vorbereiten und Ausführen
Der Ansatz zum Portieren des OCLIENT-Beispiels zu MFC/OLE besteht darin, mit der Erstellung zu beginnen und die offensichtlichen Compilerfehler zu beheben, die sich ergeben. Wenn Sie das OCLIENT-Beispiel von MFC 2.0 verwenden und unter dieser Version von MFC kompilieren, werden Sie feststellen, dass es nicht so viele Fehler gibt, die behoben werden müssen. Die Fehler in der Reihenfolge, in der sie aufgetreten sind, werden unten beschrieben.
Kompilieren und Beheben von Fehlern
\oclient\mainview.cpp(104) : error C2660: 'Draw' : function does not take 4 parameters
Der erste Fehler betrifft COleClientItem::Draw
. In MFC/OLE1 dauerte es mehr Parameter als die MFC/OLE-Version. Die zusätzlichen Parameter waren häufig nicht erforderlich und normalerweise NULL (wie in diesem Beispiel). Diese Version von MFC kann die Werte für lpWBounds automatisch bestimmen, wenn das CDC, auf das gezeichnet wird, eine Metadatei DC ist. Darüber hinaus ist der pFormatDC-Parameter nicht mehr erforderlich, da das Framework eins aus dem "Attribut DC" des übergebenen pDC erstellt. Um dieses Problem zu beheben, entfernen Sie einfach die beiden zusätzlichen NULL-Parameter zum Draw-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 '
Die obigen Fehler ergeben sich aus der COleClientItem::CreateXXXX
Tatsache, dass alle Funktionen in MFC/OLE1 erforderlich sind, dass ein eindeutiger Name übergeben wird, um das Element darzustellen. Dies war eine Anforderung der zugrunde liegenden OLE-API. Dies ist in MFC/OLE 2 nicht erforderlich, da OLE 2 DDE nicht als zugrunde liegenden Kommunikationsmechanismus verwendet (der Name wurde in DDE-Unterhaltungen verwendet). Um dieses Problem zu beheben, können Sie die CreateNewName
Funktion sowie alle Verweise darauf entfernen. Es ist einfach herauszufinden, was jede MFC/OLE-Funktion in dieser Version erwartet, indem Sie einfach den Cursor auf den Aufruf setzen und F1 drücken.
Ein anderer Bereich, der sich erheblich unterscheidet, ist die Behandlung von OLE 2-Zwischenablage. Mit OLE1 haben Sie die Windows-Zwischenablage-APIs mit der Zwischenablage interagiert. Mit OLE 2 erfolgt dies mit einem anderen Mechanismus. Die MFC/OLE1-APIs haben angenommen, dass die Zwischenablage geöffnet war, bevor ein COleClientItem
Objekt in die Zwischenablage kopiert wurde. Dies ist nicht mehr erforderlich und führt dazu, dass alle MFC/OLE-Zwischenablagevorgänge fehlschlagen. Während Sie den Code bearbeiten, um Abhängigkeiten CreateNewName
zu entfernen, sollten Sie auch den Code entfernen, der geöffnet wird und die Windows-Zwischenablage 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'
Diese Fehler ergeben sich aus dem CMainView::OnInsertObject
Handler. Die Behandlung des Befehls "Neues Objekt einfügen" ist ein weiterer Bereich, in dem sich die Dinge ziemlich geändert haben. In diesem Fall ist es am einfachsten, die ursprüngliche Implementierung mit der von AppWizard für eine neue OLE-Containeranwendung bereitgestellten zusammenzuführen. Tatsächlich ist dies eine Technik, die Sie zum Portieren anderer Anwendungen anwenden können. In MFC/OLE1 haben Sie das Dialogfeld "Objekt einfügen" durch Aufrufen AfxOleInsertDialog
der Funktion angezeigt. In dieser Version erstellen Sie ein COleInsertObject
Dialogobjekt und rufen sie auf DoModal
. Darüber hinaus werden neue OLE-Elemente mit einer CLSID anstelle einer Klassennamenzeichenfolge erstellt. Das Endergebnis sollte ungefähr wie folgt 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ügen" kann für Ihre Anwendung unterschiedlich sein):
Es ist auch erforderlich, afxodlgs.h> einzuschließen<, das die Deklaration für die COleInsertObject
Dialogklasse sowie die anderen Standarddialoge enthält, die von MFC bereitgestellt 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 dadurch verursacht, dass sich einige OLE1-Konstanten in OLE 2 geändert haben, auch wenn sie im Konzept identisch sind. In diesem Fall OLEVERB_PRIMARY
hat sich geändert in OLEIVERB_PRIMARY
. Sowohl in OLE1 als auch in OLE 2 wird das primäre Verb in der Regel von einem Container ausgeführt, wenn der Benutzer auf ein Element doppelklippelt.
Darüber hinaus DoVerb
wird nun ein zusätzlicher Parameter verwendet – ein Zeiger auf eine Ansicht (CView
*). Dieser Parameter wird nur verwendet, um "Visuelle Bearbeitung" (oder direkte Aktivierung) zu implementieren. Für den Moment legen Sie diesen Parameter auf NULL fest, da Sie dieses Feature zurzeit nicht implementieren.
Um sicherzustellen, dass das Framework niemals versucht, die Direkte Aktivierung zu aktivieren, sollten Sie folgendes überschreiben COleClientItem::CanActivate
:
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 sie SetBounds
verwendet, COleClientItem::GetBounds
um den Umfang eines Elements abzufragen und zu bearbeiten (die left
Elemente top
waren immer null). In MFC/OLE 2 wird dies direkt von COleClientItem::GetExtent
und SetExtent
unterstützt, die sich mit einer GRÖßE oder CSize
stattdessen befassen.
Der Code für ihren neuen 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 MFC/OLE1 wurden synchrone API-Aufrufe von einem Container zu einem Server simuliert, da OLE1 in vielen Fällen inhärent asynchron war. Es war erforderlich, vor dem Verarbeiten von Befehlen des Benutzers nach einem ausstehenden asynchronen Aufruf zu suchen. MFC/OLE1 hat dazu die COleClientItem::InWaitForRelease
Funktion bereitgestellt. In MFC/OLE 2 ist dies nicht erforderlich, sodass Sie die Außerkraftsetzung von OnCommand in CMainFrame zusammen entfernen können.
An diesem Punkt wird OCLIENT kompiliert und verknüpft.
Weitere erforderliche Änderungen
Es gibt jedoch einige Dinge, die nicht ausgeführt werden, damit OCLIENT nicht ausgeführt wird. Es ist besser, diese Probleme jetzt statt später zu beheben.
Zunächst ist es erforderlich, die OLE-Bibliotheken zu initialisieren. Dies erfolgt durch Aufrufen AfxOleInit
von InitInstance
:
if (!AfxOleInit())
{
AfxMessageBox("Failed to initialize OLE libraries");
return FALSE;
}
Es empfiehlt sich auch, auf virtuelle Funktionen für Parameterlistenänderungen zu überprüfen. Eine solche Funktion ist COleClientItem::OnChange
in jeder MFC/OLE-Containeranwendung überschrieben. Wenn Sie sich die Onlinehilfe ansehen, sehen Sie, dass ein zusätzlicher "DWORD dwParam" hinzugefügt wurde. Das neue CRectItem::OnChange sieht wie folgt aus:
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 haben Containeranwendungen die Dokumentklasse von COleClientDoc
. In MFC/OLE 2 wurde diese Klasse entfernt und ersetzt durch COleDocument
(diese neue Organisation erleichtert das Erstellen von Container-/Serveranwendungen). Es gibt eine #define , die COleClientDoc
der COleDocument
Portierung von MFC/OLE1-Anwendungen zu MFC/OLE 2, z. B. OCLIENT, zugeordnet ist. Eines der Features, die von COleDocument
diesem Feature nicht bereitgestellt COleClientDoc
wurden, ist die standardmäßige Befehlsmeldungszuordnungseinträge. Dies geschieht so, dass Serveranwendungen, die auch (indirekt) verwenden COleDocument
, nicht den Aufwand dieser Befehlshandler tragen, es sei denn, sie sind eine Container-/Serveranwendung. Sie müssen der CMainDoc-Nachrichtenzuordnung die folgenden Einträge 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 all dieser Befehle ist die COleDocument
Basisklasse für Ihr Dokument.
Zu diesem Zeitpunkt ist OCLIENT eine funktionale OLE-Containeranwendung. Es ist möglich, Elemente eines beliebigen Typs (OLE1 oder OLE 2) einzufügen. Da der erforderliche Code zum Aktivieren der direkten Aktivierung nicht implementiert ist, werden Elemente in einem separaten Fenster ähnlich wie bei OLE1 bearbeitet. Im nächsten Abschnitt werden die erforderlichen Änderungen erläutert, um die direkte Bearbeitung zu aktivieren (manchmal auch als "Visuelle Bearbeitung" bezeichnet).
Hinzufügen von "Visuelle Bearbeitung"
Eines der interessantesten Features von OLE ist die direkte Aktivierung (oder "Visuelle Bearbeitung"). Mit diesem Feature kann die Serveranwendung Teile der Benutzeroberfläche des Containers übernehmen, um dem Benutzer eine nahtlose Bearbeitungsoberfläche zur Verfügung zu stellen. Um die direkte Aktivierung in OCLIENT zu implementieren, müssen einige spezielle Ressourcen sowie zusätzlichen Code hinzugefügt werden. Diese Ressourcen und der Code werden normalerweise von AppWizard bereitgestellt – tatsächlich wurde ein Großteil des codes hier direkt von einer neuen AppWizard-Anwendung mit "Container"-Unterstützung geliehen.
Zunächst ist es erforderlich, eine Menüressource hinzuzufügen, die verwendet werden soll, wenn ein Element aktiv ist. Sie können diese zusätzliche Menüressource in Visual C++ erstellen, indem Sie die IDR_OCLITYPE-Ressource kopieren und alle Popups "Datei" und "Fenster" entfernen. Zwei Trennleisten werden zwischen den Popups "Datei" und "Fenster" eingefügt, um die Trennung von Gruppen anzugeben (es sollte wie folgt aussehen: File || Window
). Weitere Informationen dazu, was diese Trennzeichen bedeuten und wie die Server- und Containermenüs zusammengeführt werden, finden Sie unter Menüs und Ressourcen: Menüzusammenführung.
Nachdem Sie diese Menüs erstellt haben, müssen Sie das Framework darüber informieren. Dazu rufen CDocTemplate::SetContainerInfo
Sie die Dokumentvorlage auf, bevor Sie sie der Dokumentvorlagenliste in Ihrer InitInstance hinzufügen. Der neue Code zum Registrieren der Dokumentvorlage 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.
Zum Aktivieren der direkten Aktivierung gibt es einige Dinge, die sowohl in der CView
abgeleiteten Klasse (CMainView) als auch in der COleClientItem
abgeleiteten Klasse (CRectItem) geändert werden müssen. Alle diese Außerkraftsetzungen werden von AppWizard bereitgestellt, und die meisten Implementierungen stammen direkt aus einer Standardmäßig-AppWizard-Anwendung.
Im ersten Schritt dieses Ports wurde die direkte Aktivierung vollständig durch Außerkraftsetzung COleClientItem::CanActivate
deaktiviert. Diese Außerkraftsetzung sollte entfernt werden, um die direkte Aktivierung zu ermöglichen. Darüber hinaus wurde NULL an alle Aufrufe DoVerb
übergeben (es gibt zwei davon), da die Bereitstellung der Ansicht nur für die direkte Aktivierung erforderlich war. Um die direkte Aktivierung vollständig zu implementieren, ist es erforderlich, die richtige Ansicht im DoVerb
Aufruf zu übergeben. Einer dieser Anrufe ist in CMainView::OnInsertObject
:
pItem->DoVerb(OLEIVERB_SHOW, this);
Ein weiteres ist in CMainView::OnLButtonDblClk
:
m_pSelection->DoVerb(OLEIVERB_PRIMARY, this);
Es ist notwendig, außer Kraft zu setzen COleClientItem::OnGetItemPosition
. Dadurch wird dem Server mitgeteilt, wo das Fenster relativ zum Containerfenster platziert werden soll, wenn das Element aktiviert ist. Für OCLIENT ist die Implementierung trivial:
void CRectItem::OnGetItemPosition(CRect& rPosition)
{
rPosition = m_rect;
}
Die meisten Server implementieren auch die sogenannte "direkte Größenänderung". Dadurch kann die Größe des Serverfensters angepasst und verschoben werden, während der Benutzer das Element bearbeitet. Der Container muss an dieser Aktion teilnehmen, da sich das Verschieben oder Ändern der Größe des Fensters in der Regel auf die Position und Größe innerhalb des Containerdokuments selbst auswirkt. Die Implementierung für OCLIENT synchronisiert das interne Rechteck Standard durch m_rect mit der neuen Position und Größe.
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 genügend Code, um zuzulassen, dass ein Element aktiviert wird und sich mit der Größenanpassung und dem Verschieben des Elements befasst, wenn es aktiv ist, aber kein Code ermöglicht es dem Benutzer, die Bearbeitungssitzung zu beenden. Obwohl einige Server diese Funktionalität selbst durch Behandeln der Escapetaste bereitstellen, wird empfohlen, dass Container zwei Möglichkeiten zum Deaktivieren eines Elements bieten: (1) durch Klicken außerhalb des Elements und (2) durch Drücken der ESCAPE-TASTE.
Fügen Sie für die ESCAPE-Taste eine Zugriffstaste mit Visual C++ hinzu, die die VK_ESCAPE Taste einem Befehl zuordnet, ID_CANCEL_EDIT den Ressourcen hinzugefügt wird. Der Handler für diesen Befehl folgt:
// 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 klickt, fügen Sie den folgenden Code am Anfang von CMainView::SetSelection
:
if (pNewSel != m_pSelection || pNewSel == NULL)
{
COleClientItem* pActiveItem =
GetDocument()->GetInPlaceActiveItem(this);
if (pActiveItem != NULL&& pActiveItem != pNewSel)
pActiveItem->Close();
}
Wenn ein Element aktiv ist, sollte es den Fokus haben. Um sicherzustellen, dass dies der Fall ist, behandeln Sie OnSetFocus, damit der Fokus immer auf das aktive 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 Größe der Ansicht geändert wird, müssen Sie das aktive Element benachrichtigen, dass sich das Beschneidungsrechteck geändert hat. Dazu stellen Sie einen Handler für 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();
}
Fallstudie: HIERSVR von MFC 2.0
HIERSVR wurde auch in MFC 2.0 enthalten und OLE mit MFC/OLE1 implementiert. In diesem Hinweis werden die Schritte beschrieben, mit denen diese Anwendung zunächst in die MFC/OLE 2-Klassen konvertiert wurde. Eine Reihe von Features wurden hinzugefügt, nachdem der erste Port abgeschlossen wurde, um die MFC/OLE 2-Klassen besser zu veranschaulichen. Diese Features werden hier nicht behandelt; weitere Informationen zu diesen erweiterten Features finden Sie im Beispiel selbst.
Hinweis
Die Compilerfehler und der schrittweise Prozess wurden mit Visual C++ 2.0 erstellt. Bestimmte Fehlermeldungen und Speicherorte haben sich möglicherweise mit Visual C++ 4.0 geändert, aber die konzeptionellen Informationen sind gültig Standard.
Vorbereiten und Ausführen
Der Ansatz zum Portieren des HIERSVR-Beispiels zu MFC/OLE besteht darin, mit der Erstellung zu beginnen und die offensichtlichen Compilerfehler zu beheben, die dazu führen. Wenn Sie das HIERSVR-Beispiel von MFC 2.0 verwenden und unter dieser Version von MFC kompilieren, werden Sie feststellen, dass es nicht viele Fehler zu beheben gibt (obwohl es mehr als bei dem OCLIENT-Beispiel gibt). Die Fehler in der Reihenfolge, in der sie normalerweise auftreten, werden unten beschrieben.
Kompilieren und Beheben von Fehlern
\hiersvr\hiersvr.cpp(83) : error C2039: 'RunEmbedded' : is not a member of 'COleTemplateServer'
Dieser erste Fehler weist auf ein viel größeres Problem mit der InitInstance
Funktion für Server hin. Die für einen OLE-Server erforderliche Initialisierung ist wahrscheinlich eine der größten Änderungen, die Sie an Ihrer MFC/OLE1-Anwendung vornehmen müssen, um sie auszuführen. Am besten sollten Sie sich ansehen, was AppWizard für einen OLE-Server erstellt und ihren Code entsprechend ändert. Die folgenden Punkte müssen Sie beachten:
Es ist erforderlich, die OLE-Bibliotheken durch Aufrufen von AfxOleInit
Rufen Sie SetServerInfo für das Dokumentvorlagenobjekt auf, um Serverressourcenhandles und Laufzeitklasseninformationen festzulegen, die Sie nicht mit dem CDocTemplate
Konstruktor festlegen können.
Zeigen Sie das Standard Fenster Ihrer Anwendung nicht an, wenn /Embedding in der Befehlszeile vorhanden ist.
Sie benötigen eine GUID für Ihr Dokument. Dies ist ein eindeutiger Bezeichner für den Typ Ihres Dokuments (128 Bit). AppWizard erstellt eine für Sie – wenn Sie also die hier beschriebene Technik zum Kopieren des neuen Codes aus einer neuen appWizard-generierten Serveranwendung verwenden, können Sie einfach die GUID aus dieser Anwendung "stehlen". Wenn nicht, können Sie das Hilfsprogramm GUIDGEN.EXE im BIN-Verzeichnis verwenden.
Es ist erforderlich, das COleTemplateServer
Objekt mit der Dokumentvorlage zu verbinden, indem sie aufgerufen wird COleTemplateServer::ConnectTemplate
.
Aktualisieren Sie die Systemregistrierung, wenn Ihre Anwendung eigenständig ausgeführt wird. Wenn der Benutzer die EXE-Datei für Ihre Anwendung verschiebt, wird die Windows-Systemregistrierungsdatenbank von seinem neuen Speicherort aus aktualisiert, um auf den neuen Speicherort zu verweisen.
Nachdem Sie alle diese Änderungen basierend auf der Erstellung von AppWizard angewendet InitInstance
haben, sollte die InitInstance
(und zugehörige GUID) für HIERSVR wie folgt lauten:
// 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;
}
Sie werden feststellen, dass der obige Code auf eine neue Ressourcen-ID verweist, IDR_HIERSVRTYPE_SRVR_EMB. Dies ist die Menüressource, die verwendet werden soll, wenn ein Dokument, das in einen anderen Container eingebettet ist, bearbeitet wird. In MFC/OLE1 wurden die Menüelemente, die für die Bearbeitung eines eingebetteten Elements spezifisch sind, im Flug geändert. Die Verwendung einer völlig anderen Menüstruktur beim Bearbeiten eines eingebetteten Elements anstelle der Bearbeitung eines dateibasierten Dokuments erleichtert die Bereitstellung verschiedener Benutzeroberflächen für diese beiden separaten Modi. Wie Sie später sehen, wird beim Bearbeiten eines eingebetteten Objekts eine völlig separate Menüressource verwendet.
Laden Sie zum Erstellen dieser Ressource das Ressourcenskript in Visual C++ und kopieren Sie die vorhandene IDR_HIERSVRTYPE Menüressource. Benennen Sie die neue Ressource in IDR_HIERSVRTYPE_SRVR_EMB um (dies ist die gleiche Benennungskonvention, die AppWizard verwendet). Nächste Änderung "Datei speichern" in "Dateiaktualisierung"; befehls-ID ID_FILE_UPDATE. Ändern Sie auch "Datei speichern unter" in "Datei speichern unter"; 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 eine Reihe von Fehlern, die sich aus der Außerkraftsetzung OnSetData
ergeben, da sie auf den OLESTATUS-Typ verweist. OLESTATUS war die Art und Weise, wie OLE1 Fehler zurückgibt. Dies hat sich in OLE 2 in HRESULT geändert, obwohl MFC in der Regel ein HRESULT in einen COleException
mit dem Fehler konvertiert. In diesem speziellen Fall ist die Außerkraftsetzung OnSetData
nicht mehr erforderlich, daher ist es am einfachsten zu tun, es 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 die Speicherverwaltung für die COleServerItem
Objekte erfolgt. Durch Festlegen auf "TRUE" behandelt das Framework die Speicherverwaltung dieser Objekte , und löscht sie, wenn sie nicht mehr benötigt werden. HIERSVR verwendet CServerItem
(abgeleitet von COleServerItem
) Objekten als Teil der systemeigenen Daten, sodass Sie dieses Kennzeichen auf FALSE festlegen. Auf diese Weise kann HIERSVR bestimmen, wann 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 bedeuten, gibt es einige "reine virtuelle" Funktionen, die in CServerItem nicht überschrieben wurden. Dies wird wahrscheinlich durch die Tatsache verursacht, dass sich die Parameterliste von OnDraw 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". Auf diese Weise können Sie die Größe der Zeichnung bei Bedarf ausfüllen. Diese Größe muss in HIMETRIC sein. In diesem Fall ist es nicht praktisch, diesen Wert auszufüllen, daher ruft OnGetExtent
das Framework den Umfang ab. Damit dies funktioniert, müssen Sie Folgendes implementieren 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 *'
In der CServerItem::CalcNodeSize-Funktion wird die Elementgröße in HIMETRIC konvertiert und in m_rectBounds gespeichert. Das nicht dokumentierte Element "m_rectBounds" COleServerItem
ist nicht vorhanden (es wurde teilweise durch m_sizeExtent ersetzt, aber in OLE 2 hat dieses Element eine etwas andere Verwendung als m_rectBounds in OLE1). Anstatt die HIMETRIC-Größe in diese Membervariable festzulegen, geben Sie sie zurück. Dieser Rückgabewert wird in OnGetExtent
, zuvor implementiert, verwendet.
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 COleServerItem::OnGetTextData
auch . Diese Funktion ist in MFC/OLE veraltet und wird durch einen anderen Mechanismus ersetzt. Die MFC 3.0-Version des MFC OLE-Beispiels HIERSVR implementiert diese Funktionalität durch Überschreiben COleServerItem::OnRenderFileData
. Diese Funktionalität ist für diesen grundlegenden Port nicht wichtig, sodass Sie die OnGetTextData-Außerkraftsetzung entfernen können.
Es gibt viele weitere Fehler in svritem.cpp, die nicht behoben wurden. Sie sind keine "echten" Fehler – nur Fehler, die durch vorherige Fehler verursacht werden.
\hiersvr\svrview.cpp(325) : error C2660: 'CopyToClipboard' : function does not take 2 parameters
COleServerItem::CopyToClipboard
unterstützt das bIncludeNative
Flag nicht mehr. Die systemeigenen Daten (die von der Serialize-Funktion des Serverelements geschriebenen Daten) werden immer kopiert, sodass Sie den ersten Parameter entfernen. Darüber hinaus löst eine Ausnahme aus, CopyToClipboard
wenn ein Fehler auftritt, 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 aufgrund der Kompilierung der MFC 2.0-Version von HIERSVR gab als für dieselbe Version von OCLIENT, gab es tatsächlich weniger Änderungen.
An diesem Punkt wird HIERSVR kompiliert und verknüpft und als OLE-Server funktionieren, jedoch ohne die direkte Bearbeitungsfunktion, die als nächstes implementiert wird.
Hinzufügen von "Visuelle Bearbeitung"
Um dieser Serveranwendung "Visuelle Bearbeitung" (oder direkte Aktivierung) hinzuzufügen, müssen Sie nur einige Dinge erledigen:
Sie benötigen eine spezielle Menüressource, die verwendet werden soll, wenn das Element aktiv ist.
Diese Anwendung verfügt über eine Symbolleiste, daher benötigen Sie eine Symbolleiste mit nur einer Teilmenge der normalen Symbolleiste, um den menübefehlen zu entsprechen, die vom Server verfügbar sind (entspricht der menüressource Erwähnung oben).
Sie benötigen eine neue Klasse, die von
COleIPFrameWnd
der die direkte Benutzeroberfläche bereitstellt (ähnlich wie CMainFrame, abgeleitet vonCMDIFrameWnd
, stellt die MDI-Benutzeroberfläche bereit).Sie müssen das Framework über diese speziellen Ressourcen und Klassen informieren.
Die Menüressource ist einfach zu erstellen. Führen Sie Visual C++ aus, kopieren Sie die Menüressource IDR_HIERSVRTYPE in eine Menüressource namens IDR_HIERSVRTYPE_SRVR_IP. Ändern Sie das Menü so, dass nur die Popups im Menü "Bearbeiten" und "Hilfe" verbleiben. Fügen Sie dem Menü zwei Trennzeichen zwischen den Menüs "Bearbeiten" und "Hilfe" hinzu (es sollte wie folgt aussehen: Edit || Help
). Weitere Informationen dazu, was diese Trennzeichen bedeuten und wie die Server- und Containermenüs zusammengeführt werden, finden Sie unter Menüs und Ressourcen: Menüzusammenführung.
Die Bitmap für die Untermengesymbolleiste kann einfach erstellt werden, indem die Datei aus einer neuen generierten AppWizard-Anwendung mit aktivierter Option "Server" kopiert wird. Diese Bitmap kann dann in Visual C++ importiert werden. Achten Sie darauf, der Bitmap eine ID von IDR_HIERSVRTYPE_SRVR_IP zuzuweisen.
Die von COleIPFrameWnd
einer appWizard generierte Anwendung kann auch mit Serverunterstützung kopiert werden. Kopieren Sie beide Dateien, IPFRAME. CPP und IPFRAME. H, und fügen Sie sie dem Projekt hinzu. Stellen Sie sicher, dass sich der LoadBitmap
Aufruf auf IDR_HIERSVRTYPE_SRVR_IP bezieht, die im vorherigen Schritt erstellte Bitmap.
Nachdem nun alle neuen Ressourcen und Klassen erstellt wurden, fügen Sie den erforderlichen Code hinzu, damit das Framework über diese Informationen informiert ist (und weiß, dass diese Anwendung jetzt die direkte Bearbeitung unterstützt). Dazu fügen Sie dem Aufruf in der SetServerInfo
InitInstance
Funktion einige weitere Parameter hinzu:
pDocTemplate->SetServerInfo(IDR_HIERSVRTYPE_SRVR_EMB,
IDR_HIERSVRTYPE_SRVR_IP,
RUNTIME_CLASS(CInPlaceFrame));
Sie kann jetzt in jedem Container ausgeführt werden, der auch die direkte Aktivierung unterstützt. Es gibt jedoch noch einen kleinen Fehler, der im Code lauert. HIERSVR unterstützt ein Kontextmenü, das angezeigt wird, wenn der Benutzer die rechte Maustaste drückt. Dieses Menü funktioniert, wenn HIERSVR vollständig geöffnet ist, aber nicht beim Bearbeiten eines eingebetteten Elements funktioniert. Der Grund kann an diese einzelne Codezeile in CServerView::OnRButtonDown 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 aktiviert ist, hat er ein Standard Fenster und m_pMainWnd ist festgelegt, ist aber in der Regel unsichtbar. Darüber hinaus bezieht sich dieses Fenster auf das Standard Fenster der Anwendung, das MDI-Framefenster, das angezeigt wird, wenn der Server vollständig geöffnet oder eigenständig ausgeführt wird. Es bezieht sich nicht auf das aktive Framefenster , das bei aktivierter Position ein Framefenster ist, das von COleIPFrameWnd
. Um das richtige aktive Fenster auch bei der direkten Bearbeitung zu erhalten, fügt diese Version von MFC eine neue Funktion hinzu. AfxGetMainWnd
Im Allgemeinen sollten Sie diese Funktion anstelle von AfxGetApp()->m_pMainWnd
. Dieser Code muss wie folgt geändert werden:
pMenu->TrackPopupMenu(TPM_CENTERALIGN | TPM_RIGHTBUTTON,
point.x,
point.y,
AfxGetMainWnd());
Jetzt ist ein OLE-Server minimal für die funktionale In-Situ-Aktivierung aktiviert. Es gibt jedoch noch viele Features, die mit MFC/OLE 2 verfügbar waren, die in MFC/OLE1 nicht verfügbar waren. Weitere Ideen zu Features, die Sie implementieren möchten, finden Sie im HIERSVR-Beispiel. Einige der Features, die HIERSVR implementiert, sind nachfolgend aufgeführt:
Zoomen, für echtes WYSIWYG-Verhalten im Hinblick auf den Container.
Ziehen/Ablegen und ein benutzerdefiniertes Zwischenablageformat.
Scrollen des Containerfensters, während die Auswahl geändert wird.
Das HIERSVR-Beispiel in MFC 3.0 verwendet auch ein etwas anderes Design für seine Serverelemente. Dies trägt dazu bei, Arbeitsspeicher zu sparen und Ihre Verknüpfungen flexibler zu gestalten. Mit der 2.0-Version von HIERSVR ist jeder Knoten in der Struktur einsCOleServerItem
. COleServerItem
trägt einen etwas mehr Aufwand, als für jeden dieser Knoten unbedingt erforderlich ist, aber für jede aktive Verknüpfung ist eine COleServerItem
erforderlich. Aber meistens gibt es nur wenige aktive Links zu einem bestimmten Zeitpunkt. Um dies effizienter zu gestalten, trennt der HIERSVR in dieser Version von MFC den Knoten vom COleServerItem
. Es verfügt sowohl über einen CServerNode als auch über eine CServerItem
Klasse. Die CServerItem
(abgeleitet von COleServerItem
) wird nur nach Bedarf erstellt. Sobald der Container (oder Container) die Verwendung dieses bestimmten Links zu diesem bestimmten Knoten beendet hat, wird das dem CServerNode zugeordnete CServerItem-Objekt gelöscht. Dieses Design ist effizienter und flexibler. Seine Flexibilität kommt beim Umgang mit Mehrfachauswahllinks ins Ziel. Keine dieser beiden Versionen von HIERSVR unterstützt mehrfache Auswahl, aber es wäre viel einfacher, Links zu solchen Auswahlen mit der MFC 3.0-Version von HIERSVR hinzuzufügen (und links zu diesen Auswahlen zu unterstützen), da die Daten COleServerItem
von den systemeigenen Daten getrennt sind.
Siehe auch
Technische Hinweise – nach Nummern geordnet
Technische Hinweise – nach Kategorien geordnet