Behandeln von Shelldatenübertragungs-Szenarien

Im Shell Data Object-Dokument wurde der allgemeine Ansatz erläutert, mit dem Shell-Daten mit Drag-and-Drop oder der Zwischenablage übertragen werden. Um jedoch shell-Datenübertragungen in Ihrer Anwendung zu implementieren, müssen Sie auch verstehen, wie Sie diese allgemeinen Prinzipien und Techniken auf die unterschiedliche Art und Weise anwenden können, wie Shell-Daten übertragen werden können. In diesem Dokument werden allgemeine Shell-Datenübertragungsszenarien erläutert und erläutert, wie sie jede in Ihrer Anwendung implementieren.

Hinweis

Obwohl jedes dieser Szenarien einen bestimmten Datenübertragungsvorgang behandelt, gelten viele davon für eine Vielzahl verwandter Szenarien. So liegt beispielsweise der Hauptunterschied zwischen den meisten Zwischenablage- und Drag-and-Drop-Übertragungen darin, wie das Datenobjekt an das Ziel gelangt. Sobald das Ziel über einen Zeiger auf die IDataObject-Schnittstelle des Datenobjekts verfügt, sind die Verfahren zum Extrahieren von Informationen weitgehend identisch für beide Arten der Datenübertragung. Einige der Szenarien sind jedoch auf einen bestimmten Vorgangstyp beschränkt. Ausführliche Informationen finden Sie im einzelnen Szenario.

 

Allgemeine Richtlinien

In jedem der folgenden Abschnitte wird ein einzelnes, relativ spezifisches Datenübertragungsszenario erläutert. Datenübertragungen sind jedoch häufig komplexer und können Aspekte mehrerer Szenarien umfassen. In der Regel wissen Sie nicht, welches Szenario Sie tatsächlich verarbeiten müssen. Im Folgenden sind einige allgemeine Richtlinien aufgeführt, die Sie berücksichtigen sollten.

Für Datenquellen:

  • Die Shell-Zwischenablageformate mit Ausnahme von CF_HDROP sind nicht vordefinierte. Jedes format, das Sie verwenden möchten, muss durch Aufrufen von RegisterClipboardFormat registriert werden.
  • Die Formate in den Datenobjekten werden in der Reihenfolge der Voreinstellung aus der Quelle bereitgestellt. Auflisten des Datenobjekts und Auswählen des ersten Datenobjekts, das Sie verwenden können.
  • Fügen Sie so viele Formate wie möglich ein. Im Allgemeinen wissen Sie nicht, wo das Datenobjekt abgelegt wird. Diese Vorgehensweise verbessert die Wahrscheinlichkeit, dass das Datenobjekt ein Format enthält, das das Dropziel akzeptieren kann.
  • Vorhandene Dateien sollten mit dem CF_HDROP Format angeboten werden.
  • Bieten Sie dateiähnliche Daten mit CFSTR_FILECONTENTS/CFSTR_FILEDESCRIPTOR Formaten an. Mit diesem Ansatz kann das Ziel eine Datei aus einem Datenobjekt erstellen, ohne etwas über den zugrunde liegenden Datenspeicher wissen zu müssen. Normalerweise sollten Sie die Daten als IStream-Schnittstelle darstellen. Dieser Mechanismus für die Datenübertragung ist flexibler als ein globales Speicherobjekt und verwendet viel weniger Arbeitsspeicher.
  • Ziehquellen sollten das CFSTR_SHELLIDLIST Format beim Ziehen von Shellelementen bieten. Datenobjekte für Elemente können entweder über die Methoden "IShellFolder::GetUIObjectOf" oder "IShellItem::BindToHandler" abgerufen werden. Datenquellen können eine Standarddatenobjektimplementierung erstellen, die das CFSTR_SHELLIDLIST Format mithilfe von SHCreateDataObject unterstützt.
  • Drop-Ziele, die zu den Elementen führen möchten, die mithilfe des Shellelementprogrammiermodells gezogen werden, können ein IDataObject in ein IShellItemArray mit SHCreateShellItemArrayFromDataObject konvertieren.
  • Verwenden Sie standardmäßige Feedbackcursor.
  • Unterstützen Sie das Ziehen nach links und rechts.
  • Verwenden Sie das Datenobjekt selbst aus einem eingebetteten Objekt. Mit diesem Ansatz kann Ihre Anwendung alle zusätzlichen Formate abrufen, die das Datenobjekt bieten muss, und verhindert die Erstellung einer zusätzlichen Eindämmungsebene. Beispielsweise wird ein eingebettetes Objekt von Server A von Server/Container B gezogen und in Container C abgelegt, sollte ein eingebettetes Objekt von Server A und nicht ein eingebettetes Objekt von Server B erstellen, das ein eingebettetes Objekt von Server B enthält, das ein eingebettetes Objekt von Server A enthält.
  • Denken Sie daran, dass die Shell beim Verschieben von Dateien möglicherweise optimierte Verschiebungs - oder Löschvorgänge verwendet. Ihre Anwendung sollte diese Vorgänge erkennen und entsprechend reagieren können.

Für Datenziele:

  • Die Shell-Zwischenablageformate mit Ausnahme von CF_HDROP sind nicht vordefinierte. Jedes format, das Sie verwenden möchten, muss durch Aufrufen von RegisterClipboardFormat registriert werden.
  • Implementieren und Registrieren eines OLE-Drop-Ziels. Vermeiden Sie nach Möglichkeit die Verwendung von Windows 3.1-Zielen oder der WM_DROPFILES Nachricht.
  • Die in einem Datenobjekt enthaltenen Formate variieren je nach Herkunft des Objekts. Da Sie im Voraus nicht wissen, wo ein Datenobjekt stammt, gehen Sie nicht davon aus, dass ein bestimmtes Format vorhanden ist. Das Datenobjekt sollte die Formate in der Reihenfolge der Qualität aufzählen, beginnend mit dem besten. Um das beste verfügbare Format zu erhalten, enumerieren Anwendungen normalerweise die verfügbaren Formate und verwenden das erste Format in der Enumeration, die sie unterstützen können.
  • Unterstützung von rechts ziehen. Sie können das Kontextmenü anpassen, indem Sie einen Drag-and-Drop-Handler erstellen.
  • Wenn Ihre Anwendung vorhandene Dateien akzeptiert, muss sie das CF_HDROP Format verarbeiten können.
  • Im Allgemeinen sollten Anwendungen, die Dateien akzeptieren, auch die CFSTR_FILECONTENTS/CFSTR_FILEDESCRIPTOR Formate behandeln. Während Dateien aus dem Dateisystem das CF_HDROP Format aufweisen, verwenden Dateien von Anbietern wie Namespaceerweiterungen in der Regel CFSTR_FILECONTENTS/CFSTR_FILEDESCRIPTOR. Beispiele sind Windows CE-Ordner, FTP-Ordner (File Transfer Protocol), Webordner und CAB-Ordner. Die Quelle implementiert normalerweise eine IStream-Schnittstelle , um Daten aus dem Speicher als Datei darzustellen.
  • Denken Sie daran, dass die Shell beim Verschieben von Dateien möglicherweise optimierte Verschiebungs - oder Löschvorgänge verwendet. Ihre Anwendung sollte diese Vorgänge erkennen und entsprechend reagieren können.

Kopieren von Dateinamen aus der Zwischenablage in eine Anwendung

Szenario: Ein Benutzer wählt eine oder mehrere Dateien im Windows-Explorer aus und kopiert sie in die Zwischenablage. Ihre Anwendung extrahiert die Dateinamen und fügt sie in das Dokument ein.

Dieses Szenario kann z. B. verwendet werden, um es einem Benutzer zu ermöglichen, einen HTML-Link zu erstellen, indem die Datei ausgeschnitten und in Ihre Anwendung eingefügt wird. Die Anwendung kann dann den Dateinamen aus dem Datenobjekt extrahieren und verarbeiten, um ein Ankertag zu erstellen.

Wenn ein Benutzer eine Datei im Windows-Explorer auswählt und in die Zwischenablage kopiert, erstellt die Shell ein Datenobjekt. Anschließend wird OleSetClipboard aufgerufen, um einen Zeiger auf die IDataObject-Schnittstelle des Datenobjekts in der Zwischenablage zu platzieren.

Wenn der Benutzer den Befehl "Einfügen" aus dem Menü oder der Symbolleiste Ihrer Anwendung auswählt:

  1. Rufen Sie OleGetClipboard auf, um die IDataObject-Schnittstelle des Datenobjekts abzurufen.
  2. Rufen Sie IDataObject::EnumFormatEtc auf, um ein Enumerationsobjekt anzufordern.
  3. Verwenden Sie die IEnumFORMATETC-Schnittstelle des Enumeratorobjekts, um die im Datenobjekt enthaltenen Formate aufzählen zu können.

Hinweis

Die letzten beiden Schritte in diesem Verfahren sind zur Vollständigkeit enthalten. Sie sind in der Regel nicht für einfache Dateiübertragungen erforderlich. Alle Datenobjekte, die für diese Art der Datenübertragung verwendet werden, sollten das CF_HDROP Format enthalten, das verwendet werden kann, um die Namen der dateien zu bestimmen, die im Objekt enthalten sind. Für allgemeinere Datenübertragungen sollten Sie jedoch die Formate aufzählen und die beste auswählen, die Ihre Anwendung verarbeiten kann.

 

Extrahieren der Dateinamen aus dem Data-Objekt

Der nächste Schritt besteht darin, einen oder mehrere Dateinamen aus dem Datenobjekt zu extrahieren und in Ihre Anwendung einzufügen. Beachten Sie, dass das in diesem Abschnitt erläuterte Verfahren zum Extrahieren eines Dateinamens aus einem Datenobjekt gleichermaßen gut für Drag-and-Drop-Übertragungen gilt.

Die einfachste Möglichkeit zum Abrufen von Dateinamen aus einem Datenobjekt ist das CF_HDROP Format:

  1. Rufen Sie IDataObject::GetData auf. Legen Sie das cfFormat-Element der FORMATTC-Struktur auf CF_HDROP und das tymed-Element auf TYMED_HGLOBAL fest. Der dwAspect-Member ist normalerweise auf DVASPECT_CONTENT festgelegt. Wenn Sie jedoch den Pfad der Datei im kurzen Format (8.3) haben müssen, legen Sie dwAspect auf DVASPECT_SHORT fest.

    Wenn IDataObject::GetData zurückgegeben wird, verweist das hGlobal-Element der STGMEDIUM-Struktur auf ein globales Speicherobjekt, das die Daten enthält.

  2. Erstellen Sie eine HDROP-Variable, und legen Sie sie auf das hGlobal-Element der STGMEDIUM-Struktur fest. Die HDROP-Variable ist jetzt ein Handle für eine DROPFILES-Struktur gefolgt von einer doppelten null-beendeten Zeichenfolge, die die vollqualifizierten Dateipfade der kopierten Dateien enthält.

  3. Bestimmen Sie, wie viele Dateipfade in der Liste enthalten sind, indem Sie DragQueryFile aufrufen, wobei der iFile-Parameter auf 0xFFFFFFFF festgelegt ist. Die Funktion gibt die Anzahl der Dateipfade in der Liste zurück. Der nullbasierte Index des Dateipfads in dieser Liste wird im nächsten Schritt verwendet, um einen bestimmten Pfad zu identifizieren.

  4. Extrahieren Sie die Dateipfade aus dem globalen Speicherobjekt, indem Sie DragQueryFile einmal für jede Datei aufrufen, wobei iFile auf den Index der Datei festgelegt ist.

  5. Verarbeiten Sie die Dateipfade nach Bedarf, und fügen Sie sie in Ihre Anwendung ein.

  6. Rufen Sie ReleaseStgMedium auf, und übergeben Sie den Zeiger an die STGMEDIUM-Struktur , die Sie in Schritt 1 an IDataObject::GetData übergeben haben. Nachdem Sie die Struktur freigegeben haben, ist der HDROP-Wert, den Sie in Schritt 2 erstellt haben, nicht mehr gültig und sollte nicht mehr verwendet werden.

Kopieren des Inhalts einer abgelegten Datei in eine Anwendung

Szenario: Ein Benutzer zieht eine oder mehrere Dateien aus dem Windows-Explorer und legt sie im Fenster Ihrer Anwendung ab. Ihre Anwendung extrahiert den Inhalt der Datei (n) und fügt sie in die Anwendung ein.

In diesem Szenario wird drag-and-drop verwendet, um die Dateien von Windows Explorer in die Anwendung zu übertragen. Vor dem Vorgang muss Ihre Anwendung Folgendes ausführen:

  1. Rufen Sie RegisterClipboardFormat auf, um alle erforderlichen Shell-Zwischenablageformate zu registrieren.
  2. Rufen Sie RegisterDragDrop auf, um ein Zielfenster und die IDropTarget-Schnittstelle Ihrer Anwendung zu registrieren.

Nachdem der Benutzer den Vorgang initiiert hat, indem er eine oder mehrere Dateien auswählt und mit dem Ziehen beginnt:

  1. Windows Explorer erstellt ein Datenobjekt und lädt die unterstützten Formate in das Objekt.
  2. Windows Explorer ruft DoDragDrop auf, um die Ziehschleife zu initiieren.
  3. Wenn das Ziehbild das Zielfenster erreicht, benachrichtigt das System Sie durch Aufrufen von IDropTarget::D ragEnter.
  4. Um zu bestimmen, was das Datenobjekt enthält, rufen Sie die IDataObject::EnumFormatEtc-Methode des Datenobjekts auf. Verwenden Sie das Enumeratorobjekt, das von der Methode zurückgegeben wird, um die im Datenobjekt enthaltenen Formate aufzählen zu können. Wenn Ihre Anwendung eines dieser Formate nicht akzeptieren möchte, geben Sie DROPEFFECT_NONE zurück. Im Rahmen dieses Szenarios sollte Ihre Anwendung alle Datenobjekte ignorieren, die keine Formate zum Übertragen von Dateien enthalten, z . B. CF_HDROP.
  5. Wenn der Benutzer die Daten abbricht, ruft das System IDropTarget::D rop auf.
  6. Verwenden Sie die IDataObject-Schnittstelle , um den Inhalt der Dateien zu extrahieren.

Es gibt verschiedene Möglichkeiten, den Inhalt eines Shell-Objekts aus einem Datenobjekt zu extrahieren. Verwenden Sie im Allgemeinen die folgende Reihenfolge:

Wenn der Datenextraktionsvorgang lang ist, sollten Sie den Vorgang asynchron in einem Hintergrundthread ausführen. Der primäre Thread kann dann ohne unnötige Verzögerungen fortgesetzt werden. Eine Erläuterung zur Behandlung asynchroner Datenextraktion finden Sie unter Ziehen und Ablegen von Shellobjekten asynchron.

Verwenden des CFSTR_FILECONTENTS Formats zum Extrahieren von Daten aus einer Datei

Das CFSTR_FILECONTENTS-Format bietet eine sehr flexible und leistungsstarke Möglichkeit, den Inhalt einer Datei zu übertragen. Es ist nicht einmal erforderlich, dass die Daten als einzelne Datei gespeichert werden. Alles, was für dieses Format erforderlich ist, ist, dass das Datenobjekt die Daten als Datei an das Ziel darstellt. Beispielsweise kann es sich bei den tatsächlichen Daten um einen Abschnitt eines Textdokuments oder um einen Datenblock handeln, der aus einer Datenbank extrahiert wurde. Das Ziel kann die Daten als Datei behandeln und muss nichts über den zugrunde liegenden Speichermechanismus wissen.

Namespaceerweiterungen verwenden normalerweise CFSTR_FILECONTENTS zum Übertragen von Daten, da in diesem Format kein bestimmter Speichermechanismus vorausgesetzt wird. Eine Namespaceerweiterung kann jeden geeigneten Speichermechanismus verwenden und dieses Format verwenden, um seine Objekte an Anwendungen zu präsentieren, als wären sie Dateien.

Der Datenübertragungsmechanismus für CFSTR_FILECONTENTS ist normalerweise TYMED_ISTREAM. Das Übertragen eines IStream-Schnittstellenzeigers erfordert viel weniger Arbeitsspeicher als das Laden der Daten in ein globales Speicherobjekt, und IStream ist eine einfachere Möglichkeit, Daten darzustellen als IStorage.

Ein CFSTR_FILECONTENTS Format wird immer mit einem CFSTR_FILEDESCRIPTOR-Format begleitet. Sie müssen zuerst den Inhalt dieses Formats untersuchen. Wenn mehrere Dateien übertragen werden, enthält das Datenobjekt tatsächlich mehrere CFSTR_FILECONTENTS Formate, eine für jede Datei. Das CFSTR_FILEDESCRIPTOR Format enthält den Namen und die Attribute jeder Datei und stellt einen Indexwert für jede Datei bereit, die zum Extrahieren des CFSTR_FILECONTENTS-Formats einer bestimmten Datei erforderlich ist.

So extrahieren Sie ein CFSTR_FILECONTENTS Format:

  1. Extrahieren Sie das CFSTR_FILEDESCRIPTOR Format als TYMED_HGLOBAL Wert.
  2. Das hGlobal-Element der zurückgegebenen STGMEDIUM-Struktur verweist auf ein globales Speicherobjekt. Sperren Sie dieses Objekt, indem Sie den hGlobal-Wert an GlobalLock übergeben.
  3. Wandeln Sie den von GlobalLock zurückgegebenen Zeiger in einen FILEGROUPDESCRIPTOR-Zeiger um. Er verweist auf eine FILEGROUPDESCRIPTOR-Struktur, gefolgt von einer oder mehreren FILEDESCRIPTOR-Strukturen. Jede FILEDESCRIPTOR-Struktur enthält eine Beschreibung einer Datei, die in einem der zugehörigen CFSTR_FILECONTENTS Formate enthalten ist.
  4. Überprüfen Sie die FILEDESCRIPTOR-Strukturen , um zu bestimmen, welcher der Datei entspricht, die Sie extrahieren möchten. Der nullbasierte Index dieser FILEDESCRIPTOR-Struktur wird verwendet, um das CFSTR_FILECONTENTS Format der Datei zu identifizieren. Da die Größe eines globalen Speicherblocks nicht bytegenau ist, verwenden Sie die nFileSizeLow- und nFileSizeHigh-Member der Struktur, um zu bestimmen, wie viele Bytes die Datei im globalen Speicherobjekt darstellen.
  5. Rufen Sie "IDataObject::GetData" auf, wobei das cfFormat-Element der FORMATTC-Struktur auf den wert CFSTR_FILECONTENTS und das lIndex-Element auf den Index festgelegt ist, den Sie im vorherigen Schritt festgelegt haben. Das tymed-Element ist in der Regel auf TYMED_HGLOBAL | TYMED_ISTREAM | TYMED_ISTORAGE. Das Datenobjekt kann dann den bevorzugten Datenübertragungsmechanismus auswählen.
  6. Die STGMEDIUM-Struktur, die IDataObject::GetData zurückgibt, enthält einen Zeiger auf die Daten der Datei. Untersuchen Sie das tymed-Element der Struktur, um den Mechanismus für die Datenübertragung zu bestimmen.
  7. Wenn tymed auf TYMED_ISTREAM oder TYMED_ISTORAGE festgelegt ist, verwenden Sie die Schnittstelle, um die Daten zu extrahieren. Wenn tymed auf TYMED_HGLOBAL festgelegt ist, sind die Daten in einem globalen Speicherobjekt enthalten. Eine Erläuterung zum Extrahieren von Daten aus einem globalen Speicherobjekt finden Sie im Abschnitt "Extrahieren eines globalen Speicherobjekts aus einem Datenobjektabschnitt von Shell Data Object".
  8. Rufen Sie GlobalLock auf, um das globale Speicherobjekt zu entsperren, das Sie in Schritt 2 gesperrt haben.

Umgang mit optimierten Verschiebungsvorgängen

Szenario: Eine Datei wird mithilfe einer optimierten Verschiebung aus dem Dateisystem in eine Namespaceerweiterung verschoben.

Bei einem herkömmlichen Verschiebungsvorgang erstellt das Ziel eine Kopie der Daten, und die Quelle löscht das Original. Dieses Verfahren kann ineffizient sein, da es zwei Kopien der Daten erfordert. Bei großen Objekten wie Datenbanken ist ein herkömmlicher Verschiebungsvorgang möglicherweise nicht einmal praktisch.

Mit einer optimierten Verschiebung verwendet das Ziel sein Verständnis dafür, wie die Daten gespeichert werden, um den gesamten Verschiebungsvorgang zu verarbeiten. Es gibt nie eine zweite Kopie der Daten, und es ist nicht erforderlich, dass die Quelle die ursprünglichen Daten löscht. Shelldaten eignen sich gut für optimierte Verschiebungen, da das Ziel den gesamten Vorgang mithilfe der Shell-API verarbeiten kann. Ein typisches Beispiel ist das Verschieben von Dateien. Sobald das Ziel den Pfad einer zu verschiebenden Datei hat, kann es SHFileOperation verwenden, um sie zu verschieben. Die Ursprüngliche Datei muss von der Quelle nicht gelöscht werden.

Hinweis

Die Shell verwendet normalerweise eine optimierte Verschiebung zum Verschieben von Dateien. Um die Shell-Datenübertragung ordnungsgemäß zu verarbeiten, muss Ihre Anwendung in der Lage sein, eine optimierte Verschiebung zu erkennen und zu verarbeiten.

 

Optimierte Verschiebungen werden wie folgt behandelt:

  1. Die Quelle ruft DoDragDrop auf, wobei der dwEffect-Parameter auf DROPEFFECT_MOVE festgelegt ist, um anzugeben, dass die Quellobjekte verschoben werden können.

  2. Das Ziel empfängt den DROPEFFECT_MOVE Wert über eine der IDropTarget-Methoden , die angeben, dass eine Verschiebung zulässig ist.

  3. Das Ziel kopiert entweder das Objekt (nicht optimierte Verschiebung) oder verschiebt das Objekt (optimierte Verschiebung).

  4. Das Ziel teilt der Quelle dann mit, ob die ursprünglichen Daten gelöscht werden müssen.

    Eine optimierte Verschiebung ist der Standardvorgang, wobei die Daten vom Ziel gelöscht werden. So informieren Sie die Quelle, dass eine optimierte Verschiebung durchgeführt wurde:

      • Das Ziel legt den pdwEffect-Wert fest, der über die IDropTarget::D rop-Methode empfangen wurde, auf einen anderen Wert als DROPEFFECT_MOVE. Sie wird in der Regel entweder auf DROPEFFECT_NONE oder DROPEFFECT_COPY festgelegt. Der Wert wird von DoDragDrop an die Quelle zurückgegeben.
      • Das Ziel ruft auch die IDataObject::SetData-Methode des Datenobjekts auf und übergibt es einen CFSTR_PERFORMEDDROPEFFECT Formatbezeichner, der auf DROPEFFECT_NONE festgelegt ist. Dieser Methodenaufruf ist erforderlich, da einige Drop-Ziele den pdwEffect-Parameter von DoDragDrop möglicherweise nicht ordnungsgemäß festlegen. Das CFSTR_PERFORMEDDROPEFFECT-Format ist die zuverlässige Methode, um anzugeben, dass eine optimierte Verschiebung stattgefunden hat.

    Wenn das Ziel eine nicht optimierte Verschiebung ausgeführt hat, müssen die Daten von der Quelle gelöscht werden. So informieren Sie die Quelle, dass eine nicht optimierte Verschiebung durchgeführt wurde:

      • Das Ziel legt den pdwEffect-Wert fest, der über die IDropTarget::D rop-Methode empfangen wurde, auf DROPEFFECT_MOVE. Der Wert wird von DoDragDrop an die Quelle zurückgegeben.
      • Das Ziel ruft auch die IDataObject::SetData-Methode des Datenobjekts auf und übergibt es einen CFSTR_PERFORMEDDROPEFFECT Formatbezeichner, der auf DROPEFFECT_MOVE festgelegt ist. Dieser Methodenaufruf ist erforderlich, da einige Drop-Ziele den pdwEffect-Parameter von DoDragDrop möglicherweise nicht ordnungsgemäß festlegen. Das CFSTR_PERFORMEDDROPEFFECT Format ist die zuverlässige Methode, um anzugeben, dass eine nicht optimierte Verschiebung stattgefunden hat.
  5. Die Quelle prüft die beiden Werte, die vom Ziel zurückgegeben werden können. Wenn beide auf DROPEFFECT_MOVE festgelegt sind, wird die nicht optimierte Verschiebung abgeschlossen, indem die ursprünglichen Daten gelöscht werden. Andernfalls wurde das Ziel durch eine optimierte Verschiebung und die ursprünglichen Daten gelöscht.

Behandeln von Löschvorgängen beim Einfügen

Szenario: Mindestens eine Datei wird aus einem Ordner im Windows-Explorer ausgeschnitten und in eine Namespaceerweiterung eingefügt. Windows Explorer lässt die Dateien hervorgehoben, bis sie Feedback zum Ergebnis des Einfügevorgangs erhält.

Wenn ein Benutzer Daten schneidet, verschwindet er traditionell sofort aus der Ansicht. Dies ist möglicherweise nicht effizient, und es kann zu Problemen mit der Benutzerfreundlichkeit führen, wenn der Benutzer besorgt wird, was mit den Daten passiert ist. Ein alternativer Ansatz besteht darin, einen Löschvorgang beim Einfügen zu verwenden.

Bei einem Löschvorgang werden die ausgewählten Daten nicht sofort aus der Ansicht entfernt. Stattdessen markiert die Quellanwendung sie als ausgewählt, z. B. durch Ändern der Schrift- oder Hintergrundfarbe. Nachdem die Zielanwendung die Daten eingefügt hat, benachrichtigt sie die Quelle über das Ergebnis des Vorgangs. Wenn das Ziel eine optimierte Verschiebung durchgeführt hat, kann die Quelle einfach die Anzeige aktualisieren. Wenn das Ziel eine normale Verschiebung durchgeführt hat, muss die Quelle auch die Kopie der Daten löschen. Wenn der Einfügevorgang fehlschlägt, stellt die Quellanwendung die ausgewählten Daten auf die ursprüngliche Darstellung wieder her.

Hinweis

Die Shell verwendet normalerweise "delete-on-paste", wenn ein Ausschneiden/Einfügen-Vorgang zum Verschieben von Dateien verwendet wird. Löschvorgänge mit Shell-Objekten verwenden normalerweise eine optimierte Verschiebung , um die Dateien zu verschieben. Um die Shell-Datenübertragung ordnungsgemäß zu verarbeiten, muss Ihre Anwendung in der Lage sein, Löschvorgänge zu erkennen und zu verarbeiten.

 

Die wesentliche Anforderung für das Löschen beim Einfügen besteht darin, dass das Ziel das Ergebnis des Vorgangs an die Quelle melden muss. Standardtechniken für die Zwischenablage können jedoch nicht zum Implementieren von "Delete-on-Paste" verwendet werden, da sie keine Möglichkeit für die Kommunikation mit der Quelle bieten. Stattdessen verwendet die Zielanwendung die IDataObject::SetData-Methode des Datenobjekts, um das Ergebnis dem Datenobjekt zu melden. Das Datenobjekt kann dann über eine private Schnittstelle mit der Quelle kommunizieren.

Das grundlegende Verfahren für einen Löschvorgang beim Einfügen lautet wie folgt:

  1. Die Quelle markiert die Bildschirmanzeige der ausgewählten Daten.
  2. Die Quelle erstellt ein Datenobjekt. Es gibt einen Ausschneidevorgang an, indem das CFSTR_PREFERREDDROPEFFECT-Format mit einem Datenwert von DROPEFFECT_MOVE hinzugefügt wird.
  3. Die Quelle platziert das Datenobjekt in der Zwischenablage mithilfe von OleSetClipboard.
  4. Das Ziel ruft das Datenobjekt aus der Zwischenablage mithilfe von OleGetClipboard ab.
  5. Das Ziel extrahiert die CFSTR_PREFERREDDROPEFFECT Daten. Wenn sie nur auf DROPEFFECT_MOVE festgelegt ist, kann das Ziel entweder eine optimierte Verschiebung ausführen oder einfach die Daten kopieren.
  6. Wenn das Ziel keine optimierte Verschiebung ausführt, ruft es die IDataObject::SetData-Methode auf, wobei das CFSTR_PERFORMEDDROPEFFECT Format auf DROPEFFECT_MOVE festgelegt ist.
  7. Wenn das Einfügen abgeschlossen ist, ruft das Ziel die IDataObject::SetData-Methode auf, wobei das CFSTR_PASTESUCCEEDED Format auf DROPEFFECT_MOVE festgelegt ist.
  8. Wenn die IDataObject::SetData-Methode der Quelle aufgerufen wird, wobei das CFSTR_PASTESUCCEEDED Format auf DROPEFFECT_MOVE festgelegt ist, muss überprüft werden, ob sie auch das auf DROPEFFECT_MOVE festgelegte CFSTR_PERFORMEDDROPEFFECT Format empfangen hat. Wenn beide Formate vom Ziel gesendet werden, muss die Quelle die Daten löschen. Wenn nur das CFSTR_PASTESUCCEEDED Format empfangen wird, kann die Quelle die Daten einfach aus der Anzeige entfernen. Wenn die Übertragung fehlschlägt, aktualisiert die Quelle die Anzeige auf die ursprüngliche Darstellung.

Übertragen von Daten in und aus virtuellen Ordnern

Szenario: Ein Benutzer zieht ein Objekt aus einem virtuellen Ordner oder legt es ab.

Virtuelle Ordner enthalten Objekte, die im Allgemeinen nicht Teil des Dateisystems sind. Einige virtuelle Ordner, z. B. der Papierkorb, können Daten darstellen, die auf der Festplatte gespeichert sind, aber nicht als gewöhnliche Dateisystemobjekte. Einige können gespeicherte Daten darstellen, die sich auf einem Remotesystem befinden, z. B. einem Handheld-PC oder einem FTP-Standort. Andere, z. B. der Ordner "Drucker", enthalten Objekte, die überhaupt keine gespeicherten Daten darstellen. Während einige virtuelle Ordner Teil des Systems sind, können Entwickler auch benutzerdefinierte virtuelle Ordner erstellen und installieren, indem sie eine Namespaceerweiterung implementieren.

Unabhängig vom Typ der Daten oder deren Speicherung werden die Ordner- und Dateiobjekte, die in einem virtuellen Ordner enthalten sind, von der Shell so dargestellt, als wären sie normale Dateien und Ordner. Es liegt in der Verantwortung des virtuellen Ordners, die darin enthaltenen Daten zu übernehmen und der Shell entsprechend zu präsentieren. Diese Anforderung bedeutet, dass virtuelle Ordner normalerweise Drag-and-Drop- und Zwischenablage-Datenübertragungen unterstützen.

Es gibt also zwei Entwicklergruppen, die sich mit der Datenübertragung in und aus virtuellen Ordnern befassen müssen:

  • Entwickler, deren Anwendungen Daten akzeptieren müssen, die aus einem virtuellen Ordner übertragen werden.
  • Entwickler, deren Namespaceerweiterungen die Datenübertragung ordnungsgemäß unterstützen müssen.

Akzeptieren von Daten aus einem virtuellen Ordner

Virtuelle Ordner können praktisch jede Art von Daten darstellen und diese Daten auf beliebige Weise speichern. Einige virtuelle Ordner enthalten möglicherweise normale Dateisystemdateien und Ordner. Andere können beispielsweise alle ihre Objekte in ein einzelnes Dokument oder eine datenbank packen.

Wenn ein Dateisystemobjekt an eine Anwendung übertragen wird, enthält das Datenobjekt normalerweise ein CF_HDROP Format mit dem vollqualifizierten Pfad des Objekts. Ihre Anwendung kann diese Zeichenfolge extrahieren und die normalen Dateisystemfunktionen verwenden, um die Datei zu öffnen und ihre Daten zu extrahieren. Da virtuelle Ordner in der Regel jedoch keine normalen Dateisystemobjekte enthalten, verwenden sie in der Regel keine CF_HDROP.

Statt CF_HDROP werden Daten normalerweise aus virtuellen Ordnern mit den CFSTR_FILEDESCRIPTOR CFSTR_FILECONTENTS/ Formaten übertragen. Das CFSTR_FILECONTENTS Format hat gegenüber CF_HDROP zwei Vorteile:

  • Es wird keine bestimmte Methode der Datenspeicherung vorausgesetzt.
  • Das Format ist flexibler. Es unterstützt drei Datenübertragungsmechanismen: ein globales Speicherobjekt, eine IStream-Schnittstelle oder eine IStorage-Schnittstelle.

Globale Speicherobjekte werden selten verwendet, um Daten an oder aus virtuellen Objekten zu übertragen, da die Daten vollständig in den Arbeitsspeicher kopiert werden müssen. Das Übertragen eines Schnittstellenzeigers erfordert fast keinen Arbeitsspeicher und ist viel effizienter. Bei sehr großen Dateien kann ein Schnittstellenzeiger der einzige praktische Mechanismus für die Datenübertragung sein. In der Regel werden Daten durch einen IStream-Zeiger dargestellt, da diese Schnittstelle etwas flexibler als IStorage ist. Das Ziel extrahiert den Zeiger aus dem Datenobjekt und verwendet die Schnittstellenmethoden zum Extrahieren der Daten.

Weitere Informationen zur Behandlung der CFSTR_FILEDESCRIPTOR/CFSTR_FILECONTENTS Formate finden Sie unter Verwenden des CFSTR_FILECONTENTS Formats zum Extrahieren von Daten aus einer Datei.

Übertragen von Daten in und aus einer NameSpace-Erweiterung

Wenn Sie eine Namespaceerweiterung implementieren, sollten Sie normalerweise Drag-and-Drop-Funktionen unterstützen. Befolgen Sie die Empfehlungen für Drop-Quellen und Ziele, die in allgemeinen Richtlinien erläutert werden. Insbesondere muss eine Namespaceerweiterung:

  • Sie können die CFSTR_FILEDESCRIPTOR/CFSTR_FILECONTENTS Formate verarbeiten. Diese beiden Formate werden normalerweise verwendet, um Objekte in und aus Namespaceerweiterungen zu übertragen.
  • Sie können optimierte Verschiebungen verarbeiten. Die Shell erwartet, dass Shell-Objekte mit einer optimierten Verschiebung verschoben werden.
  • Sie können einen Löschvorgang beim Einfügen verarbeiten. Die Shell verwendet delete-on-paste, wenn Objekte mit einem Ausschneiden/Einfügen-Vorgang aus der Shell verschoben werden.
  • Sie können die Datenübertragung über eine IStream- oder IStorage-Schnittstelle verarbeiten. Die Datenübertragung an oder aus einem virtuellen Ordner wird normalerweise durch Übertragen eines dieser beiden Schnittstellenzeiger behandelt, in der Regel ein IStream-Zeiger . Anschließend ruft das Ziel die Schnittstellenmethoden zum Extrahieren der Daten auf:
      • Als Drop-Quelle muss die Namespaceerweiterung die Daten aus dem Speicher extrahieren und über diese Schnittstelle an das Ziel übergeben.
      • Als Drop-Ziel muss eine Namespaceerweiterung Daten aus einer Quelle über diese Schnittstelle akzeptieren und ordnungsgemäß speichern.

Ablegen von Dateien im Papierkorb

Szenario: Der Benutzer legt eine Datei im Papierkorb ab. Ihre Anwendungs- oder Namespaceerweiterung löscht die ursprüngliche Datei.

Der Papierkorb ist ein virtueller Ordner, der als Repository für Dateien verwendet wird, die nicht mehr benötigt werden. Solange der Papierkorb nicht geleert wurde, kann der Benutzer die Datei später wiederherstellen und an das Dateisystem zurückgeben.

Das Übertragen von Shell-Objekten in den Papierkorb funktioniert größtenteils wie jeder andere Ordner. Wenn ein Benutzer jedoch eine Datei im Papierkorb abbricht, muss die Quelle das Original löschen, auch wenn das Feedback aus dem Ordner einen Kopiervorgang angibt. Normalerweise hat eine Drop-Quelle keine Möglichkeit zu wissen, für welchen Ordner das Datenobjekt abgelegt wurde. Für Windows 2000- und höhere Systeme ruft die Shell jedoch beim Ablegen eines Datenobjekts im Papierkorb die IDataObject::SetData-Methode des Datenobjekts mit einem CFSTR_TARGETCLSID Format auf den Klassenbezeichner (CLSID) (CLSID) (clSID) (CLSID_RecycleBin) des Datenobjekts auf. Damit der Papierkorb ordnungsgemäß behandelt werden kann, sollte Das Datenobjekt dieses Format erkennen und die Informationen über eine private Schnittstelle an die Quelle übermitteln können.

Hinweis

Wenn IDataObject::SetData mit einem CFSTR_TARGETCLSID Format aufgerufen wird, das auf CLSID_RecycleBin festgelegt ist, sollte die Datenquelle alle geöffneten Handles für die Objekte schließen, die übertragen werden, bevor sie von der Methode zurückgegeben werden. Andernfalls können Sie Freigabeverletzungen erstellen.

 

Erstellen und Importieren von Scrap-Dateien

Szenario: Ein Benutzer zieht einige Daten aus der Datendatei einer OLE-Anwendung und legt sie auf dem Desktop oder Dem Windows-Explorer ab.

Windows ermöglicht Es Benutzern, ein Objekt aus der Datendatei einer OLE-Anwendung zu ziehen und es auf dem Desktop oder einem Dateisystemordner abzulegen. Dieser Vorgang erstellt eine Scrap-Datei, die die Daten oder eine Verknüpfung mit den Daten enthält. Der Dateiname stammt aus dem kurznamen, der für die CLSID des Objekts und die CF_TEXT Daten registriert ist. Damit die Shell eine Scrap-Datei mit Daten erstellt, muss die IDataObject-Schnittstelle der Anwendung das CF_EMBEDSOURCE Zwischenablageformat unterstützen. Zum Erstellen einer Datei mit einem Link muss IDataObject das CF_LINKSOURCE Format unterstützen.

Es gibt auch drei optionale Features, die eine Anwendung implementieren kann, um Scrap-Dateien zu unterstützen:

  • Hin- und Rücktransfer
  • Zwischengespeicherte Datenformate
  • Verzögertes Rendering

Hin- und Rücktransfer

Ein Roundtrip umfasst das Übertragen eines Datenobjekts in einen anderen Container und dann zurück zum Ursprünglichen Dokument. Beispielsweise könnte ein Benutzer eine Gruppe von Zellen aus einer Kalkulationstabelle auf den Desktop übertragen, wodurch eine Scrap-Datei mit den Daten erstellt wird. Wenn der Benutzer den Scrap dann wieder in das Arbeitsblatt überträgt, müssen die Daten wie vor der ursprünglichen Übertragung in das Dokument integriert werden.

Wenn die Shell die Scrap-Datei erstellt, stellt sie die Daten als Einbettungsobjekt dar. Wenn der Schrott in einen anderen Container übertragen wird, wird er als Einbettungsobjekt übertragen, auch wenn er an das originale Dokument zurückgegeben wird. Ihre Anwendung ist dafür verantwortlich, das im Scrap enthaltene Datenformat zu bestimmen und die Daten bei Bedarf wieder in das systemeigene Format einzufügen.

Um das Format des eingebetteten Objekts festzulegen, bestimmen Sie die CLSID, indem Sie das CF_OBJECTDESCRIPTOR Format des Objekts abrufen. Wenn die CLSID ein Datenformat angibt, das zur Anwendung gehört, sollte sie die systemeigenen Daten übertragen, anstatt OleCreateFromData aufzurufen.

Zwischengespeicherte Datenformate

Wenn die Shell eine Scrap-Datei erstellt, überprüft sie die Registrierung auf die Liste der verfügbaren Formate. Standardmäßig sind zwei Formate verfügbar: CF_EMBEDSOURCE und CF_LINKSOURCE. Es gibt jedoch eine Reihe von Szenarien, in denen Anwendungen möglicherweise Scrap-Dateien in verschiedenen Formaten haben müssen:

  • Damit Verschrottungen auf Nicht-OLE-Container übertragen werden können, die eingebettete Objektformate nicht akzeptiert werden können.
  • So ermöglichen Sie Anwendungssammlungen die Kommunikation mit einem privaten Format.
  • Um Roundtrips einfacher zu handhaben.

Anwendungen können dem Scrap Formate hinzufügen, indem sie in der Registrierung zwischengespeichert werden. Es gibt zwei Arten von zwischengespeicherten Formaten:

  • Prioritätscacheformate. Bei diesen Formaten werden die Daten vollständig aus dem Datenobjekt in den Schrott kopiert.
  • Verzögert gerenderte Formate. Bei diesen Formaten wird das Datenobjekt nicht in den Scrap kopiert. Stattdessen wird das Rendern verzögert, bis ein Ziel die Daten anfordert. Das Verzögerungsrendering wird im nächsten Abschnitt ausführlicher erläutert.

Wenn Sie einen Prioritätscache oder ein verzögertes Gerendertes Format hinzufügen möchten, erstellen Sie einen DataFormat-Unterschlüssel unter dem CLSID-Schlüssel der Anwendung, die die Quelle der Daten ist. Erstellen Sie unter diesem Unterschlüssel einen PriorityCacheFormats - oder DelayRenderFormats-Unterschlüssel . Erstellen Sie für jedes Prioritätscache- oder verzögerungsgerendertes Format einen nummerierten Unterschlüssel beginnend mit Null. Legen Sie den Wert dieses Schlüssels auf eine Zeichenfolge mit dem registrierten Namen des Formats oder einen #X Wert fest, wobei X die Formatnummer eines Standard-Zwischenablageformats darstellt.

Das folgende Beispiel zeigt zwischengespeicherte Formate für zwei Anwendungen. Die MyProg1-Anwendung verfügt über das Rich-Text-Format als Prioritätscacheformat und ein privates Format "Mein Format" als verzögert gerendertes Format. Die MyProg2-Anwendung weist das CF_BITMAP Format (#8") als Prioritätscacheformat auf.

HKEY_CLASSES_ROOT
   CLSID
      {GUID}
         (Default) = MyProg1
         DataFormats
            PriorityCacheFormats
               0
                  (Default) = Rich Text Format
            DelayRenderFormats
               0
                  (Default) = My Format
      {GUID}
         (Default) = MyProg2
         DataFormats
            PriorityCacheFormats
               0
                  (Default) = #8

Zusätzliche Formate können hinzugefügt werden, indem zusätzliche nummerierte Unterschlüssel erstellt werden.

Verzögertes Rendern

Mit einem verzögerten Renderingformat kann eine Anwendung eine Scrap-Datei erstellen, aber die Kosten für das Rendern der Daten verzögern, bis sie von einem Ziel angefordert wird. Die IDataObject-Schnittstelle eines Scraps bietet die verzögerten Renderingformate zusammen mit systemeigenen und zwischengespeicherten Daten an das Ziel. Wenn das Ziel ein verzögertes Renderingformat anfordert, führt die Shell die Anwendung aus und stellt die Daten für das Ziel aus dem aktiven Objekt bereit.

Hinweis

Da das verzögerte Rendering etwas riskant ist, sollte es mit Vorsicht verwendet werden. Es funktioniert nicht, wenn der Server nicht verfügbar ist, oder auf Anwendungen, die nicht OLE-aktiviert sind.

 

Asynchrones Ziehen und Ablegen von Shellobjekten

Szenario: Ein Benutzer überträgt einen großen Datenblock von der Quelle an das Ziel. Um zu vermeiden, dass beide Anwendungen für eine erhebliche Zeit blockiert werden, extrahiert das Ziel die Daten asynchron.

Normalerweise ist Drag-and-Drop ein synchroner Vorgang. Kurz gesagt:

  1. Die Drop-Quelle ruft DoDragDrop auf und blockiert den primären Thread, bis die Funktion zurückgegeben wird. Das Blockieren des primären Threads blockiert normalerweise die Ui-Verarbeitung.
  2. Nachdem die IDropTarget::D rop-Methode des Ziels aufgerufen wurde, extrahiert das Ziel die Daten aus dem Datenobjekt im primären Thread. Dieses Verfahren blockiert normalerweise die UI-Verarbeitung des Ziels für die Dauer des Extraktionsprozesses.
  3. Nachdem die Daten extrahiert wurden, gibt das Ziel den IDropTarget::D rop-Aufruf zurück, das System gibt DoDragDrop zurück, und beide Threads können fortfahren.

Kurz gesagt, die synchrone Datenübertragung kann die primären Threads beider Anwendungen für einen erheblichen Zeitraum blockieren. Insbesondere müssen beide Threads warten, während das Ziel die Daten extrahiert. Bei kleinen Datenmengen ist die zum Extrahieren von Daten erforderliche Zeit recht gut und die synchrone Datenübertragung funktioniert sehr gut. Das synchrone Extrahieren großer Datenmengen kann jedoch zu langen Verzögerungen führen und die Benutzeroberfläche sowohl des Ziels als auch der Quelle beeinträchtigen.

Die IAsyncOperation/IDataObjectAsyncCapability-Schnittstelle ist eine optionale Schnittstelle, die von einem Datenobjekt implementiert werden kann. Es bietet dem Dropziel die Möglichkeit, Daten aus dem Datenobjekt asynchron in einem Hintergrundthread zu extrahieren. Sobald die Datenextraktion an den Hintergrundthread übergeben wird, können die primären Threads beider Anwendungen fortgesetzt werden.

Verwenden von IASyncOperation/IDataObjectAsyncCapability

Hinweis

Die Schnittstelle wurde ursprünglich als IAsyncOperation bezeichnet, dies wurde jedoch später in "IDataObjectAsyncCapability" geändert. Andernfalls sind die beiden Schnittstellen identisch.

 

Der Zweck von IAsyncOperation/IDataObjectAsyncCapability besteht darin, die Dropquelle und das Dropziel zuzulassen, um auszuhandeln, ob Daten asynchron extrahiert werden können. Das folgende Verfahren beschreibt, wie die Dropquelle die Schnittstelle verwendet:

  1. Erstellen Sie ein Datenobjekt, das IAsyncOperation/IDataObjectAsyncCapability verfügbar macht.
  2. Rufen Sie SetAsyncMode mit fDoOpAsync auf VARIANT_TRUE auf, um anzugeben, dass ein asynchroner Vorgang unterstützt wird.
  3. Rufen Sie "InOperation" auf, nachdem DoDragDrop zurückgegeben wurde:
    • Wenn InOperation fehlschlägt oder VARIANT_FALSE zurückgibt, wurde eine normale synchrone Datenübertragung durchgeführt, und der Datenextraktionsvorgang ist abgeschlossen. Die Quelle sollte alle erforderlichen Bereinigungen ausführen und fortfahren.
    • Wenn InOperation VARIANT_TRUE zurückgibt, werden die Daten asynchron extrahiert. Bereinigungsvorgänge sollten von EndOperation behandelt werden.
  4. Geben Sie das Datenobjekt frei.
  5. Wenn die asynchrone Datenübertragung abgeschlossen ist, benachrichtigt das Datenobjekt die Quelle normalerweise über eine private Schnittstelle.

Das folgende Verfahren beschreibt, wie das Drop-Ziel die IAsyncOperation/IDataObjectAsyncCapability-Schnittstelle verwendet, um Daten asynchron zu extrahieren:

  1. Wenn das System IDropTarget::D rop aufruft, rufen Sie IDataObject::QueryInterface auf, und fordern Sie eine IAsyncOperation/IDataObjectAsyncCapability-Schnittstelle (IID_IAsyncOperation/IID_IDataObjectAsyncCapability) aus dem Datenobjekt an.
  2. Rufen Sie GetAsyncMode auf. Wenn die Methode VARIANT_TRUE zurückgibt, unterstützt das Datenobjekt die asynchrone Datenextraktion.
  3. Erstellen Sie einen separaten Thread, um die Datenextraktion zu verarbeiten und StartOperation aufzurufen.
  4. Geben Sie den IDropTarget::D rop-Aufruf wie für einen normalen Datenübertragungsvorgang zurück. DoDragDrop gibt die Dropquelle zurück und entsperrt sie. Rufen Sie IDataObject::SetData nicht auf, um das Ergebnis eines optimierten Verschiebungs- oder Löschvorgangs anzugeben. Warten Sie, bis der Vorgang abgeschlossen ist.
  5. Extrahieren Sie die Daten im Hintergrundthread. Der primäre Thread des Ziels wird entsperrt und kann fortgesetzt werden.
  6. Wenn die Datenübertragung ein optimierter Verschiebungs- oder Löschvorgang war, rufen Sie IDataObject::SetData auf, um das Ergebnis anzugeben.
  7. Benachrichtigen Sie das Datenobjekt, dass die Extraktion abgeschlossen ist, indem Sie EndOperation aufrufen.