(* ImageEn Build 7.0.0.06.2637 @ 7-4-17 14:58:42.679 *) (* Copyright (c) 1998-2017 by Carlotta Calandra. All rights reserved. Copyright (c) 2011-2017 by Xequte Software. This software comes without express or implied warranty. In no case shall the author be liable for any damage or unwanted behavior of any computer hardware and/or software. Author grants you the right to include the component in your application, whether COMMERCIAL, SHAREWARE, or FREEWARE. ImageEn, IEvolution and ImageEn ActiveX may not be included in any commercial, shareware or freeware libraries or components. www.ImageEn.com *) (* File: iexWindowsFunctions.pas Description: Windows File Functions File version: 1002 *) unit iexWindowsFunctions; {$I ie.inc} interface uses {$ifdef IEHASUITYPES} System.UITypes, {$endif} {$IFDEF IEINCLUDEMULTIVIEW} iemview, {$endif} Windows, Forms, ActiveX, ComObj, Controls, Classes, hyiedefs, Messages; type {!! TIEFileDropEvent Declaration } TIEFileDropEvent = procedure(Sender: TObject; ssFiles : TStrings; dwEffect: Integer) of object; {!!} {!! TIEFileDragDrop Declaration TIEFileDragDrop = class(TInterfacedObject, IDropTarget, IDropSource) public property ActivateDropping : Boolean; property DropActions : ; constructor Create(Control: TWinControl; OnFileDrop : ); destructor Destroy; override; procedure InitiateDragging(ssFilenames : TStrings; DragActions: ); end; Description Class to provide support for dragging and dropping files from/to Windows Explorer (and other applications). Used by . See iexFolderMView.pas for example usage. !!} // CLASS TO HANDLE DRAGGING/DROPPING FILES TO/FROM WINDOWS EXPLORER TIEFileDragDrop = class(TInterfacedObject, IDropTarget, IDropSource) private fControl : TWinControl; fControlHandle : HWND; fDroppingActive : Boolean; fOnFileDrop : TIEFileDropEvent; fDropActions : TIEFileDragDropActions; procedure GetFileNames(const dataObj: IDataObject; Dest: TStrings); function GetDataObject(ssFiles: TStrings): IDataObject; function GetDragEffect(KeyState : Integer) : Integer; procedure SetDroppingActive(const Value: Boolean); // Supporting IDropTarget function DragEnter(const dataObj: IDataObject; grfKeyState: Integer; pt: TPoint; var dwEffect: Integer): HResult; stdcall; function DragOver(grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HResult; stdcall; function DragLeave: HResult; stdcall; function Drop(const dataObj: IDataObject; grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HResult; stdcall; // Supporting IDropSource function QueryContinueDrag(fEscapePressed: BOOL; grfKeyState: Longint): HResult; stdcall; function GiveFeedback(dwEffect: Longint): HResult; stdcall; public property ActivateDropping : Boolean read fDroppingActive write SetDroppingActive; property DropActions : TIEFileDragDropActions read fDropActions write fDropActions; constructor Create(Control: TWinControl; OnFileDrop : TIEFileDropEvent); destructor Destroy; override; procedure InitiateDragging(ssFilenames : TStrings; DragActions: TIEFileDragDropActions); end; // CLASS TO MONITOR CHANGES TO A FOLDER // Based on Cromis.DirectoryWatch // http://www.cromis.net/blog/downloads/ // Created by Iztok Kacin, Cromis (iztok.kacin@gmail.com). // Used by permission (10-7-13) const cShutdownTimeout = 3000; cFileWaitTimeout = 0; type {!! TWatchOptions Declaration } // the filters that control when the watch is triggered TWatchOption = (woFileName, woFolderName, woAttributes, woSize, woLastWrite, woLastAccess, woCreation, woSecurity); TWatchOptions = set of TWatchOption; {!!} {!! TWatchActions Declaration } // the filters that control when the watch is triggered // the actions that are the result of the watch being triggered TWatchAction = (waAdded, waRemoved, waModified, waRenamedOld, waRenamedNew); TWatchActions = set of TWatchAction; {!!} {!! TFileChangeNotifyEvent Declaration } TFileChangeNotifyEvent = procedure(const Sender: TObject; const Action: TWatchAction; const FileName: string ) of Object; {!!} {!! TOnError Declaration } TOnError = procedure(const Sender: TObject; const ErrorCode: Integer; const ErrorMessage: string ) of Object; {!!} {!! TIEFolderWatch Declaration TIEFolderWatch = class public constructor Create; destructor Destroy; override; procedure Start; procedure Stop; function Running: Boolean; property WatchSubTree: Boolean; property WatchOptions: ; property WatchActions: ; property BufferSize: Integer; property Path: string; property OnNotify: ; property OnChange: TNotifyEvent; property OnError: ; end; Description Class that automatically notifies changes to files within a specified folder. Used by . See iexFolderMView.pas for example usage. Attribution Based on Cromis.DirectoryWatch by Iztok Kacin, Cromis (iztok.kacin@gmail.com). www.cromis.net !!} TIEFolderWatch = class private FWatchOptions : TWatchOptions; FWatchActions : TWatchActions; FWatchSubTree : Boolean; FWatchThread : TThread; FBufferSize : Integer; FWndHandle : HWND; FPath : string; FAbortEvent : THandle; FOnError : TOnError; FOnChange : TNotifyEvent; FOnNotify : TFileChangeNotifyEvent; procedure WatchWndProc(var Msg: TMessage); procedure SetPath(const Value: string); procedure SetWatchOptions(const Value: TWatchOptions); procedure SetWatchActions(const Value: TWatchActions); procedure SetWatchSubTree(const Value: Boolean); procedure DeallocateHWnd(Wnd: HWND); function MakeFilter: Integer; protected procedure Change; virtual; procedure AllocWatchThread; procedure ReleaseWatchThread; procedure RestartWatchThread; procedure Notify(const Action: Integer; const FileName: string ); virtual; public constructor Create; destructor Destroy; override; procedure Start; procedure Stop; function Running: Boolean; property WatchSubTree: Boolean read FWatchSubTree write SetWatchSubTree; property WatchOptions: TWatchOptions read FWatchOptions write SetWatchOptions; property WatchActions: TWatchActions read FWatchActions write SetWatchActions; property BufferSize: Integer read FBufferSize write FBufferSize; property Path: string read FPath write SetPath; // notification properties. Notify about internal and external changes property OnNotify: TFileChangeNotifyEvent read FOnNotify write FOnNotify; property OnChange: TNotifyEvent read FOnChange write FOnChange; property OnError: TOnError read FOnError write FOnError; end; // waits for the file to be ready (it is not in use anymore) or timeout occurs procedure WaitForFileReady(const FileName: string; const Timeout: Cardinal = cFileWaitTimeout); // Clipboard function CutFilesToClipboard(Handle : HWND; Filenames: TStrings): Boolean; function CopyFilesToClipboard(Handle : HWND; Filenames: TStrings): Boolean; function PasteFilesFromClipboard(Handle : HWND; ssFilenames: TStrings; out bMoveFiles : Boolean) : Boolean; function CanPasteFilesFromClipboard(Handle : HWND) : Boolean; procedure PopupSystemMenu(Handle: HWND; ssFileList: TStrings; x, y: integer); // Similar to FileCtrl.SelectDirectory function WindowsSelectDirectory(const Caption: string; var Directory: string; Parent: TWinControl = nil; bAllowNewFolder : Boolean = True): Boolean; function WindowsLocalAppDataFolder : string; function WindowsMyDocumentsFolder : string; function WindowsMyPicturesFolder : string; function WindowsMyMusicFolder : string; function WindowsMyVideosFolder : string; function WindowsDesktopFolder : string; function WindowsProgramFilesFolder : String; function WindowsTempFolder() : String; procedure WindowsAddToRecentDocs(const Filename: string); {To allow multiple source files, replace spaces with #0} function WindowsCopy(Handle : HWnd; sFromFilename, sDestFolder: String; bRenameOnCollision, bShowConfirmation, bShowProgress: boolean; bVerboseErrors : boolean = True): Boolean; overload; function WindowsMove(Handle : HWnd; sFromFilename, sDestFolder: String; bRenameOnCollision, bShowConfirmation, bShowProgress: boolean; bVerboseErrors : boolean = True): Boolean; overload; function WindowsErase(Handle : HWnd; sFilename: String; bSendToRecycleBin, bShowConfirmation, bShowProgress: Boolean; bVerboseErrors : boolean = True): Boolean; overload; function WindowsCopy(Handle : HWnd; ssFiles : TStrings; const sDestFolder : String; bRenameOnCollision, bShowConfirmation, bShowProgress: boolean; bVerboseErrors : boolean = True): Boolean; overload; function WindowsMove(Handle : HWnd; ssFiles : TStrings; const sDestFolder : String; bRenameOnCollision, bShowConfirmation, bShowProgress: boolean; bVerboseErrors : boolean = True): Boolean; overload; function WindowsErase(Handle : HWnd; ssFiles : TStrings; bSendToRecycleBin, bShowConfirmation, bShowProgress: Boolean; bVerboseErrors : boolean = True): Boolean; overload; // sNewName must be a name only without a file path function WindowsRename(Handle : HWnd; const sFilename, sNewName: String; bRenameOnCollision, bShowConfirmation : Boolean; bVerboseErrors : boolean = True; bCheckForOverwrite : Boolean = True): Boolean; procedure WindowsLaunchFile(Handle: THandle; const sFilename: string); // CSIDL_... definitions in ShlObj // return a path such as CSIDL_LOCAL_APPDATA or CSIDL_BITBUCKET // fpMypictures = CSIDL_MYPICTURES // fpMyMusic = CSIDL_COMMON_MUSIC // Non-local app data CSIDL_APPDATA function GetWindowsSpecialFolder(const iCSIDL: Integer): string; {$IFNDEF Delphi6orNewer} function DirectoryExists(const Directory: string): Boolean; {$ENDIF} {$IFDEF IEINCLUDEMULTIVIEW} procedure ShowWindowsPrintWizard(ImageEnMView: TImageEnMView; bAllImages: Boolean); overload; procedure ShowWindowsPrintWizard(ssFiles: TStrings); overload; {$ENDIF} implementation uses {$IFNDEF Delphi6orNewer} FileCtrl, {$ENDIF} Sysutils, ShlObj, shellapi, Dialogs, consts, hyieutils {$IfDef UNICODE}, AnsiStrings {$EndIf} {$ifdef IENEWVARIANTS} , variants {$endif}; {$IFNDEF Delphi2007orNewer} const CSIDL_PROGRAM_FILES = $0026; { C:\Program Files } CSIDL_LOCAL_APPDATA = $001c; { \Local Settings\Application Data (non roaming) } CSIDL_MYPICTURES = $0027; { C:\Program Files\My Pictures } CSIDL_MYDOCUMENTS = $000c; { logical "My Documents" desktop icon } CSIDL_MYMUSIC = $000d; { "My Music" folder } CSIDL_MYVIDEO = $000e; { "My Video" folder } BIF_NONEWFOLDERBUTTON = $00000200; BIF_NEWDIALOGSTYLE = $0040; BIF_SHAREABLE = $8000; function DirectoryExists(const Directory: string): Boolean; var Code: Integer; begin Code := GetFileAttributes(PChar(Directory)); Result := (Code <> -1) and (FILE_ATTRIBUTE_DIRECTORY and Code <> 0); end; {$ENDIF} type // Required by _PopupSystemMenu IShellCommandVerb = interface ['{7D2A7245-2376-4D33-8008-A130935A2E8B}'] procedure ExecuteCommand(Verb: string; var Handled: boolean); procedure CommandCompleted(Verb: string; Succeeded: boolean); end; PArrayOfPItemIDList = ^TArrayOfPItemIDList; TArrayOfPItemIDList = array[0..0] of PItemIDList; {!! WindowsLaunchFile Declaration procedure WindowsLaunchFile(Handle: THandle; const sFilename: string); Description Calls ShellExecute to launch the specified file in the default application. Can also be used to launch a web page. Example WindowsLaunchFile(Form1.Handle, IEFolderMView1.SelectedFilename); WindowsLaunchFile(Form1.Handle, 'http://www.ImageEn.com'); !!} procedure WindowsLaunchFile(Handle: THandle; const sFilename: string); var ret: integer; begin ret := ShellExecute(Handle, nil, PChar(sFileName), nil, nil, SW_SHOWNORMAL); if ret <= 32 then raise EIEException.create(SysErrorMessage(GetLastError)); end; {!! WindowsProgramFilesFolder Declaration function WindowsProgramFilesFolder : String; Description Returns the full path to "Program Files" (using CSIDL_PROGRAM_FILES) Example IEFolderMView1.Folder := WindowsProgramFilesFolder; !!} function WindowsProgramFilesFolder : String; begin Result := GetWindowsSpecialFolder(CSIDL_PROGRAM_FILES); end; function WindowsTempFolder() : String; var tempPath: array[ 0..MAX_PATH ] of Char; begin GetTempPath( 250, tempPath ); Result := IncludeTrailingBackslash( string( temppath )); end; {!! GetWindowsSpecialFolder Declaration function GetWindowsSpecialFolder(const iCSIDL: Integer): string; Description Calls SHGetSpecialFolderPath to return a Windows special folder. Example // Display the contents of the Recycle Bin IEFolderMView1.Folder := GetWindowsSpecialFolder(CSIDL_BITBUCKET); !!} // return a path such as CSIDL_LOCAL_APPDATA or CSIDL_BITBUCKET function GetWindowsSpecialFolder(const iCSIDL: Integer): string; var Path: array[0..1023] of Char; begin Result := ''; try if SHGetSpecialFolderPath(0, Path, iCSIDL, False) then Result := Path; except { ERROR } end; end; {!! WindowsAddToRecentDocs Declaration procedure WindowsAddToRecentDocs(Const Filename: string); Description Adds a file to Windows' list of recent documents (displayed under the Start menu) Example WindowsAddToRecentDocs('C:\My File.jpg'); !!} procedure WindowsAddToRecentDocs(Const Filename: string); begin SHAddToRecentDocs(SHARD_PATH, @Filename[1]); end; function ShGetKnownFolderPath(const rfid: TGUID; dwFlags: DWord; hToken: THandle; out ppszPath: PWideChar): HResult; type TShGetKnownFolderPath = function(const rfid: TGUID; dwFlags: DWord; hToken: THandle; out ppszPath: PWideChar): HResult; stdcall; var Shell: HModule; Fn: TShGetKnownFolderPath; begin Shell := LoadLibrary('shell32.dll'); Win32Check(Shell <> 0); try @Fn := GetProcAddress(Shell, 'SHGetKnownFolderPath'); Win32Check(Assigned(Fn)); Result := Fn(rfid, dwFlags, hToken, ppszPath); finally FreeLibrary(Shell); end; end; function GetKnownFolderPath(const rfid: TGUID; dwFlags: DWord; hToken: THandle): WideString; var buffer: PWideChar; ret: HResult; begin ret := ShGetKnownFolderPath(rfid, dwFlags, hToken, buffer); OleCheck(ret); try Result := buffer; finally CoTaskMemFree(buffer); end; end; function PerformSHFileOperation(Handle : HWnd; iAction : UINT; sFromFilename, sDestFolder: String; bRenameOnCollision, bSendToRecycleBin, bShowConfirmation, bShowProgress, bVerboseErrors : boolean): Boolean; var Struct : TSHFileOpStructA; bDelete: Boolean; bAborted : Boolean; begin bAborted := False; bDelete := iAction = FO_Delete; sFromFilename := sFromFilename + #0#0; if not bDelete then sDestFolder := sDestFolder + #0#0; with Struct do begin wnd := Handle; wFunc := iAction; pFrom := PAnsiChar(AnsiString(sFromFilename)); if bDelete then pTo := nil else pTo := PAnsiChar(AnsiString(sDestFolder)); If (bDelete = False) or bSendToRecycleBin then fFlags := FOF_ALLOWUNDO or FOF_FILESONLY else fFlags := 0 or FOF_FILESONLY; If bShowConfirmation = false then fFlags := fFlags or FOF_NOCONFIRMATION; if bShowProgress = False then fFlags := fFlags or FOF_SILENT; If (bDelete = False) and bRenameOnCollision then fFlags := fFlags or FOF_RENAMEONCOLLISION; if bVerboseErrors = false then fFlags := fFlags or FOF_NOERRORUI; fAnyOperationsAborted := bAborted; hNameMappings := nil; lpszProgressTitle := nil; end; result := (SHFileOperationA(Struct) = 0) and (not bAborted); end; function PerformSHFileOperation2(Handle : HWnd; iAction : UINT; ssFiles : TStrings; const sDestFolder : String; bRenameOnCollision, bSendToRecycleBin, bShowConfirmation, bShowProgress, bVerboseErrors : boolean): Boolean; var sFromFilename : String; I: Integer; bDelete: Boolean; begin Result := False; bDelete := iAction = FO_Delete; if (bDelete = False) and (sDestFolder = '') then exit; sFromFilename := ''; for I := 0 to ssFiles.Count - 1 do sFromFilename := sFromFilename + ssFiles[I] + #0; if sFromFilename = '' then exit; // Remove final #0 Delete(sFromFilename, Length(sFromFilename), 1); Result := PerformSHFileOperation(Handle, iAction, sFromFilename, sDestFolder, bRenameOnCollision, bSendToRecycleBin, bShowConfirmation, bShowProgress, bVerboseErrors); end; {!! WindowsCopy Declaration function WindowsCopy(Handle : HWnd; sFromFilename, sDestFolder: String; bRenameOnCollision, bShowConfirmation, bShowProgress: boolean; bVerboseErrors : boolean = True): Boolean; function WindowsCopy(Handle : HWnd; ssFiles : TStrings; const sDestFolder : String; bRenameOnCollision, bShowConfirmation, bShowProgress: boolean; bVerboseErrors : boolean = True): Boolean; overload; Description Uses PerformSHFileOperation to copy files to a folder. Parameter Description sFromFilename/ssFiles The file(s) to copy. If using sFromFilename the multiple files can be delimited using #0 sDestFolder The folder to copy files to bRenameOnCollision Give the file being operated on a new name if a file with the target name already exists at the destination (pertains to FOF_RENAMEONCOLLISION bShowConfirmation If not specified then will automatically respond with "Yes to All" for any dialog box that is displayed (undefines FOF_NOCONFIRMATION bShowProgress Displays a progress dialog box (undefines FOF_SILENT bVerboseErrors Displays a dialog to the user if an error occurs (undefines FOF_NOERRORUI
Result is true unless the operation fails or is aborted. Example WindowsCopy(Form1.Handle, 'C:\My Image.jpg', 'C:\My Files', True, True, True); !!} function WindowsCopy(Handle : HWnd; sFromFilename, sDestFolder: String; bRenameOnCollision, bShowConfirmation, bShowProgress: boolean; bVerboseErrors : boolean = True): Boolean; begin Result := PerformSHFileOperation(Handle, FO_Copy, sFromFilename, sDestFolder, bRenameOnCollision, True, bShowConfirmation, bShowProgress, bVerboseErrors); end; function WindowsCopy(Handle : HWnd; ssFiles : TStrings; const sDestFolder : String; bRenameOnCollision, bShowConfirmation, bShowProgress: boolean; bVerboseErrors : boolean = True): Boolean; overload; begin Result := PerformSHFileOperation2(Handle, FO_Copy, ssFiles, sDestFolder, bRenameOnCollision, True, bShowConfirmation, bShowProgress, bVerboseErrors); end; {!! WindowsMove Declaration function WindowsMove(Handle : HWnd; sFromFilename, sDestFolder: String; bRenameOnCollision, bShowConfirmation, bShowProgress: boolean; bVerboseErrors : boolean = True): Boolean; function WindowsMove(Handle : HWnd; ssFiles : TStrings; const sDestFolder : String; bRenameOnCollision, bShowConfirmation, bShowProgress: boolean; bVerboseErrors : boolean = True): Boolean; overload; Description Uses PerformSHFileOperation to move files to a folder. Parameter Description sFromFilename/ssFiles The file(s) to move. If using sFromFilename the multiple files can be delimited using #0 sDestFolder The folder to move files to bRenameOnCollision Give the file being operated on a new name if a file with the target name already exists at the destination (pertains to FOF_RENAMEONCOLLISION bShowConfirmation If not specified then will automatically respond with "Yes to All" for any dialog box that is displayed (undefines FOF_NOCONFIRMATION bShowProgress Displays a progress dialog box (undefines FOF_SILENT bVerboseErrors Displays a dialog to the user if an error occurs (undefines FOF_NOERRORUI
Result is true unless the operation fails or is aborted. Example WindowsMove(Form1.Handle, 'C:\My Image.jpg', 'C:\My Files', True, True, True); !!} function WindowsMove(Handle : HWnd; sFromFilename, sDestFolder: String; bRenameOnCollision, bShowConfirmation, bShowProgress: boolean; bVerboseErrors : boolean = True): Boolean; begin Result := PerformSHFileOperation(Handle, FO_Move, sFromFilename, sDestFolder, bRenameOnCollision, True, bShowConfirmation, bShowProgress, bVerboseErrors); end; function WindowsMove(Handle : HWnd; ssFiles : TStrings; const sDestFolder : String; bRenameOnCollision, bShowConfirmation, bShowProgress: boolean; bVerboseErrors : boolean = True): Boolean; overload; begin Result := PerformSHFileOperation2(Handle, FO_Move, ssFiles, sDestFolder, bRenameOnCollision, True, bShowConfirmation, bShowProgress, bVerboseErrors); end; {!! WindowsErase Declaration function WindowsErase(Handle : HWnd; sFilename: String; bSendToRecycleBin, bShowConfirmation, bShowProgress: Boolean; bVerboseErrors : boolean = True): Boolean; function WindowsErase(Handle : HWnd; ssFiles : TStrings; bSendToRecycleBin, bShowConfirmation, bShowProgress: Boolean; bVerboseErrors : boolean = True): Boolean; overload; Description Uses PerformSHFileOperation to delete files from a folder. Parameter Description sFilename/ssFiles The file(s) to delete. If using sFromFilename the multiple files can be delimited using #0 bSendToRecycleBin Moves the file to the Recycle Bin rather than permanently deleting it (pertains to FOF_ALLOWUNDO bShowConfirmation If not specified then will automatically respond with "Yes to All" for any dialog box that is displayed (undefines FOF_NOCONFIRMATION bShowProgress Displays a progress dialog box (undefines FOF_SILENT bVerboseErrors Displays a dialog to the user if an error occurs (undefines FOF_NOERRORUI
Result is true unless the operation fails or is aborted. Example WindowsErase(Form1.Handle, 'C:\My Image.jpg', True, True, True); !!} function WindowsErase(Handle : HWnd; sFilename: String; bSendToRecycleBin, bShowConfirmation, bShowProgress: Boolean; bVerboseErrors : boolean = True): Boolean; begin Result := PerformSHFileOperation(Handle, FO_Delete, sFilename, '', False, bSendToRecycleBin, bShowConfirmation, bShowProgress, bVerboseErrors); end; function WindowsErase(Handle : HWnd; ssFiles : TStrings; bSendToRecycleBin, bShowConfirmation, bShowProgress: Boolean; bVerboseErrors : boolean = True): Boolean; overload; begin Result := PerformSHFileOperation2(Handle, FO_Delete, ssFiles, '', False, bSendToRecycleBin, bShowConfirmation, bShowProgress, bVerboseErrors); end; {!! WindowsRename Declaration function WindowsRename(Handle : HWnd; const sFilename, sNewName: String; bRenameOnCollision, bShowConfirmation : Boolean; bVerboseErrors : boolean = True; bCheckForOverwrite : Boolean = True): Boolean; overload; Description Uses PerformSHFileOperation to delete files from a folder. Parameter Description sFilename/ssFiles The file(s) to rename (only one can be specified sNewName The new filename for the file (do not include path) bRenameOnCollision Give the file being operated on a new name if a file with the target name already exists at the destination (pertains to FOF_RENAMEONCOLLISION bShowConfirmation If not specified then will automatically respond with "Yes to All" for any dialog box that is displayed (undefines FOF_NOCONFIRMATION bVerboseErrors Displays a dialog to the user if an error occurs (undefines FOF_NOERRORUI
Result is true unless the operation fails or is aborted. Example WindowsRename(Form1.Handle, 'C:\My Image.jpg', 'My New Name.jpg', True, True); !!} // sNewName must be a name only without a file path function WindowsRename(Handle : HWnd; const sFilename, sNewName: String; bRenameOnCollision, bShowConfirmation : Boolean; bVerboseErrors : boolean = True; bCheckForOverwrite : Boolean = True): Boolean; overload; var sNewFilename: string; begin Result := False; if (sFilename = '') or (sNewName = '') then exit; sNewFilename := IEAddBackSlash(ExtractFilepath(sFilename)) + sNewName; if bCheckForOverwrite and FileExists(sNewFilename) then begin Result := False; if bVerboseErrors then MessageDlg(format('A file already exists of the name, "%s"', [sNewName]), mtInformation, [mbOK], 0); exit; end; Result := PerformSHFileOperation(Handle, FO_Rename, sFilename, sNewFilename, bRenameOnCollision, True, bShowConfirmation, False, bVerboseErrors); end; {!! WindowsLocalAppDataFolder Declaration function WindowsLocalAppDataFolder : string; Description Returns the full path to the "Local Apps" folder (using CSIDL_LOCAL_APPDATA) Example IEFolderMView1.Folder := WindowsLocalAppDataFolder; !!} function WindowsLocalAppDataFolder : string; begin Result := IEAddBackSlash(GetWindowsSpecialFolder(CSIDL_LOCAL_APPDATA)); end; {!! WindowsMyDocumentsFolder Declaration function WindowsMyDocumentsFolder: string; Description Returns the full path to the "My Documents" folder (using CSIDL_PERSONAL) Example IEFolderMView1.Folder := WindowsMyDocumentsFolder; !!} function WindowsMyDocumentsFolder: string; begin Result := IEAddBackSlash(GetWindowsSpecialFolder(CSIDL_PERSONAL)); end; {!! WindowsMyPicturesFolder Declaration function WindowsMyPicturesFolder: string; Description Returns the full path to the "My Pictures" folder (using CSIDL_MYPICTURES) Example IEFolderMView1.Folder := WindowsMyPicturesFolder; !!} function WindowsMyPicturesFolder: string; begin Result := IEAddBackSlash(GetWindowsSpecialFolder(CSIDL_MYPICTURES)); end; {!! WindowsMyMusicFolder Declaration function WindowsMyMusicFolder: string; Description Returns the full path to the "My Music" folder (using CSIDL_MYMUSIC) Example IEFolderMView1.Folder := WindowsMyMusicFolder; !!} function WindowsMyMusicFolder: string; begin Result := IEAddBackSlash(GetWindowsSpecialFolder(CSIDL_MYMUSIC)); end; {!! WindowsMyVideosFolder Declaration function WindowsMyVideosFolder: string; Description Returns the full path to the "My Videos" folder (using CSIDL_MYVIDEO) Example IEFolderMView1.Folder := WindowsMyVideosFolder; !!} function WindowsMyVideosFolder: string; begin Result := IEAddBackSlash(GetWindowsSpecialFolder(CSIDL_MYVIDEO)); end; {!! WindowsDesktopFolder Declaration function WindowsDesktopFolder: string; Description Returns the full path to the "Desktop" (using CSIDL_DESKTOP) Example IEFolderMView1.Folder := WindowsDesktopFolder; !!} function WindowsDesktopFolder: string; begin Result := IEAddBackSlash(GetWindowsSpecialFolder(CSIDL_DESKTOP)); end; {$IFDEF Delphi6orNewer} type TSelectDirCallback = class(TObject) private FDirectory: string; protected function SelectDirCB(Wnd: HWND; uMsg: UINT; lParam, lpData: LPARAM): Integer; public constructor Create(const ADirectory: string); end; function SelectDirCB(Wnd: HWND; uMsg: UINT; lParam, lpData: LPARAM): Integer stdcall; begin Result := TSelectDirCallback(lpData).SelectDirCB(Wnd, uMsg, lParam, lpData); end; {$ENDIF} {!! WindowsSelectDirectory Declaration function WindowsSelectDirectory(const Caption: string; var Directory: string; Parent: TWinControl = nil; bAllowNewFolder : Boolean = True): Boolean; Description Displays a dialog to the user which allows the to select a folder. Note: Similar to FileCtrl.SelectDirectory Example sFolder := IEFolderMView1.Folder; WindowsSelectDirectory('Specify Folder', sFolder); IEFolderMView1.Folder := sFolder; !!} {$IFNDEF Delphi6orNewer} function WindowsSelectDirectory(const Caption: string; var Directory: string; Parent: TWinControl = nil; bAllowNewFolder : Boolean = True): Boolean; var Ops : TSelectDirOpts; begin if not DirectoryExists(Directory) then Directory := ''; if bAllowNewFolder then Ops := [sdAllowCreate, sdPerformCreate, sdPrompt] else Ops := []; Result := FileCtrl.SelectDirectory(Directory, Ops, 0); end; {$ELSE} function WindowsSelectDirectory(const Caption: string; var Directory: string; Parent: TWinControl = nil; bAllowNewFolder : Boolean = True): Boolean; const Display_Sharable_Resources = True; Validate_Directory = True; var WindowList: Pointer; BrowseInfo: TBrowseInfo; Buffer: PChar; OldErrorMode: Cardinal; RootItemIDList, ItemIDList: PItemIDList; ShellMalloc: IMalloc; IDesktopFolder: IShellFolder; Eaten, Flags: LongWord; CoInitResult: HRESULT; SelectDirCallback: TSelectDirCallback; Root: WideString; begin Result := False; Root := ''; if not DirectoryExists(Directory) then Directory := ''; FillChar(BrowseInfo, SizeOf(BrowseInfo), 0); if (ShGetMalloc(ShellMalloc) = S_OK) and (ShellMalloc <> nil) then begin Buffer := ShellMalloc.Alloc(MAX_PATH); try RootItemIDList := nil; if Root <> '' then begin SHGetDesktopFolder(IDesktopFolder); IDesktopFolder.ParseDisplayName(Application.Handle, nil, POleStr(Root), Eaten, RootItemIDList, Flags); end; with BrowseInfo do begin if (Parent = nil) or not Parent.HandleAllocated then hwndOwner := Application.Handle else hwndOwner := Parent.Handle; pidlRoot := RootItemIDList; pszDisplayName := Buffer; lpszTitle := PChar(Caption); ulFlags := BIF_RETURNONLYFSDIRS or BIF_NEWDIALOGSTYLE or BIF_EDITBOX; if not bAllowNewFolder then ulFlags := ulFlags or BIF_NONEWFOLDERBUTTON; if Display_Sharable_Resources then ulFlags := ulFlags or BIF_SHAREABLE; if Validate_Directory then ulFlags := ulFlags or BIF_VALIDATE; lpfn := SelectDirCB; end; SelectDirCallback := TSelectDirCallback.Create(Directory); try BrowseInfo.lParam := Integer(SelectDirCallback); CoInitResult := CoInitializeEx(nil, COINIT_APARTMENTTHREADED); if CoInitResult = RPC_E_CHANGED_MODE then BrowseInfo.ulFlags := BrowseInfo.ulFlags and not BIF_NEWDIALOGSTYLE; try WindowList := DisableTaskWindows(0); OldErrorMode := SetErrorMode(SEM_FAILCRITICALERRORS); try ItemIDList := ShBrowseForFolder(BrowseInfo); finally SetErrorMode(OldErrorMode); EnableTaskWindows(WindowList); end; finally CoUninitialize; end; finally SelectDirCallback.Free; end; Result := ItemIDList <> nil; if Result then begin ShGetPathFromIDList(ItemIDList, Buffer); ShellMalloc.Free(ItemIDList); Directory := Buffer; end; finally ShellMalloc.Free(Buffer); end; end; end; { TSelectDirCallback } constructor TSelectDirCallback.Create(const ADirectory: string); begin inherited Create; FDirectory := ADirectory; end; function TSelectDirCallback.SelectDirCB(Wnd: HWND; uMsg: UINT; lParam, lpData: LPARAM): Integer; var Rect: TRect; Monitor: TMonitor; begin Result := 0; if uMsg = BFFM_INITIALIZED then begin if Assigned(Application.MainForm) then Monitor := Screen.MonitorFromWindow(Application.MainForm.Handle) else Monitor := Screen.MonitorFromWindow(0); GetWindowRect(Wnd, Rect); SetWindowPos(Wnd, 0, (Monitor.Width - (Rect.Right - Rect.Left)) div 2, (Monitor.Height - (Rect.Bottom - Rect.Top)) div 2, 0, 0, SWP_NOSIZE or SWP_NOZORDER); if FDirectory <> '' then SendMessage(Wnd, BFFM_SETSELECTION, Integer(True), Windows.LPARAM(PChar(FDirectory))); end else if uMsg = BFFM_VALIDATEFAILED then begin MessageDlg(Format(iemsg(IEMsg_XIsNotAValidFolder), [PChar(lParam)]), mtError, [mbOK], 0); Result := 1; end; end; {$ENDIF} const CF_PreferredDropEffect_Str = 'Preferred DropEffect'; // Windows Explorer Clipboard format string for specifying whether a file is moved or copied {!! CutFilesToClipboard Declaration function CutFilesToClipboard(Handle : HWND; Filenames: TStrings): Boolean; Description Adds a list of filenames to the clipboard and specifies that they should moved (same as selecting Ctrl+X in Windows Explorer). The filenames can then be pasted into other Windows applications such as Explorer using Ctrl+V. After pasting they will be removed from the source folder. Example CutFilesToClipboard(Form1.Handle, ssFilenames); !!} function CutFilesToClipboard(Handle : HWND; Filenames: TStrings): Boolean; {$IFNDEF Delphi6orNewer} type PCardinal = ^Cardinal; {$ENDIF} var sFilenames: String; iIndex: Integer; hBuffer: HGLOBAL; pBuffer: PDropFiles; f: UINT; d: pcardinal; begin Result := False; if (Filenames = nil) and (Filenames.Count = 0) then Exit; // Filenames are separated by #0 and end with a double #0#0 sFilenames := ''; for iIndex := 0 to Filenames.Count - 1 do sFilenames := sFilenames + ExcludeTrailingBackslash(Filenames.Strings[iIndex]) + #0; sFilenames := sFilenames + #0; if OpenClipboard(Handle) = False then Exit; EmptyClipboard(); hBuffer := GlobalAlloc(GMEM_MOVEABLE or GMEM_ZEROINIT, SizeOf(DROPFILES) + Length(sFilenames)); try if hBuffer = 0 then exit; pBuffer := GlobalLock(hBuffer); // prepare the "DROPFILES" structure pBuffer^.pFiles := SizeOf(DROPFILES); pBuffer := Pointer(NativeInt(pBuffer) + SizeOf(DROPFILES)); CopyMemory(pBuffer, PChar(sFilenames), Length(sFilenames)); GlobalUnlock(hBuffer); SetClipboardData(CF_HDROP, hBuffer); // Preferred DropEffect f := RegisterClipboardFormat(PChar(CF_PreferredDropEffect_Str)); hBuffer := GlobalAlloc(GMEM_SHARE or GMEM_MOVEABLE or GMEM_ZEROINIT, sizeof(dword)); d := pcardinal(GlobalLock(hBuffer)); d^ := DROPEFFECT_MOVE; SetClipboardData(f, hBuffer); GlobalUnlock(hBuffer); CloseClipboard; Result := True; except // free only if handle could not be passed to the clipboard GlobalFree(hBuffer); Raise; end; end; {!! CopyFilesToClipboard Declaration function CopyFilesToClipboard(Handle : HWND; Filenames: TStrings): Boolean; Description Adds a list of filenames to the clipboard (same as selecting Ctrl+C in Windows Explorer). The filenames can then be pasted into other Windows applications such as Explorer using Ctrl+V. Example CopyFilesToClipboard(Form1.Handle, ssFilenames); !!} // Source: http://www.martinstoeckli.ch/delphi/ function CopyFilesToClipboard(Handle : HWND; Filenames: TStrings): Boolean; var sFilenames: String; iIndex: Integer; hBuffer: HGLOBAL; pBuffer: PDropFiles; begin Result := False; if (Filenames = nil) and (Filenames.Count = 0) then Exit; // Filenames are separated by #0 and end with a double #0#0 sFilenames := ''; for iIndex := 0 to Filenames.Count - 1 do sFilenames := sFilenames + ExcludeTrailingBackslash(Filenames.Strings[iIndex]) + #0; sFilenames := sFilenames + #0; // allocate memory with the size of the "DropFiles" structure plus the // length of the filename buffer. hBuffer := GlobalAlloc(GMEM_MOVEABLE or GMEM_ZEROINIT, SizeOf(DROPFILES) + Length(sFilenames)); try if hBuffer = 0 then exit; pBuffer := GlobalLock(hBuffer); try // prepare the "DROPFILES" structure pBuffer^.pFiles := SizeOf(DROPFILES); // behind the "DROPFILES" structure we place the filenames pBuffer := Pointer(NativeInt(pBuffer) + SizeOf(DROPFILES)); CopyMemory(pBuffer, PChar(sFilenames), Length(sFilenames)); finally GlobalUnlock(hBuffer); end; // copy buffer to the clipboard if OpenClipboard(Handle) = False then raise EIEException.CreateRes(@SCannotOpenClipboard); try EmptyClipboard; SetClipboardData(CF_HDROP, hBuffer); finally CloseClipboard; end; Result := True; except // free only if handle could not be passed to the clipboard GlobalFree(hBuffer); Raise; end; end; procedure _FilenamesFromDropHandle(hDropHandle: HDROP; Dest : TStrings); var szBuffer: PChar; iCount, iIndex: Integer; iLength: Integer; begin Dest.Clear; iCount := DragQueryFile(hDropHandle, $FFFFFFFF, nil, 0); for iIndex := 0 to iCount - 1 do begin iLength := DragQueryFile(hDropHandle, iIndex, nil, 0); szBuffer := StrAlloc(iLength + 1); try DragQueryFile(hDropHandle, iIndex, szBuffer, iLength + 1); Dest.Add(szBuffer); finally StrDispose(szBuffer); end; end; end; {!! PasteFilesFromClipboard Declaration function PasteFilesFromClipboard(Handle : HWND; ssFilenames: TStrings; out bMoveFiles : Boolean) : Boolean; Description If there are files om the clipboard that have been copied from Windows Explorer or
then they will be assigned to ssFilenames. bMoveFiles will be true if they are marked to be moved (i.e. Ctrl+X was used instead of Ctrl+V). Result is true if there are files on the clipboard Example bHaveFiles := PasteFilesFromClipboard(Form1.Handle, ssFilenames, bMoveFiles); if bHaveFiles then ShowMessage('No Files to paste') else if bMoveFiles then ShowMessage('Files to Move:' + #13#10 + ssFilenames.Text) else ShowMessage('Files to Copy:' + #13#10 + ssFilenames.Text) !!} // Source: http://www.martinstoeckli.ch/delphi/ // Can pass nil to ssFilenames to test if there are files on the clipboard function PasteFilesFromClipboard(Handle : HWND; ssFilenames: TStrings; out bMoveFiles : Boolean) : Boolean; var hDropHandle: HDROP; ClipFormat, hn: Cardinal; szBuffer: array [0 .. 511] of Char; FormatID: string; pMem: Pointer; iDropEffect : Cardinal; begin Result := False; bMoveFiles := False; if assigned(ssFilenames) then ssFilenames.Clear; if OpenClipboard(Handle) = False then Exit; try // does clipboard contain filenames? Result := IsClipboardFormatAvailable(CF_HDROP); if assigned(ssFilenames) and result then begin hDropHandle := GetClipboardData(CF_HDROP); _FilenamesFromDropHandle(hDropHandle, ssFilenames); end; ClipFormat := EnumClipboardFormats(0); while ClipFormat <> 0 do begin GetClipboardFormatName(ClipFormat, szBuffer, SizeOf(szBuffer)); FormatID := string(szBuffer); if SameText(FormatID, CF_PreferredDropEffect_Str) then begin hn := GetClipboardData(ClipFormat); pMem := GlobalLock(hn); Move(pMem^, iDropEffect, 4); bMoveFiles := iDropEffect = DROPEFFECT_MOVE; GlobalUnlock(hn); Break; end; ClipFormat := EnumClipboardFormats(ClipFormat); end; finally CloseClipboard; end; end; {!! CanPasteFilesFromClipboard Declaration function CanPasteFilesFromClipboard(Handle : HWND) : Boolean; Description Returns True if there are files on the clipboard that have been copied from Windows Explorer or and can be pasted using . Example btnPaste.Enabled := CanPasteFilesFromClipboard(Form1.Handle); !!} function CanPasteFilesFromClipboard(Handle : HWND) : Boolean; var bDummy: Boolean; begin try Result := PasteFilesFromClipboard(Handle, nil, bDummy); except Result := False; end; end; {!! PopupSystemMenu Declaration procedure PopupSystemMenu(Handle: HWND; ssFileList: TStrings; x, y: integer); Description Displays the system menu for the specified files at position x,y. This menu is the same as that displayed when right-clicking a selection of files in Windows Explorer Example PopupSystemMenu(Form1.Handle, ssSelectedFiles, ClickPos.X, ClickPos.Y); !!} // Source: http://blog.issamsoft.com/index.php?q=en/node/65 procedure PopupSystemMenu(Handle: HWND; ssFileList: TStrings; x, y: integer); var Root: IShellFolder; ShellParentFolder: IShellFolder; chEaten, dwAttributes: ULONG; FilePIDL, ParentFolderPIDL: PItemIDList; CM: IContextMenu; Menu: HMenu; Command: LongBool; ICM2: IContextMenu2; ICI: TCMInvokeCommandInfo; ICmd: integer; ZVerb: array[0..255] of AnsiChar; Verb: string; Handled: boolean; SCV: IShellCommandVerb; HR: HResult; P: TPoint; ItemIDListArray: PArrayOfPItemIDList; idx: Integer; Begin if ssFileList.Count = 0 then Exit; OleCheck(SHGetDesktopFolder(Root));//Get the Desktop IShellFolder interface OleCheck(Root.ParseDisplayName(Handle, nil, PWideChar(WideString(ExtractFilePath(ssFileList[0]))), chEaten, ParentFolderPIDL, dwAttributes)); // Get the PItemIDList of the parent folder OleCheck(Root.BindToObject(ParentFolderPIDL, nil, IShellFolder, ShellParentFolder)); // Get the IShellFolder Interface of the Parent Folder //allocate memory for the PItemIDList array ItemIDListArray := AllocMem(SizeOf(PItemIDList) * ssFileList.Count); try for idx := 0 to ssFileList.Count - 1 do Begin // Get the relative PItemIDList of each file in the list OleCheck(ShellParentFolder.ParseDisplayName(Handle, nil, PWideChar(WideString(ExtractFileName(ssFileList[idx]))), chEaten, FilePIDL, dwAttributes)); ItemIDListArray^[idx] := FilePIDL; End; // get the IContextMenu Interace for the file array ShellParentFolder.GetUIObjectOf(Handle, ssFileList.Count, ItemIDListArray^[0], IID_IContextMenu, nil, CM); finally FreeMem(ItemIDListArray); end; if CM = nil then Exit; P.X := X; P.Y := Y; Windows.ClientToScreen(Handle, P); Menu := CreatePopupMenu; try CM.QueryContextMenu(Menu, 0, 1, $7FFF, CMF_EXPLORE or CMF_CANRENAME); CM.QueryInterface(IID_IContextMenu2, ICM2); //To handle submenus. try Command := TrackPopupMenu(Menu, TPM_LEFTALIGN or TPM_LEFTBUTTON or TPM_RIGHTBUTTON or TPM_RETURNCMD, p.X, p.Y, 0, Handle, nil); finally ICM2 := nil; end; if Command then begin ICmd := LongInt(Command) - 1; HR := CM.GetCommandString(ICmd, GCS_VERBA, nil, ZVerb, SizeOf(ZVerb)); Verb := string( {$IfDef DelphiXE4orNewer}AnsiStrings.{$EndIf}StrPas(ZVerb) ); // Verb := String(PAnsiString(@ZVerb[0])); <-- v5.0.6. Fails in 64bit Handled := False; if Supports(nil, IShellCommandVerb, SCV) then begin HR := 0; SCV.ExecuteCommand(Verb, Handled); end; if not Handled then begin FillChar(ICI, SizeOf(ICI), #0); with ICI do begin cbSize := SizeOf(ICI); hWND := 0; lpVerb := MakeIntResourceA(ICmd); nShow := SW_SHOWNORMAL; end; HR := CM.InvokeCommand(ICI); end; if Assigned(SCV) then SCV.CommandCompleted(Verb, HR = S_OK); end; finally DestroyMenu(Menu) end; End; {$IFDEF IEINCLUDEMULTIVIEW} {!! ShowWindowsPrintWizard Declaration procedure ShowWindowsPrintWizard(ImageEnMView: TImageEnMView; bAllImages: Boolean); overload; procedure ShowWindowsPrintWizard(ssFiles: TStrings); overload; Description Display the Windows Print Wizard with the images of a or a file list. Example ShowWindowsPrintWizard( ImageEnMView1, True ); !!} procedure ShowWindowsPrintWizard(ImageEnMView: TImageEnMView; bAllImages: Boolean); // Bill Miller: http://www.imageen.com/ieforum/topic.asp?TOPIC_ID=2129 var i: integer; ssFiles: TStringList; begin ssFiles := TStringList.create; try if bAllImages then begin for i := 0 to ImageEnMView.ImageCount - 1 do ssFiles.Add( ImageEnMView.MIO.Params[ i ].FileName ); end else begin for i := 0 to ImageEnMView.MultiSelectedImagesCount - 1 do ssFiles.Add( ImageEnMView.MIO.Params[ ImageEnMView.MultiSelectedImages[ i ] ].FileName ); end; ShowWindowsPrintWizard( ssFiles ); finally ssFiles.Free; end; end; procedure ShowWindowsPrintWizard(ssFiles: TStrings); // Bill Miller: http://www.imageen.com/ieforum/topic.asp?TOPIC_ID=2129 var i: integer; vFiles: OleVariant; vCommDlg: OleVariant; begin Screen.Cursor := crHourGlass; try vCommDlg := CreateOleObject( 'WIA.CommonDialog' ); vFiles := CreateOleObject( 'WIA.Vector' ); for i := 0 to ssFiles.Count - 1 do vFiles.Add( ssFiles[ i ] ); vCommDlg.ShowPhotoPrintingWizard( vFiles ); vFiles := Unassigned; vCommDlg := Unassigned; finally Screen.Cursor := crDefault; end; end; {$ENDIF} { TIEFileDragDrop } constructor TIEFileDragDrop.Create(Control: TWinControl; OnFileDrop : TIEFileDropEvent); begin inherited Create; fDroppingActive := False; fControl := Control; fOnFileDrop := OnFileDrop; fDropActions := [iedaCopy, iedaMove]; fControlHandle := 0; end; destructor TIEFileDragDrop.Destroy; begin if fControlHandle <> 0 then RevokeDragDrop(fControlHandle); fControlHandle := 0; inherited; end; function TIEFileDragDrop.GetDragEffect(KeyState : Integer) : Integer; var bIsShiftKeyPressed : Boolean; begin if fDropActions = [iedaCopy, iedaMove] then begin bIsShiftKeyPressed := (KeyState and MK_SHIFT = MK_SHIFT); if bIsShiftKeyPressed then Result := DROPEFFECT_MOVE else Result := DROPEFFECT_COPY; end else if fDropActions = [iedaCopy] then Result := DROPEFFECT_COPY else if fDropActions = [iedaMove] then Result := DROPEFFECT_MOVE else Result := DROPEFFECT_NONE; end; function TIEFileDragDrop.DragEnter(const dataObj: IDataObject; grfKeyState: Integer; pt: TPoint; var dwEffect: Integer): HResult; begin dwEffect := GetDragEffect(grfKeyState); result := S_OK; end; function TIEFileDragDrop.DragLeave: HResult; begin Result := S_OK; end; function TIEFileDragDrop.DragOver(grfKeyState: Integer; pt: TPoint; var dwEffect: Integer): HResult; begin dwEffect := GetDragEffect(grfKeyState); result := S_OK; end; function TIEFileDragDrop.Drop(const dataObj: IDataObject; grfKeyState: Integer; pt: TPoint; var dwEffect: Integer): HResult; var ssFilenames: TStringList; begin Result := S_OK; Try ssFilenames := TStringList.create; try dwEffect := GetDragEffect(grfKeyState); GetFilenames(dataObj, ssFilenames); fOnFileDrop(Self, ssFilenames, dwEffect); finally ssFilenames.free; end; Except Application.HandleException(Self); End; end; procedure TIEFileDragDrop.GetFileNames(const dataObj: IDataObject; Dest: TStrings); var formatetcIn: TFormatEtc; medium: TStgMedium; hDropHandle: HDROP; begin Dest.Clear; formatetcIn.cfFormat := CF_HDROP; formatetcIn.ptd := nil; formatetcIn.dwAspect := DVASPECT_CONTENT; formatetcIn.lindex := -1; formatetcIn.tymed := TYMED_HGLOBAL; if dataObj.GetData(formatetcIn, medium)=S_OK then begin (* This cast needed because HDROP is incorrectly declared as Longint in ShellAPI.pas. It should be declared as THandle which is an unsigned integer. Without this fix the routine fails in top-down memory allocation scenarios. *) hDropHandle := HDROP(medium.hGlobal); _FilenamesFromDropHandle(hDropHandle, Dest); end; end; function TIEFileDragDrop.GiveFeedback(dwEffect: Integer): HResult; begin Result := DRAGDROP_S_USEDEFAULTCURSORS; end; function TIEFileDragDrop.QueryContinueDrag(fEscapePressed: BOOL; grfKeyState: Integer): HResult; begin if fEscapePressed or ((MK_LBUTTON or MK_RBUTTON) = (grfKeyState and (MK_LBUTTON or MK_RBUTTON))) then Result := DRAGDROP_S_CANCEL else if grfKeyState and MK_LBUTTON = 0 then Result := DRAGDROP_S_DROP else Result := S_OK; end; procedure TIEFileDragDrop.SetDroppingActive(const Value: Boolean); begin if fDroppingActive <> Value then begin fDroppingActive := Value; if fDroppingActive and (fControlHandle = 0) then begin fControlHandle := fControl.Handle; RegisterDragDrop(fControlHandle, Self); end; end; end; function TIEFileDragDrop.GetDataObject(ssFiles: TStrings): IDataObject; var Malloc: IMalloc; Root: IShellFolder; p: PArrayOfPItemIDList; chEaten, dwAttributes: ULONG; i, iFileCount: Integer; begin Result := nil; iFileCount := ssFiles.Count; if iFileCount = 0 then Exit; OleCheck(SHGetMalloc(Malloc)); OleCheck(SHGetDesktopFolder(Root)); p := AllocMem(SizeOf(PItemIDList) * iFileCount); try for i := 0 to iFileCount - 1 do try OleCheck(Root.ParseDisplayName(GetActiveWindow, nil, PWideChar(WideString(ssFiles[i])), chEaten, p^[i], dwAttributes)); except // ERROR end; OleCheck(Root.GetUIObjectOf(GetActiveWindow, iFileCount, p^[0], IDataObject, nil, Pointer(Result))); finally for i := 0 to iFileCount - 1 do begin if p^[i] <> nil then Malloc.Free(p^[i]); end; FreeMem(p); end; end; procedure TIEFileDragDrop.InitiateDragging(ssFilenames : TStrings; DragActions: TIEFileDragDropActions); var DataObject: IDataObject; dwEffect: Integer; dwOKEffects: Longint; begin DataObject := GetDataObject(ssFilenames); if DataObject = nil then exit; dwEffect := DROPEFFECT_NONE; if DragActions = [iedaCopy] then dwOKEffects := DROPEFFECT_COPY else if DragActions = [iedaMove] then dwOKEffects := DROPEFFECT_MOVE else dwOKEffects := DROPEFFECT_COPY or DROPEFFECT_MOVE; DoDragDrop(DataObject, Self, dwOKEffects, dwEffect); end; { TIEFolderWatch } const FILE_NOTIFY_CHANGE_FILE_NAME = $00000001; FILE_NOTIFY_CHANGE_DIR_NAME = $00000002; FILE_NOTIFY_CHANGE_ATTRIBUTES = $00000004; FILE_NOTIFY_CHANGE_SIZE = $00000008; FILE_NOTIFY_CHANGE_LAST_WRITE = $00000010; FILE_NOTIFY_CHANGE_LAST_ACCESS = $00000020; FILE_NOTIFY_CHANGE_CREATION = $00000040; FILE_NOTIFY_CHANGE_SECURITY = $00000100; type PFILE_NOTIFY_INFORMATION = ^TFILE_NOTIFY_INFORMATION; TFILE_NOTIFY_INFORMATION = record NextEntryOffset : Cardinal; Action : Cardinal; FileNameLength : Cardinal; FileName : array[0..MAX_PATH - 1] of WideChar; end; const WM_FOLDERWATCH_ERROR = WM_USER + 137; WM_FOLDERWATCH_NOTIFY = WM_USER + 138; FILE_LIST_DIRECTORY = $0001; const // error messages cErrorInWatchThread = 'Error "%s" in watch thread. Error code: %d'; cErrorCreateWatchError = 'Error trying to create file handle for "%s". Error code: %d'; type TIEFolderWatchThread = class(TThread) private FWatchSubTree : Boolean; FAbortEvent : THandle; FChangeEvent : THandle; FBufferSize : Integer; FWndHandle : HWND; FDirHandle : THandle; FPath : string; FIOResult : Pointer; FFilter : Integer; procedure SignalError(const ErrorMessage: string; ErrorCode: Cardinal = 0); protected procedure Execute; override; public constructor Create(const Path: string; const WndHandle: HWND; const BufferSize: Integer; const AbortEvent: THandle; const TypeFilter: Cardinal; const aWatchSubTree: Boolean); destructor Destroy; override; end; function _MilliSecondsBetween(const ANow, AThen: TDateTime): Int64; begin if ANow < AThen then Result := Trunc(MSecsPerDay * (AThen - ANow)) else Result := Trunc(MSecsPerDay * (ANow - AThen)); end; procedure WaitForFileReady(const FileName: string; const Timeout: Cardinal); var hFile: THandle; StartTime: TDateTime; begin StartTime := Now; // wait to close while (_MilliSecondsBetween(Now, StartTime) < Timeout) or (Timeout = 0) do begin hFile := CreateFile(PChar(FileName), GENERIC_READ, 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if hFile <> INVALID_HANDLE_VALUE then begin CloseHandle(hFile); Break; end; // wait for file Sleep(50); end; end; procedure TIEFolderWatchThread.Execute; var NotifyData: PFILE_NOTIFY_INFORMATION; Events: array [0..1] of THandle; ErrorMessage: string; WaitResult: DWORD; NextEntry: Cardinal; FileName: PWideChar; Overlap: TOverlapped; ResSize: Cardinal; begin FillChar(Overlap, SizeOf(TOverlapped), 0); Overlap.hEvent := FChangeEvent; // set the array of events Events[0] := FChangeEvent; Events[1] := FAbortEvent; while not Terminated do try if ReadDirectoryChangesW(FDirHandle, FIOResult, FBufferSize, FWatchSubtree, FFilter, @ResSize, @Overlap, nil) then begin WaitResult := WaitForMultipleObjects(Length(Events), @Events, FALSE, INFINITE); // check if we have terminated the thread if WaitResult <> WAIT_OBJECT_0 then begin Terminate; Exit; end; if WaitResult = WAIT_OBJECT_0 then begin if GetOverlappedResult(FDirHandle, Overlap, ResSize, False) then begin NotifyData := FIOResult; // check overflow if ResSize = 0 then begin ErrorMessage := SysErrorMessage(ERROR_NOTIFY_ENUM_DIR); SignalError(ErrorMessage, ERROR_NOTIFY_ENUM_DIR); end; repeat NextEntry := NotifyData^.NextEntryOffset; // get memory for filename and fill it with data GetMem(FileName, NotifyData^.FileNameLength + SizeOf(WideChar)); Move(NotifyData^.FileName, Pointer(FileName)^, NotifyData^.FileNameLength); PWord(Cardinal(FileName) + NotifyData^.FileNameLength)^ := 0; // send the message about the filename information and advance to the next entry PostMessage(FWndHandle, WM_FOLDERWATCH_NOTIFY, NotifyData^.Action, LParam(FileName)); PByte(NotifyData) := PByte(DWORD(NotifyData) + NextEntry); until (NextEntry = 0); end else begin ErrorMessage := SysErrorMessage(GetLastError); SignalError(ErrorMessage); end; end; end else begin ErrorMessage := SysErrorMessage(GetLastError); SignalError(ErrorMessage); end; except on E:Exception do begin ErrorMessage := E.Message; SignalError(ErrorMessage); end; end; end; procedure TIEFolderWatchThread.SignalError(const ErrorMessage: string; ErrorCode: Cardinal); var ErrorMsg: PChar; MessageSize: Integer; begin if ErrorCode = 0 then ErrorCode := GetLastError; // calculate the size of the error message buffer MessageSize := Length(ErrorMessage) * SizeOf(Char) + SizeOf(WideChar); GetMem(ErrorMsg, MessageSize); StrPCopy(ErrorMsg, ErrorMessage); PostMessage(FWndHandle, WM_FOLDERWATCH_ERROR, ErrorCode, LPARAM(ErrorMsg)); end; constructor TIEFolderWatchThread.Create(const Path: string; const WndHandle: HWND; const BufferSize: Integer; const AbortEvent: THandle; const TypeFilter: Cardinal; const aWatchSubTree: Boolean); begin // // Retrieve proc pointer, open directory to // watch and allocate buffer for notification data. // (note, it is done before calling inherited // create (that calls BeginThread) so any exception // will be still raised in caller's thread) // FDirHandle := CreateFile(PChar(Path), FILE_LIST_DIRECTORY, FILE_SHARE_READ OR FILE_SHARE_DELETE OR FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS OR FILE_FLAG_OVERLAPPED, 0); if FDirHandle = INVALID_HANDLE_VALUE then raise EIEException.CreateFmt(cErrorCreateWatchError, [Path, GetLastError]); FChangeEvent := CreateEvent(nil, FALSE, FALSE, nil); FAbortEvent := AbortEvent; // allocate the buffer memory FBufferSize := BufferSize * SizeOf(TFILE_NOTIFY_INFORMATION); GetMem(FIOResult, FBufferSize); FWatchSubTree := aWatchSubtree; FWndHandle := WndHandle; FPath := Path; FFilter := TypeFilter; inherited Create(False); end; destructor TIEFolderWatchThread.Destroy; begin CloseHandle(FChangeEvent); if FDirHandle <> INVALID_HANDLE_VALUE then CloseHandle(FDirHandle); if Assigned(FIOResult) then FreeMem(FIOResult); inherited Destroy; end; procedure TIEFolderWatch.AllocWatchThread; begin if FWatchThread = nil then begin FAbortEvent := CreateEvent(nil, FALSE, FALSE, nil); FWatchThread := TIEFolderWatchThread.Create(Path, FWndHandle, FBufferSize, FAbortEvent, MakeFilter, WatchSubtree); end; end; procedure TIEFolderWatch.ReleaseWatchThread; var AResult: Cardinal; ThreadHandle: THandle; begin if FWatchThread <> nil then begin ThreadHandle := FWatchThread.Handle; // set and close event SetEvent(FAbortEvent); // wait and block until thread is finished AResult := WaitForSingleObject(ThreadHandle, cShutdownTimeout); // check if we timed out if AResult = WAIT_TIMEOUT then TerminateThread(ThreadHandle, 0); FreeAndNil(FWatchThread); CloseHandle(FAbortEvent); end; end; procedure TIEFolderWatch.RestartWatchThread; begin Stop; Start; end; function TIEFolderWatch.Running: Boolean; begin Result := FWatchThread <> nil; end; procedure TIEFolderWatch.DeallocateHWnd(Wnd: HWND); var Instance: Pointer; begin Instance := Pointer(GetWindowLong(Wnd, GWL_WNDPROC)); if Instance <> @DefWindowProc then begin { make sure we restore the default windows procedure before freeing memory } SetWindowLong(Wnd, GWL_WNDPROC, Longint(@DefWindowProc)); FreeObjectInstance(Instance); end; DestroyWindow(Wnd); end; destructor TIEFolderWatch.Destroy; begin Stop; DeallocateHWnd(FWndHandle); inherited Destroy; end; constructor TIEFolderWatch.Create; begin FWndHandle := AllocateHWnd(WatchWndProc); FWatchSubtree := True; FBufferSize := 32; // construct the default watch actions and options FWatchActions := [waAdded, waRemoved, waModified, waRenamedOld, waRenamedNew]; FWatchOptions := [woFileName, woFolderName, woAttributes, woSize, woLastWrite, woLastAccess, woCreation, woSecurity]; end; procedure TIEFolderWatch.SetWatchActions(const Value: TWatchActions); begin if FWatchActions <> Value then begin FWatchActions := Value; if Running then RestartWatchThread; Change; end; end; procedure TIEFolderWatch.SetWatchOptions(const Value: TWatchOptions); begin if FWatchOptions <> Value then begin FWatchOptions := Value; if Running then RestartWatchThread; Change; end; end; procedure TIEFolderWatch.WatchWndProc(var Msg : TMessage); var ErrorCode: Cardinal; ErrorMessage: string; begin case Msg.msg of WM_FOLDERWATCH_NOTIFY: // // Retrieve notify data and forward // the event to TIEFolderWatch's notify // handler. Free filename string (allocated // in WatchThread's notify handler.) // begin try Notify(Msg.wParam, WideCharToString(PWideChar(Msg.lParam))); finally if Msg.lParam <> 0 then FreeMem(Pointer(Msg.lParam)); end; end; WM_FOLDERWATCH_ERROR: // // Disable dir watch and re-raise // exception on error // begin try ErrorMessage := StrPas(PChar(Msg.lParam)); ErrorCode := Msg.WParam; if Assigned(FOnError) then FOnError(Self, ErrorCode, ErrorMessage); finally if Msg.lParam <> 0 then FreeMem(Pointer(Msg.lParam)); end; end; // // pass all other messages down the line // else begin Msg.Result := DefWindowProc(FWndHandle, Msg.Msg, Msg.wParam, Msg.lParam); Exit; end; end; end; function TIEFolderWatch.MakeFilter: Integer; const FilterFlags: array [TWatchOption] of Integer = (FILE_NOTIFY_CHANGE_FILE_NAME, FILE_NOTIFY_CHANGE_DIR_NAME, FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_NOTIFY_CHANGE_SIZE, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_NOTIFY_CHANGE_LAST_ACCESS, FILE_NOTIFY_CHANGE_CREATION, FILE_NOTIFY_CHANGE_SECURITY); {$IFDEF Delphi2005orNewer} var Flag: TWatchOption; begin Result := 0; for Flag in FWatchOptions do Result := Result or FilterFlags[Flag]; end; {$ELSE} begin Result := 0; if woFileName in FWatchOptions then Result := Result or FILE_NOTIFY_CHANGE_FILE_NAME; if woFolderName in FWatchOptions then Result := Result or FILE_NOTIFY_CHANGE_DIR_NAME; if woAttributes in FWatchOptions then Result := Result or FILE_NOTIFY_CHANGE_ATTRIBUTES; if woSize in FWatchOptions then Result := Result or FILE_NOTIFY_CHANGE_SIZE; if woLastWrite in FWatchOptions then Result := Result or FILE_NOTIFY_CHANGE_LAST_WRITE; if woLastAccess in FWatchOptions then Result := Result or FILE_NOTIFY_CHANGE_LAST_ACCESS; if woCreation in FWatchOptions then Result := Result or FILE_NOTIFY_CHANGE_CREATION; if woSecurity in FWatchOptions then Result := Result or FILE_NOTIFY_CHANGE_SECURITY; end; {$ENDIF} procedure TIEFolderWatch.SetWatchSubTree(const Value : Boolean); begin if Value <> FWatchSubtree then begin FWatchSubtree := Value; if Running then RestartWatchThread; Change; end; end; procedure TIEFolderWatch.Start; begin if FPath = '' then raise EIEException.Create('No Path!'); if not Running then begin AllocWatchThread; Change; end; end; procedure TIEFolderWatch.Stop; begin if Running then begin ReleaseWatchThread; Change; end; end; procedure TIEFolderWatch.SetPath(const Value: string); begin if StrIComp(PChar(Trim(Value)), PChar(FPath)) <> 0 then begin FPath := Trim(Value); if Running then RestartWatchThread; Change; end; end; procedure TIEFolderWatch.Change; begin if Assigned(FOnChange) then FOnChange(Self); end; procedure TIEFolderWatch.Notify(const Action: Integer; const FileName: string); begin if Assigned(FOnNotify) then if TWatchAction(Action - 1) in FWatchActions then FOnNotify(Self, TWatchAction(Action - 1), IEAddBackSlash(FPath) + FileName); end; initialization OleInitialize(nil); finalization OleUninitialize; {!! iexWindowsFunctions iexWindowsFunctions.pas provides access to Windows API methods for file and folder management. It is mainly used for . CLASSES FILE OPERATION FUNCTIONS CLIPBOARD FUNCTIONS SPECIAL FOLDER FUNCTIONS OTHER FUNCTIONS See Also - - - !!} end.