Behandeln von Shelldatenübertragungs-Szenarien

Im Dokument Shell-Datenobjekt wurde der allgemeine Ansatz erläutert, der zum Übertragen von Shelldaten mit Drag-and-Drop oder der Zwischenablage verwendet wird. Um jedoch die Shell-Datenübertragung in Ihrer Anwendung zu implementieren, müssen Sie auch wissen, wie Sie diese allgemeinen Prinzipien und Techniken auf die verschiedenen Möglichkeiten anwenden, wie Shell-Daten übertragen werden können. In diesem Dokument werden allgemeine Shell-Datenübertragungsszenarien vorgestellt und erläutert, wie Sie die einzelnen Shell-Datenübertragungen in Ihrer Anwendung implementieren.

Hinweis

Obwohl jedes dieser Szenarien einen bestimmten Datenübertragungsvorgang behandelt, gelten viele von ihnen für eine Vielzahl von verwandten Szenarien. Bei instance besteht der Hauptunterschied zwischen den meisten Übertragungen zwischen Zwischenablage und Drag-and-Drop darin, wie das Datenobjekt beim Ziel ankommt. Sobald das Ziel über einen Zeiger auf die IDataObject-Schnittstelle des Datenobjekts verfügt, sind die Verfahren zum Extrahieren von Informationen für beide Arten der Datenübertragung weitgehend identisch. 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, ziemlich spezifisches Datenübertragungsszenario erläutert. Datenübertragungen sind jedoch häufig komplexer und können Aspekte mehrerer Szenarien umfassen. In der Regel wissen Sie im Voraus nicht, welches Szenario Sie tatsächlich behandeln müssen. Im Folgenden finden Sie einige allgemeine Richtlinien, die Sie beachten sollten.

Für Datenquellen:

  • Die Shell-Zwischenablageformate mit Ausnahme von CF_HDROP sind nicht vordefiniert. Jedes Format, das Sie verwenden möchten, muss durch Aufrufen von RegisterClipboardFormat registriert werden.
  • Die Formate in den Datenobjekten werden in der Reihenfolge der Präferenz aus der Quelle bereitgestellt. Enumerieren Sie das Datenobjekt, und wählen Sie das erste Objekt aus, das Sie nutzen können.
  • Fügen Sie so viele Formate ein, wie Sie unterstützen können. In der Regel wissen Sie nicht, wo das Datenobjekt gelöscht wird. Diese Vorgehensweise verbessert die Wahrscheinlichkeit, dass das Datenobjekt ein Format enthält, das das Ablageziel akzeptieren kann.
  • Vorhandene Dateien sollten im format CF_HDROP angeboten werden.
  • Bieten Sie dateiähnliche Daten mit CFSTR_FILECONTENTS/CFSTR_FILEDESCRIPTOR Formaten an. Dieser Ansatz ermöglicht es dem Ziel, eine Datei aus einem Datenobjekt zu erstellen, ohne etwas über den zugrunde liegenden Datenspeicher wissen zu müssen. Normalerweise sollten Sie die Daten als IStream-Schnittstelle darstellen. Dieser Datenübertragungsmechanismus ist flexibler als ein globales Speicherobjekt und benötigt viel weniger Arbeitsspeicher.
  • Ziehquellen sollten beim Ziehen von Shellelementen das CFSTR_SHELLIDLIST Format aufweisen. Datenobjekte für Elemente können entweder über die Methoden IShellFolder::GetUIObjectOf oder IShellItem::BindToHandler abgerufen werden. Datenquellen können mithilfe von SHCreateDataObject eine Standarddatenobjektimplementierung erstellen, die das CFSTR_SHELLIDLIST-Format unterstützt.
  • Drop-Ziele, die über die Elemente, die mit dem Shellelementprogrammmodell gezogen werden, argumentieren möchten, können ein IDataObject mithilfe von SHCreateShellItemArrayFromDataObject in ein IShellItemArray konvertieren.
  • Verwenden Sie standardmäßige Feedbackcursor.
  • Unterstützung des Links- und Rechtsziehens.
  • Verwenden Sie das Datenobjekt selbst aus einem eingebetteten Objekt. Dieser Ansatz ermöglicht es Ihrer Anwendung, alle zusätzlichen Formate abzurufen, die das Datenobjekt bietet, und verhindert das Erstellen einer zusätzlichen Eindämmungsebene. Für instance wird ein eingebettetes Objekt von Server A von Server/Container B gezogen und in Container C abgelegt. C sollte ein eingebettetes Objekt von Server A erstellen, kein eingebettetes Objekt von Server B, 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 beim Einfügen verwendet. Ihre Anwendung sollte in der Lage sein, diese Vorgänge zu erkennen und entsprechend zu reagieren.

Für Datenziele:

  • Die Shell-Zwischenablageformate mit Ausnahme von CF_HDROP sind nicht vordefiniert. Jedes Format, das Sie verwenden möchten, muss durch Aufrufen von RegisterClipboardFormat registriert werden.
  • Implementieren und registrieren Sie ein OLE-Ablageziel. Vermeiden Sie nach Möglichkeit die Verwendung von Windows 3.1-Zielen oder der WM_DROPFILES Meldung.
  • Die in einem Datenobjekt enthaltenen Formate variieren je nachdem, wo das Objekt herkommt. Da Sie im Allgemeinen im Voraus nicht wissen, woher 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 den besten. Um also das beste verfügbare Format zu erhalten, enumerieren Anwendungen normalerweise die verfügbaren Formate und verwenden das erste Format in der Enumeration, das sie unterstützen können.
  • Unterstützung für rechts ziehen. Sie können das Kontextmenü zum Ziehen 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 Formaten verarbeiten. Während Dateien aus dem Dateisystem das CF_HDROP Format haben, verwenden Dateien von Anbietern wie Namespaceerweiterungen in der Regel CFSTR_FILECONTENTS/CFSTR_FILEDESCRIPTOR. Beispiele hierfür sind Windows CE Ordner, FTP-Ordner (File Transfer Protocol), Webordner und CAB-Ordner. Die Quelle implementiert normalerweise eine IStream-Schnittstelle , um Daten aus ihrem Speicher als Datei darzustellen.
  • Denken Sie daran, dass die Shell beim Verschieben von Dateien möglicherweise optimierte Verschiebungs - oder Löschvorgänge beim Einfügen verwendet. Ihre Anwendung sollte in der Lage sein, diese Vorgänge zu erkennen und entsprechend zu reagieren.

Kopieren von Dateinamen aus der Zwischenablage in eine Anwendung

Szenario: Ein Benutzer wählt eine oder mehrere Dateien in 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 für instance verwendet werden, um es einem Benutzer zu ermöglichen, einen HTML-Link zu erstellen, indem er die Datei ausschneidet und in Ihre Anwendung einzufügen. Ihre Anwendung kann dann den Dateinamen aus dem Datenobjekt extrahieren und zum Erstellen eines Ankertags verarbeiten.

Wenn ein Benutzer eine Datei in 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 im Menü oder in der Symbolleiste Ihrer Anwendung den Befehl Einfügen auswählt:

  1. Rufen Sie OleGetClipboard auf, um die IDataObject-Schnittstelle des Datenobjekts abzurufen.
  2. Rufen Sie IDataObject::EnumFormatEtc auf, um ein Enumeratorobjekt anzufordern.
  3. Verwenden Sie die IEnumFORMATETC-Schnittstelle des Enumeratorobjekts, um die im Datenobjekt enthaltenen Formate aufzulisten.

Hinweis

Die letzten beiden Schritte in diesem Verfahren sind aus Gründen der 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, mit dem die Namen der im Objekt enthaltenen Dateien bestimmt werden können. Für allgemeinere Datenübertragungen sollten Sie jedoch die Formate aufzählen und das beste Format auswählen, das Ihre Anwendung am besten verarbeiten kann.

 

Extrahieren der Dateinamen aus dem Datenobjekt

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 auch 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 den cfFormat-Member 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 Kurzformat (8.3) benötigen, legen Sie dwAspect auf DVASPECT_SHORT fest.

    Wenn IDataObject::GetData zurückgibt, zeigt 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-Zeichenfolge, die die vollqualifizierten Dateipfade der kopierten Dateien enthält.

  3. Ermitteln Sie, wie viele Dateipfade in der Liste enthalten sind, indem Sie DragQueryFile aufrufen, wobei der Parameter iFile 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 auf 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 gelöschten Datei in eine Anwendung

Szenario: Ein Benutzer zieht eine oder mehrere Dateien aus Windows Explorer und löscht sie im Fenster Ihrer Anwendung. Ihre Anwendung extrahiert den Inhalt der Datei(en) und fügt ihn 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 dieses.
  2. Windows Explorer ruft DoDragDrop auf, um die Ziehschleife zu initiieren.
  3. Wenn das Bild das Zielfenster erreicht, benachrichtigt Sie das System 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 von der -Methode zurückgegebene Enumeratorobjekt, um die im Datenobjekt enthaltenen Formate aufzulisten. Wenn Ihre Anwendung keines dieser Formate akzeptieren möchte, geben Sie DROPEFFECT_NONE zurück. Für die Zwecke dieses Szenarios sollte Ihre Anwendung alle Datenobjekte ignorieren, die keine Formate enthalten, die zum Übertragen von Dateien verwendet werden, z. B. CF_HDROP.
  5. Wenn der Benutzer die Daten löscht, 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 Datenextraktionsprozess langwierig ist, sollten Sie den Vorgang asynchron in einem Hintergrundthread ausführen. Ihr primärer Thread kann dann ohne unnötige Verzögerungen fortgesetzt werden. Eine Erläuterung zum Behandeln der asynchronen 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 notwendig, dass die Daten als einzelne Datei gespeichert werden. Für dieses Format ist nur erforderlich, dass das Datenobjekt die Daten dem Ziel wie eine Datei präsentiert. Bei instance können die tatsächlichen Daten ein Abschnitt eines Textdokuments oder ein Aus einer Datenbank extrahierter Datenblock sein. 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 dieses Format keinen bestimmten Speichermechanismus vorausnimmt. Eine Namespaceerweiterung kann einen beliebigen geeigneten Speichermechanismus verwenden und dieses Format verwenden, um ihre Objekte anwendungen so zu präsentieren, als wären sie Dateien.

Der Datenübertragungsmechanismus für CFSTR_FILECONTENTS wird 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 von einem CFSTR_FILEDESCRIPTOR-Format begleitet. Sie müssen zunächst den Inhalt dieses Formats untersuchen. Wenn mehr als eine Datei übertragen wird, enthält das Datenobjekt tatsächlich mehrere CFSTR_FILECONTENTS Formate, eines 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. Es zeigt auf eine FILEGROUPDESCRIPTOR-Struktur gefolgt von mindestens einer FILEDESCRIPTOR-Struktur . Jede FILEDESCRIPTOR-Struktur enthält eine Beschreibung einer Datei, die in einem der begleitenden CFSTR_FILECONTENTS-Formate enthalten ist.
  4. Untersuchen Sie die FILEDESCRIPTOR-Strukturen , um zu ermitteln, welche 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 Elemente nFileSizeLow und nFileSizeHigh 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 FORMATETC-Struktur auf den CFSTR_FILECONTENTS-Wert 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 seinen bevorzugten Datenübertragungsmechanismus auswählen.
  6. Die von IDataObject::GetData zurückgegebene STGMEDIUM-Struktur enthält einen Zeiger auf die Daten der Datei. Untersuchen Sie das Tymed-Element der -Struktur, um den Datenübertragungsmechanismus 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 Datenobjekt von Shell Data Object.
  8. Rufen Sie GlobalLock auf, um das globale Speicherobjekt zu entsperren, das Sie in Schritt 2 gesperrt haben.

Behandeln optimierter Verschiebungsvorgänge

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 zwei Kopien der Daten erforderlich sind. Bei großen Objekten wie Datenbanken ist ein konventioneller Verschiebungsvorgang möglicherweise nicht einmal praktikabel.

Bei einer optimierten Verschiebung nutzt das Ziel sein Verständnis der Speicherung der Daten, um den gesamten Verschiebungsvorgang zu verarbeiten. Es gibt nie eine zweite Kopie der Daten, und die Quelle muss die ursprünglichen Daten nicht löschen. 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 über den Pfad einer zu verschiebenden Datei verfügt, kann es SHFileOperation verwenden, um es zu verschieben. Die Quelle muss die ursprüngliche Datei nicht löschen.

Hinweis

Die Shell verwendet normalerweise eine optimierte Verschiebung, um Dateien zu verschieben. Damit die Shell-Datenübertragung ordnungsgemäß verarbeitet werden kann, 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 seiner IDropTarget-Methoden , die angibt, dass eine Verschiebung zulässig ist.

  3. Das Ziel kopiert entweder das Objekt (nicht optimiertes Verschieben) oder verschiebt das Objekt (optimiertes Verschieben).

  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 ausgeführt wurde:

      • Das Ziel legt den pdwEffect-Wert , den es über seine IDropTarget::D rop-Methode erhalten hat, auf einen anderen Wert als DROPEFFECT_MOVE fest. Sie ist in der Regel 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 einen CFSTR_PERFORMEDDROPEFFECT Formatbezeichner an DROPEFFECT_NONE. Dieser Methodenaufruf ist erforderlich, da einige Dropziele 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 ausgeführt wurde:

  5. Die Quelle überprü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 hat das Ziel eine optimierte Verschiebung durchgeführt, und die ursprünglichen Daten wurden gelöscht.

Behandeln von Löschvorgängen beim Einfügen

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

Wenn ein Benutzer Daten ausschneidet, werden diese in der Regel sofort aus der Ansicht entfernt. Dies ist möglicherweise nicht effizient und kann zu Problemen mit der Benutzerfreundlichkeit führen, wenn sich der Benutzer Gedanken darüber macht, was mit den Daten passiert ist. Ein alternativer Ansatz ist die Verwendung eines Delete-on-Paste-Vorgangs.

Bei einem Vorgang zum Löschen beim Einfügen werden die ausgewählten Daten nicht sofort aus der Ansicht entfernt. Stattdessen markiert die Quellanwendung sie als ausgewählt, z. B. durch Ändern der Schriftart oder Der 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 ihre Anzeige aktualisieren. Wenn das Ziel eine normale Verschiebung ausgeführt hat, muss die Quelle auch ihre Kopie der Daten löschen. Wenn beim Einfügen ein Fehler auftritt, stellt die Quellanwendung die ausgewählten Daten in ihrem ursprünglichen Erscheinungsbild 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 bei Shellobjekten verwenden normalerweise eine optimierte Verschiebung , um die Dateien zu verschieben. Damit die Shell-Datenübertragung ordnungsgemäß verarbeitet werden kann, muss Ihre Anwendung in der Lage sein, Löschvorgänge beim Einfügen zu erkennen und zu verarbeiten.

 

Die wesentliche Voraussetzung für delete-on-paste ist, dass das Ziel das Ergebnis des Vorgangs an die Quelle melden muss. Standardmäßige Zwischenablagetechniken können jedoch nicht zum Implementieren von Delete-on-Paste verwendet werden, da sie dem Ziel keine Möglichkeit bieten, mit der Quelle zu kommunizieren. 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 Vorgang zum Löschen 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 Ausschneidenvorgang an, indem das CFSTR_PREFERREDDROPEFFECT-Format mit dem Datenwert DROPEFFECT_MOVE hinzugefügt wird.
  3. Die Quelle platziert das Datenobjekt mithilfe von OleSetClipboard in der Zwischenablage.
  4. Das Ziel ruft das Datenobjekt mithilfe von OleGetClipboard aus der Zwischenablage ab.
  5. Das Ziel extrahiert die CFSTR_PREFERREDDROPEFFECT Daten. Wenn es nur auf DROPEFFECT_MOVE festgelegt ist, kann das Ziel entweder eine optimierte Verschiebung durchführen oder einfach die Daten kopieren.
  6. Wenn das Ziel keine optimierte Verschiebung durchführt, ruft es die IDataObject::SetData-Methode auf, wobei das CFSTR_PERFORMEDDROPEFFECT Format auf DROPEFFECT_MOVE festgelegt ist.
  7. Wenn der Einfügevorgang 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 ihre 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 normale 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 Printers, 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 ihrer 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, alle darin enthaltenen Daten zu übernehmen und der Shell entsprechend zu präsentieren. Diese Anforderung bedeutet, dass virtuelle Ordner normalerweise Drag-and-Drop- und Zwischenablagedatenübertragungen unterstützen.

Es gibt also zwei Gruppen von Entwicklern, 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 jeden Datentyp darstellen und diese Daten auf beliebige Weise speichern. Einige virtuelle Ordner enthalten möglicherweise normale Dateisystemdateien und -ordner. Andere können für instance alle ihre Objekte in ein einzelnes Dokument oder eine einzelne 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 jedoch in der Regel keine normalen Dateisystemobjekte enthalten, verwenden sie in der Regel keine CF_HDROP.

Anstelle von 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 zum Übertragen von Daten in oder aus virtuellen Objekten verwendet, da die Daten vollständig in den Arbeitsspeicher kopiert werden müssen. Die Übertragung eines Schnittstellenzeigers benötigt fast keinen Arbeitsspeicher und ist viel effizienter. Bei sehr großen Dateien kann ein Schnittstellenzeiger der einzige praktische Datenübertragungsmechanismus 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, um die Daten zu extrahieren.

Weitere Informationen zum Umgang mit CFSTR_FILEDESCRIPTOR/CFSTR_FILECONTENTS Formaten finden Sie unter Verwenden des CFSTR_FILECONTENTS-Formats zum Extrahieren von Daten aus einer Datei.

Übertragen von Daten an 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 Ablagequellen und Ziele, die in allgemeinen Richtlinien erläutert werden. Insbesondere muss eine Namespaceerweiterung Folgendes ausführen:

  • Sie können die CFSTR_FILEDESCRIPTOR/CFSTR_FILECONTENTS-Formate verarbeiten. Diese beiden Formate werden normalerweise verwendet, um Objekte in und aus Namespaceerweiterungen zu übertragen.
  • In der Lage sein, optimierte Verschiebungen zu verarbeiten. Die Shell erwartet, dass Shell-Objekte mit einer optimierten Verschiebung verschoben werden.
  • Sie können einen Vorgang zum Löschen 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 zu oder aus einem virtuellen Ordner erfolgt normalerweise durch Übertragen eines dieser beiden Schnittstellenzeiger, in der Regel ein IStream-Zeiger . Das Ziel ruft dann die Schnittstellenmethoden auf, um die Daten zu extrahieren:
      • Als Ablagequelle muss die Namespaceerweiterung die Daten aus dem Speicher extrahieren und über diese Schnittstelle an das Ziel übergeben.
      • Als Ablageziel muss eine Namespaceerweiterung Daten aus einer Quelle über diese Schnittstelle akzeptieren und ordnungsgemäß speichern.

Löschen 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 nicht mehr benötigte Dateien verwendet wird. Solange der Papierkorb nicht geleert wurde, kann der Benutzer die Datei später wiederherstellen und an das Dateisystem zurückgeben.

In den meisten Fällen funktioniert das Übertragen von Shellobjekten in den Papierkorb wie jeder andere Ordner. Wenn ein Benutzer jedoch eine Datei im Papierkorb abwirft, muss die Quelle das Original löschen, auch wenn das Feedback aus dem Ordner auf einen Kopiervorgang hinweist. Normalerweise kann eine Ablagequelle nicht wissen, in welchem Ordner ihr Datenobjekt abgelegt wurde. Wenn jedoch bei Systemen unter Windows 2000 und höher ein Datenobjekt im Papierkorb abgelegt wird, ruft die Shell die IDataObject::SetData-Methode des Datenobjekts mit einem CFSTR_TARGETCLSID Format auf, das auf den Klassenbezeichner des Papierkorbs (CLSID) festgelegt ist (CLSID_RecycleBin). Um den Papierkorb-Fall ordnungsgemäß zu behandeln, sollte Ihr Datenobjekt in der Lage sein, dieses Format zu erkennen und die Informationen über eine private Schnittstelle an die Quelle zu übermitteln.

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 wird. Andernfalls können Sie Verstöße gegen die Freigabe erstellen.

 

Erstellen und Importieren von Ausschussdateien

Szenario: Ein Benutzer zieht einige Daten aus der Datendatei einer OLE-Anwendung und löscht sie auf dem Desktop oder windows Explorer.

Mit Windows können Benutzer ein Objekt aus der Datendatei einer OLE-Anwendung ziehen und es auf dem Desktop oder in einem Dateisystemordner ablegen. Bei diesem Vorgang wird eine Scrap-Datei erstellt, die die Daten oder einen Link zu den Daten enthält. Der Dateiname stammt aus dem Kurznamen, der für die CLSID des Objekts registriert ist, und den CF_TEXT Daten. Damit die Shell eine Scrap-Datei mit Daten erstellt, muss die IDataObject-Schnittstelle der Anwendung das format CF_EMBEDSOURCE Zwischenablage unterstützen. Um eine Datei zu erstellen, die einen Link enthält, muss IDataObject das CF_LINKSOURCE Format unterstützen.

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

  • Roundtrip-Unterstützung
  • Zwischengespeicherte Datenformate
  • Verzögertes Rendern

Roundtrip-Support

Ein Roundtrip umfasst die Übertragung eines Datenobjekts in einen anderen Container und dann zurück zum originalen Dokument. Für instance kann ein Benutzer eine Gruppe von Zellen aus einer Kalkulationstabelle auf den Desktop übertragen und eine Auslagerungsdatei mit den Daten erstellen. Wenn der Benutzer dann den Ausschuss zurück in die Tabelle ü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 Ausschuss enthaltene Datenformat zu bestimmen und die Daten bei Bedarf wieder in das systemeigene Format zu versetzen.

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

Zwischengespeicherte Datenformate

Wenn die Shell eine Auswenddatei erstellt, überprüft sie die Registrierung auf die Liste der verfügbaren Formate. Standardmäßig stehen zwei Formate zur Verfügung: CF_EMBEDSOURCE und CF_LINKSOURCE. Es gibt jedoch eine Reihe von Szenarien, in denen Anwendungen möglicherweise Ausschussdateien in verschiedenen Formaten benötigen:

  • Um die Übertragung von Scraps in Nicht-OLE-Container zu ermöglichen, die eingebettete Objektformate nicht akzeptieren können.
  • So können Anwendungssammlungen mit einem privaten Format kommunizieren.
  • Um Die Handhabung von Roundtrips zu vereinfachen.

Anwendungen können dem Ausschuss Formate hinzufügen, indem sie sie in der Registrierung zwischenspeichern. Es gibt zwei Typen von zwischengespeicherten Formaten:

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

Um einen Prioritätscache oder ein verzögert gerendertes Format hinzuzufügen, 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 jeden Prioritätscache oder jedes verzögert gerenderte Format einen nummerierten Unterschlüssel, der mit 0 beginnt. Legen Sie den Wert dieses Schlüssels entweder auf eine Zeichenfolge mit dem registrierten Namen des Formats oder auf einen #X Wert fest, wobei X die Formatnummer eines Standardformats für die Zwischenablage 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 das private Format "My Format" als verzögert gerendertes Format. Die MyProg2-Anwendung verfügt über das CF_BITMAP Format (#8") als Prioritätscacheformat.

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

Ein verzögertes Renderingformat ermöglicht es einer Anwendung, eine Auswiederherstellungsdatei zu erstellen, aber den Aufwand für das Rendern der Daten zu verzögern, bis sie von einem Ziel angefordert werden. Die IDataObject-Schnittstelle eines Scraps bietet dem Ziel die verzögerten Renderingformate zusammen mit nativen und zwischengespeicherten Daten. Wenn das Ziel ein verzögertes Renderingformat anfordert, führt die Shell die Anwendung aus und stellt die Daten aus dem aktiven Objekt für das Ziel bereit.

Hinweis

Da verzögertes Rendern etwas riskant ist, sollte es mit Vorsicht verwendet werden. Dies funktioniert nicht, wenn der Server nicht verfügbar ist oder anwendungen verwendet wird, die nicht OLE-fähig sind.

 

Asynchrones Ziehen und Ablegen von Shellobjekten

Szenario: Ein Benutzer überträgt einen großen Datenblock von der Quelle zum Ziel. Um zu vermeiden, dass beide Anwendungen für einen längeren Zeitraum 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. Diese Prozedur 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 längeren 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 gering, und die synchrone Datenübertragung funktioniert recht gut. Das synchrone Extrahieren großer Datenmengen kann jedoch zu langen Verzögerungen führen und die Benutzeroberfläche des Ziels und der Quelle beeinträchtigen.

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

Verwenden von IASyncOperation/IDataObjectAsyncCapability

Hinweis

Die Schnittstelle hieß ursprünglich IAsyncOperation, aber dies wurde später in IDataObjectAsyncCapability geändert. Andernfalls sind die beiden Schnittstellen identisch.

 

Der Zweck von IAsyncOperation/IDataObjectAsyncCapability besteht darin, der Ablagequelle und dem Ablageziel zu ermöglichen, auszuhandeln, ob Daten asynchron extrahiert werden können. Im folgenden Verfahren wird beschrieben, wie die Dropquelle die -Schnittstelle verwendet:

  1. Erstellen Sie ein Datenobjekt, das IAsyncOperation/IDataObjectAsyncCapability verfügbar macht.
  2. Rufen Sie SetAsyncMode auf, wobei fDoOpAsync auf VARIANT_TRUE festgelegt ist, um anzugeben, dass ein asynchroner Vorgang unterstützt wird.
  3. Nachdem DoDragDrop zurückgegeben wurde, rufen Sie InOperation auf:
    • Wenn InOperation fehlschlägt oder VARIANT_FALSE zurückgibt, wurde eine normale synchrone Datenübertragung durchgeführt, und der Datenextraktionsprozess ist abgeschlossen. Die Quelle sollte alle erforderlichen Bereinigungen durchführen und fortfahren.
    • Wenn InOperationVARIANT_TRUE zurückgibt, werden die Daten asynchron extrahiert. Bereinigungsvorgänge sollten von EndOperation verarbeitet 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.

Im folgenden Verfahren wird beschrieben, wie das Ablageziel 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 behandeln, und rufen Sie StartOperation auf.
  4. Geben Sie den IDropTarget::D rop-Aufruf wie bei einem normalen Datenübertragungsvorgang zurück. DoDragDrop gibt die Ablagequelle zurück und hebt die Blockierung auf. Rufen Sie IDataObject::SetData nicht auf, um das Ergebnis eines optimierten Vorgangs zum Verschieben oder Löschen beim Einfügen anzugeben. Warten Sie, bis der Vorgang abgeschlossen ist.
  5. Extrahieren Sie die Daten im Hintergrundthread. Der primäre Thread des Ziels ist nicht blockiert und kann fortgesetzt werden.
  6. Wenn die Datenübertragung ein optimierter Vorgang zum Verschieben oder Löschen beim Einfügen war, rufen Sie IDataObject::SetData auf, um das Ergebnis anzugeben.
  7. Benachrichtigen Sie das Datenobjekt, dass die Extraktion abgeschlossen ist, indem Sie EndOperation aufrufen.