Beschreibung:
Mit der Klasse können Dateien / Ordner mittels Shell-Operationen gelöscht, verschoben und kopiert werden. Dabei werden die Windows-Standard-Dialoge für die jeweilige Operation angezeigt.
Umgesetzt habe ich die Klasse nachdem ich die Frage in Windows Dialog zum Löschen von Ordnern aus eigenen Programm heraus benutzen gelesen habe und die Grundlegende Idee nicht schlecht fand.
[color][SIZE][B]Hinweis![/B][/SIZE][/COLOR]
Die Klasse funktioniert womöglich nicht unter Windows 7. Ich habe sie damals nur auf Windows XP getestet. Auf meinem privaten Notebook (Windows 7) funktionieren die Methoden zumindest nicht.
[SIZE]Getestet am 16.04.2012[/SIZE]
public class ShellProvider
{
#region imports
[DllImport("shell32.dll")]
static extern int SHFileOperation([In] ref SHFILEOPSTRUCT lpFileOp);
#endregion
#region nested
private enum FO_Func : uint
{
FO_MOVE = 0x0001,
FO_COPY = 0x0002,
FO_DELETE = 0x0003,
FO_RENAME = 0x0004,
}
private struct SHFILEOPSTRUCT
{
public IntPtr hwnd;
public Int32 wFunc;
[MarshalAs(UnmanagedType.LPWStr)]
public string pFROM;
[MarshalAs(UnmanagedType.LPWStr)]
public string pTo;
public FILEOP_FLAGS_ENUM fFlags;
public bool fAnyOperationsAborted;
public Int32 hNameMappings;
public string lpszProgressTitle;
}
private enum FILEOP_FLAGS_ENUM : ushort
{
FOF_MULTIDESTFILES = 0x0001,
FOF_CONFIRMMOUSE = 0x0002,
FOF_SILENT = 0x0004, // don't create progress/report
FOF_RENAMEONCOLLISION = 0x0008,
FOF_NOCONFIRMATION = 0x0010, // Don't prompt the user.
FOF_WANTMAPPINGHANDLE = 0x0020, // Fill in SHFILEOPSTRUCT.hNameMappings
// Must be freed using SHFreeNameMappings
FOF_ALLOWUNDO = 0x0040,
FOF_FILESONLY = 0x0080, // on *.*, do only files
FOF_SIMPLEPROGRESS = 0x0100, // means don't show names of files
FOF_NOCONFIRMMKDIR = 0x0200, // don't confirm making any needed dirs
FOF_NOERRORUI = 0x0400, // don't put up error UI
FOF_NOCOPYSECURITYATTRIBS = 0x0800, // dont copy NT file Security Attributes
FOF_NORECURSION = 0x1000, // don't recurse into directories.
FOF_NO_CONNECTED_ELEMENTS = 0x2000, // don't operate on connected elements.
FOF_WANTNUKEWARNING = 0x4000, // during delete operation, warn if nuking instead of recycling (partially overrides FOF_NOCONFIRMATION)
FOF_NORECURSEREPARSE = 0x8000, // treat reparse points as objects, not containers
}
#endregion
#region Fields
#endregion
#region Properties
#endregion
#region Construction / Destruction
#endregion
#region Methods
/// <summary>
/// Method to delete a file or folder by shell
/// </summary>
/// <param name="sPath">path to the file / folder</param>
public void Delete(string sPath, bool bMoveToBin, bool bWithDialog)
{
this.RunFileOperation(sPath, string.Empty, FO_Func.FO_DELETE, (bMoveToBin ? FILEOP_FLAGS_ENUM.FOF_ALLOWUNDO : 0) | (!bWithDialog ? FILEOP_FLAGS_ENUM.FOF_NOCONFIRMATION: 0));
}
/// <summary>
/// Method to move a file
/// </summary>
/// <param name="sSource">the source path</param>
/// <param name="sDestination">the destination path</param>
public void Move(string sSource, string sDestination, bool bWithDialog)
{
this.RunFileOperation(sSource, sDestination, FO_Func.FO_MOVE, !bWithDialog ? FILEOP_FLAGS_ENUM.FOF_NOCONFIRMATION : 0);
}
/// <summary>
/// Method to copy a file
/// </summary>
/// <param name="sSource">the source of the file</param>
/// <param name="sDestination">the files destination</param>
public void Copy(string sSource, string sDestination, bool bWithDialog)
{
this.RunFileOperation(sSource, sDestination, FO_Func.FO_COPY, !bWithDialog ? FILEOP_FLAGS_ENUM.FOF_NOCONFIRMATION : 0);
}
/// <summary>
/// Method to rename a file or folder
/// </summary>
public void Rename(string sSource, string sDestination, bool bWithDialog)
{
this.RunFileOperation(sSource, sDestination, FO_Func.FO_RENAME, !bWithDialog ? FILEOP_FLAGS_ENUM.FOF_NOCONFIRMATION : 0);
}
/// <summary>
/// Method to run the file operation
/// </summary>
/// <param name="sSource">the source file</param>
/// <param name="sDestination">the destination file</param>
/// <param name="func">the func</param>
/// <param name="flags">the flags</param>
/// <returns>result code</returns>
private int RunFileOperation(string sSource, string sDestination, FO_Func func, FILEOP_FLAGS_ENUM flags)
{
SHFILEOPSTRUCT structure = new SHFILEOPSTRUCT();
structure.hwnd = IntPtr.Zero;
structure.wFunc = (int)func;
structure.pFROM = sSource;
structure.pTo = sDestination;
structure.fFlags = flags;
return SHFileOperation(ref structure);
}
#endregion
}
Schlagwörter: Löschen mit Standard-Dialog, Shelloperationen, Windows Shell, Shell Kopiervorgang, Shell Verschieben
Wissen ist nicht alles. Man muss es auch anwenden können.
PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |
Als kleinen Verbesserungsvorschlag solltest du noch die Rückgabewerten der API-Funktionen auswerten und entsprechend Exceptions werfen.
Zudem solltest es auch möglich sein, das Marshalling der Strings mit Attributen zu lösen, statt manuell Nullbytes anzuhängen.
Zu den Rückgabewerten:
Ich bin mir nicht sicher, ob das sinnvoll ist. Ich habe das ganze jetzt mehrfach getestet und immer wenn es fehlschlägt, wird durch das Betriebssystem bereits eine Fehlermeldung ausgegeben. Insofern gehe ich davon aus, dass eine weitere Verarbeitung des Result-Codes hier nicht notwendig ist.
Zum Marshalling statt manueller Nullbytes:
Das werd ich heut Abend noch hinzufügen.
Wissen ist nicht alles. Man muss es auch anwenden können.
PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |
Außerdem ist Delete noch falsch implementiert bei bMoveToBin = true und bWithDialog = false (dafür gibt es den |= Operator 😛).
Es scheint auch, als würde moveToBin nicht richtig funktionieren, auf der Verschieben von Dateien in den Papierkorb wird auch darauf hingewiesen, das man mit diesem Flag aufpassen muss (Vollständiger Pfadname?!). Das ändern des Flags bringt zumindest beim angeben von Ordnern, die gelöscht werden sollen, keinen Unterschied.
Ich habe die offensichtlichen Fehler korrigiert, und den Grundlegenden API Aufruf in eine eigene Methode gepackt.
Die Auswertung des Fehlercodes habe ich aus oben genannten Gründen erst einmal weg gelassen.
// EDIT: Die volle Flagliste ist nicht notwendig, lasse ich aber drin, falls jemand die Operationen mit anderen Flags ausführen lassen möchte.
Wissen ist nicht alles. Man muss es auch anwenden können.
PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |