I want to loop through all the files in a given directory and return their version number and exe name. I have tried digging into the shell to see if I can pull this off, however I have not been able to find a solution. Any tips would be appreciated.
This does it:
Drop a TMemo and a TButton on your form and do
type
TVerInfo = packed record
vMajor, vMinor, vRelease, vBuild: word;
end;
function GetFileVerNumbers(const FileName: string): TVerInfo;
var
len, dummy: cardinal;
verdata: pointer;
verstruct: pointer;
const
InvalidVersion: TVerInfo = (vMajor: 0; vMinor: 0; vRelease: 0; vBuild: 0);
begin
len := GetFileVersionInfoSize(PWideChar(FileName), dummy);
if len = 0 then
Exit(InvalidVersion);
GetMem(verdata, len);
try
GetFileVersionInfo(PWideChar(FileName), 0, len, verdata);
VerQueryValue(verdata, '\', verstruct, dummy);
result.vMajor := HiWord(TVSFixedFileInfo(verstruct^).dwFileVersionMS);
result.vMinor := LoWord(TVSFixedFileInfo(verstruct^).dwFileVersionMS);
result.vRelease := HiWord(TVSFixedFileInfo(verstruct^).dwFileVersionLS);
result.vBuild := LoWord(TVSFixedFileInfo(verstruct^).dwFileVersionLS);
finally
FreeMem(verdata);
end;
end;
function GetFileVer(const FileName: string): string;
begin
with GetFileVerNumbers(FileName) do
result := IntToStr(vMajor) + '.' +
IntToStr(vMinor) + '.' + IntToStr(vRelease) + '.' +
IntToStr(vBuild);
end;
procedure TForm1.Button1Click(Sender: TObject);
const
path = 'C:\WINDOWS';
var
SR: TSearchRec;
begin
Memo1.Clear;
if FindFirst(IncludeTrailingBackslash(path) + '*.exe', faAnyFile, SR) = 0 then
try
repeat
Memo1.Lines.Add(SR.Name + #9 +
GetFileVer(IncludeTrailingBackslash(path) + SR.Name));
until FindNext(SR) <> 0;
finally
FindClose(SR);
end;
end;
Related
I have a following function to generate random passwords:
function GeneratePassword(ALength: Integer; Mode: TPasswordMode): string;
const
cLower = 'abcdefghijklmnopqrstuvwxyz';
cUpper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
cNumbers = '0123456789';
cExtra = '_.';
var
i : Integer;
S : string;
iM: BYTE;
begin
if Mode = [] then Exit;
i := 0;
Randomize;
while (i < ALength) do
begin
iM := RANDOM(4);
case iM of
0: if (pmLower in Mode) then begin
S := S + cLower[1+RANDOM(Length(cLower))];
Inc(i);
end;
1: if (pmUpper in Mode) then begin
S := S + cUpper[1+RANDOM(Length(cUpper))];
Inc(i);
end;
2: if (pmNumbers in Mode) then begin
S := S + cNumbers[1+RANDOM(Length(cNumbers))];
Inc(i);
end;
3: if (pmExtra in Mode) then begin
S := S + cExtra[1+RANDOM(Length(cExtra))];
Inc(i);
end;
end;
end;
Result := S;
end;
How to make this function so that a capital letter and a special character appear only once, but always? Sometimes there is no capital letter or special character when I'm generating passwords.
To be sure to have one special char and one uppercase you can do that :
function GeneratePassword(ALength: Integer; Mode: TPasswordModes): string;
const
cLower = 'abcdefghijklmnopqrstuvwxyz';
cUpper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
cNumbers = '0123456789';
cExtra = '_.';
var
iM: Byte;
i: integer;
begin
if Mode = [] then Exit;
Result := '';
i := 0;
if pmUpper in Mode then
Inc(i);
if pmExtra in Mode then
Inc(i);
// add lower case and/or number
while Result.Length < (ALength - i) do
begin
iM := Random(2);
case iM of
0: if (pmLower in Mode) then begin
Result := Result + cLower[1 + Random(Length(cLower))];
end;
1: if (pmNumbers in Mode) then begin
Result := Result + cNumbers[1 + Random(Length(cNumbers))];
end;
end;
end;
// add uppercase and/or extra
if i > 0 then
begin
if pmUpper in Mode then
Result := Result.Insert(1 + Random(Length(Result)), cUpper[1 + Random(Length(cUpper))]);
if pmExtra in Mode then
Result := Result.Insert(1 + Random(Length(Result)), cExtra[1 + Random(Length(cExtra))]);
end;
end;
type
TPasswordMode = (pmLower, pmUpper, pmNumbers, pmExtra);
TPasswordModes = set of TPasswordMode;
implementation
function GeneratePassword(ALength: Integer; Mode: TPasswordModes): string;
const
cLower = 'abcdefghijklmnopqrstuvwxyz';
cUpper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
cNumbers = '0123456789';
cExtra = '_.';
var
i : Integer;
S : string;
iM: BYTE;
begin
if Mode = [] then Exit;
i := 0;
Randomize;
while (i < ALength) do
begin
iM := RANDOM(4);
case iM of
0: if (pmLower in Mode) then begin
S := S + cLower[1+RANDOM(Length(cLower))];
Inc(i);
end;
1: if (pmUpper in Mode) then begin
S := S + cUpper[1+RANDOM(Length(cUpper))];
Inc(i);
Mode := Mode - [pmUpper]; // This I added
end;
2: if (pmNumbers in Mode) then begin
S := S + cNumbers[1+RANDOM(Length(cNumbers))];
Inc(i);
end;
3: if (pmExtra in Mode) then begin
S := S + cExtra[1+RANDOM(Length(cExtra))];
Inc(i);
Mode := Mode - [pmExtra]; // This I added
end;
end;
end;
Result := S;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage(GeneratePassword(10,[pmLower,pmUpper,pmNumbers,pmExtra]));
end;
This is not a complete solution but with this you will at least remove Upper and Extra from the requirements as soon as they get taken. You now check in the end if they ever were ever added if required and then add them if so required.
Edit:
I was in a hurry when I typed the above. You just need to check in the end if the generated password contains an Upper and Extra character. If not, you still need to add them as that was one of your requirements.
Here is example that first makes sure all extra modes are filled and the rest. It prefills Result with spaces and then replaces with random chars until all spaces are replaced.
function GetRandomEmptyPos(const aStr: string): integer; inline;
begin
// find random empty position
repeat
Result := Random(Length(aStr)) + 1;
until aStr[Result] = ' ';
end;
function GeneratePassword2(aLength: Integer; aModes: TPasswordModes): string;
const
cLower = 'abcdefghijklmnopqrstuvwxyz';
cUpper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
cNumbers = '0123456789';
cExtra = '_.';
var
i,vPos: integer;
vMode: TPasswordMode;
begin
if (aLength = 0) or (aModes = []) then Exit;
Randomize;
// Prefill Result with empty spaces
Result := StringOfChar(' ', aLength);
// Add extra characters at random places
for vMode in aModes do
begin
vPos := GetRandomEmptyPos(Result);
case vMode of
pmLower: Result[vPos] := cLower[Random(Length(cLower)) + 1];
pmUpper: Result[vPos] := cUpper[Random(Length(cUpper)) + 1];
pmNumbers: Result[vPos] := cNumbers[Random(Length(cNumbers)) + 1];
pmExtra: Result[vPos] := cExtra[Random(Length(cExtra)) + 1];
end;
end;
// Add random char on emtpy spaces
for i := 1 to Result.Length do
if Result[i] = ' ' then
Result[i] := String(cLower + cNumbers)[Random(Length(cLower) + Length(cNumbers)) + 1];
end;
unrefined code but maybe it can be useful ...
function RandomPassword(PLen: Integer): string;
var
strBase: string;
strUpper: string;
strSpecial: string;
strRecombine: string;
begin
strRecombine:='';
Result := '';
Randomize;
//string with all possible chars
strBase := 'abcdefghijklmnopqrstuvwxyz1234567890';
strUpper:='ABCDEFGHIJKLMNOPQRSTUVWXYZ';
strSpecial:='#!_';
// Start Random
strRecombine:= strUpper[Random(Length(strUpper)) + 1];
Result:=strRecombine;
strRecombine:= strSpecial[Random(Length(strSpecial))+1];
repeat
Result := Result + strBase[Random(Length(strBase)) + 1];
until (Length(Result) = PLen);
RandomRange(2, Length(strBase));
Result[RandomRange(2, PLen)]:=strRecombine[1];
//result:=Result+strRecombine;
end;
I want to execute an EXE File that is compiled with my application as a Resource. I want to execute it directly in Memory.
I have seen this Topic :
Is it possible to embed and run exe file in a Delphi executable app?
And this Code :
http://www.coderprofile.com/networks/source-codes/138/execute-resource-directly-in-memory
I used this Code :
type
TSections = array [0..0] of TImageSectionHeader;
...
{$IMAGEBASE $10000000}
function GetAlignedSize(Size: dword; Alignment: dword): dword;
begin
if ((Size mod Alignment) = 0) then
Result := Size
else
Result := ((Size div Alignment) + 1) * Alignment;
end;
function ImageSize(Image: pointer): dword;
var
Alignment: dword;
ImageNtHeaders: PImageNtHeaders;
PSections: ^TSections;
SectionLoop: dword;
begin
ImageNtHeaders := pointer(dword(dword(Image)) + dword(PImageDosHeader(Image)._lfanew));
Alignment := ImageNtHeaders.OptionalHeader.SectionAlignment;
if ((ImageNtHeaders.OptionalHeader.SizeOfHeaders mod Alignment) = 0) then
begin
Result := ImageNtHeaders.OptionalHeader.SizeOfHeaders;
end
else
begin
Result := ((ImageNtHeaders.OptionalHeader.SizeOfHeaders div Alignment) + 1) * Alignment;
end;
PSections := pointer(pchar(#(ImageNtHeaders.OptionalHeader)) + ImageNtHeaders.FileHeader.SizeOfOptionalHeader);
for SectionLoop := 0 to ImageNtHeaders.FileHeader.NumberOfSections - 1 do
begin
if PSections[SectionLoop].Misc.VirtualSize <> 0 then
begin
if ((PSections[SectionLoop].Misc.VirtualSize mod Alignment) = 0) then
begin
Result := Result + PSections[SectionLoop].Misc.VirtualSize;
end
else
begin
Result := Result + (((PSections[SectionLoop].Misc.VirtualSize div Alignment) + 1) * Alignment);
end;
end;
end;
end;
procedure CreateProcessEx(FileMemory: pointer);
var
BaseAddress, Bytes, HeaderSize, InjectSize, SectionLoop, SectionSize: dword;
Context: TContext;
FileData: pointer;
ImageNtHeaders: PImageNtHeaders;
InjectMemory: pointer;
ProcInfo: TProcessInformation;
PSections: ^TSections;
StartInfo: TStartupInfo;
begin
ImageNtHeaders := pointer(dword(dword(FileMemory)) + dword(PImageDosHeader(FileMemory)._lfanew));
InjectSize := ImageSize(FileMemory);
GetMem(InjectMemory, InjectSize);
try
FileData := InjectMemory;
HeaderSize := ImageNtHeaders.OptionalHeader.SizeOfHeaders;
PSections := pointer(pchar(#(ImageNtHeaders.OptionalHeader)) + ImageNtHeaders.FileHeader.SizeOfOptionalHeader);
for SectionLoop := 0 to ImageNtHeaders.FileHeader.NumberOfSections - 1 do
begin
if PSections[SectionLoop].PointerToRawData < HeaderSize then HeaderSize := PSections[SectionLoop].PointerToRawData;
end;
CopyMemory(FileData, FileMemory, HeaderSize);
FileData := pointer(dword(FileData) + GetAlignedSize(ImageNtHeaders.OptionalHeader.SizeO fHeaders, ImageNtHeaders.OptionalHeader.SectionAlignment));
for SectionLoop := 0 to ImageNtHeaders.FileHeader.NumberOfSections - 1 do
begin
if PSections[SectionLoop].SizeOfRawData > 0 then
begin
SectionSize := PSections[SectionLoop].SizeOfRawData;
if SectionSize > PSections[SectionLoop].Misc.VirtualSize then SectionSize := PSections[SectionLoop].Misc.VirtualSize;
CopyMemory(FileData, pointer(dword(FileMemory) + PSections[SectionLoop].PointerToRawData), SectionSize);
FileData := pointer(dword(FileData) + GetAlignedSize(PSections[SectionLoop].Misc.VirtualSize, ImageNtHeaders.OptionalHeader.SectionAlignment));
end
else
begin
if PSections[SectionLoop].Misc.VirtualSize <> 0 then FileData := pointer(dword(FileData) + GetAlignedSize(PSections[SectionLoop].Misc.VirtualSize, ImageNtHeaders.OptionalHeader.SectionAlignment));
end;
end;
ZeroMemory(#StartInfo, SizeOf(StartupInfo));
ZeroMemory(#Context, SizeOf(TContext));
CreateProcess(nil, pchar(ParamStr(0)), nil, nil, False, CREATE_SUSPENDED, nil, nil, StartInfo, ProcInfo);
Context.ContextFlags := CONTEXT_FULL;
GetThreadContext(ProcInfo.hThread, Context);
ReadProcessMemory(ProcInfo.hProcess, pointer(Context.Ebx + 8), #BaseAddress, 4, Bytes);
VirtualAllocEx(ProcInfo.hProcess, pointer(ImageNtHeaders.OptionalHeader.ImageBase), InjectSize, MEM_RESERVE or MEM_COMMIT, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(ProcInfo.hProcess, pointer(ImageNtHeaders.OptionalHeader.ImageBase), InjectMemory, InjectSize, Bytes);
WriteProcessMemory(ProcInfo.hProcess, pointer(Context.Ebx + 8), #ImageNtHeaders.OptionalHeader.ImageBase, 4, Bytes);
Context.Eax := ImageNtHeaders.OptionalHeader.ImageBase + ImageNtHeaders.OptionalHeader.AddressOfEntryPoint;
SetThreadContext(ProcInfo.hThread, Context);
ResumeThread(ProcInfo.hThread);
finally
FreeMemory(InjectMemory);
end;
end;
procedure Execute;
var
RS : TResourceStream;
begin
RS := TResourceStream.Create(HInstance, 'MrResource', RT_RCDATA);
try
CreateProcessEx(RS.Memory);
finally
RS.Free;
end;
end;
but I got " Out of Memory " error in this line ( of CreateProcessEX ) :
GetMem(InjectMemory, InjectSize);
can someone help me solve this error ? or give me some working code/solution ?
thanks before ...
An excelent unit for what you need has already been done with support for windows 64 bit.
you can find it here:
uExecFromMem by steve10120 fixed by test
here is a trivial approach written by me if you don't want to use that unit
var
eu:array of byte;
FS:TFileStream;
CONT:TContext;
imgbase,btsIO:DWORD;
IDH:PImageDosHeader;
INH:PImageNtHeaders;
ISH:PImageSectionHeader;
i:Integer;
PInfo:TProcessInformation;
SInfo:TStartupInfo;
begin
if OpenDialog1.Execute then
begin
FS:=TFileStream.Create(OpenDialog1.FileName,fmOpenRead or fmShareDenyNone);
SetLength(eu,FS.Size);
FS.Read(eu[0],FS.Size);
FS.Free;
Sinfo.cb:=Sizeof(TStartupInfo);
CreateProcess(nil,Pchar(paramstr(0)),nil,nil,FALSE,CREATE_SUSPENDED,nil,nil,SInfo,PInfo);
IDH:=#eu[0];
INH:=#eu[IDH^._lfanew];
imgbase:=DWORD(VirtualAllocEx(PInfo.hProcess,Ptr(INH^.OptionalHeader.ImageBase),INH^.OptionalHeader.SizeOfImage,MEM_COMMIT or MEM_RESERVE,PAGE_EXECUTE_READWRITE));
ShowMessage(IntToHex(imgbase,8));
WriteProcessMemory(PInfo.hProcess,Ptr(imgbase),#eu[0],INH^.OptionalHeader.SizeOfHeaders,btsIO);
for i:=0 to INH^.FileHeader.NumberOfSections - 1 do
begin
ISH:=#eu[IDH^._lfanew + Sizeof(TImageNtHeaders) + i * Sizeof(TImageSectionHeader)];
WriteProcessMemory(PInfo.hProcess,Ptr(imgbase + ISH^.VirtualAddress),#eu[ISH^.PointerToRawData],ISH^.SizeOfRawData,btsIO);
end;
CONT.ContextFlags:=CONTEXT_FULL;
GetThreadContext(PInfo.hThread,CONT);
CONT.Eax:=imgbase + INH^.OptionalHeader.AddressOfEntryPoint;
WriteProcessMemory(PInfo.hProcess,Ptr(CONT.Ebx+8),#imgbase,4,btsIO);
ShowMessage('Press ok on ENTER');
SetThreadContext(PInfo.hThread,CONT);
ResumeThread(PInfo.hThread);
CloseHandle(Pinfo.hThread);
CloseHandle(PInfo.hProcess);
end;
end;
To get opc0de's answer working on both 32bit and 64bit platforms change the context setting as follows,
GetThreadContext(PInfo.hThread,CONT);
{$IFDEF WIN64}
CONT.P6Home:=imgbase + INH^.OptionalHeader.AddressOfEntryPoint;
WriteProcessMemory(PInfo.hProcess,Ptr(CONT.P3Home+8),#imgbase,4,btsIO);
{$ELSE}
CONT.Eax:=imgbase + INH^.OptionalHeader.AddressOfEntryPoint;
WriteProcessMemory(PInfo.hProcess,Ptr(CONT.Ebx+8),#imgbase,4,btsIO);
{$ENDIF}
ShowMessage('Press ok on ENTER');
SetThreadContext(PInfo.hThread,CONT);
Your expected API pointer layout sounds not correct, and the returned size is not.
How did you define all the PImageNtHeaders and such TSections types? What is the record alignment? Shouldn't it need to be packed or aligned with some granularity? Perhaps you forgot some {$A..} or enumeration size when copy/paste the original code into your unit...
Difficult to guess without the whole source code.
I used to use this function to convert hex string to string in Delphi 6 :
const
testSign = '207F8060287F585054505357FFD55861';
function Hex2Dec(const data: string): byte;
var
nH1, nH2: byte;
begin
if data[1] in ['0' .. '9'] then
nH1 := strtoint(data[1])
else
nH1 := 9 + ord(data[1]) - 64;
if data[2] in ['0' .. '9'] then
nH2 := strtoint(data[2])
else
nH2 := 9 + ord(data[2]) - 64;
Result := nH1 * 16 + nH2;
end;
function HexStrToStr(const HexStr: string): string;
var
BufStr: string;
LenHex: Integer;
x, y: Integer;
begin
LenHex := Length(HexStr) div 2;
x := 1;
y := 0;
while y <> LenHex do
begin
Inc(y);
BufStr := BufStr + Chr(Hex2Dec(HexStr[x] + HexStr[x + 1]));
Inc(x, 2);
end;
Result := BufStr;
end;
Now I want to use the function with Delphi 2010.
const
testSign: AnsiString = '207F8060287F585054505357FFD55861';
function Hex2Dec(const data: ansistring): byte;
var
nH1, nH2: byte;
begin
if data[1] in ['0' .. '9'] then
nH1 := strtoint(data[1])
else
nH1 := 9 + ord(data[1]) - 64;
if data[2] in ['0' .. '9'] then
nH2 := strtoint(data[2])
else
nH2 := 9 + ord(data[2]) - 64;
Result := nH1 * 16 + nH2;
end;
function HexStrToStr(const HexStr: ansistring): ansistring;
var
BufStr: ansistring;
LenHex: Integer;
x, y: Integer;
begin
LenHex := Length(HexStr) div 2;
x := 1;
y := 0;
while y <> LenHex do
begin
Inc(y);
BufStr := BufStr + Chr(Hex2Dec(HexStr[x] + HexStr[x + 1]));
Inc(x, 2);
end;
Result := BufStr;
end;
Output from first code in D6 :
' '#$7F'€`('#$7F'XPTPSWÿÕXa'
Output from second code in D2010 :
' '#$7F#$0080'`('#$7F'XPTPSWÿÕXa'
How do I fix the code in D2010 so it can produces same result like D6?
Besides the solutions others provided, you can also make use of the built-in function:
function HexStrToStr(const HexStr: string): string;
var
tmp: AnsiString;
begin
Assert(not Odd(Length(HexStr)), 'HexToStr input length must be an even number');
SetLength(tmp, Length(HexStr) div 2);
HexToBin(PWideChar(HexStr), #tmp[1], Length(tmp));
result := tmp;
end;
This implementation assumes that the hex-encoded string has been an Ansistring in the first place. For flexibility I suggest to use TBytes instead.
I am making my own installer and it's almost complete. The only thing lacking is the sound when installation is complete. Is that a windows API call or I will need to find that audio file and play that from the source code?
Use the MessageBeep function.
This Small Collection of Functions Will Load, Playback, Stop, and Dump (Free Memory) for any MCI Supported Sound Files. [*.wav, *.mp3, *.wma, etc...]
uses MMSystem;
function LoadMediaFile(absoluteFile,clipName: String): Integer;
var
pc2: PChar;
pc3: String;
begin
pc3 := '"'+absoluteFile+'"';
pc2 := PChar('Open ' + pc3 + ' Alias '+ clipName);
Result := mciSendString(pc2, PChar(0), 0, 0);
end;
function StartMediaFile(clipName: String) : Integer;
var
pc2: PChar;
begin
pc2 := PChar('Play ' + clipName + ' From ' + '0');
Result := mciSendString(pc2, PChar(0), 0, 0);
end;
function StopMediaFile(clipName: String): Integer;
var
pc2: PChar;
i: Integer;
begin
pc2 := PChar('Stop ' + clipName + ' wait');
i := 0;
while (mciSendString(pc2, PChar(0), 0, 0)<>0) and (i < 250) do
begin
Result := mciSendString(pc2, PChar(0), 0, 0); i := i + 1;
end;
end;
function DumpMediaFile(clipName: String): Integer;
var
pc2,pc3: PChar;
i: Integer;
begin
pc2 := PChar('Stop ' + clipName + ' wait');
pc3 := PChar('Close ' + clipName + ' Wait');
i := 0;
while (mciSendString(pc2, PChar(0), 0, 0)<>0) and (i < 250) do
begin
mciSendString(pc2, PChar(0), 0, 0); i := i + 1;
end;
i := 0;
while (mciSendString(pc3, PChar(0), 0, 0)<>0) and (i < 250) do
begin
Result := mciSendString(pc3, PChar(0), 0, 0); i := i + 1;
end;
end;
Use them like this:
ResultInteger1 := LoadMediaFile('X:\Path\To\File.WAV', 'ClipName');
ResultInteger2 := StartMediaFile('ClipName');
Sleep(3000);
ResultInteger3 := StopMediaFile('ClipName');
ResultInteger4 := DumpMediaFile('ClipName');
Will Play 3 Seconds of The X:\Path\To\File.WAV file.
You can use:
if ResultInteger2 <> 0 then ShowMessage('ClipName did not play.');
//or
if ResultInteger2 = 0 then ShowMessage('ClipName did play.');
You can easily play the default system sounds by using:
System.Media.SystemSounds.Beep.Play();
System.Media.SystemSounds.Asterisk.Play();
System.Media.SystemSounds.Exclamation.Play();
System.Media.SystemSounds.Hand.Play();
System.Media.SystemSounds.Question.Play();
I am using Delphi2006 and I want to find the location of a particular program using Delphi code.
Here's a Delphi program that can find all files called aFileName, and puts the results into the aDestFiles stringlist.
function findFilesCalled(aFileName : String; aDestFiles : TStringList) : boolean;
var
subDirs : TStringList;
dir : Char;
sRec : TSearchRec;
toSearch : string;
begin
subdirs := TStringList.Create;
for dir := 'A' to 'Z' do
if DirectoryExists(dir + ':\') then
subdirs.add(dir + ':');
try
while (subdirs.count > 0) do begin
toSearch := subdirs[subdirs.count - 1];
subdirs.Delete(subdirs.Count - 1);
if FindFirst(toSearch + '\*.*', faDirectory, sRec) = 0 then begin
repeat
if (sRec.Attr and faDirectory) <> faDirectory then
Continue;
if (sRec.Name = '.') or (sRec.Name = '..') then
Continue;
subdirs.Add(toSearch + '\' + sRec.Name);
until FindNext(sRec) <> 0;
end;
FindClose(sRec);
if FindFirst(toSearch + '\' + aFileName, faAnyFile, sRec) = 0 then begin
repeat
aDestFiles.Add(toSearch + '\' + sRec.Name);
until FindNext(sRec) <> 0;
end;
FindClose(sRec);
end;
finally
FreeAndNil(subdirs);
end;
Result := aDestFiles.Count > 0;
end;