{**************************************************************************************************} { } { Project JEDI Code Library (JCL) } { } { The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); } { you may not use this file except in compliance with the License. You may obtain a copy of the } { License at http://www.mozilla.org/MPL/ } { } { Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF } { ANY KIND, either express or implied. See the License for the specific language governing rights } { and limitations under the License. } { } { The Original Code is JclShell.pas. } { } { The Initial Developers of the Original Code are documented in the accompanying help file } { JCLHELP.hlp. Portions created by these individuals are Copyright (C) of these individuals. } { } {**************************************************************************************************} { } { This unit contains routines and classes which makes working with the Windows Shell a bit easier. } { Included are routines for working with PIDL's, special folder's, file and folder manipulation } { through shell interfaces, shortcut's and program execution. } { } { Unit owner: Marcel van Brakel } { } {**************************************************************************************************} // $Id: JclShell.pas,v 1.2 2004/04/14 21:55:07 druid Exp $ unit JclShell; {$I jcl.inc} {$IFDEF SUPPORTS_WEAKPACKAGEUNIT} {$WEAKPACKAGEUNIT ON} {$ENDIF SUPPORTS_WEAKPACKAGEUNIT} interface uses Windows, SysUtils, {$IFNDEF FPC} ShlObj, {$ENDIF} JclWin32; //-------------------------------------------------------------------------------------------------- // Files and Folders //-------------------------------------------------------------------------------------------------- type TSHDeleteOption = (doSilent, doAllowUndo, doFilesOnly); TSHDeleteOptions = set of TSHDeleteOption; TSHRenameOption = (roSilent, roRenameOnCollision); TSHRenameOptions = set of TSHRenameOption; TUnicodePath = array[0..MAX_PATH-1] of WideChar; TAnsiPath = array[0..MAX_PATH-1] of char; function SHDeleteFiles(Parent: HWND; const Files: string; Options: TSHDeleteOptions): Boolean; function SHDeleteFolder(Parent: HWND; const Folder: string; Options: TSHDeleteOptions): Boolean; function SHRenameFile(const Src, Dest: string; Options: TSHRenameOptions): Boolean; type TEnumFolderFlag = (efFolders, efNonFolders, efIncludeHidden); TEnumFolderFlags = set of TEnumFolderFlag; TEnumFolderRec = record DisplayName: string; Attributes: DWORD; IconLarge: HICON; IconSmall: HICON; Item: PItemIdList; EnumIdList: IEnumIdList; Folder: IShellFolder; end; function SHEnumFolderFirst(const Folder: string; Flags: TEnumFolderFlags; var F: TEnumFolderRec): Boolean; function SHEnumSpecialFolderFirst(SpecialFolder: DWORD; Flags: TEnumFolderFlags; var F: TEnumFolderRec): Boolean; procedure SHEnumFolderClose(var F: TEnumFolderRec); function SHEnumFolderNext(var F: TEnumFolderRec): Boolean; function GetSpecialFolderLocation(const Folder: Integer): string; function DisplayPropDialog(const Handle: HWND; const FileName: string): Boolean; overload; function DisplayPropDialog(const Handle: HWND; Item: PItemIdList): Boolean; overload; function DisplayContextMenuPidl(const Handle: HWND; const Folder: IShellFolder; Item: PItemIdList; Pos: TPoint): Boolean; function DisplayContextMenu(const Handle: HWND; const FileName: string; Pos: TPoint): Boolean; function OpenFolder(const Path: string; Parent: HWND = 0): Boolean; function OpenSpecialFolder(FolderID: Integer; Parent: HWND = 0): Boolean; //-------------------------------------------------------------------------------------------------- // Memory Management //-------------------------------------------------------------------------------------------------- function SHReallocMem(var P: Pointer; Count: Integer): Boolean; function SHAllocMem(out P: Pointer; Count: Integer): Boolean; function SHGetMem(var P: Pointer; Count: Integer): Boolean; function SHFreeMem(var P: Pointer): Boolean; //-------------------------------------------------------------------------------------------------- // Paths and PIDLs //-------------------------------------------------------------------------------------------------- function DriveToPidlBind(const DriveName: string; out Folder: IShellFolder): PItemIdList; function PathToPidl(const Path: string; Folder: IShellFolder): PItemIdList; function PathToPidlBind(const FileName: string; out Folder: IShellFolder): PItemIdList; function PidlBindToParent(IdList: PItemIdList; out Folder: IShellFolder; out Last: PItemIdList): Boolean; function PidlCompare(Pidl1, Pidl2: PItemIdList): Boolean; function PidlCopy(Source: PItemIdList; out Dest: PItemIdList): Boolean; function PidlFree(var IdList: PItemIdList): Boolean; function PidlGetDepth(Pidl: PItemIdList): Integer; function PidlGetLength(Pidl: PItemIdList): Integer; function PidlGetNext(Pidl: PItemIdList): PItemIdList; function PidlToPath(IdList: PItemIdList): string; function StrRetFreeMem(StrRet: TStrRet): Boolean; function StrRetToString(IdList: PItemIdList; StrRet: TStrRet; Free: Boolean): string; //-------------------------------------------------------------------------------------------------- // Shortcuts / Shell link //-------------------------------------------------------------------------------------------------- type PShellLink = ^TShellLink; TShellLink = record Arguments: string; ShowCmd: Integer; WorkingDirectory: string; IdList: PItemIDList; Target: string; Description: string; IconLocation: string; IconIndex: Integer; HotKey: Word; end; procedure ShellLinkFree(var Link: TShellLink); function ShellLinkResolve(const FileName: string; var Link: TShellLink): HRESULT; function ShellLinkCreate(const Link: TShellLink; const FileName: string): HRESULT; function ShellLinkCreateSystem(const Link: TShellLink; const Folder: Integer; const FileName: string): HRESULT; function ShellLinkIcon(const Link: TShellLink): HICON; overload; function ShellLinkIcon(const FileName: string): HICON; overload; //-------------------------------------------------------------------------------------------------- // Miscellaneous //-------------------------------------------------------------------------------------------------- function SHDllGetVersion(const FileName: string; var Version: TDllVersionInfo): Boolean; function GetSystemIcon(IconIndex: Integer; Flags: Cardinal): HICON; function OverlayIcon(var Icon: HICON; Overlay: HICON; Large: Boolean): Boolean; function OverlayIconShortCut(var Large, Small: HICON): Boolean; function OverlayIconShared(var Large, Small: HICON): Boolean; function SHGetItemInfoTip(const Folder: IShellFolder; Item: PItemIdList): string; function ShellExecEx(const FileName: string; const Parameters: string = ''; const Verb: string = ''; CmdShow: Integer = SW_SHOWNORMAL): Boolean; function ShellExec(Wnd: Integer; const Operation, FileName, Parameters, Directory: string; ShowCommand: Integer): Boolean; function ShellExecAndWait(const FileName: string; const Parameters: string = ''; const Verb: string = ''; CmdShow: Integer = SW_SHOWNORMAL): Boolean; function ShellOpenAs(const FileName: string): Boolean; function ShellRasDial(const EntryName: string): Boolean; function ShellRunControlPanel(const NameOrFileName: string; AppletNumber: Integer = 0): Boolean; function GetFileNameIcon(const FileName: string; Flags: Cardinal = 0): HICON; type TJclFileExeType = (etError, etMsDos, etWin16, etWin32Gui, etWin32Con); function GetFileExeType(const FileName: TFileName): TJclFileExeType; function ShellFindExecutable(const FileName, DefaultDir: string): string; implementation uses ActiveX, {$IFNDEF FPC} CommCtrl, {$ENDIF FPC} Messages, ShellApi, JclFileUtils, JclStrings, JclSysInfo, JclSysUtils; const cVerbProperties = 'properties'; cVerbOpen = 'open'; //================================================================================================== // Files and Folders //================================================================================================== // Helper function and constant to map a TSHDeleteOptions set to a Cardinal const FOF_COMPLETELYSILENT = FOF_SILENT or FOF_NOCONFIRMATION or FOF_NOERRORUI or FOF_NOCONFIRMMKDIR; function DeleteOptionsToCardinal(Options: TSHDeleteOptions): Cardinal; begin Result := 0; if doSilent in Options then Result := Result or FOF_COMPLETELYSILENT; if doAllowUndo in Options then Result := Result or FOF_ALLOWUNDO; if doFilesOnly in Options then Result := Result or FOF_FILESONLY; end; //-------------------------------------------------------------------------------------------------- function SHDeleteFiles(Parent: HWND; const Files: string; Options: TSHDeleteOptions): Boolean; var FileOp: TSHFileOpStruct; Source: string; begin FillChar(FileOp, SizeOf(FileOp), #0); with FileOp do begin {$IFDEF FPC} hwnd := Parent; {$ELSE} Wnd := Parent; {$ENDIF} wFunc := FO_DELETE; Source := Files + #0#0; pFrom := PChar(Source); fFlags := DeleteOptionsToCardinal(Options); end; {$IFDEF FPC} Result := SHFileOperation(@FileOp) = 0; {$ELSE} Result := SHFileOperation(FileOp) = 0; {$ENDIF} end; //-------------------------------------------------------------------------------------------------- function SHDeleteFolder(Parent: HWND; const Folder: string; Options: TSHDeleteOptions): Boolean; begin Exclude(Options, doFilesOnly); Result := SHDeleteFiles(Parent, PathAddSeparator(Folder) + '*.*', Options); if Result then SHDeleteFiles(Parent, Folder, Options); end; //-------------------------------------------------------------------------------------------------- // Helper function to map a TSHRenameOptions set to a cardinal function RenameOptionsToCardinal(Options: TSHRenameOptions): Cardinal; begin Result := 0; if roRenameOnCollision in Options then Result := Result or FOF_RENAMEONCOLLISION; if roSilent in Options then Result := Result or FOF_COMPLETELYSILENT; end; function SHRenameFile(const Src, Dest: string; Options: TSHRenameOptions): Boolean; var FileOp: TSHFileOpStruct; Source, Destination: string; begin FillChar(FileOp, SizeOf(FileOp), #0); with FileOp do begin {$IFDEF FPC} hwnd := GetDesktopWindow; {$ELSE} Wnd := GetDesktopWindow; {$ENDIF} wFunc := FO_RENAME; Source := Src + #0#0; Destination := Dest + #0#0; pFrom := PChar(Source); pTo := PChar(Destination); fFlags := RenameOptionsToCardinal(Options); end; {$IFDEF FPC} Result := SHFileOperation(@FileOp) = 0; {$ELSE} Result := SHFileOperation(FileOp) = 0; {$ENDIF} end; //-------------------------------------------------------------------------------------------------- function EnumFolderFlagsToCardinal(Flags: TEnumFolderFlags): Cardinal; begin Result := 0; if efFolders in Flags then Result := Result or SHCONTF_FOLDERS; if efNonFolders in Flags then Result := Result or SHCONTF_NONFOLDERS; if efIncludeHidden in Flags then Result := Result or SHCONTF_INCLUDEHIDDEN; end; //-------------------------------------------------------------------------------------------------- procedure ClearEnumFolderRec(var F: TEnumFolderRec; const Free, Release: Boolean); begin if Release then begin F.EnumIdList := nil; F.Folder := nil; end; if Free then begin PidlFree(F.Item); DestroyIcon(F.IconLarge); DestroyIcon(F.IconSmall); end; F.Attributes := 0; F.Item := nil; F.IconLarge := 0; F.IconSmall := 0; end; //-------------------------------------------------------------------------------------------------- procedure SHEnumFolderClose(var F: TEnumFolderRec); begin ClearEnumFolderRec(F, True, True); end; //-------------------------------------------------------------------------------------------------- function SHEnumFolderNext(var F: TEnumFolderRec): Boolean; const Attr = Cardinal(SFGAO_CAPABILITYMASK or SFGAO_DISPLAYATTRMASK or SFGAO_CONTENTSMASK); var DisplayNameRet: TStrRet; ItemsFetched: ULONG; ExtractIcon: IExtractIcon; IconFile: TUnicodePath; IconIndex: Integer; Flags: DWORD; begin Result := False; ClearEnumFolderRec(F, True, False); if (F.EnumIdList = nil) or (F.Folder = nil) then Exit; if F.EnumIdList.Next(1, F.Item, ItemsFetched) = NO_ERROR then begin F.Folder.GetDisplayNameOf(F.Item, SHGDN_INFOLDER, DisplayNameRet); F.DisplayName := StrRetToString(F.Item, DisplayNameRet, True); F.Attributes := Attr; F.Folder.GetAttributesOf(1, F.Item, F.Attributes); F.Folder.GetUIObjectOf(0, 1, F.Item, IID_IExtractIconW, nil, Pointer(ExtractIcon)); Flags := 0; ExtractIcon.GetIconLocation(0, @IconFile, MAX_PATH, IconIndex, Flags); if (IconIndex < 0) and ((Flags and GIL_NOTFILENAME) = GIL_NOTFILENAME) then ExtractIconEx(@IconFile, IconIndex, F.IconLarge, F.IconSmall, 1) else ExtractIcon.Extract(@IconFile, IconIndex, F.IconLarge, F.IconSmall, MakeLong(32, 16)); Result := True; end; end; //-------------------------------------------------------------------------------------------------- function SHEnumSpecialFolderFirst(SpecialFolder: DWORD; Flags: TEnumFolderFlags; var F: TEnumFolderRec): Boolean; var DesktopFolder: IShellFolder; FolderPidl: PItemIdList; begin ClearEnumFolderRec(F, False, False); SHGetDesktopFolder(DesktopFolder); if SpecialFolder = CSIDL_DESKTOP then F.Folder := DesktopFolder else begin SHGetSpecialFolderLocation(0, SpecialFolder, FolderPidl); try DesktopFolder.BindToObject(FolderPidl, nil, IID_IShellFolder, Pointer(F.Folder)); finally PidlFree(FolderPidl); end; end; F.Folder.EnumObjects(0, EnumFolderFlagsToCardinal(Flags), F.EnumIdList); Result := SHEnumFolderNext(F); if not Result then SHEnumFolderClose(F); end; //-------------------------------------------------------------------------------------------------- function SHEnumFolderFirst(const Folder: string; Flags: TEnumFolderFlags; var F: TEnumFolderRec): Boolean; var DesktopFolder: IShellFolder; FolderPidl: PItemIdList; begin ClearEnumFolderRec(F, False, False); SHGetDesktopFolder(DesktopFolder); FolderPidl := PathToPidl(PathAddSeparator(Folder), DesktopFolder); try DesktopFolder.BindToObject(FolderPidl, nil, IID_IShellFolder, Pointer(F.Folder)); F.Folder.EnumObjects(0, EnumFolderFlagsToCardinal(Flags), F.EnumIdList); Result := SHEnumFolderNext(F); if not Result then SHEnumFolderClose(F); finally PidlFree(FolderPidl); end; end; //-------------------------------------------------------------------------------------------------- function GetSpecialFolderLocation(const Folder: Integer): string; var FolderPidl: PItemIdList; begin if Succeeded(SHGetSpecialFolderLocation(0, Folder, FolderPidl)) then begin Result := PidlToPath(FolderPidl); PidlFree(FolderPidl); end else Result := ''; end; //-------------------------------------------------------------------------------------------------- function DisplayPropDialog(const Handle: HWND; const FileName: string): Boolean; var Info: TShellExecuteInfo; begin FillChar(Info, SizeOf(Info), #0); with Info do begin cbSize := SizeOf(Info); lpFile := PChar(FileName); nShow := SW_SHOW; fMask := SEE_MASK_INVOKEIDLIST; Wnd := Handle; lpVerb := cVerbProperties; end; Result := ShellExecuteEx(@Info); end; //-------------------------------------------------------------------------------------------------- function DisplayPropDialog(const Handle: HWND; Item: PItemIdList): Boolean; var Info: TShellExecuteInfo; begin FillChar(Info, SizeOf(Info), #0); with Info do begin cbSize := SizeOf(Info); nShow := SW_SHOW; lpIDList := Item; fMask := SEE_MASK_INVOKEIDLIST or SEE_MASK_IDLIST; Wnd := Handle; lpVerb := cVerbProperties; end; Result := ShellExecuteEx(@Info); end; //-------------------------------------------------------------------------------------------------- // Window procedure for the callback window created by DisplayContextMenu. // It simply forwards messages to the folder. If you don't do this then the // system created submenu's will be empty (except for 1 stub item!) // note: storing the IContextMenu2 pointer in the window's user data was // 'inspired' by (read: copied from) code by Brad Stowers. function MenuCallback(Wnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall; var ContextMenu2: IContextMenu2; begin case Msg of WM_CREATE: begin ContextMenu2 := IContextMenu2(PCreateStruct(lParam).lpCreateParams); SetWindowLong(Wnd, GWL_USERDATA, Longint(ContextMenu2)); Result := DefWindowProc(Wnd, Msg, wParam, lParam); end; WM_INITMENUPOPUP: begin ContextMenu2 := IContextMenu2(GetWindowLong(Wnd, GWL_USERDATA)); ContextMenu2.HandleMenuMsg(Msg, wParam, lParam); Result := 0; end; WM_DRAWITEM, WM_MEASUREITEM: begin ContextMenu2 := IContextMenu2(GetWindowLong(Wnd, GWL_USERDATA)); ContextMenu2.HandleMenuMsg(Msg, wParam, lParam); Result := 1; end; else Result := DefWindowProc(Wnd, Msg, wParam, lParam); end; end; //-------------------------------------------------------------------------------------------------- // Helper function for DisplayContextMenu, creates the callback window. function CreateMenuCallbackWnd(const ContextMenu: IContextMenu2): HWND; const IcmCallbackWnd = 'ICMCALLBACKWND'; var WndClass: TWndClass; begin FillChar(WndClass, SizeOf(WndClass), #0); WndClass.lpszClassName := PChar(IcmCallbackWnd); WndClass.lpfnWndProc := @MenuCallback; WndClass.hInstance := HInstance; Windows.RegisterClass(WndClass); Result := CreateWindow(IcmCallbackWnd, IcmCallbackWnd, WS_POPUPWINDOW, 0, 0, 0, 0, 0, 0, HInstance, Pointer(ContextMenu)); end; //-------------------------------------------------------------------------------------------------- function DisplayContextMenuPidl(const Handle: HWND; const Folder: IShellFolder; Item: PItemIdList; Pos: TPoint): Boolean; var Cmd: Cardinal; ContextMenu: IContextMenu; ContextMenu2: IContextMenu2; Menu: HMENU; CommandInfo: TCMInvokeCommandInfo; CallbackWindow: HWND; begin Result := False; if (Item = nil) or (Folder = nil) then Exit; Folder.GetUIObjectOf(Handle, 1, Item, IID_IContextMenu, nil, Pointer(ContextMenu)); if ContextMenu <> nil then begin Menu := CreatePopupMenu; if Menu <> 0 then begin if Succeeded(ContextMenu.QueryContextMenu(Menu, 0, 1, $7FFF, CMF_EXPLORE)) then begin CallbackWindow := 0; if Succeeded(ContextMenu.QueryInterface(IContextMenu2, ContextMenu2)) then begin CallbackWindow := CreateMenuCallbackWnd(ContextMenu2); end; ClientToScreen(Handle, Pos); Cmd := Cardinal(TrackPopupMenu(Menu, TPM_LEFTALIGN or TPM_LEFTBUTTON or TPM_RIGHTBUTTON or TPM_RETURNCMD, Pos.X, Pos.Y, 0, CallbackWindow, nil)); if Cmd <> 0 then begin FillChar(CommandInfo, SizeOf(CommandInfo), #0); CommandInfo.cbSize := SizeOf(TCMInvokeCommandInfo); CommandInfo.hwnd := Handle; CommandInfo.lpVerb := MakeIntResource(Cmd - 1); CommandInfo.nShow := SW_SHOWNORMAL; Result := Succeeded(ContextMenu.InvokeCommand(CommandInfo)); end; if CallbackWindow <> 0 then DestroyWindow(CallbackWindow); end; DestroyMenu(Menu); end; end; end; //-------------------------------------------------------------------------------------------------- function DisplayContextMenu(const Handle: HWND; const FileName: string; Pos: TPoint): Boolean; var ItemIdList: PItemIdList; Folder: IShellFolder; begin Result := False; ItemIdList := PathToPidlBind(FileName, Folder); if ItemIdList <> nil then begin Result := DisplayContextMenuPidl(Handle, Folder, ItemIdList, Pos); PidlFree(ItemIdList); end; end; //-------------------------------------------------------------------------------------------------- function OpenFolder(const Path: string; Parent: HWND): Boolean; var Sei: TShellExecuteInfo; begin Result := False; if IsDirectory(Path) then begin FillChar(Sei, SizeOf(Sei), #0); with Sei do begin cbSize := SizeOf(Sei); Wnd := Parent; lpVerb := cVerbOpen; lpFile := PChar(Path); nShow := SW_SHOWNORMAL; end; Result := ShellExecuteEx(@Sei); end; end; //-------------------------------------------------------------------------------------------------- function OpenSpecialFolder(FolderID: Integer; Parent: HWND): Boolean; var Malloc: IMalloc; Pidl: PItemIDList; Sei: TShellExecuteInfo; begin Result := False; if Succeeded(SHGetMalloc(Malloc)) and Succeeded(SHGetSpecialFolderLocation(Parent, FolderID, Pidl)) then begin FillChar(Sei, SizeOf(Sei), #0); with Sei do begin cbSize := SizeOf(Sei); Wnd := Parent; fMask := SEE_MASK_INVOKEIDLIST; lpVerb := cVerbOpen; lpIDList := Pidl; nShow := SW_SHOWNORMAL; if PidlToPath(Pidl) = '' then begin fMask := SEE_MASK_INVOKEIDLIST; lpIDList := Pidl; end else lpFile := PChar(PidlToPath(Pidl)); end; Result := ShellExecuteEx(@Sei); Malloc.Free(Pidl); end; end; //================================================================================================== // Memory Management //================================================================================================== function SHAllocMem(out P: Pointer; Count: Integer): Boolean; var Malloc: IMalloc; begin Result := False; P := nil; if Succeeded(SHGetMalloc(Malloc)) then begin P := Malloc.Alloc(Count); if P <> nil then begin FillChar(P^, Count, #0); Result := True; end; end; end; //-------------------------------------------------------------------------------------------------- function SHFreeMem(var P: Pointer): Boolean; var Malloc: IMalloc; begin Result := False; if P <> nil then begin if Succeeded(SHGetMalloc(Malloc)) and (Malloc.DidAlloc(P) > 0) then begin Malloc.Free(P); P := nil; Result := True; end; end; end; //-------------------------------------------------------------------------------------------------- function SHGetMem(var P: Pointer; Count: Integer): Boolean; var Malloc: IMalloc; begin Result := False; if Succeeded(SHGetMalloc(Malloc)) then begin P := Malloc.Alloc(Count); if P <> nil then Result := True; end; end; //-------------------------------------------------------------------------------------------------- function SHReallocMem(var P: Pointer; Count: Integer): Boolean; var Malloc: IMalloc; begin Result := False; if Succeeded(SHGetMalloc(Malloc)) then begin if (P <> nil) and (Malloc.DidAlloc(P) <= 0) then Exit; P := Malloc.ReAlloc(P, Count); Result := (P <> nil) or (Count = 0); end; end; //================================================================================================== // Paths and PIDLs //================================================================================================== function DriveToPidlBind(const DriveName: string; out Folder: IShellFolder): PItemIdList; var Attr: ULONG; Eaten: ULONG; DesktopFolder: IShellFolder; Drives: PItemIdList; Path: TUnicodePath; begin Result := nil; if Succeeded(SHGetDesktopFolder(DesktopFolder)) then begin if Succeeded(SHGetSpecialFolderLocation(0, CSIDL_DRIVES, Drives)) then begin if Succeeded(DesktopFolder.BindToObject(Drives, nil, IID_IShellFolder, Pointer(Folder))) then begin MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, PChar(PathAddSeparator(DriveName)), -1, Path, MAX_PATH); if FAILED(Folder.ParseDisplayName(0, nil, Path, Eaten, Result, Attr)) then begin Folder := nil; // Failure probably means that this is not a drive. However, do not // call PathToPidlBind() because it may cause infinite recursion. end; end; end; PidlFree(Drives); end; end; //-------------------------------------------------------------------------------------------------- function PathToPidl(const Path: string; Folder: IShellFolder): PItemIdList; var DesktopFolder: IShellFolder; CharsParsed, Attr: ULONG; WidePath: TUnicodePath; begin Result := nil; MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, PChar(Path), -1, WidePath, MAX_PATH); if Folder <> nil then Folder.ParseDisplayName(0, nil, WidePath, CharsParsed, Result, Attr) else if Succeeded(SHGetDesktopFolder(DesktopFolder)) then DesktopFolder.ParseDisplayName(0, nil, WidePath, CharsParsed, Result, Attr); end; //-------------------------------------------------------------------------------------------------- function PathToPidlBind(const FileName: string; out Folder: IShellFolder): PItemIdList; var Attr, Eaten: ULONG; PathIdList: PItemIdList; DesktopFolder: IShellFolder; Path, ItemName: TUnicodePath; begin Result := nil; MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, PChar(ExtractFilePath(FileName)), -1, Path, MAX_PATH); MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, PChar(ExtractFileName(FileName)), -1, ItemName, MAX_PATH); if Succeeded(SHGetDesktopFolder(DesktopFolder)) then begin if Succeeded(DesktopFolder.ParseDisplayName(0, nil, Path, Eaten, PathIdList, Attr)) then begin if Succeeded(DesktopFolder.BindToObject(PathIdList, nil, IID_IShellFolder, Pointer(Folder))) then begin if FAILED(Folder.ParseDisplayName(0, nil, ItemName, Eaten, Result, Attr)) then begin Folder := nil; Result := DriveToPidlBind(FileName, Folder); end; end; PidlFree(PathIdList); end else Result := DriveToPidlBind(FileName, Folder); end; end; //-------------------------------------------------------------------------------------------------- function PidlBindToParent(IdList: PItemIdList; out Folder: IShellFolder; out Last: PItemIdList): Boolean; var Path: string; begin Last := nil; Path := PidlToPath(IdList); Last := PathToPidlBind(Path, Folder); Result := Last <> nil; if Last = nil then Folder := nil; end; //-------------------------------------------------------------------------------------------------- function PidlCompare(Pidl1, Pidl2: PItemIdList): Boolean; var L: Integer; begin Result := False; L := PidlGetLength(Pidl1); if L = PidlGetLength(Pidl2) then Result := CompareMem(Pidl1, Pidl2, L); end; //-------------------------------------------------------------------------------------------------- function PidlCopy(Source: PItemIdList; out Dest: PItemIdList): Boolean; var L: Integer; begin Result := False; Dest := Source; if Source <> nil then begin L := PidlGetLength(Source) + 2; if SHAllocMem(Pointer(Dest), L) then begin Move(Source^, Dest^, L); Result := True; end; end; end; //-------------------------------------------------------------------------------------------------- function PidlFree(var IdList: PItemIdList): Boolean; var Malloc: IMalloc; begin Result := False; if IdList = nil then Result := True else begin if Succeeded(SHGetMalloc(Malloc)) and (Malloc.DidAlloc(IdList) > 0) then begin Malloc.Free(IdList); IdList := nil; Result := True; end; end; end; //-------------------------------------------------------------------------------------------------- function PidlGetDepth(Pidl: PItemIdList): Integer; var P: PItemIdList; begin Result := 0; if Pidl <> nil then begin P := Pidl; while (P^.mkId.cb <> 0) and (Result < MAX_PATH) do begin Inc(Result); P := PItemIdList(@P^.mkId.abID[P^.mkId.cb - 2]); end; end; if Result = MAX_PATH then Result := -1; end; //-------------------------------------------------------------------------------------------------- function PidlGetLength(Pidl: PItemIdList): Integer; var P: PItemIdList; I: Integer; begin Result := 0; if Pidl <> nil then begin I := 0; P := Pidl; while (P^.mkId.cb <> 0) and (I < MAX_PATH) do begin Inc(I); Inc(Result, P^.mkId.cb); P := PItemIdList(@P^.mkId.abID[P^.mkId.cb - 2]); end; if I = MAX_PATH then Result := -1; end; end; //-------------------------------------------------------------------------------------------------- function PidlGetNext(Pidl: PItemIdList): PItemIdList; begin Result := nil; if (Pidl <> nil) and (Pidl^.mkid.cb <> 0) then begin Result := PItemIdList(@Pidl^.mkId.abID[Pidl^.mkId.cb - 2]); if Result^.mkid.cb = 0 then Result := nil; end; end; //-------------------------------------------------------------------------------------------------- function PidlToPath(IdList: PItemIdList): string; begin SetLength(Result, MAX_PATH); if SHGetPathFromIdList(IdList, PChar(Result)) then StrResetLength(Result) else Result := ''; end; //-------------------------------------------------------------------------------------------------- function StrRetFreeMem(StrRet: TStrRet): Boolean; begin Result := False; if StrRet.uType = STRRET_WSTR then Result := SHFreeMem(Pointer(StrRet.pOleStr)); end; //-------------------------------------------------------------------------------------------------- function StrRetToString(IdList: PItemIdList; StrRet: TStrRet; Free: Boolean): string; begin case StrRet.uType of STRRET_WSTR: begin Result := WideCharToString(StrRet.pOleStr); if Free then SHFreeMem(Pointer(StrRet.pOleStr)); end; STRRET_OFFSET: if IdList <> nil then Result := PChar(IdList) + StrRet.uOffset else Result := ''; STRRET_CSTR: Result := StrRet.cStr; else Result := ''; end; end; //================================================================================================== // ShortCuts / Shell link //================================================================================================== procedure ShellLinkFree(var Link: TShellLink); begin PidlFree(Link.IdList); end; //-------------------------------------------------------------------------------------------------- const IID_IShellLink: TGUID = ( { IID_IShellLinkA } D1:$000214EE; D2:$0000; D3:$0000; D4:($C0,$00,$00,$00,$00,$00,$00,$46)); function ShellLinkCreateSystem(const Link: TShellLink; const Folder: Integer; const FileName: string): HRESULT; var Path: string; Pidl: PItemIDList; begin Result := E_INVALIDARG; SetLength(Path, MAX_PATH); if Succeeded(SHGetSpecialFolderLocation(0, Folder, Pidl)) then begin Path := PidltoPath(Pidl); if Path <> '' then begin StrResetLength(Path); Result := ShellLinkCreate(Link, PathAddSeparator(Path) + FileName); end; end; end; //-------------------------------------------------------------------------------------------------- function ShellLinkCreate(const Link: TShellLink; const FileName: string): HRESULT; var ShellLink: IShellLink; PersistFile: IPersistFile; LinkName: TUnicodePath; begin Result := CoCreateInstance(CLSID_ShellLink, nil, CLSCTX_INPROC_SERVER, IID_IShellLink, ShellLink); if Succeeded(Result) then begin ShellLink.SetArguments(PChar(Link.Arguments)); ShellLink.SetShowCmd(Link.ShowCmd); ShellLink.SetWorkingDirectory(PChar(Link.WorkingDirectory)); ShellLink.SetPath(PChar(Link.Target)); ShellLink.SetDescription(PChar(Link.Description)); ShellLink.SetHotkey(Link.HotKey); ShellLink.SetIconLocation(PChar(Link.IconLocation), Link.IconIndex); PersistFile := ShellLink as IPersistFile; MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, PChar(FileName), -1, LinkName, MAX_PATH); Result := PersistFile.Save(LinkName, True); end; end; //-------------------------------------------------------------------------------------------------- function ShellLinkResolve(const FileName: string; var Link: TShellLink): HRESULT; var ShellLink: IShellLink; PersistFile: IPersistFile; LinkName: TUnicodePath; Buffer: string; Win32FindData: TWin32FindData; FullPath: string; begin Result := CoCreateInstance(CLSID_ShellLink, nil, CLSCTX_INPROC_SERVER, IID_IShellLink, ShellLink); if Succeeded(Result) then begin PersistFile := ShellLink as IPersistFile; // PersistFile.Load fails if the filename is not fully qualified FullPath := ExpandFileName(FileName); MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, PChar(FullPath), -1, LinkName, MAX_PATH); Result := PersistFile.Load(LinkName, STGM_READ); if Succeeded(Result) then begin Result := ShellLink.Resolve(0, SLR_ANY_MATCH); if Succeeded(Result) then begin SetLength(Buffer, MAX_PATH); ShellLink.GetPath(PChar(Buffer), MAX_PATH, Win32FindData, SLGP_SHORTPATH); Link.Target := PChar(Buffer); ShellLink.GetArguments(PChar(Buffer), MAX_PATH); Link.Arguments := PChar(Buffer); ShellLink.GetShowCmd(Link.ShowCmd); ShellLink.GetWorkingDirectory(PChar(Buffer), MAX_PATH); Link.WorkingDirectory := PChar(Buffer); ShellLink.GetDescription(PChar(Buffer), MAX_PATH); Link.Description := PChar(Buffer); ShellLink.GetIconLocation(PChar(Buffer), MAX_PATH, Link.IconIndex); Link.IconLocation := PChar(Buffer); ShellLink.GetHotkey(Link.HotKey); ShellLink.GetIDList(Link.IdList); end; end; end; end; //-------------------------------------------------------------------------------------------------- function ShellLinkIcon(const Link: TShellLink): HICON; overload; var LocExt: string; Info: TSHFileInfo; begin Result := 0; LocExt := LowerCase(ExtractFileExt(Link.IconLocation)); // 1. See if IconLocation specifies a valid icon file if (LocExt = '.ico') and (FileExists(Link.IconLocation)) then begin { TODO : Implement loading from an .ico file } end; // 2. See if IconLocation specifies an executable if Result = 0 then begin if (LocExt = '.dll') or (LocExt = '.exe') then Result := ExtractIcon(0, PChar(Link.IconLocation), Link.IconIndex); end; // 3. See if target specifies a file if Result = 0 then begin if FileExists(Link.Target) then Result := ExtractIcon(0, PChar(Link.Target), Link.IconIndex); end; // 4. See if the target is an object if Result = 0 then begin if Link.IdList <> nil then begin FillChar(Info, SizeOf(Info), 0); if SHGetFileInfo(PChar(Link.IdList), 0, Info, SizeOf(Info), SHGFI_PIDL or SHGFI_ICON) <> 0 then Result := Info.hIcon; end; end; end; //-------------------------------------------------------------------------------------------------- function ShellLinkIcon(const FileName: string): HICON; overload; var Link: TShellLink; begin if Succeeded(ShellLinkResolve(FileName, Link)) then begin Result := ShellLinkIcon(Link); ShellLinkFree(Link); end else Result := 0; end; //================================================================================================== // Miscellaneous //================================================================================================== function SHGetItemInfoTip(const Folder: IShellFolder; Item: PItemIdList): string; var QueryInfo: IQueryInfo; InfoTip: PWideChar; begin Result := ''; if (Item = nil) or (Folder = nil) then Exit; if Succeeded(Folder.GetUIObjectOf(0, 1, Item, IQueryInfo, nil, Pointer(QueryInfo))) then begin if Succeeded(QueryInfo.GetInfoTip(0, InfoTip)) then begin Result := WideCharToString(InfoTip); SHFreeMem(Pointer(InfoTip)); end; end; end; //-------------------------------------------------------------------------------------------------- function SHDllGetVersion(const FileName: string; var Version: TDllVersionInfo): Boolean; type TDllGetVersionProc = function (var pdvi: TDllVersionInfo): HRESULT; stdcall; var _DllGetVersion: TDllGetVersionProc; LibHandle: HINST; begin Result := False; LibHandle := LoadLibrary(PChar(FileName)); if LibHandle <> 0 then begin @_DllGetVersion := GetProcAddress(LibHandle, PChar('DllGetVersion')); if @_DllGetVersion <> nil then begin Version.cbSize := SizeOf(TDllVersionInfo); Result := Succeeded(_DllGetVersion(Version)); end; FreeLibrary(LibHandle); end; end; //-------------------------------------------------------------------------------------------------- function OverlayIcon(var Icon: HICON; Overlay: HICON; Large: Boolean): Boolean; var Source, Dest: HIMAGELIST; Width, Height: Integer; begin Result := False; if Large then begin Width := GetSystemMetrics(SM_CXICON); Height := GetSystemMetrics(SM_CYICON); Source := ImageList_Create(Width, Height, ILC_MASK or ILC_COLOR32, 1, 0); end else begin Width := GetSystemMetrics(SM_CXSMICON); Height := GetSystemMetrics(SM_CYSMICON); Source := ImageList_Create(Width, Height, ILC_MASK or ILC_COLOR32, 1, 0); end; if Source <> 0 then begin if (ImageList_AddIcon(Source, Icon) <> -1) and (ImageList_AddIcon(Source, Overlay) <> -1) then begin Dest := HIMAGELIST(ImageList_Merge(Source, 0, Source, 1, 0, 0)); if Dest <> 0 then begin DestroyIcon(Icon); Icon := ImageList_ExtractIcon(0, Dest, 0); ImageList_Destroy(Dest); Result := True; end; end; ImageList_Destroy(Source); end; end; //-------------------------------------------------------------------------------------------------- function OverlayIconShortCut(var Large, Small: HICON): Boolean; var OvlLarge, OvlSmall: HICON; begin Result := False; if ExtractIconEx(PChar('shell32.dll'), 29, OvlLarge, OvlSmall, 1) = 2 then begin OverlayIcon(Large, OvlLarge, True); OverlayIcon(Small, OvlSmall, False); end; end; //-------------------------------------------------------------------------------------------------- function OverlayIconShared(var Large, Small: HICON): Boolean; var OvlLarge, OvlSmall: HICON; begin Result := False; if ExtractIconEx(PChar('shell32.dll'), 28, OvlLarge, OvlSmall, 1) = 2 then begin OverlayIcon(Large, OvlLarge, True); OverlayIcon(Small, OvlSmall, False); end; end; //-------------------------------------------------------------------------------------------------- function GetSystemIcon(IconIndex: Integer; Flags: Cardinal): HICON; var FileInfo: TSHFileInfo; ImageList: HIMAGELIST; begin FillChar(FileInfo, SizeOf(FileInfo), #0); if Flags = 0 then Flags := SHGFI_SHELLICONSIZE; ImageList := SHGetFileInfo('', 0, FileInfo, SizeOf(FileInfo), Flags or SHGFI_SYSICONINDEX); Result := ImageList_ExtractIcon(0, ImageList, IconIndex); end; //-------------------------------------------------------------------------------------------------- function ShellExecEx(const FileName: string; const Parameters: string; const Verb: string; CmdShow: Integer): Boolean; var Sei: TShellExecuteInfo; begin FillChar(Sei, SizeOf(Sei), #0); Sei.cbSize := SizeOf(Sei); Sei.fMask := SEE_MASK_DOENVSUBST or SEE_MASK_FLAG_NO_UI; Sei.lpFile := PChar(FileName); Sei.lpParameters := PCharOrNil(Parameters); Sei.lpVerb := PCharOrNil(Verb); Sei.nShow := CmdShow; Result := ShellExecuteEx(@Sei); end; //-------------------------------------------------------------------------------------------------- // TODOC author Jeff note, ShellExecEx() above used to be ShellExec()... function ShellExec(Wnd: Integer; const Operation, FileName, Parameters, Directory: string; ShowCommand: Integer): Boolean; begin Result := ShellExecute(Wnd, PChar(Operation), PChar(FileName), PChar(Parameters), PChar(Directory), ShowCommand) > 32; end; //-------------------------------------------------------------------------------------------------- function ShellExecAndWait(const FileName: string; const Parameters: string; const Verb: string; CmdShow: Integer): Boolean; var Sei: TShellExecuteInfo; Res: LongBool; Msg: tagMSG; begin FillChar(Sei, SizeOf(Sei), #0); Sei.cbSize := SizeOf(Sei); Sei.fMask := SEE_MASK_DOENVSUBST or SEE_MASK_FLAG_NO_UI or SEE_MASK_NOCLOSEPROCESS or SEE_MASK_FLAG_DDEWAIT; Sei.lpFile := PChar(FileName); Sei.lpParameters := PCharOrNil(Parameters); Sei.lpVerb := PCharOrNil(Verb); Sei.nShow := CmdShow; Result := ShellExecuteEx(@Sei); if Result then begin WaitForInputIdle(Sei.hProcess, INFINITE); while (WaitForSingleObject(Sei.hProcess, 10) = WAIT_TIMEOUT) do begin repeat Res := PeekMessage(Msg, Sei.Wnd, 0, 0, PM_REMOVE); if Res then begin TranslateMessage(Msg); DispatchMessage(Msg); end; until (Res = False); end; CloseHandle(Sei.hProcess); end; end; //-------------------------------------------------------------------------------------------------- function ShellOpenAs(const FileName: string): Boolean; begin Result := ShellExecEx('rundll32', Format('shell32.dll,OpenAs_RunDLL "%s"', [FileName]), '', SW_SHOWNORMAL); end; //-------------------------------------------------------------------------------------------------- function ShellRasDial(const EntryName: string): Boolean; var Info: TRasDialDlg; RasDlg: HModule; RasDialDlgA: TRasDialDlgA; begin if IsWinNT then begin Result := False; RasDlg := LoadLibrary(PChar('rasdlg.dll')); if RasDlg <> 0 then try @RasDialDlgA := GetProcAddress(RasDlg, PChar('RasDialDlgA')); if @RasDialDlgA <> nil then begin FillChar(Info, SizeOf(Info), 0); Info.dwSize := SizeOf(Info); Result := RasDialDlgA(nil, PChar(EntryName), nil, @Info); end; finally FreeLibrary(RasDlg); end; end else Result := ShellExecEx('rundll32', Format('rnaui.dll,RnaDial "%s"', [EntryName]), '', SW_SHOWNORMAL); end; //-------------------------------------------------------------------------------------------------- // You can pass simple name of standard system control panel (e.g. 'timedate') // or full qualified file name (Window 95 only? doesn't work on Win2K!) function ShellRunControlPanel(const NameOrFileName: string; AppletNumber: Integer): Boolean; var FileName: TFileName; begin if ExtractFilePath(NameOrFileName) = '' then FileName := ChangeFileExt(PathAddSeparator(GetWindowsSystemFolder) + NameOrFileName, '.cpl') else FileName := NameOrFileName; if FileExists(FileName) then Result := ShellExecEx('rundll32', Format('shell32.dll,Control_RunDLL "%s", @%d', [FileName, AppletNumber]), '', SW_SHOWNORMAL) else begin Result := False; SetLastError(ERROR_FILE_NOT_FOUND); end; end; //-------------------------------------------------------------------------------------------------- function GetFileExeType(const FileName: TFileName): TJclFileExeType; var FileInfo: TSHFileInfo; R: DWORD; begin R := SHGetFileInfo(PChar(FileName), 0, FileInfo, SizeOf(FileInfo), SHGFI_EXETYPE); case LoWord(R) of IMAGE_DOS_SIGNATURE: Result := etMsDos; IMAGE_OS2_SIGNATURE: Result := etWin16; Word(IMAGE_NT_SIGNATURE): if HiWord(R) = 0 then Result := etWin32Con else Result := etWin32Gui; else Result := etError; end; end; //-------------------------------------------------------------------------------------------------- function ShellFindExecutable(const FileName, DefaultDir: string): string; var Res: HINST; Buffer: TAnsiPath; I: Integer; begin FillChar(Buffer, SizeOf(Buffer), #0); Res := FindExecutable(PChar(FileName), PCharOrNil(DefaultDir), Buffer); if Res > 32 then begin // FindExecutable replaces #32 with #0 for I := Low(Buffer) to High(Buffer) - 1 do if Buffer[I] = #0 then Buffer[I] := #32; Buffer[High(Buffer)] := #0; Result := Trim(Buffer); end else Result := ''; end; //-------------------------------------------------------------------------------------------------- function GetFileNameIcon(const FileName: string; Flags: Cardinal = 0): HICON; var FileInfo: TSHFileInfo; ImageList: HIMAGELIST; begin FillChar(FileInfo, SizeOf(FileInfo), #0); if Flags = 0 then Flags := SHGFI_SHELLICONSIZE; ImageList := SHGetFileInfo(PChar(FileName), 0, FileInfo, SizeOf(FileInfo), Flags or SHGFI_SYSICONINDEX); if ImageList <> 0 then Result := ImageList_ExtractIcon(0, ImageList, FileInfo.iIcon) else Result := 0; end; //-------------------------------------------------------------------------------------------------- end.