Componentes.Terceros.jvcl/official/3.32/archive/JvFileUtil.pas

970 lines
28 KiB
ObjectPascal

{-----------------------------------------------------------------------------
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/MPL-1.1.html
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
the specific language governing rights and limitations under the License.
The Original Code is: JvFileUtil.PAS, released on 2002-07-04.
The Initial Developers of the Original Code are: Fedor Koshevnikov, Igor Pavluk and Serge Korolev
Copyright (c) 1997, 1998 Fedor Koshevnikov, Igor Pavluk and Serge Korolev
Copyright (c) 2001,2002 SGB Software
All Rights Reserved.
Portions Copyright (c) 1998 Ritting Information Systems
Contributor(s):
Roman Kovbasiouk [roko att users dott sourceforge dott net] (TJvBrowseFolderDlg removal)
Last Modified: 2003-03-17
You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
located at http://jvcl.sourceforge.net
Known Issues:
-----------------------------------------------------------------------------}
{$I JVCL.INC}
{$I WINDOWSONLY.INC}
unit JvFileUtil;
interface
uses
Windows,
{$IFDEF COMPILER6_UP}
RTLConsts,
{$ENDIF}
Messages, SysUtils, Classes, Consts, Controls {, JvComponent};
procedure CopyFile(const FileName, DestName: string; ProgressControl: TControl);
procedure CopyFileEx(const FileName, DestName: string;
OverwriteReadOnly, ShellDialog: Boolean; ProgressControl: TControl);
procedure MoveFile(const FileName, DestName: TFileName);
procedure MoveFileEx(const FileName, DestName: TFileName; ShellDialog: Boolean);
{$IFDEF COMPILER4_UP}
function GetFileSize(const FileName: string): Int64;
{$ELSE}
function GetFileSize(const FileName: string): Longint;
{$ENDIF}
function FileDateTime(const FileName: string): TDateTime;
function HasAttr(const FileName: string; Attr: Integer): Boolean;
function DeleteFiles(const FileMask: string): Boolean;
function DeleteFilesEx(const FileMasks: array of string): Boolean;
function ClearDir(const Path: string; Delete: Boolean): Boolean;
function NormalDir(const DirName: string): string;
function RemoveBackSlash(const DirName: string): string;
function ValidFileName(const FileName: string): Boolean;
function DirExists(Name: string): Boolean;
procedure ForceDirectories(Dir: string);
function FileLock(Handle: Integer; Offset, LockSize: Longint): Integer;
{$IFDEF COMPILER4_UP} overload; {$ENDIF}
{$IFDEF COMPILER4_UP}
function FileLock(Handle: Integer; Offset, LockSize: Int64): Integer; overload;
{$ENDIF}
function FileUnlock(Handle: Integer; Offset, LockSize: Longint): Integer;
{$IFDEF COMPILER4_UP} overload; {$ENDIF}
{$IFDEF COMPILER4_UP}
function FileUnlock(Handle: Integer; Offset, LockSize: Int64): Integer; overload;
{$ENDIF}
function GetTempDir: string;
function GetWindowsDir: string;
function GetSystemDir: string;
{$IFDEF WIN32}
function ShortToLongFileName(const ShortName: string): string;
function ShortToLongPath(const ShortName: string): string;
function LongToShortFileName(const LongName: string): string;
function LongToShortPath(const LongName: string): string;
procedure CreateFileLink(const FileName, DisplayName: string; Folder: Integer);
procedure DeleteFileLink(const DisplayName: string; Folder: Integer);
{$ENDIF WIN32}
{$IFNDEF COMPILER3_UP}
function IsPathDelimiter(const S: string; Index: Integer): Boolean;
{$ENDIF}
implementation
uses
{$IFDEF WIN32}
{$IFDEF COMPILER3_UP}
ActiveX, ComObj, ShlObj,
{$ELSE}
Ole2,
OleAuto,
{$ENDIF}
{$ENDIF}
{$IFDEF COMPILER5}
FileCtrl,
{$ENDIF}
ShellAPI, Forms,
JvDateUtil, JvVCLUtils, JvProgressUtils;
{$IFDEF WIN32}
//=== TJvFileOperator ========================================================
type
TFileOperation = (foCopy, foDelete, foMove, foRename);
TFileOperFlag = (flAllowUndo, flConfirmMouse, flFilesOnly, flMultiDest,
flNoConfirmation, flNoConfirmMkDir, flRenameOnCollision, flSilent,
flSimpleProgress, flNoErrorUI);
TFileOperFlags = set of TFileOperFlag;
TJvFileOperator = class(TComponent)
private
FAborted: Boolean;
FOperation: TFileOperation;
FOptions: TFileOperFlags;
FProgressTitle: string;
FSource: string;
FDestination: string;
function TaskModalDialog(DialogFunc: Pointer; var DialogData): Boolean;
public
constructor Create(AOwner: TComponent); override;
function Execute: Boolean; virtual;
property Aborted: Boolean read FAborted;
published
property Destination: string read FDestination write FDestination;
property Operation: TFileOperation read FOperation write FOperation
default foCopy;
property Options: TFileOperFlags read FOptions write FOptions
default [flAllowUndo, flNoConfirmMkDir];
property ProgressTitle: string read FProgressTitle write FProgressTitle;
property Source: string read FSource write FSource;
end;
{$IFNDEF COMPILER3_UP}
const
FOF_NOERRORUI = $0400;
{$ENDIF}
constructor TJvFileOperator.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FOptions := [flAllowUndo, flNoConfirmMkDir];
end;
function TJvFileOperator.TaskModalDialog(DialogFunc: Pointer; var DialogData): Boolean;
type
TDialogFunc = function(var DialogData): Integer; stdcall;
var
ActiveWindow: HWND;
WindowList: Pointer;
begin
ActiveWindow := GetActiveWindow;
WindowList := DisableTaskWindows(0);
try
Result := TDialogFunc(DialogFunc)(DialogData) = 0;
finally
EnableTaskWindows(WindowList);
SetActiveWindow(ActiveWindow);
end;
end;
function TJvFileOperator.Execute: Boolean;
const
OperTypes: array [TFileOperation] of UINT =
(FO_COPY, FO_DELETE, FO_MOVE, FO_RENAME);
OperOptions: array [TFileOperFlag] of FILEOP_FLAGS =
(FOF_ALLOWUNDO, FOF_CONFIRMMOUSE, FOF_FILESONLY, FOF_MULTIDESTFILES,
FOF_NOCONFIRMATION, FOF_NOCONFIRMMKDIR, FOF_RENAMEONCOLLISION,
FOF_SILENT, FOF_SIMPLEPROGRESS, FOF_NOERRORUI);
var
OpStruct: TSHFileOpStruct;
Flag: TFileOperFlag;
function AllocFileStr(const S: string): PChar;
var
P: PChar;
begin
Result := nil;
if S <> '' then
begin
Result := StrCopy(StrAlloc(Length(S) + 2), PChar(S));
P := Result;
while P^ <> #0 do
begin
if (P^ = ';') or (P^ = '|') then
P^ := #0;
Inc(P);
end;
Inc(P);
P^ := #0;
end;
end;
begin
FAborted := False;
FillChar(OpStruct, SizeOf(OpStruct), 0);
with OpStruct do
try
if (Application.MainForm <> nil) and
Application.MainForm.HandleAllocated then
Wnd := Application.MainForm.Handle
else
Wnd := Application.Handle;
wFunc := OperTypes[Operation];
pFrom := AllocFileStr(FSource);
pTo := AllocFileStr(FDestination);
fFlags := 0;
for Flag := Low(Flag) to High(Flag) do
if Flag in FOptions then
fFlags := fFlags or OperOptions[Flag];
lpszProgressTitle := PChar(FProgressTitle);
Result := TaskModalDialog(@SHFileOperation, OpStruct);
FAborted := fAnyOperationsAborted;
finally
if pFrom <> nil then
StrDispose(pFrom);
if pTo <> nil then
StrDispose(pTo);
end;
end;
{$ELSE}
function BrowseDirectory(var AFolderName: string; const DlgText: string;
AHelpContext: THelpContext): Boolean;
begin
Result := SelectDirectory(AFolderName, [], AHelpContext);
end;
{$ENDIF WIN32}
function NormalDir(const DirName: string): string;
begin
Result := DirName;
if (Result <> '') and
{$IFDEF COMPILER3_UP}
not (AnsiLastChar(Result)^ in [':', '\']) then
{$ELSE}
not (Result[Length(Result)] in [':', '\']) then
{$ENDIF}
if (Length(Result) = 1) and (UpCase(Result[1]) in ['A'..'Z']) then
Result := Result + ':\'
else
Result := Result + '\';
end;
function RemoveBackSlash(const DirName: string): string;
begin
Result := DirName;
if (Length(Result) > 1) and
{$IFDEF COMPILER3_UP}
(AnsiLastChar(Result)^ = '\') then
{$ELSE}
(Result[Length(Result)] = '\') then
{$ENDIF}
if not ((Length(Result) = 3) and (UpCase(Result[1]) in ['A'..'Z']) and
(Result[2] = ':')) then
Delete(Result, Length(Result), 1);
end;
{$IFDEF WIN32}
function DirExists(Name: string): Boolean;
var
Code: Integer;
begin
Code := GetFileAttributes(PChar(Name));
Result := (Code <> -1) and (FILE_ATTRIBUTE_DIRECTORY and Code <> 0);
end;
{$ELSE}
function DirExists(Name: string): Boolean;
var
SR: TSearchRec;
begin
if Name[Length(Name)] = '\' then
Dec(Name[0]);
if (Length(Name) = 2) and (Name[2] = ':') then
Name := Name + '\*.*';
Result := FindFirst(Name, faDirectory, SR) = 0;
Result := Result and (SR.Attr and faDirectory <> 0);
end;
{$ENDIF}
procedure ForceDirectories(Dir: string);
begin
if Length(Dir) = 0 then
Exit;
{$IFDEF COMPILER3_UP}
if (AnsiLastChar(Dir) <> nil) and (AnsiLastChar(Dir)^ = '\') then
{$ELSE}
if Dir[Length(Dir)] = '\' then
{$ENDIF}
Delete(Dir, Length(Dir), 1);
if (Length(Dir) < 3) or DirectoryExists(Dir) or
(ExtractFilePath(Dir) = Dir) then
Exit;
ForceDirectories(ExtractFilePath(Dir));
{$IFDEF WIN32}
CreateDir(Dir);
{$ELSE}
MkDir(Dir);
{$ENDIF}
end;
{$IFDEF WIN32}
procedure CopyMoveFileShell(const FileName, DestName: string; Confirmation,
AllowUndo, MoveFile: Boolean);
begin
with TJvFileOperator.Create(nil) do
try
Source := FileName;
Destination := DestName;
if MoveFile then
begin
if AnsiCompareText(ExtractFilePath(FileName),
ExtractFilePath(DestName)) = 0 then
Operation := foRename
else
Operation := foMove;
end
else
Operation := foCopy;
if not AllowUndo then
Options := Options - [flAllowUndo];
if not Confirmation then
Options := Options + [flNoConfirmation];
if not Execute or Aborted then
SysUtils.Abort;
finally
Free;
end;
end;
{$ENDIF}
procedure CopyFile(const FileName, DestName: string; ProgressControl: TControl);
begin
CopyFileEx(FileName, DestName, False, False, ProgressControl);
end;
procedure CopyFileEx(const FileName, DestName: string;
OverwriteReadOnly, ShellDialog: Boolean; ProgressControl: TControl);
const
ChunkSize = 8192;
var
CopyBuffer: Pointer;
Source, Dest: Integer;
Destination: TFileName;
FSize, BytesCopied, TotalCopied: Longint;
Attr: Integer;
begin
{$IFDEF WIN32}
if NewStyleControls and ShellDialog then
begin
CopyMoveFileShell(FileName, DestName, not OverwriteReadOnly,
False, False);
Exit;
end;
{$ENDIF}
Destination := DestName;
if HasAttr(Destination, faDirectory) then
Destination := NormalDir(Destination) + ExtractFileName(FileName);
GetMem(CopyBuffer, ChunkSize);
try
TotalCopied := 0;
FSize := GetFileSize(FileName);
Source := FileOpen(FileName, fmShareDenyWrite);
if Source < 0 then
raise EFOpenError.CreateFmt(ResStr(SFOpenError), [FileName]);
try
if ProgressControl <> nil then
begin
SetProgressMax(ProgressControl, FSize);
SetProgressMin(ProgressControl, 0);
SetProgressValue(ProgressControl, 0);
end;
ForceDirectories(ExtractFilePath(Destination));
if OverwriteReadOnly then
begin
Attr := FileGetAttr(Destination);
if (Attr >= 0) and ((Attr and faReadOnly) <> 0) then
FileSetAttr(Destination, Attr and not faReadOnly);
end;
Dest := FileCreate(Destination);
if Dest < 0 then
raise EFCreateError.CreateFmt(ResStr(SFCreateError), [Destination]);
try
repeat
BytesCopied := FileRead(Source, CopyBuffer^, ChunkSize);
if BytesCopied = -1 then
raise EReadError.Create(ResStr(SReadError));
TotalCopied := TotalCopied + BytesCopied;
if BytesCopied > 0 then
begin
if FileWrite(Dest, CopyBuffer^, BytesCopied) = -1 then
raise EWriteError.Create(ResStr(SWriteError));
end;
if ProgressControl <> nil then
SetProgressValue(ProgressControl, TotalCopied);
until BytesCopied < ChunkSize;
FileSetDate(Dest, FileGetDate(Source));
finally
FileClose(Dest);
end;
finally
FileClose(Source);
end;
finally
FreeMem(CopyBuffer, ChunkSize);
if ProgressControl <> nil then
SetProgressValue(ProgressControl, 0);
end;
end;
procedure MoveFile(const FileName, DestName: TFileName);
var
Destination: TFileName;
Attr: Integer;
begin
Destination := ExpandFileName(DestName);
if not RenameFile(FileName, Destination) then
begin
Attr := FileGetAttr(FileName);
if Attr < 0 then
Exit;
if (Attr and faReadOnly) <> 0 then
FileSetAttr(FileName, Attr and not faReadOnly);
CopyFile(FileName, Destination, nil);
DeleteFile(FileName);
end;
end;
procedure MoveFileEx(const FileName, DestName: TFileName;
ShellDialog: Boolean);
begin
{$IFDEF WIN32}
if NewStyleControls and ShellDialog then
CopyMoveFileShell(FileName, DestName, False, False, True)
else
{$ENDIF}
MoveFile(FileName, DestName);
end;
{$IFDEF COMPILER4_UP}
function GetFileSize(const FileName: string): Int64;
var
Handle: THandle;
FindData: TWin32FindData;
begin
Handle := FindFirstFile(PChar(FileName), FindData);
if Handle <> INVALID_HANDLE_VALUE then
begin
Windows.FindClose(Handle);
if (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
begin
Int64Rec(Result).Lo := FindData.nFileSizeLow;
Int64Rec(Result).Hi := FindData.nFileSizeHigh;
Exit;
end;
end;
Result := -1;
end;
{$ELSE}
function GetFileSize(const FileName: string): Longint;
var
SearchRec: TSearchRec;
begin
if FindFirst(ExpandFileName(FileName), faAnyFile, SearchRec) = 0 then
Result := SearchRec.Size
else
Result := -1;
FindClose(SearchRec);
end;
{$ENDIF COMPILER4_UP}
function FileDateTime(const FileName: string): System.TDateTime;
var
Age: Longint;
begin
Age := FileAge(FileName);
{$IFDEF MSWINDOWS}
// [roko] -1 is valid FileAge value on Linux
if Age = -1 then
Result := NullDate
else
{$ENDIF}
Result := FileDateToDateTime(Age);
end;
function HasAttr(const FileName: string; Attr: Integer): Boolean;
var
FileAttr: Integer;
begin
FileAttr := FileGetAttr(FileName);
Result := (FileAttr >= 0) and (FileAttr and Attr = Attr);
end;
function DeleteFiles(const FileMask: string): Boolean;
var
SearchRec: TSearchRec;
begin
Result := FindFirst(ExpandFileName(FileMask), faAnyFile, SearchRec) = 0;
try
if Result then
repeat
// if (SearchRec.Name[1] <> '.') and
// !!! BUG !!!
// (rom) added '..' to complete the fix
if (SearchRec.Name <> '.') and (SearchRec.Name <> '..') and
(SearchRec.Attr and faVolumeID <> faVolumeID) and
(SearchRec.Attr and faDirectory <> faDirectory) then
begin
Result := DeleteFile(ExtractFilePath(FileMask) + SearchRec.Name);
if not Result then
Break;
end;
until FindNext(SearchRec) <> 0;
finally
FindClose(SearchRec);
end;
end;
function DeleteFilesEx(const FileMasks: array of string): Boolean;
var
I: Integer;
begin
Result := True;
for I := Low(FileMasks) to High(FileMasks) do
Result := Result and DeleteFiles(FileMasks[I]);
end;
function ClearDir(const Path: string; Delete: Boolean): Boolean;
const
{$IFDEF WIN32}
FileNotFound = 18;
{$ELSE}
FileNotFound = -18;
{$ENDIF}
var
FileInfo: TSearchRec;
DosCode: Integer;
begin
Result := DirExists(Path);
if not Result then
Exit;
DosCode := FindFirst(NormalDir(Path) + '*.*', faAnyFile, FileInfo);
try
while DosCode = 0 do
begin
// if (FileInfo.Name[1] <> '.') and (FileInfo.Attr <> faVolumeID) then
// !!! BUG !!!
if (FileInfo.Name <> '.') and (FileInfo.Name <> '..') and (FileInfo.Attr <> faVolumeID) then
begin
if (FileInfo.Attr and faDirectory) = faDirectory then
Result := ClearDir(NormalDir(Path) + FileInfo.Name, Delete) and Result
else
if (FileInfo.Attr and faVolumeID) <> faVolumeID then
begin
if (FileInfo.Attr and faReadOnly) = faReadOnly then
FileSetAttr(NormalDir(Path) + FileInfo.Name, faArchive);
Result := DeleteFile(NormalDir(Path) + FileInfo.Name) and Result;
end;
end;
DosCode := FindNext(FileInfo);
end;
finally
FindClose(FileInfo);
end;
if Delete and Result and (DosCode = FileNotFound) and
not ((Length(Path) = 2) and (Path[2] = ':')) then
begin
RmDir(Path);
Result := (IOResult = 0) and Result;
end;
end;
{$IFDEF WIN32}
function GetTempDir: string;
var
Buffer: array [0..MAX_PATH] of Char;
begin
SetString(Result, Buffer, GetTempPath(SizeOf(Buffer), Buffer));
end;
{$ELSE}
function GetTempDir: string;
var
Buffer: array [0..255] of Char;
begin
GetTempFileName(GetTempDrive(#0), '$', 1, Buffer);
Result := ExtractFilePath(StrPas(Buffer));
end;
{$ENDIF}
{$IFDEF WIN32}
function GetWindowsDir: string;
var
Buffer: array [0..MAX_PATH] of Char;
begin
SetString(Result, Buffer, GetWindowsDirectory(Buffer, SizeOf(Buffer)));
end;
{$ELSE}
function GetWindowsDir: string;
begin
Result[0] := Char(GetWindowsDirectory(@Result[1], 254));
end;
{$ENDIF}
{$IFDEF WIN32}
function GetSystemDir: string;
var
Buffer: array [0..MAX_PATH] of Char;
begin
SetString(Result, Buffer, GetSystemDirectory(Buffer, SizeOf(Buffer)));
end;
{$ELSE}
function GetSystemDir: string;
begin
Result[0] := Char(GetSystemDirectory(@Result[1], 254));
end;
{$ENDIF}
{$IFDEF WIN32}
function ValidFileName(const FileName: string): Boolean;
function HasAny(const Str, Substr: string): Boolean;
var
I: Integer;
begin
Result := False;
for I := 1 to Length(Substr) do
begin
if Pos(Substr[I], Str) > 0 then
begin
Result := True;
Break;
end;
end;
end;
begin
Result := (FileName <> '') and (not HasAny(FileName, '/<>"?*|'));
if Result then
Result := Pos('\', ExtractFileName(FileName)) = 0;
end;
function FileLock(Handle: Integer; Offset, LockSize: Longint): Integer;
begin
if LockFile(Handle, Offset, 0, LockSize, 0) then
Result := 0
else
Result := GetLastError;
end;
function FileUnlock(Handle: Integer; Offset, LockSize: Longint): Integer;
begin
if UnlockFile(Handle, Offset, 0, LockSize, 0) then
Result := 0
else
Result := GetLastError;
end;
{$IFDEF COMPILER4_UP}
function FileLock(Handle: Integer; Offset, LockSize: Int64): Integer;
begin
if LockFile(Handle, Int64Rec(Offset).Lo, Int64Rec(Offset).Hi,
Int64Rec(LockSize).Lo, Int64Rec(LockSize).Hi) then
Result := 0
else
Result := GetLastError;
end;
function FileUnlock(Handle: Integer; Offset, LockSize: Int64): Integer;
begin
if UnlockFile(Handle, Int64Rec(Offset).Lo, Int64Rec(Offset).Hi,
Int64Rec(LockSize).Lo, Int64Rec(LockSize).Hi) then
Result := 0
else
Result := GetLastError;
end;
{$ENDIF COMPILER4_UP}
{$ELSE}
function ValidFileName(const FileName: string): Boolean;
const
MaxNameLen = 12; { file name and extension }
MaxExtLen = 4; { extension with point }
MaxPathLen = 79; { full file path in DOS }
var
Dir, Name, Ext: TFileName;
function HasAny(Str, SubStr: string): Boolean; near; assembler;
asm
PUSH DS
CLD
LDS SI,Str
LES DI,SubStr
INC DI
MOV DX,DI
XOR AH,AH
LODSB
MOV BX,AX
OR BX,BX
JZ @@2
MOV AL,ES:[DI-1]
XCHG AX,CX
@@1: PUSH CX
MOV DI,DX
LODSB
REPNE SCASB
POP CX
JE @@3
DEC BX
JNZ @@1
@@2: XOR AL,AL
JMP @@4
@@3: MOV AL,1
@@4: POP DS
end;
begin
Result := True;
Dir := Copy(ExtractFilePath(FileName), 1, MaxPathLen);
Name := Copy(ExtractFileName(FileName), 1, MaxNameLen);
Ext := Copy(ExtractFileExt(FileName), 1, MaxExtLen);
if (Dir + Name <> FileName) or HasAny(Name, ';,=+<>|"[] \') or
HasAny(Copy(Ext, 2, 255), ';,=+<>|"[] \.') then
Result := False;
end;
function LockFile(Handle: Integer; StartPos, Length: Longint;
Unlock: Boolean): Integer; assembler;
asm
PUSH DS
MOV AH,5CH
MOV AL,Unlock
MOV BX,Handle
MOV DX,StartPos.Word[0]
MOV CX,StartPos.Word[2]
MOV DI,Length.Word[0]
MOV SI,Length.Word[2]
INT 21H
JNC @@1
NEG AX
JMP @@2
@@1: MOV AX,0
@@2: POP DS
end;
function FileLock(Handle: Integer; Offset, LockSize: Longint): Integer;
begin
Result := LockFile(Handle, Offset, LockSize, False);
end;
function FileUnlock(Handle: Integer; Offset, LockSize: Longint): Integer;
begin
Result := LockFile(Handle, Offset, LockSize, True);
end;
{$ENDIF WIN32}
{$IFDEF WIN32}
function ShortToLongFileName(const ShortName: string): string;
var
Temp: TWin32FindData;
SearchHandle: THandle;
begin
SearchHandle := FindFirstFile(PChar(ShortName), Temp);
if SearchHandle <> INVALID_HANDLE_VALUE then
begin
Result := Temp.cFileName;
if Result = '' then
Result := Temp.cAlternateFileName;
end
else
Result := '';
Windows.FindClose(SearchHandle);
end;
function LongToShortFileName(const LongName: string): string;
var
Temp: TWin32FindData;
SearchHandle: THandle;
begin
SearchHandle := FindFirstFile(PChar(LongName), Temp);
if SearchHandle <> INVALID_HANDLE_VALUE then
begin
Result := Temp.cAlternateFileName;
if Result = '' then
Result := Temp.cFileName;
end
else
Result := '';
Windows.FindClose(SearchHandle);
end;
function ShortToLongPath(const ShortName: string): string;
var
LastSlash: PChar;
TempPathPtr: PChar;
begin
Result := '';
TempPathPtr := PChar(ShortName);
LastSlash := StrRScan(TempPathPtr, '\');
while LastSlash <> nil do
begin
Result := '\' + ShortToLongFileName(TempPathPtr) + Result;
if LastSlash <> nil then
begin
LastSlash^ := char(0);
LastSlash := StrRScan(TempPathPtr, '\');
end;
end;
Result := TempPathPtr + Result;
end;
function LongToShortPath(const LongName: string): string;
var
LastSlash: PChar;
TempPathPtr: PChar;
begin
Result := '';
TempPathPtr := PChar(LongName);
LastSlash := StrRScan(TempPathPtr, '\');
while LastSlash <> nil do
begin
Result := '\' + LongToShortFileName(TempPathPtr) + Result;
if LastSlash <> nil then
begin
LastSlash^ := Char(0);
LastSlash := StrRScan(TempPathPtr, '\');
end;
end;
Result := TempPathPtr + Result;
end;
const
IID_IPersistFile: TGUID =
(D1: $0000010B; D2: $0000; D3: $0000; D4: ($C0, $00, $00, $00, $00, $00, $00, $46));
{$IFNDEF COMPILER3_UP}
const
IID_IShellLinkA: TGUID =
(D1: $000214EE; D2: $0000; D3: $0000; D4: ($C0, $00, $00, $00, $00, $00, $00, $46));
CLSID_ShellLink: TGUID =
(D1: $00021401; D2: $0000; D3: $0000; D4: ($C0, $00, $00, $00, $00, $00, $00, $46));
type
IShellLink = class(IUnknown) { sl }
function GetPath(pszFile: LPSTR; cchMaxPath: Integer;
var pfd: TWin32FindData; fFlags: DWORD): HResult; virtual; stdcall; abstract;
function GetIDList(var ppidl: PItemIDList): HResult; virtual; stdcall; abstract;
function SetIDList(pidl: PItemIDList): HResult; virtual; stdcall; abstract;
function GetDescription(pszName: LPSTR; cchMaxName: Integer): HResult; virtual; stdcall; abstract;
function SetDescription(pszName: LPSTR): HResult; virtual; stdcall; abstract;
function GetWorkingDirectory(pszDir: LPSTR; cchMaxPath: Integer): HResult; virtual; stdcall; abstract;
function SetWorkingDirectory(pszDir: LPSTR): HResult; virtual; stdcall; abstract;
function GetArguments(pszArgs: LPSTR; cchMaxPath: Integer): HResult; virtual; stdcall; abstract;
function SetArguments(pszArgs: LPSTR): HResult; virtual; stdcall; abstract;
function GetHotkey(var pwHotkey: Word): HResult; virtual; stdcall; abstract;
function SetHotkey(wHotkey: Word): HResult; virtual; stdcall; abstract;
function GetShowCmd(var piShowCmd: Integer): HResult; virtual; stdcall; abstract;
function SetShowCmd(iShowCmd: Integer): HResult; virtual; stdcall; abstract;
function GetIconLocation(pszIconPath: LPSTR; cchIconPath: Integer;
var piIcon: Integer): HResult; virtual; stdcall; abstract;
function SetIconLocation(pszIconPath: LPSTR; iIcon: Integer): HResult; virtual; stdcall; abstract;
function SetRelativePath(pszPathRel: LPSTR; dwReserved: DWORD): HResult; virtual; stdcall; abstract;
function Resolve(Wnd: HWND; fFlags: DWORD): HResult; virtual; stdcall; abstract;
function SetPath(pszFile: LPSTR): HResult; virtual; stdcall; abstract;
end;
{$ENDIF}
const
LinkExt = '.lnk';
procedure CreateFileLink(const FileName, DisplayName: string; Folder: Integer);
var
ShellLink: IShellLink;
PersistFile: IPersistFile;
ItemIDList: PItemIDList;
FileDestPath: array [0..MAX_PATH] of Char;
FileNameW: array [0..MAX_PATH] of WideChar;
begin
CoInitialize(nil);
try
OleCheck(CoCreateInstance(CLSID_ShellLink, nil, CLSCTX_SERVER,
IID_IShellLinkA, ShellLink));
try
OleCheck(ShellLink.QueryInterface(IID_IPersistFile, PersistFile));
try
OleCheck(SHGetSpecialFolderLocation(0, Folder, ItemIDList));
SHGetPathFromIDList(ItemIDList, FileDestPath);
StrCat(FileDestPath, PChar('\' + DisplayName + LinkExt));
ShellLink.SetPath(PChar(FileName));
ShellLink.SetIconLocation(PChar(FileName), 0);
MultiByteToWideChar(CP_ACP, 0, FileDestPath, -1, FileNameW, MAX_PATH);
OleCheck(PersistFile.Save(FileNameW, True));
finally
{$IFDEF COMPILER3_UP}
PersistFile := nil;
{$ELSE}
PersistFile.Release;
{$ENDIF}
end;
finally
{$IFDEF COMPILER3_UP}
ShellLink := nil;
{$ELSE}
ShellLink.Release;
{$ENDIF}
end;
finally
CoUninitialize;
end;
end;
procedure DeleteFileLink(const DisplayName: string; Folder: Integer);
var
ShellLink: IShellLink;
ItemIDList: PItemIDList;
FileDestPath: array [0..MAX_PATH] of Char;
begin
CoInitialize(nil);
try
OleCheck(CoCreateInstance(CLSID_ShellLink, nil, CLSCTX_SERVER,
IID_IShellLinkA, ShellLink));
try
OleCheck(SHGetSpecialFolderLocation(0, Folder, ItemIDList));
SHGetPathFromIDList(ItemIDList, FileDestPath);
StrCat(FileDestPath, PChar('\' + DisplayName + LinkExt));
DeleteFile(FileDestPath);
finally
{$IFDEF COMPILER3_UP}
ShellLink := nil;
{$ELSE}
ShellLink.Release;
{$ENDIF}
end;
finally
CoUninitialize;
end;
end;
{$ENDIF WIN32}
{$IFNDEF COMPILER3_UP}
function IsPathDelimiter(const S: string; Index: Integer): Boolean;
begin
Result := (Index > 0) and (Index <= Length(S)) and (S[Index] = '\');
end;
{$ENDIF}
end.