Programmieren mit C++

Visual C++

Systemnahe Lösungen mit VC++

Umgang mit SHFILEOPSTRUCT

Wie wird die Funktion SHFileOperation() und die dafür benötigte Struktur SHFILEOPSTRUCT eingesetzt, speziell wenn Namenskollisionen behandelt werden sollen?

Frage

Die Funktion SHFileOperation() erlaubt das Kopieren, Verschieben oder Löschen eines Objekts des Dateisystems. Dazu sind sämtliche Parameter in einer Datenstruktur des Typs SHFILEOPSTRUCT zu übergeben.

Lösung

WINSHELLAPI int WINAPI SHFileOperation(
     LPSHFILEOPSTRUCT lpFileOp);

Die Struktur SHFILEOPSTRUCT ist wie folgt deklariert:

Funktion SHFileOperation

typedef struct _SHFILEOPSTRUCT{
 HWND          hwnd;
 UINT          wFunc;
 LPCSTR        pFrom;
 LPCSTR        pTo;
 FILEOP_FLAGS  fFlags;
 BOOL          fAnyOperationsAborted;
 LPVOID        hNameMappings;
 LPCSTR        lpszProgressTitle;
} SHFILEOPSTRUCT, FAR *LPSHFILEOPSTRUCT;

Obwohl die Struktur SHFILEOPSTRUCT auf den ersten Blick recht einfach aussieht, beinhaltet sie einige Fallen, in die man tappen kann. Die Basisinformationen werden in den Parametern wFunc sowie pFrom und pTo übergeben. Die Dateinamen pFrom und pTo werden abhängig von wFunc integriert bzw. verwendet und ggfs. ignoriert. Kommen beide Zeiger zum Einsatz, werden die Namen des jeweils gleichen Index zusammengefasst und adressiert.

Konstante

Beschreibung

FO_COPY

Kopiert die Dateien in pFrom nach pTo

FO_DELETE

Löscht die Dateien in pFrom

FO_MOVE

Verschiebt die Dateien von pFrom nach pTo

FO_RENAME

Benennt die Dateien in pFrom nach pTo um

Sofern das Flag FOF_MULTIDESTFILES gesetzt ist, enthält pTo für jede Datei einen separaten Zielnamen. Ansonsten steht an Indexposition 0 ein Zielpfad, in den alle Dateien kopiert oder verschoben werden. Im Strukturmitglied fFlags werden die Optionen für die Funktion festgelegt. Mögliche Flags sind:

Konstante

Bedeutung

FOF_ALLOWUNDO

Sofern möglich, werden Undo-Informationen gespeichert

FOF_FILESONLY

Enthält der Dateiname Wildcards, soll die Operation nur auf Dateien ausgeführt werden

FOF_MULTIDESTFILES

Der Parameter pTo enthält für jede Datei in pFrom einen Dateinamen als Ziel der Operation anstelle eines Verzeichnisses, das für alle Dateien gilt

FOF_NOCONFIRMATION

Bestätigt mit „Ja für alle“ für alle anzuzeigenden Dialogboxen

FOF_NOCONFIRMMKDIR

Neue Verzeichnisse werden ohne Bestätigung angelegt

FOF_RENAMEONCOLLISION

Existiert ein Zielname bereits, wird zum Vermeiden der Namenskollision ein künstlicher Name der Art „Kopie # von...“ erzeugt.

FOF_SILENT

Zeigt keine Fortschrittsanzeige

FOF_SIMPLEPROGRESS

Zeigt einen Fortschrittsbalken, ohne jedoch die einzelnen Dateinamen anzuzeigen

FOF_WANTMAPPINGHANDLE

Füllt das Member hNameMappings mit den Namen der umgemappten Dateinamen

Von besonderem Interesse sind hier die Flags FOF_RENAMEONCOLLISION und FOF_WANTMAPPINGSHANDLE, die das Behandeln von Dateinamen bei Namenskollisionen steuern. hNameMappings ist dabei fast immer ein reiner Eingabeparameter der Struktur SHFILEOPSTRUCT. Wenn SHFileOperation() erfolgreich zurückkehrt, wird hNameMappings normalerweise auf NULL gesetzt. Lediglich wenn Kollisionen auftreten, enthält hNameMappings dann, wenn die Flags FOF_RENAMEONCOLLISION und FOF_WANTMAPPINGSHANDLE in fFlags gesetzt sind, die Namen, die vom System erzeugt wurden, um Namenskollisionen zu vermeiden. Die einzelnen Namen werden in einer SHNAMEMAPPING-Struktur returniert, die arrayähnlich hintereinander in hNameMappings stehen, wobei jedoch das erste Element ein Integer ist, der die Anzahl der nachfolgenden SHNAMEMAPPING-Strukturen anzeigt. Die Struktur SHNAMEMAPPING selbst ist wie folgt aufgebaut:

Datenstruktur SHFILEOPSTRUCT

typedef struct _SHNAMEMAPPING {
  LPSTR pszOldPath;
  LPSTR pszNewPath; 
  int   cchOldPath; 
  int   cchNewPath; 
} SHNAMEMAPPING, FAR *LPSHNAMEMAPPING;

In pszOldPath und pszNewPath stehen jeweils die Namen aus pFrom der Struktur SHFILEOPSTRUCT sowie der vom Sys-tem erzeugte Alternativname. Die Felder cchOldPath und cchNewPath geben die Längen der entsprechenden Dateinamen an.

Obwohl die Namen pszOldPath und pszNewPath als LPSTR deklariert sind, sind sie intern als DWORD und nicht als char ausgelegt, so dass String-Kopierfunktionen beim Kopieren der Dateinamen scheitern.

Die nachfolgende Codesequenz ist ein Beispiel, wie mit der Funktion SHFileOperation() umgegangen werden kann. Der Code ist nicht die einzig mögliche Vorgehensweise, sondern zeigt nur beispielhaft, wie man es machen kann.

Zunächst wird die Struktur SHFILEOPSTRUCT ausgefüllt.

CString OldPath, CString NewPath;
SHFILEOPSTRUCT shFileOp;
 
// ... Standard-Parameter ausfüllen
 
shFileOp.fFlags = FOF_WANTMAPPINGHANDLE | FOF_RENAMEONCOLLISION;
shFileOp.lpszProgressTitle = "Test";
shFileOp.fAnyOperationsAborted = FALSE;

Anschließend erfolgt der Aufruf der Funktion SHFileOperation(). Über die Rückgabe des Parameters hNameMappings erfolgt nach dem Aufruf der Test auf Namenskollisionen. Ist hNameMappings NULL, ging alles glatt und die Aktion ist beendet.

SHFileOperation(&shFileOp);
 
if(!shFileOp.hNameMappings) 
 return; 

Liegen Namenskollisionen vor, werden diese nun behandelt. Den Zugriff auf die Strukturen erleichtert dabei die nachfolgende Struktur TMPMAP, die den tatsächlichen Aufbau des Buffers hNameMappings abbildet und so einen direkten Zugriff erlaubt. Deklariert ist die Struktur daher wie folgt:

Datenstruktur SHNAMEMAPPING

struct TMPMAP {
  int Indx;
  SHNAMEMAPPING *pMapping;
};

Nachfolgend wird der Zugriff auf die SHNAMEMAPPING-Strukturen über eine temporäre Variable des Typs TMPMAP realisiert, die auf den Buffer hNameMappings verweist.

TMPMAP *pTmpMap = (TMPMAP*)shFileOp.hNameMappings;

Nach diesem Cast kann eine einfache Schleife eingesetzt werden, die über den Wert von Indx der TMPMAP-Struktur läuft.

for(int in = 0; in < pTmpMap->Indx; in++)
{
  SHNAMEMAPPING *pMap = &pTmpMap->pMapping[in];

Für alle Indizes müssen der alte und neue Pfad ermittelt werden. Da ein Aufruf der Form

strcpy(buf, (char*)pMap->pszOldPath);

nicht funktioniert, werden die einzelnen Zeichen in einer Schleife in den lokalen Buffer kopiert.

  char *buf = csNewPath.GetBufferSetLength(pMap->cchNewPath);
 
  for(int dw = 0 ; dw < 2 * pMap->cchNewPath - 1 ; dw+=2)
  {
    *buf = (pMap->pszNewPath[dw]);
    buf++;
  }
  buf = 0;
  csNewPath.ReleaseBuffer();

Analog zum vorgestellten Codeblock ist auch der String pszOldPath zu übernehmen, was man sich aber sparen kann, wenn die Namen aus der Liste pFrom übernommen werden.

Anschließend kann die Anwendung geeignete Maßnahmen für das Behandeln der Namenskollisionen ergreifen. Dies erfolgt hier innerhalb der for-Schleife, die über Indx läuft und ist sehr individuell, so dass hier auf die Darstellung verzichtet wird, da letztlich jede Anwendung und jeder Anwender anders reagieren wird, wenn das System Dateinamen unplanmäßig ändert. Prinzipiell ist eine Reaktion eigentlich nur dann notwendig, wenn die Anwendung eine Liste der Dateien verwaltet und den exakten neuen Namen kennen muss.

Zum Abschluss ist dann jedoch der Buffer hNameMappings wieder freizugeben.

SHFreeNameMappings(shFileOp.hNameMappings); 

Datenstruktur TMPMAP

Anhand des hier vorgestellten Codes kann die Funktion SHFileOperation() einschließlich der vom System gesteuerten Auflösung der Namenskollision eingesetzt werden.

Wie letztlich auf eine eventuell auftretende Namenskollision reagiert wird, ist jedoch von der Art der Anwendung abhängig und muss individuell gestaltet und implementiert werden.

Zusammenfassung





Sachgebiet


© 2009-2012 by Alojado Publishing. Alle Rechte vorbehalten. Ausgewiesene Marken gehören ihren jeweiligen Eigentümern.
Mit der Benutzung dieser Seite erkennen Sie die Nutzungsbedingungen und die Datenschutzerklärung an. Der Betreiber übernimmt keine Haftung für den Inhalt verlinkter externer Internetseiten.
Seite erzeugt 2012-02-22 22:11:10 von textarchiv.alojado.de