Allgemeine Fragen im Zusammenhang mit Migration
Die Entwurfsziele für die OLE 2 Klassen in MFC 2.5 (oder höher) gehörte, viel von der gleichen Architektur geschaffen für OLE-1.0-Unterstützung in MFC 2.0 zu behalten. Infolgedessen gibt es viele der gleichen OLE-Klassen in MFC 2.0 noch in dieser Version von MFC (COleDocument COleServerDoc, COleClientItem, COleServerItem). Darüber hinaus sind viele der APIs in diesen Klassen genau das gleiche. Allerdings ist OLE 2 drastisch anders als OLE-1.0, so können Sie erwarten, dass einige Details geändert haben. Wenn Sie mit MFC 2.0 OLE1 Unterstützung vertraut sind, fühlen Sie zu Hause mit MFC 2.0 Unterstützung.
Wenn Sie eine vorhandene Anwendung MFC/OLE1 nehmen und OLE 2 Funktionalität hinzufügen, sollten Sie zuerst diese Anmerkung lesen. Dieser Hinweis betrifft einige allgemeine Probleme, die Sie bei der Portierung der Funktionalität Ihres OLE1 auf MFC/OLE 2 können auftreten und dann beschreibt die Probleme, die beim Portieren von zwei Anwendungen enthalten MFC 2.0 entdeckt: die MFC-OLE-Beispiele OCLIENT und HIERSVR.
MFC-Dokument-/ Ansichtarchitektur ist wichtig
Wenn Ihre keine Dokument-/Ansichtarchitektur von MFC Anwendung und OLE 2-Unterstützung für Ihre Anwendung hinzufügen möchten, ist jetzt die Zeit, zu Dokument/Ansicht wechseln. Viele der Vorteile von MFC OLE 2 Klassen werden nur realisiert, sobald Ihre Anwendung die integrierte Architektur und Komponenten von MFC verwendet.
Implementieren eines Servers oder Container ohne Verwendung der MFC-Architektur ist möglich, aber nicht empfohlen.
Verwenden Sie anstelle von eigenen MFC-Implementierung
MFC "konservierte Umsetzung" Klassen, z. B. CToolBar, CStatusBarund CScrollView haben integrierte besondere Fall Code für OLE 2-Unterstützung. Also, wenn Sie diese Klassen in Ihrer Anwendung verwenden können profitieren Sie von der Anstrengung hinein, sie OLE aufmerksam zu machen. Wieder, es ist möglich, hier "Roll-your-own" Klassen für diese Zwecke, aber es wird nicht empfohlen. Wenn Sie ähnlichen Funktionalität implementieren müssen, ist der MFC-Quellcode eine hervorragende Referenz für den Umgang mit einigen der die Feinheiten von OLE (besonders wenn es kommt zu in-Aktivierung).
Überprüfen Sie den MFC-Beispielcode
Es gibt eine Reihe von MFC-Beispielen, die OLE-Funktionen enthalten. Jede dieser Anwendungen implementiert OLE aus einem anderen Blickwinkel:
HIERSVR - vor allem für die Nutzung als Serveranwendung. Sie wurde wurde in MFC 2.0 als Anwendung MFC/OLE1 aufgenommen und auf MFC/OLE 2 portiert und dann erweitert, so dass es viele OLE-Funktionen in OLE 2 implementiert.
OCLIENT - Dies ist eine eigenständige Containeranwendung, viele der OLE-Funktionen aus Sicht der Container demonstrieren soll. Es wurde auch portiert von MFC 2.0, und dann erweitert, um viele der erweiterten OLE-Funktionen, wie z. B. benutzerdefinierte Zwischenablageformate und Links zu eingebettete Elemente unterstützen.
DRAWCLI - diese Anwendung implementiert OLE-Container-Unterstützung viel wie OCLIENT tut, außer dass es dies im Rahmen einer bestehenden objektorientiertes Zeichenprogramm tut. Es zeigt Ihnen, wie Sie OLE-Container-Unterstützung implementieren und integrieren sie in Ihre bestehende Anwendung.
SUPERPAD - diese Anwendung, als auch eine feine eigenständige Anwendung ist auch ein OLE-Server. Die Serverunterstützung, die es implementiert ist sehr minimalistisch. Von besonderem Interesse ist, wie es OLE Zwischenablage Dienstleistungen verwendet, um Daten in die Zwischenablage kopieren, sondern verwendet die Funktionen, die Windows eingebaut "Bearbeiten" Steuerelement zum Implementieren von Zwischenablage einfügen Funktionen. Dies zeigt eine interessante Mischung aus traditionellen Windows-API-Nutzung sowie Integration in das neue OLE-APIs.
Weitere Informationen zu den Beispielanwendungen finden Sie unter der "MFC-Beispiel Hilfe".
Fallstudie: OCLIENT von MFC 2.0
Wie oben beschrieben OCLIENT wurde aufgenommen in MFC 2.0 und implementiert OLE mit MFC/OLE1. Die Schritte, durch die diese Anwendung zunächst umgewandelt wurde, der MFC/OLE 2 Klassen, werden im folgenden beschrieben. Eine Reihe von Features wurden hinzugefügt, nachdem der ursprüngliche Hafen abgeschlossen wurde, um die MFC/OLE-Klassen besser zu veranschaulichen. Diese Funktionen werden hier nicht behandelt werden; Siehe das Beispiel selbst weitere Informationen zu diesen erweiterten Funktionen.
Hinweis&Nbsp; Die Compiler-Fehler und Schritt für Schritt wurde mit Visual C++ 2.0 erstellt. Spezifische Fehlermeldungen und Standorte können mit Visual C++ 4.0 geändert haben, aber die konzeptionelle Informationen bleibt gültig.
Es unternehmungslustig
Das OCLIENT-Beispiel zu MFC/OLE portieren Ansatz ist zu Beginn Bau und Behebung der offensichtlich Compilerfehler, die führen. Wenn Sie das OCLIENT-Beispiel von MFC 2.0 nehmen und unter dieser Version von MFC kompilieren, finden Sie, dass es nicht so viele Fehler gibt zu beheben. Die Fehler in der Reihenfolge, in der sie aufgetreten, sind unten beschrieben.
Kompilieren und Fix Störungen
\oclient\mainview.cpp(104): Fehler C2660: 'Ziehen': Funktion übernimmt keine 4 Parameter
Der erste Fehler betrifft COleClientItem::Draw. In MFC/OLE1 dauerte es mehr Parameter als die MFC/OLE-Version braucht. Die zusätzlichen Parameter wurden oft nicht notwendig und in der Regel NULL (wie in diesem Beispiel). Diese Version von MFC kann die Werte für die LpWBounds automatisch ermitteln, wann die CDC, das gezeichnet wird gerade, ein Metadatei-DC ist. Darüber hinaus ist der pFormatDC-Parameter nicht mehr notwendig, da im Rahmen eines vom "Attribut DC" des pDC übergebenen aufbauen wird. Also um dieses Problem zu beheben, Sie einfach die beiden entfernen zusätzlichen NULL-Parameter an die Draw-Aufruf.
\oclient\mainview.cpp(273): Fehler C2065: 'OLE_MAXNAMESIZE': nicht deklarierter Bezeichner
\oclient\mainview.cpp(273): Fehler C2057: konstanten Ausdruck erwartet
\oclient\mainview.cpp(280): Error C2664: 'CreateLinkFromClipboard': kann nicht Parameter 1 in 'char [1]' konvertieren ' Enum:: TagOLERENDER '
\oclient\mainview.cpp(286): Error C2664: 'CreateFromClipboard': kann nicht Parameter 1 in 'char [1]' konvertieren ' Enum:: TagOLERENDER '
\oclient\mainview.cpp(288): Error C2664: 'CreateStaticFromClipboard': kann nicht Parameter 1 in 'char [1]' konvertieren ' Enum:: TagOLERENDER '
Die oben genannten Fehler ergeben sich aus der Tatsache, dass alle COleClientItem::CreateXXXX Funktionen in MFC/OLE1 erforderlich, dass ein eindeutiger Name übergeben werden, um das Element darzustellen. Dies war eine Voraussetzung für die zugrunde liegenden OLE-API. Dies ist nicht erforderlich, MFC/OLE 2, da OLE 2 nicht DDE als der zugrunde liegende Mechanismus der Kommunikation verwendet (der Name wurde in DDE-Dialoge verwendet). Um dieses Problem zu beheben, können Sie der CreateNewName -Funktion sowie alle Verweise darauf entfernen. Es ist einfach, herauszufinden, was jeder MFC/OLE-Funktion in dieser Version erwartet wird, indem Sie einfach platzieren Sie den Cursor auf den Anruf und drücken von F1.
Ein weiterer Bereich, der sich deutlich unterscheidet ist OLE 2 Zwischenablage Handhabung. Mit OLE1 haben Sie die Windows-Zwischenablage, die APIs zu interagieren mit der Zwischenablage verwendet. Mit OLE 2 ist dies mit einem anderen Mechanismus. Die MFC/OLE1 APIs davon ausgegangen, dass die Zwischenablage geöffnet war, bevor Sie ein COleClientItem -Objekt in die Zwischenablage kopieren. Dies ist nicht mehr nötig und bewirkt, dass alle MFC/OLE Zwischenablagevorgänge fehlschlagen. Während Sie den Code zum Entfernen von Abhängigkeiten von CreateNewNamebearbeiten, sollten Sie auch den Code entfernen, der öffnet und schließt die Windows-Zwischenablage.
\oclient\mainview.cpp(332): Fehler C2065: 'AfxOleInsertDialog': nicht deklarierter Bezeichner
\oclient\mainview.cpp(332): Error C2064 generiert: Ausdruck wird nicht ausgewertet, um eine Funktion
\oclient\mainview.cpp(344): Fehler C2057: konstanten Ausdruck erwartet
\oclient\mainview.cpp(347): Fehler C2039: 'CreateNewObject': ist kein Member von 'CRectItem'
Diese Fehler resultieren aus der CMainView::OnInsertObject -Handler. Umgang mit den Befehl "Neues Objekt einfügen" ist ein weiterer Bereich, wo die Dinge etwas verändert haben. In diesem Fall ist es am einfachsten, einfach die ursprüngliche Implementierung von Anwendungs-Assistenten für eine neue OLE-Containeranwendung bereitgestellt zusammenführen. In der Tat ist dies eine Technik, die Sie anwenden können, um andere Anwendungen zu portieren. In MFC/OLE1 angezeigt Sie das Dialogfeld "Objekt einfügen" durch Aufrufen der AfxOleInsertDialog -Funktion. In dieser Version, die Sie erstellen ein COleInsertObject Dialog-Objekt und rufen Sie DoModal. Darüber hinaus werden neue OLE-Elemente mit einer CLSID statt ein Classname String erstellt. Das Endergebnis sollte wie folgt aussehen
COleInsertDialog Dlg;
Wenn (Dlg.DoModal()! = IDOK)
Nbsp; Rückkehr;
BeginWaitCursor();
CRectItem * pItem = NULL;
VERSUCHEN
{
/ / Erstellen Sie zunächst das C++-Objekt
pItem = GetDocument() - > CreateItem();
ASSERT_VALID(pItem);
/ / Initialisieren des Elements aus dem Dialogfelddaten.
Wenn (! Dlg.CreateItem(pItem))
AfxThrowMemoryException();
/ / eine Ausnahme machen
ASSERT_VALID(pItem);
/ / das Objekt ausgeführt werden, wenn entsprechende
Wenn (Dlg.GetSelectionType() == COleInsertDialog::createNewItem)
pItem - > DoVerb(OLEIVERB_SHOW, this);
/ / update sofort
pItem - > UpdateLink();
pItem - > UpdateItemRectFromServer();
/ / Auswahl neu eingefügte Element set
SetSelection(pItem);
pItem - > Invalidate();
}
CATCH (CException, e)
{/ / clean up Element
Wenn (pItem! = NULL)
GetDocument() - > DeleteItem(pItem);
AfxMessageBox(IDP_FAILED_TO_CREATE);
}
END_CATCH
EndWaitCursor()
Hinweis&Nbsp; Neues Objekt einfügen kann für Ihre Anwendung unterschiedlich sein):
Es ist auch erforderlich, lt;afxodlgs.h > die enthält die Deklaration der Dialogklasse COleInsertObject sowie die anderen standard-Dialoge von MFC bereitgestellte.
\oclient\mainview.cpp(367): Fehler C2065: 'OLEVERB_PRIMARY': nicht deklarierter Bezeichner
\oclient\mainview.cpp(367): Fehler C2660: 'DoVerb': Funktion übernimmt keine Parameter 1
Diese Fehler entstehen durch die Tatsache, die dass einige OLE1-Konstanten in OLE 2, geändert haben, obwohl in Konzept sie identisch sind. In diesem Fall hat OLEVERB_PRIMARY zu OLEIVERB_PRIMARYgeändert. OLE1 sowohl OLE 2 ist das primäre Verb in der Regel von einem Container ausgeführt, wenn der Benutzer auf ein Element doppelklickt.
Außerdem übernimmt DoVerb jetzt einen zusätzlichen Parameter – ein Zeiger auf einen Blick (CView*). Dieser Parameter wird nur verwendet, um "Visuelle Bearbeitung" (oder direkte Aktivierung) zu implementieren. Jetzt legen Sie diesen Parameter auf NULL, da Sie dieses Feature nicht zu diesem Zeitpunkt implementieren.
Um sicherzustellen, dass das Framework nie versucht, in-Place-aktivieren, sollten Sie CanActivate wie folgt überschreiben
BOOL CRectItem::CanActivate()
{
&Nbsp; Return FALSE;
}
\oclient\rectitem.cpp(53): Fehler C2065: 'GetBounds': nicht deklarierter Bezeichner
\oclient\rectitem.cpp(53): Error C2064 generiert: Ausdruck wird nicht ausgewertet, um eine Funktion
\oclient\rectitem.cpp(84): Fehler C2065: 'SetBounds': nicht deklarierter Bezeichner
\oclient\rectitem.cpp(84): Error C2064 generiert: Ausdruck wird nicht ausgewertet, um eine Funktion
In MFC/OLE1 waren COleClientItem::GetBounds und SetBounds zum Abfragen und bearbeiten die Maße eines Elements (die linken und oberen Mitglieder waren immer 0 (null)). In MFC/OLE 2 ist dies mehr direkt unterstützt durch COleClientItem::GetExtent und SetExtent, die eine Größe oder CSize stattdessen behandeln.
Der Code für Ihre neue SetItemRectToServer, und UpdateItemRectFromServer-Aufrufe wie folgt aussehen:
BOOL CRectItem::UpdateItemRectFromServer()
{
Nbsp; Assert(m_bTrackServerSize);
CGröße Größe;
If (!.GetExtent(&size))
Return FALSE; / / leere
/ / Bildschirmkoordinaten aus HIMETRIC zuordnen
{
CClientDC screenDC(NULL);
screenDC.SetMapMode(MM_HIMETRIC);
ScreenDC.LPtoDP(&size);
}
/ / Legen Sie die Objektgröße
Wenn (M_rect.Size()! = Größe)
{
/ / ungültig, die alte Größe/Position
Invalidate();
m_rect.Right = m_rect.left + size.cx;
m_rect.Bottom = m_rect.top + size.cy;
/ / als auch die neue Größe/Position
Invalidate();
}
TRUE zurück;
}
BOOL CRectItem::SetItemRectToServer()
{
/ / die offiziellen Grenzen für das eingebettete Element set
CGröße Größe = M_rect.Size();
{
CClientDC screenDC(NULL);
screenDC.SetMapMode(MM_HIMETRIC);
ScreenDC.DPtoLP(&size);
}
VERSUCHEN
{
SetExtent(size); / / eine Wartezeit tun können
}
CATCH (CException, e)
{
Return FALSE; / / Links wird nicht SetBounds ermöglichen
}
END_CATCH
TRUE zurück;
}
\oclient\frame.cpp(50): Fehler C2039: 'InWaitForRelease': ist kein Member von 'COleClientItem'
\oclient\frame.cpp(50): Fehler C2065: 'InWaitForRelease': nicht deklarierter Bezeichner
\oclient\frame.cpp(50): Error C2064 generiert: Ausdruck wird nicht ausgewertet, um eine Funktion
MFC/OLE1 waren synchrone API-Aufrufe aus einem Container zu einem Server simuliert, da OLE1 von Natur aus asynchron in vielen Fällen war. Es war notwendig, für einen ausstehenden asynchronen Aufruf läuft vor der Verarbeitung der Befehle des Benutzers zu überprüfen. MFC/OLE1 vorgesehen sind dabei die COleClientItem::InWaitForRelease -Funktion. In MFC/OLE 2 ist dies nicht notwendig, so Sie alle zusammen die Überschreibung des OnCommand in CMainFrame entfernen können.
An diesem Punkt wird OCLIENT kompilieren und verknüpfen.
Andere notwendigen Änderungen
Es gibt einige Dinge, die getan werden nicht, die OCLIENT ausgeführt, jedoch halten. Es ist besser, diese Probleme jetzt statt später zu beheben.
Zunächst einmal ist es notwendig, die OLE-Bibliotheken initialisieren. Dies erfolgt durch Aufrufen von AfxOleInit von InitInstance:
if (!.AfxOleInit())
{
AfxMessageBox ("Fehler beim Initialisieren der OLE-Bibliotheken");
Return FALSE;
}
Es ist auch eine gute Idee für virtuelle Funktionen für Parameter Listenänderungen überprüfen. Eine solche Funktion ist COleClientItem::OnChange, in jedem MFC/OLE-Container-Anwendung überschrieben. Blick auf online-Hilfe, sehen Sie, dass ein zusätzliches 'DWORD DwParam"hinzugefügt wurde. Der neue CRectItem::OnChange sieht wie folgt aus
void CRectItem::OnChange (OLE_NOTIFICATION wNotification, DWORD DwParam)
{
Wenn (M_bTrackServerSize Amp; &
!UpdateItemRectFromServer())
{
/ / Leere Objekt
Wenn (wNotification == OLE_CLOSED)
{
/ / keine Daten für das Objekt - zerstören
GELTEND ZU MACHEN (!.IsVisible());
GetDocument() - > DeleteItem(this);
Rückkehr; / / kein Update (Element ist jetzt weg)
}
}
Wenn (wNotification! = OLE_CLOSED)
Dirty();
Invalidate(); / / Änderungen verursacht ein Neuzeichnen
}
In MFC/OLE1 Containeranwendungen die Dokumentklasse von COleClientDocabgeleitet. In MFC/OLE 2 wurde dieser Klasse entfernt und ersetzt von COleDocument (dieser neuen Organisation macht es einfacher, Container/Server-Anwendungen zu entwickeln). Gibt es eine # define , COleClientDoc die COleDocument vereinfachen Portieren von MFC/OLE 2, z. B. OCLIENT MFC/OLE1 Anwendungen zuordnet. Eines der Features, die nicht im Lieferumfang von COleDocument-Klasse , die von COleClientDoc zur Verfügung gestellt wurde ist der Standardbefehl Nachricht Zuordnungseinträge. Dies geschieht so, dass die Serveranwendungen, die auch COleDocument (indirekt) verwenden, nicht mit ihnen den Aufwand für diese Befehlshandler tragen, es sei denn, sie eine Container-/Serveranwendung sind. So müssen Sie die Meldungszuordnung CMainDoc die folgenden Einträge hinzu:
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 Umsetzung aller dieser Befehle ist im COleDocument, das ist die Basisklasse für Ihr Dokument.
OCLIENT ist zu diesem Zeitpunkt eine funktionsfähige OLE-Container-Anwendung. Es ist möglich, zum Einfügen von Elementen eines beliebigen Typs (OLE1 oder OLE 2). Da der erforderliche Code zum vor-Ort-Aktivierung nicht implementiert ist, werden Elemente in einem separaten Fenster, ähnlich wie mit OLE1, bearbeitet. Der nächste Abschnitt erläutert die erforderlichen Änderungen zu aktivieren in-Place-Bearbeitung (manchmal genannt "Visuelle Bearbeitung").
Hinzufügen von "Visuelle Bearbeitung"
Eines der interessantesten Features von OLE ist direkte Aktivierung (oder "Visuelle Bearbeitung"). Dieses Feature ermöglicht die Server-Anwendung übernehmen Teile der Benutzeroberfläche des Containers eine nahtlosere Bearbeitung Schnittstelle für den Benutzer bereitgestellt. Um direkte Aktivierung zu OCLIENT implementieren, müssen einige speziellen Ressourcen, sowie zusätzlicher Code hinzugefügt werden. Diese Ressourcen und Code werden normalerweise von AppWizard bereitgestellt – in der Tat wurde viel von den Code hier direkt aus einer frischen AppWizard-Anwendung mit Unterstützung der "Container" entlehnt.
Zunächst einmal ist es notwendig, fügen Sie eine Menüressource verwendet werden, wenn es gibt ein Element, das in-Place aktiv. Sie können diese zusätzlichen Menüressource in Visual C++ erstellen, durch Kopieren der IDR_OCLITYPE Ressource und alle, aber die Datei und Fenster Popups entfernen. Zwei Trennlinien zwischen der Datei und Fenster Pop-ups an die Trennung von Gruppen eingefügt werden (es sollte so aussehen: Datei || Fenster). Weitere Informationen über was diese Trennzeichen, und wie der Server und Container Menüs zusammengeführt werden finden Sie unter "Menüs und Ressourcen: Menü zusammenführen" in OLE 2 Klassen.
Sobald Sie diese Menüs erstellt haben, müssen Sie das Framework über sie wissen zu lassen. Dies erfolgt durch Aufrufen von CDocTemplate::SetContainerInfo für die Dokumentvorlage, bevor Sie es der Liste Dokument Vorlage in Ihrer InitInstance hinzufügen. Sieht wie folgt aus der neue Code, um die Dokumentvorlage zu registrieren
CDocTemplate * pTemplate = neue CMultiDocTemplate ()
Nbsp; IDR_OLECLITYPE,
RUNTIME_CLASS(CMainDoc),
RUNTIME_CLASS(CMDIChildWnd), / / standard untergeordneten MDI-Rahmenfensters
RUNTIME_CLASS(CMainView));
pTemplate - > SetContainerInfo(IDR_OLECLITYPE_INPLACE);
AddDocTemplate(pTemplate)
Die IDR_OLECLITYPE_INPLACE-Ressource ist die spezielle vor-Ort-Ressource in Visual C++ erstellt.
Um direkte Aktivierung zu ermöglichen, gibt es einige Dinge, die in beiden der CView ändern müssen (CMainView) abgeleitete Klasse als auch der von COleClientItem abgeleiteten Klasse (CRectItem). Alle diese Überschreibungen von AppWizard bereitgestellt werden und den größten Teil der Implementierung kommen direkt aus einer Standard-AppWizard-Anwendung.
Im ersten Schritt von diesem Port wurde direkte Aktivierung vollständig durch Überschreiben der CanActivatedeaktiviert. Diese Überschreibung sollte entfernt werden, um direkte Aktivierung zu ermöglichen. Darüber hinaus wurde NULL für alle Aufrufe an DoVerb (es gibt zwei von ihnen) übergeben, da Voraussetzung, dass die Aussicht nur für direkte Aktivierung notwendig war. Um direkte Aktivierung vollständig zu implementieren, ist es notwendig, die richtige Ansicht im DoVerb Aufruf übergeben. Diese Aufrufe gehört in CMainView::OnInsertObject
pItem-≫ DoVerb(OLEIVERB_SHOW, this)
Ein weiterer Grund ist in CMainView::OnLButtonDblClk
m_pSelection-≫ DoVerb(OLEIVERB_PRIMARY, this)
Es ist erforderlich, COleClientItem::OnGetItemPositionzu überschreiben. Diese weist den Server wo man seine Fenster relativ zum Fenster des Containers setzen, wenn das Element direkt aktiviert ist. Die Umsetzung ist für OCLIENT trivial
priv&atevoid CRectItem::OnGetItemPosition (CRectamp; rPosition)
{
rPosition = M_rect;
}
Die meisten Server implementieren auch so genannte "in-Place Größenänderung." Dadurch wird das Serverfenster Größe und verschoben, während der Benutzer das Element bearbeitet werden. Der Container muss an dieser Aktion teilnehmen, da verschieben oder die Größenänderung des Fensters in der Regel wirkt sich auf die Position und Größe innerhalb der Containerdokument selbst. Die Implementierung für OCLIENT synchronisiert das innere Rechteck gepflegt von M_rect mit der neuen Position und Größe.
BOOL CRectItem::OnChangeItemPosition(const CRectamp; rectPos)
{
ASSERT_VALID(this);
If (!.COleClientItem::OnChangeItemPosition(rectPos))
Return FALSE;
Invalidate();
M_rect = RectPos;
Invalidate();
GetDocument() - > SetModifiedFlag();
TRUE zurück;
}
An diesem Punkt gibt es genug Code, damit ein Element direkt aktiviert sein und befassen sich mit der Größe und verschieben das Element, wenn es aktiv ist, aber keinen Code, die den Benutzer beendet die Bearbeitungssitzung zulässt. Obwohl einige Server diese Funktionalität selbst bereitstellen werden, indem die ESC-Taste, wird es vorgeschlagen, dass Container zwei Möglichkeiten bieten, ein Element zu deaktivieren: (1) indem Sie außerhalb des Elements, und (2) durch Drücken der ESC-Taste.
Für die Flucht wird Schlüssel fügen Sie einen Beschleuniger mit Visual C++, die einen Befehl, die Taste VK_ESCAPE zuordnet ID_CANCEL_EDIT auf die Ressourcen hinzugefügt. Der Handler für diesen Befehl folgt:
/ / Der folgende Befehlshandler bietet den Standard
/ / Tastatur Benutzeroberfläche ein direktes Abbrechen
/ / Bearbeiten von session.void CMainView::OnCancelEdit()
{
Nbsp; / / In-Place-aktive Element auf diese Ansicht zu schließen.
COleClientItem * pActiveItem = GetDocument() - > GetInPlaceActiveItem(this);
Wenn (pActiveItem! = NULL)
pActiveItem - > Close();
Assert(GetDocument()-> GetInPlaceActiveItem(this) == NULL);
}
Um den Fall zu behandeln, wo der Benutzer außerhalb des Elements klickt, fügen Sie den folgenden Code an den Anfang des CMainView::SetSelection
wenn (pNewSel! = M_pSelection || pNewSel == NULL)
{
Nbsp; COleClientItem * pActiveItem = GetDocument() - > GetInPlaceActiveItem(this);
Wenn (pActiveItem! = NULL & & pActiveItem! = pNewSel)
pActiveItem - > Close();
}
& nbsp
Wenn ein Element in Ort aktiv wird, sollte es den Fokus besitzen. Um sicherzustellen, dass dies der Fall ist behandeln Sie OnSetFocus, sodass der Fokus immer auf das aktive Element übertragen ist, wenn Ihre Ansicht den Fokus erhält:
/ / OnSetFocus und OnSize speziell gehandhabt werden / / wann ein Objekt wird bearbeitet vor Ort.
privatevoid CMainView::OnSetFocus (CWnd pOldWnd)
{
Nbsp; COleClientItem * pActiveItem = GetDocument() - > GetInPlaceActiveItem(this);
Wenn (pActiveItem! = NULL & &
pActiveItem - > GetItemState() == COleClientItem::activeUIState)
{
/ / Fokus auf dieses Element festgelegt, wenn es derselben Ansicht ist müssen
CWnd * pWnd = pActiveItem - > GetInPlaceWindow();
Wenn (pWnd! = NULL)
{
pWnd - > SetFocus(); / / Don't call the base Class
Rückkehr;
}
}
CView::OnSetFocus(pOldWnd);
}
Wenn die Ansicht geändert wird, müssen Sie das aktive Element zu benachrichtigen, das dass das Auswahlrechteck geändert hat. Dazu stellen Sie einen Handler für OnSize:
void CMainView::OnSize (UINT nType, Int Cx, Int cy)
{
Nbsp; CView::OnSize (nType, Cx, cy);
COleClientItem * pActiveItem = GetDocument() - > GetInPlaceActiveItem(this);
Wenn (pActiveItem! = NULL)
pActiveItem - > SetItemRects();
}
Fallstudie: HIERSVR von MFC 2.0
HIERSVR wurde ebenfalls in MFC 2.0 und implementiert OLE mit MFC/OLE1. Diese Applikationsschrift beschreibt kurz die Schritte, durch die diese Anwendung zunächst der MFC/OLE 2 Klassen konvertiert wurde. Eine Reihe von Features wurden hinzugefügt, nachdem der ursprüngliche Hafen abgeschlossen wurde, um die MFC/OLE 2 Klassen besser zu veranschaulichen. Diese Funktionen werden hier nicht behandelt werden; Siehe das Beispiel selbst weitere Informationen zu diesen erweiterten Funktionen.
Hinweis&Nbsp; Die Compiler-Fehler und Schritt für Schritt wurde mit Visual C++ 2.0 erstellt. Spezifische Fehlermeldungen und Standorte können mit Visual C++ 4.0 geändert haben, aber die konzeptionelle Informationen bleibt gültig.
Es unternehmungslustig
Das HIERSVR-Beispiel zu MFC/OLE portieren Ansatz ist zu Beginn Bau und Behebung der offensichtlich Compilerfehler, die führen. Wenn Sie das HIERSVR-Beispiel von MFC 2.0 nehmen und unter dieser Version von MFC kompilieren, finden Sie, dass es nicht viele Fehler gibt zu lösen (obwohl es mehr als das OCLIENT-Beispiel sind). Die Fehler in der Reihenfolge, in der sie in der Regel auftreten, sind unten beschrieben.
Kompilieren und Fix Störungen
\hiersvr\hiersvr.cpp(83): Fehler C2039: 'RunEmbedded': ist kein Member von 'COleTemplateServer'
Dieser erste Fehler zeigt ein viel größeres Problem mit der InitInstance -Funktion für Server. Die Initialisierung für ein OLE-Server erforderlich ist wahrscheinlich eine der größten Änderungen haben Sie zu Ihrer Anwendung MFC/OLE1 um es läuft. Das beste, was zu tun ist, schauen, was der Anwendungs-Assistent für ein OLE-Server erstellt und den Code entsprechend ändern. Hier sind einige Punkte zu beachten:
Es ist notwendig, die OLE-Bibliotheken initialisieren, indem Sie AfxOleInit
Rufen Sie SetServerInfo auf das Dokument Vorlage-Objekt, das Server Ressourcenhandles und Common Language Runtime-Klasseninformationen, die Sie nicht mit dem CDocTemplate -Konstruktor festlegen können.
Nicht das Hauptfenster der Anwendung angezeigt, wenn die Option/Embedding in der Befehlszeile vorhanden ist.
Sie müssen eine GUID für Ihr Dokument. Dies ist ein eindeutiger Bezeichner für Ihren Dokumenttyp (128 Bit). Der Anwendungs-Assistent erstellt für Sie — so dass, wenn Sie die Technik beschrieben hier verwenden kopieren Sie den neuen Code aus eine neue Server-Anwendung AppWizard generiert, können Sie einfach "stehlen" die GUID von dieser Anwendung. Wenn dies nicht der Fall ist, können Sie das GUIDGEN.EXE-Dienstprogramm in das BIN-Verzeichnis.
Es ist erforderlich, "das COleTemplateServer -Objekt mit der Dokumentvorlage verbinden" durch Aufrufen von COleTemplateServer::ConnectTemplate.
Aktualisieren der System-Registry Wenn Ihre Anwendung eigenständige ausgeführt wird. Auf diese Weise, wenn der Benutzer bewegt die.EXE für Ihre Anwendung, läuft es von der neuen Position die Windows System-Registrierungsdatenbank darauf an den neuen Speicherort aktualisiert.
Nach der Anwendung alle diese Änderungen basierend auf was AppWizard für InitInstanceerstellt, sollte die InitInstance (und die 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 bemerken, dass der oben stehende Code auf eine neue Ressource-ID IDR_HIERSVRTYPE_SRVR_EMB verweist. Dies ist die Menüressource verwendet werden, wenn ein Dokument, das in einen anderen Container eingebettet ist, bearbeitet wird. In MFC/OLE1 wurden die Menüelemente, die spezifisch für ein eingebettetes Element bearbeiten im laufenden Betrieb geändert. Eine ganz andere Menüstruktur verwenden, wenn Sie ein eingebettetes Element statt bearbeiten ein dateibasiertes Dokument bearbeiten macht es viel einfacher, verschiedene Benutzeroberflächen für diese zwei separate Modi bieten. Wie Sie später sehen werden, ist eine völlig andere Menüressource verwendet, wenn ein eingebettetes Objekt in-Place-Bearbeitung.
Um diese Ressource zu erstellen, laden Sie das Ressourcenskript in Visual C++, und kopieren Sie die vorhandene IDR_HIERSVRTYPE-Menü-Ressource. Benennen Sie die neue Ressource in IDR_HIERSVRTYPE_SRVR_EMB (Dies ist die gleiche Benennungskonvention, die Anwendungs-Assistent verwendet). Als nächstes ändern Sie "Datei speichern", "Datei aktualisieren"; Geben sie Befehl ID ID_FILE_UPDATE. Ändern Sie auch "Datei speichern unter" in "File Save Copy As"; Geben sie Befehl ID ID_FILE_SAVE_COPY_AS. Das Framework stellt die Implementierung von beiden dieser Befehle.
\hiersvr\svritem.h(60): Fehler C2433: 'OLESTATUS': 'virtuelle' nicht zulässig auf Datendeklarationen
\hiersvr\svritem.h(60): Fehler C2501: 'OLESTATUS': fehlender Decl-Spezifizierer
\hiersvr\svritem.h(60): Fehler C2146: Syntaxfehler: fehlender ';' vor Bezeichner 'OnSetData'
\hiersvr\svritem.h(60): Fehler C2061: Syntaxfehler: Bezeichner 'OLECLIPFORMAT'
\hiersvr\svritem.h(60): Fehler C2501: 'OnSetData': fehlender Decl-Spezifizierer
Es gibt eine Reihe von Fehlern, die durch die Überschreibung der OnSetData, da es auf der OLESTATUS -Typ bezieht. OLESTATUS war die Art und Weise, die OLE1 Fehler zurückgegeben. Dies hat zu HRESULT in OLE 2, geändert, obwohl MFC in der Regel ein HRESULT in einen COleException mit dem Fehler konvertiert. In diesem speziellen Fall ist die Überschreibung der OnSetData nicht mehr erforderlich, so ist die einfachste Sache zu tun, um es zu entfernen.
\hiersvr\svritem.cpp(30): Fehler C2660: 'COleServerItem::COleServerItem': Funktion übernimmt keine Parameter 1
Der COleServerItem -Konstruktor nimmt einen zusätzlichen Parameter "BOOL". Dieses Flag legt fest, wie Speicherverwaltung der COleServerItem -Objekte erfolgt. Indem es auf true festgelegt, behandelt das Framework das Memory Management dieser Objekte — sie zu löschen, wenn sie nicht mehr erforderlich sind. HIERSVR verwendet CServerItem (abgeleitet von COleServerItem) Objekte als Teil der systemeigenen Daten, so dass Sie dieses Flag auf FALSE festgelegt werden. Auf diese Weise können HIERSVR bestimmen, wenn jedes Serverelement gelöscht wird.
\hiersvr\svritem.cpp(44): Fehler C2259: 'CServerItem': unzulässiger Versuch, abstrakte Klasse instanziieren
\hiersvr\svritem.cpp(44): Fehler C2259: 'CServerItem': unzulässiger Versuch, abstrakte Klasse instanziieren
Da diese Fehler bedeuten, gibt es einige "reinen virtuellen" Funktionen, die nicht in CServerItem überschrieben wurden. Wahrscheinlich ist dies durch die Tatsache verursacht, die OnDraw der Parameterliste geändert hat. Um diesen Fehler zu beheben, ändern Sie CServerItem::OnDraw wie folgt (wie auch der Deklaration in svritem.h)
BOOL CServerItem::OnDraw(CDC* pDC, CSizeamp; rSize)
{
/ / Anfrage von OLE Knoten ziehen
pDC - > SetMapMode(MM_TEXT); / / immer in Pixel
Rückkehr DoDraw (pDC, CPoint(0,0), FALSE);
}
Der neue Parameter ist "rSize". Dadurch können Sie füllen Sie die Größe der Zeichnung, wenn bequem. Diese Größe muss in HIMETRICsein. In diesem Fall ist es nicht sinnvoll, diesen Wert in, füllen, also das Framework ruft OnGetExtent Umfang abrufen. Damit dies funktioniert müssen Sie zur Implementierung von OnGetExtent:
BOOL CServerItem::OnGetExtent(DV&ASPECT dwDrawAspect, CSizeamp; rSize)
{
Wenn (DwDrawAspect! = DVASPECT_CONTENT)
Rückkehr COleServerItem::OnGetExtent (DwDrawAspect, rSize);
rSize = CalcNodeSize();
TRUE zurück;
}
\hiersvr\svritem.cpp(104): Fehler C2065: 'M_rectBounds': nicht deklarierter Bezeichner
\hiersvr\svritem.cpp(104): Fehler C2228: Links '.SetRect' muss Typ Klasse/Struktur/Union sein
\hiersvr\svritem.cpp(106): Fehler C2664: ' void __pascal __far DPtoLP (Struct:: TagPOINT __far *, Int) __far const ': kann nicht konvertieren Parameter 1 aus ' Int __far *', ' Struct:: TagPOINT __far *'
In der CServerItem::CalcNodeSize Funktion der Objektgröße in HIMETRIC konvertiert und in M_rectBoundsgespeichert. Das undokumentierten 'M_rectBounds'-Mitglied von COleServerItem existiert nicht (es wurde teilweise durch M_sizeExtentersetzt, aber in OLE 2 dieses Mitglied hat eine etwas andere Verwendung als OLE1 M_rectBounds hat). Anstatt die HIMETRIC -Größe in diese Membervariable, werde Sie es zurück. Dieser Rückgabewert ist in OnGetExtent, zuvor implementiert verwendet.
CSize CServerItem::CalcNodeSize()
{
Nbsp; CClientDC dcScreen(NULL);
M_sizeNode = dcScreen.GetTextExtent (M_strDescription,
m_strDescription.GetLength());
M_sizeNode += CGröße (CX_INSET * 2, CY_INSET * 2);
/ / set vorgeschlagenen HIMETRIC-Größe
CGröße Größe (m_sizeNode.cx, m_sizeNode.cy);
dcScreen.SetMapMode(MM_HIMETRIC);
DcScreen.DPtoLP(&size);
Größe zurück;
}
CServerItem überschreibt auch COleServerItem::OnGetTextData. Diese Funktion ist veraltet in MFC/OLE und wird ersetzt durch einen anderen Mechanismus. Die MFC 3.0-Version von MFC-OLE-Beispiel HIERSVR implementiert diese Funktionen durch Überschreiben der COleServerItem::OnRenderFileData. Diese Funktionalität ist nicht wichtig, dass diese grundlegende Port, so dass Sie die OnGetTextData-Überschreibung entfernen können.
Es gibt viele weitere Fehler in svritem.cpp, die noch nicht behandelt wurden. Sie sind keine "echten" Fehler — nur Fehler, die durch vorherige Störungen.
\hiersvr\svrview.cpp(325): Fehler C2660: 'CopyToClipboard': Funktion dauert 2 Parameter nicht
COleServerItem::CopyToClipboard unterstützt nicht mehr die 'bIncludeNative'-Flag. Die systemeigenen Daten (die Daten durch das Serverelement Serialize-Funktion geschrieben) ist immer kopiert, sodass Sie den ersten Parameter entfernen. CopyToClipboard wird zusätzlich eine Ausnahme ausgelöst, wenn ein Fehler auftritt, statt Rückgabe FALSE. Ändern Sie den Code für die CServerView::OnEditCopy wie folgt
void CServerView::OnEditCopy()
{
Nbsp; Wenn (M_pSelectedNode == NULL)
AfxThrowNotSupportedException();
VERSUCHEN
{
M_pSelectedNode - > CopyToClipboard(TRUE);
}
CATCH_ALL(e)
{
AfxMessageBox ("Kopieren in die Zwischenablage ist fehlgeschlagen");
}
END_CATCH_ALL}
Zwar gab es weitere Störungen infolge der Kompilierung der MFC 2.0 Version von HIERSVR als gab es für die gleiche Version der OCLIENT, es gab tatsächlich weniger Änderungen.
Zu diesem Zeitpunkt wird HIERSVR kompilieren und verknüpfen und fungieren als OLE-Server, aber ohne das in-Place-Bearbeitung-Feature, das als nächstes umgesetzt werden.
Hinzufügen von "Visuelle Bearbeitung"
Um diese Serveranwendung "Visuelle Bearbeitung" (oder direkte Aktivierung) hinzuzufügen, gibt es nur wenige Dinge, die, denen Sie sich kümmern müssen:
Die Menüressource ist leicht zu erstellen. Führen Sie Visual C++, kopieren Sie die Menüressource IDR_HIERSVRTYPE in eine Menüressource als IDR_HIERSVRTYPE_SRVR_IP bezeichnet. Ändern Sie das Menü so, dass nur das Bearbeiten und Hilfe-Menü Popups gelassen werden. Das Menü zwischen den Menüs Bearbeiten und Hilfe zwei Trennzeichen hinzufügen (es sollte so aussehen: Bearbeiten || Hilfe). Weitere Informationen über was diese Trennzeichen, und wie der Server und Container Menüs zusammengeführt werden finden Sie unter "Menüs und Ressourcen: Menü zusammenführen" in OLE 2 Klassen.
Die Bitmap für die Teilmenge-Symbolleiste kann leicht erstellt werden, durch Kopieren von einer frischen AppWizard generiert-Anwendung mit der Option "Server" aktiviert. Diese Bitmap kann dann in Visual C++ importiert werden. Achten Sie darauf, der Bitmap eine ID IDR_HIERSVRTYPE_SRVR_IP geben.
Die von COleIPFrameWnd abgeleitete Klasse kann von einer Anwendung AppWizard generiert mit Serversupport sowie kopiert werden. Kopieren Sie beide Dateien, IPFRAME.CPP und IPFRAME.H und dem Projekt hinzufügen. Stellen Sie sicher, dass der Aufruf LoadBitmap auf IDR_HIERSVRTYPE_SRVR_IP, die im vorherigen Schritt erstellte Bitmap bezieht.
Jetzt, dass die neuen Ressourcen und Klassen erstellt werden, fügen Sie den erforderlichen Code hinzu, sodass das Framework diese kennt (und weiß, dass diese Anwendung nun in-Place-Bearbeitung unterstützt). Dies erfolgt durch Hinzufügen, dass einige weitere Parameter, die SetServerInfo in der InitInstance -Funktion aufrufen:
pDocTemplate-≫SetServerInfo (IDR_HIERSVRTYPE_SRVR_EMB,
IDR_HIERSVRTYPE_SRVR_IP, RUNTIME_CLASS(CInPlaceFrame))
Es ist jetzt bereit zum Ausführen von in-Place-in jedem Container die direkte Aktivierung unterstützt. Aber ist es ein kleiner Fehler im Code noch lauern. HIERSVR unterstützt ein Kontextmenü angezeigt, wenn der Benutzer die Rechte Maustaste drückt. Dieses Menü funktioniert wenn HIERSVR völlig offen ist, aber funktioniert nicht, wenn eine Einbettung in-Place-Bearbeitung. Der Grund kann auf diese einzelne Codezeile in CServerView::OnRButtonDown fixiert werden, nach unten
pMenu-Gt;TrackPopupMenu(TPM_CENTERALIGN | TPM_RIGHTBUTTON,
Point.x-, point.y, AfxGetApp() - > M_pMainWnd)
Beachten Sie den Hinweis auf AfxGetApp:)-Gt; M_pMainWnd. Wenn der Server direkt aktiviert ist, es hat ein Hauptfenster und M_pMainWnd festgelegt ist, aber es ist in der Regel unsichtbar. Darüber hinaus bezieht sich in diesem Fenster auf das Hauptfenster der Anwendung, die MDI-Rahmenfenster, die angezeigt wird, wenn der Server vollständig geöffnet ist oder Stand-Alone . Es verweist nicht auf die aktiven Frame-Fenster – die beim in-Place aktiviert ist ein Frame-Fenster von COleIPFrameWndabgeleitet. Das richtige aktive Fenster bekommen auch beim in-Place bearbeiten, diese Version der MFC-neue Funktion AfxGetMainWndhinzugefügt. Im Allgemeinen sollten Sie diese Funktion anstelle von AfxGetApp() - > M_pMainWnd. Dieser Code muss wie folgt ändern
pMenu-≫TrackPopupMenu(TPM_CENTERALIGN | TPM_RIGHTBUTTON,
Point.x, point.y, AfxGetMainWnd())
Jetzt haben Sie einen OLE-Server minimal für die funktionelle in-Place-Aktivierung aktiviert. Aber es gibt noch viele Funktionen mit MFC/OLE-2, die in MFC/OLE1 nicht verfügbar waren. Finden Sie das HIERSVR-Beispiel für weitere Ideen, auf Funktionen, die Sie implementieren möchten. Einige der Features, die implementiert HIERSVR sind unten aufgeführt.:
Das HIERSVR-Beispiel in MFC 3.0 wird auch ein etwas anderes Design für seine Serverelemente verwendet. Dies hilft, Speicher zu sparen und Ihre Links flexibler macht. Mit der Version 2.0 von HIERSVR jeden Knoten in der Struktur ist ein COleServerItem. COleServerItem trägt ein wenig mehr Aufwand als für einzelne Knoten, unbedingt notwendig ist, aber eine COleServerItem ist für jede aktive Verbindung erforderlich. Aber zum größten Teil, es gibt nur sehr wenige aktive Links zu einem bestimmten Zeitpunkt. Um dies effizienter zu gestalten, trennt das HIERSVR in dieser Version von MFC den Knoten von der COleServerItem. Es hat eine CServerNode und einer CServerItem -Klasse. Die CServerItem (abgeleitet von COleServerItem) wird nur bei Bedarf erstellt. Sobald die Container (oder Container), die mit diesem bestimmten Knoten bestimmten verknüpft aufhören, wird das zugeordnete der CServerNode CServerItem-Objekt gelöscht. Dieses Design ist effizienter und flexibler. Seine Flexibilität kommt im Umgang mit mehreren Auswahl Links. Keiner von diesen zwei Versionen von HIERSVR unterstützen Mehrfachauswahl, aber es wäre viel einfacher, hinzufügen (und Links zu solcher Auswahl unterstützen) mit der MFC 3.0-Version von HIERSVR, da der COleServerItem von systemeigenen Daten getrennt ist.
Technische Hinweise von &Nummer |nbsp; Technische Hinweise nach Kategorie