home *** CD-ROM | disk | FTP | other *** search
/ Collection of Hack-Phreak Scene Programs / cleanhpvac.zip / cleanhpvac / CHFLZ100.ZIP / CHFUTILS.PAS < prev    next >
Pascal/Delphi Source File  |  1996-09-05  |  21KB  |  818 lines

  1.  
  2. {$I LZDefine.inc}
  3.  
  4. unit ChfUtils;
  5.  
  6. {some miscellaneous routines for the ChiefLZ package}
  7.  
  8. interface
  9. {$ifdef Delphi}
  10. Uses SysUtils;
  11. {$else}
  12. {$ifndef Windows}
  13.  Uses Dos;
  14. {$endif Windows}
  15. const
  16.   fmOpenRead       = $00;
  17.   fmOpenWrite      = $01;
  18.   fmOpenReadWrite  = $02;
  19.   fmShareCompat    = $00;
  20.   fmShareExclusive = $10;
  21.   fmShareDenyWrite = $20;
  22.   fmShareDenyRead  = $30;
  23.   fmShareDenyNone  = $40;
  24. {$endif}
  25.  
  26. function AddBackSlash(Const DirName : string) : string;
  27. function RemoveBackSlash(const S: string): string;
  28. function Min(const I1, I2: LongInt): LongInt;
  29.  
  30. function FirstDirectoryBetween(const s1, s2: string): string;
  31. Function DirectoryExists(const s:String): Boolean;
  32. Function FSize(const S : String): LongInt;
  33. Function sFTime(const s:string): LongInt;
  34. Function lFTime(var f: file): LongInt;
  35.  
  36. {$ifdef Win32}
  37.  
  38. {$IFDEF Debug}
  39. type
  40.   EChiefLZDebug = class(Exception);
  41. {
  42.   AddrOfCaller ***MUST*** be called by a routine that has a stack frame!!
  43. }
  44. function AddrOfCaller: Pointer;
  45. {$ENDIF}
  46.  
  47. procedure RaiseError(const EClass: ExceptClass; const Res: Integer);
  48. procedure RaiseErrorStr(const EClass: ExceptClass;
  49.                         const Res:    Integer;
  50.                         const Mes:    string);
  51. procedure RaiseIOError(const EMess, ECode: Integer);
  52. function CreateIOError(const EMess, ECode: Integer): EInOutError;
  53.  
  54. function FileVersionInfo(const fName, StringToGet: string): string;
  55.  
  56. {$else Win32}
  57.  
  58. type
  59.   PString = ^String;
  60.  
  61. function  Str2PChar(Var s:String):PChar;
  62. function  NewString(const s: string): PString;
  63. procedure DisposeString(var P: PString);
  64. function  GetCurrentDir: string;
  65.  
  66. {$ifdef Win16}
  67. {$ifndef DPMI}
  68. Function FileVersionInfo(const Fname, StringToGet:PChar):String;
  69. {$endif DPMI}
  70. {$endif Win16}
  71.  
  72. {$IFDEF Debug}
  73. procedure RunErrorMessage(const Mes: string);
  74. procedure RunErrorMessageAt(const Mes: string; const ErrorLoc: Pointer);
  75. {
  76.   AddrOfCaller **MUST** be called by a FAR routine that has a stack frame!!
  77. }
  78. function AddrOfCaller: Pointer; inline($8B/$46/$02/   { mov ax, [bp+2] }
  79.                                        $8B/$56/$04);  { mov dx, [bp+4] }
  80. {$ENDIF}
  81.  
  82. {$endif Win32}
  83.  
  84. {$ifndef Delphi}
  85. Function ExtractFilePath(const aName:String):String;
  86. function ExtractFileName(const s:String):String;
  87. Function ExtractFileExt(const aName:String):String;
  88. Function ChangeFileExt(const aName, aExt:String):String;
  89. Function FileExists(Const S : String) : Boolean;
  90. Function Uppercase(S: String): String;
  91. {$endif Delphi}
  92.  
  93. {$ifndef Windows}
  94. Const
  95. faDirectory=Directory;
  96. faArchive=Archive;
  97.  
  98. {
  99. faReadOnly=ReadOnly;
  100. faSysFile=SysFile;
  101. faHidden=Hidden;
  102. faAnyFile=AnyFile;
  103. }
  104. {$endif Windows}
  105.  
  106. implementation
  107. uses
  108. {$ifdef Win32}
  109. Windows
  110. {$else Win32}
  111. {$ifdef Windows}
  112. {$ifndef Delphi}
  113. WinDos, Strings,
  114. {$endif Delphi}
  115. {$ifdef DPMI}
  116. WinAPI
  117. {$else DPMI}
  118. WinTypes,
  119. WinProcs,
  120. Ver
  121. {$endif DPMI}
  122. {$else Windows}
  123. Strings
  124. {$endif Windows}
  125. {$endif Win32};
  126.  
  127. {$IFDEF Debug}
  128. {$ifdef Win32}
  129. {
  130.   This function has no stack frame of its own, hence EBP is its caller's
  131.   stack frame. This means that EAX is loaded with the RETurn address of
  132.   the calling function ...
  133. }
  134. {$W-}
  135. function AddrOfCaller: Pointer; assembler;
  136. asm
  137.   MOV EAX, [EBP+4]  // DWord at [EBP] is old EBP
  138. {
  139.   Quick and dirty fix to overcome a *BUG* in ShowException()...
  140.   Add an `anti-correction' to the address so that Delphi will return
  141.   the absolute address of the exception, rather than a relative one.
  142.  
  143.   Remove this once ShowException() has been fixed ...
  144. }
  145.   ADD EAX, OFFSET TextStart
  146. end;
  147. {$W+}
  148.  
  149. {$else Win32}
  150.  
  151. type
  152.   THexStr = string[4];
  153.  
  154. function Hex4(X: Word): THexStr;
  155. var
  156.   i, j: byte;
  157. begin
  158.   Hex4[0] := chr(4);
  159.   for i := 4 downto 1 do
  160.     begin
  161.       j := lo(X) and $F;
  162.       if j > 9 then
  163.         inc(j,ord('A')-$A)
  164.       else
  165.         inc(j,ord('0'));
  166.       X := X shr 4;
  167.       Hex4[i] := chr(j)
  168.     end
  169. end;
  170.  
  171. procedure RunErrorMessageAt(const Mes: string; const ErrorLoc: Pointer);
  172. type
  173.   PtrRec = record
  174.              Ofs, Seg: word
  175.            end;
  176. {$ifdef Windows}
  177. var
  178.   NewMes: array[0..255] of Char;
  179.   HexNum: array[0..4] of Char;
  180. {$endif}
  181. begin
  182. {$ifdef Windows}
  183. {
  184.   This is untested: I have no idea whether the address here will function
  185.   correctly in the IDE. This address is the undoctored location of the
  186.   error ...
  187. }
  188.   with PtrRec(ErrorLoc) do
  189.     StrCat(StrCat(StrCat(StrCat(
  190.                StrPCopy(NewMes, Mes),
  191.                #13#10'Address for "Search|Find Error" is ' ),
  192.                StrPCopy(HexNum, Hex4(Seg)) ),
  193.                ':' ),
  194.                StrPCopy(HexNum, Hex4(Ofs)) );
  195.   {$ifndef DPMI}WinProcs.{$endif}MessageBox(HInstance, NewMes,
  196.                                              'ChiefLZ Error', MB_OK);
  197. {$else Windows}
  198. {
  199.   Perform Real-Mode segment-arithmetic to calculate logical address for
  200.   IDE. The IDE expects the segment number to be relative to the main
  201.   program's code segment. This is located immediately after the PSP,
  202.   and the PSP is 16 paragraphs long.
  203. }
  204.   Writeln;
  205.   Writeln( 'ChiefLZ Error: ', Mes );
  206.   with PtrRec(ErrorLoc) do
  207.     Writeln( 'Address for "Search|Find Error" is ',
  208.                                   Hex4(Seg-PrefixSeg-16),':',Hex4(Ofs) );
  209. {$endif Windows}
  210.   Halt
  211. end;
  212.  
  213. procedure RunErrorMessage(const Mes: string);
  214. begin
  215.   RunErrorMessageAt(Mes, AddrOfCaller)
  216. end;
  217.  
  218. {$endif Win32}
  219. {$ENDIF}
  220.  
  221. {/////////////////////////////////////////////////}
  222. {
  223.   These are general-purpose functions used by all versions ...
  224. }
  225. {/////////////////////////////////////////////////}
  226.  
  227. function AddBackSlash(Const DirName: string) : string;
  228. {-Add a default backslash to a directory name}
  229. begin
  230. {$ifdef Win32}
  231. {
  232.   Win32 version uses ExpandFileName() ... ':' ***shouldn't*** appear ...
  233. }
  234.   if (Length(DirName)=0) or (DirName[Length(DirName)]='\') then
  235.     AddBackSlash := DirName
  236.   else
  237.     begin
  238.     {$IFDEF Debug}
  239.       if DirName[Length(DirName)] = ':' then
  240.         raise EChiefLZDebug.Create('Directory name "' + DirName +
  241.                                    '" terminated by '':'' character')
  242.           at AddrOfCaller;  // Error will not be reported at THIS address,
  243.     {$ENDIF}                // but where AddBackSlash() was called.
  244.       AddBackSlash := DirName + '\'
  245.     end;
  246. {$else}
  247.   if DirName[Length(DirName)] in ['\',':',#0] then
  248.     AddBackSlash := DirName
  249.   else
  250.     AddBackSlash := DirName + '\'
  251. {$endif}
  252. end;
  253.  
  254. function RemoveBackSlash(const S: string): string;
  255. {$ifdef Win32}
  256. var
  257.   i: Integer;
  258. {$endif}
  259. {$ifndef Delphi}
  260. var
  261.   Result: string;
  262. {$endif}                       
  263. begin
  264.   Result := s;
  265. {$ifdef Win32}
  266.   i := Length(s);
  267.   if s[i] = '\' then
  268.     SetLength(Result, i-1);
  269. {$else Win32}
  270.   if s[Length(s)] = '\' then
  271.     dec(Result[0]);
  272. {$ifndef Delphi}
  273.   RemoveBackSlash := Result;
  274. {$endif Delphi}
  275. {$endif Win32}
  276. {$IFDEF Debug}
  277.   if Pos('\',Result) = 0 then
  278.   {$ifdef Win32}
  279.     raise EChiefLZDebug.Create('Removed ''\'' from root directory!')
  280.       at AddrOfCaller
  281.   {$else Win32}
  282.     RunErrorMessageAt('Removed ''\'' from root directory!', AddrOfCaller)
  283.   {$endif Win32};
  284. {$ENDIF}
  285. end;
  286.  
  287. {/////////////////////////////////////////////////////////}
  288. Function FSize(Const S: String): LongInt;
  289. {return the file size of filename "S"}
  290. var
  291. f: file;
  292. {$ifndef Win32}
  293. OldFMode: byte;
  294. {$endif}
  295.  
  296. begin
  297.   {$ifdef Win32}
  298.     AssignFile(f,s);
  299.     FileMode := fmOpenRead; { Win32 only allows 0 <= FileMode <= 2 }
  300.     Reset(f,1);             { However, share access is FILE_SHARE_READ }
  301.     try
  302.       Result := FileSize(f)
  303.     finally
  304.       CloseFile(f)
  305.     end
  306.   {$else}
  307.     FSize:=0;
  308.     Assign(f, s);
  309.     OldFMode := FileMode;
  310.     FileMode:= (fmOpenRead or fmShareDenyWrite);
  311.     Reset(f, 1);
  312.     FileMode := OldFMode;
  313.     if IOResult=0 then begin
  314.         FSize:=FileSize(f);
  315.         Close(f);  { Reset() successful and ReadOnly - Close() cannot fail }
  316.     end
  317.   {$endif}
  318. end;
  319.  
  320. {/////////////////////////////////////////////////////////}
  321. Function sFTime(Const s: string): LongInt;
  322. {get the date/time stamp of a file}
  323. var
  324. {$ifdef Delphi}
  325. Handle  : LongInt;
  326. {$else}
  327. f       : file;
  328. OldFMode: byte;
  329. Result  : LongInt;
  330. {$endif}
  331.  
  332. begin
  333.    sFtime := 0;
  334.    {$ifdef Delphi}
  335.     Handle := FileOpen(s, fmOpenRead or fmShareDenyNone);
  336.     If Handle <> -1 then begin
  337.      sFTime := FileGetDate(Handle);
  338.      FileClose(Handle);
  339.     end;
  340.    {$else}
  341.    OldFMode := FileMode;
  342.    FileMode:= (fmOpenRead or fmShareDenyNone);
  343.    Assign(f, s);
  344.    Reset(f, 1);
  345.    FileMode := OldFMode;
  346.    if IOResult=0 then begin
  347.       GetFTime(f, Result);
  348.       sfTime:=Result;
  349.       Close(f)
  350.    end;
  351.    {$endif}
  352. end;
  353.  
  354. {/////////////////////////////////////////////////////////}
  355. Function lFTime(var f:file) : LongInt;
  356. {get the date/time stamp of a file}
  357. {$ifndef Delphi}
  358. var
  359. Result:LongInt;
  360. {$endif}
  361. begin
  362. {$ifdef Delphi}
  363.   Result := FileGetDate(TFileRec(f).Handle);
  364. {$else}
  365.   GetFTime(f, Result);
  366.   lfTime:=Result;
  367. {$endif}
  368. end;
  369.  
  370. {/////////////////////////////////////////////////////////}
  371. Function DirectoryExists(Const s: String): Boolean;
  372. {does a directory exist?}
  373. var
  374. {$ifdef Win32}
  375. Attr: DWORD;
  376. {$else Win32}
  377. {$ifdef Delphi}
  378. Attr: Integer;
  379. {$else Delphi}
  380. f   : file;
  381. Attr: word;
  382. {$endif Delphi}
  383. {$endif Win32}
  384. Begin
  385. {$ifdef Win32}
  386.   Attr := Windows.GetFileAttributes(PChar(s));
  387.   Result := (Attr <> $FFFFFFFF) and                  // Success ...
  388.             (Attr and FILE_ATTRIBUTE_DIRECTORY <> 0) // Directory...
  389. {$else Win32}
  390.  {$ifdef Delphi}
  391.    Attr := FileGetAttr(s);
  392.    Result := (Attr>=0) and (Attr and faDirectory<>0)
  393.  {$else Delphi}
  394.   Assign(f,s);
  395.   GetFAttr(f,Attr);
  396.   DirectoryExists := (DosError = 0) and (Attr and faDirectory <> 0)
  397.  {$endif Delphi}
  398. {$endif Win32}
  399. End;
  400.  
  401. function FirstDirectoryBetween(const s1, s2: string): string;
  402. var
  403.   i: Integer;
  404. begin
  405. {$IFDEF Debug}
  406.   if Pos(s1,s2) = 0 then
  407.   {$ifdef Win32}
  408.     raise EChiefLZDebug.Create('FirstDirectoryBetween: ' + s1 +
  409.                                ' not a substring of ' + s2)
  410.       at AddrOfCaller
  411.   {$else Win32}
  412.     RunErrorMessageAt('FirstDirectoryBetween: ' + s1 +
  413.                        ' not a substring of ' + s2,
  414.                        AddrOfCaller)
  415.   {$endif Win32};
  416. {$ENDIF}
  417.   i := Length(s1);
  418.   repeat
  419.     inc(i)
  420.   until (i > Length(s2)) or (s2[i] = '\');
  421.   FirstDirectoryBetween := Copy(s2,1,i)
  422. end;
  423.  
  424. {$ifdef Win32}
  425.  
  426. procedure RaiseError(const EClass: ExceptClass; const Res: Integer);
  427. begin
  428.   raise EClass.CreateRes(Res)
  429. end;
  430.  
  431. procedure RaiseErrorStr(const EClass: ExceptClass;
  432.                         const Res:    Integer;
  433.                         const Mes:    string);
  434. begin
  435.   raise EClass.CreateResFmt(Res,[Mes])
  436. end;
  437.  
  438. {
  439.   These functions enable IO-errors to be raised artificially ...
  440. }
  441. function CreateIOError(const EMess, ECode: Integer): EInOutError;
  442. begin
  443.   Result := EInOutError.CreateRes(EMess);
  444.   Result.ErrorCode := ECode
  445. end;
  446.  
  447. procedure RaiseIOError(const EMess, ECode: Integer);
  448. begin
  449.   raise CreateIOError(EMess,ECode)
  450. end;
  451.  
  452. function Min(const I1, I2: LongInt): LongInt;
  453. begin
  454.   if I2 < I1 then
  455.     Result := I2
  456.   else
  457.     Result := I1
  458. end;
  459.  
  460. {$else Win32}
  461.  
  462. {
  463.   These functions provide tools not required in Delphi 2 ...
  464. }
  465. type
  466.   LongRec = record
  467.               Lo, Hi: Word
  468.             end;
  469.  
  470. function Min(const I1, I2: LongInt): LongInt; assembler;
  471. asm
  472. {$ifdef Delphi}
  473.   DB $66; MOV AX, [BP+OFFSET I1]  (* mov eax, I1       *)
  474.   DB $66; MOV DX, [BP+OFFSET I2]  (* mov edx, I2       *)
  475.   DB $66; CMP AX, DX              (* cmp eax, edx      *)
  476.   JLE @Exit
  477.   DB $66; MOV AX, DX              (* mov eax, edx      *)
  478. @Exit:
  479.   DB $66, $0F, $A4, 11000010b, 16 (* shld edx, eax, 16 *)
  480. {$else}
  481.   MOV AX, LongRec[BP+OFFSET I1].Lo
  482.   MOV DX, LongRec[BP+OFFSET I1].Hi
  483.   MOV CX, LongRec[BP+OFFSET I2].Lo
  484.   MOV BX, LongRec[BP+OFFSET I2].Hi
  485.   CMP DX, BX
  486.   JL @Exit
  487.   JG @Swap
  488.   CMP AX, CX
  489.   JBE @Exit
  490. @Swap:
  491.   MOV AX, CX
  492.   MOV DX, BX
  493. @Exit:
  494. {$endif}
  495. end;
  496.  
  497. {/////////////////////////////////////////////////}
  498. function Str2PChar(Var s: String): PChar;
  499. {convert string to pChar type}
  500. var
  501.   i: integer;
  502. Begin
  503. {$ifdef Win32}
  504. { Str2PChar UNNECESSARY under Win32 }
  505.   raise EChiefLZDebug.Create('Called Str2PChar in Win32 code')
  506.     at AddrOfCaller;
  507. {$endif Win32}
  508.   i := Length(s);
  509.   if i=0 then
  510.     Str2PChar := @s
  511.   else
  512.     begin
  513.       if s[i]<>#0 then
  514.         s[i+1] := #0;  { Heap-strings have an extra byte allocated for #0 }
  515.       Str2PChar := @s[1]
  516.     end
  517. End;
  518.  
  519. function NewString(const s: string): PString;
  520. {$ifndef Delphi}
  521. var
  522.   Result: PString;
  523. {$endif}
  524. begin
  525. {
  526.  If Windows code, we must allow for the possibility that someone might
  527.  try and place a #0 on the end of the string ... allocate an extra byte...
  528. }
  529.   GetMem(Result, 2*SizeOf(Char)+Length(s));
  530.   if Result <> nil then
  531.     Result^ := s;
  532. {$ifndef Delphi}
  533.   NewString := Result
  534. {$endif}
  535. end;
  536.  
  537. procedure DisposeString(var P: PString);
  538. begin
  539.   if P <> nil then
  540.     begin
  541. {
  542.   We allocated an extra byte in case someone called Str2PChar()
  543.   using this string ... This byte must be deallocated ...
  544. }
  545.       FreeMem(P, 2*SizeOf(Char)+Length(P^));
  546.       P := nil
  547.     end
  548. end;
  549.  
  550. {/////////////////////////////////////////////////////////}
  551. Function GetCurrentDir: String;
  552. {return the current directory}
  553. {$ifndef Delphi}
  554. var
  555.   Result: string;
  556. {$endif Delphi}
  557. begin
  558.   GetDir(0,Result);
  559. {$ifndef Delphi}
  560.   GetCurrentDir := Result
  561. {$endif Delphi}
  562. end;
  563. {$endif Win32}
  564.  
  565. {$ifndef Delphi}
  566. {/////////////////////////////////////////////////}
  567. {
  568.   These functions provide string and file-handling services that
  569.   Delphi offers in SysUtils ...
  570. }
  571. {/////////////////////////////////////////////////}
  572. Function Uppercase(s: String): String;
  573. {return uppercase of string}
  574. var
  575. i:Integer;
  576. Begin
  577.    for i:= 1 to Length(s) do s[i] := UpCase(s[i]);
  578.    Uppercase := s;
  579. end;
  580.  
  581. {/////////////////////////////////////////////////////////}
  582. Function ChangeFileExt(const aName, aExt: String): String;
  583. Var
  584. i, j:Integer;
  585. Begin
  586.   i := Length(aName);
  587.   j := i;
  588.   while (i > 0) and (aName[i]<>'\') and (aName[i]<>':') do
  589.     begin
  590.       if aName[i] = '.' then
  591.         begin
  592.           j := i-1;
  593.           break
  594.         end;
  595.       dec(i)
  596.     end;
  597.   ChangeFileExt := Copy(aName,1,j) + aExt
  598. End;
  599.  
  600. {/////////////////////////////////////////////////////////}
  601. function IsUNC(Const s:string):boolean;
  602. {// look for UNC name in one string (at beginning only) //}
  603. begin
  604.   IsUNC := (Length(s) > 3) and (s[1]='\') and (s[2]='\');
  605. end;
  606.  
  607. {/////////////////////////////////////////////////////////}
  608. (*
  609. Function ExtractFilePath(aName:String):String;
  610. {return the path only - strip filename out}
  611. {$ifdef TPW}
  612. var
  613.   P: array[0..79] of Char;
  614. {$endif TPW}
  615. Var
  616. i:Integer;
  617. begin
  618. {$ifdef Delphi}
  619.   aName := ExpandFileName(aName);
  620. {$else Delphi}
  621.   {$ifdef Windows}
  622.   FileExpand(P, Str2PChar(aName));
  623.   aName := StrPas(p);
  624.   {$else Windows}
  625.   aName := FExpand(aName);
  626.   {$endif Windows}
  627. {$endif Delphi}
  628.  
  629.   i := Length(aName);
  630.   while aName[i] <> '\' do   { Expanded filenames must have '\' }
  631.     dec(i);
  632.   ExtractFilePath := Copy(aName,1,i)
  633. end;
  634. *)
  635.  
  636. Function ExtractFilePath(const aName: String): String;
  637. {return the pathname only - strip filename out}
  638. Var
  639. i: Word;
  640. Begin
  641.   i := Length(aName);
  642.   While not (aName[i] in ['\', ':']) and (i <> 0) do
  643.      Dec(i);
  644.   If i = 0 then
  645.     ExtractFilePath := ''
  646.   else if i = 1 then
  647.     ExtractFilePath := aName[1]
  648.   else
  649.     ExtractFilePath := AddBackSlash(Copy(aName, 1, i))
  650. End;
  651.  
  652. {////////////////////////////////////////}
  653. Function ExtractFileExt(const aName: String): String;
  654. {return the fileextension}
  655. Var
  656.   i: Word;
  657. Begin
  658.    i := Length(aName);
  659.    while (i > 0) and (aName[i]<>'\') and (aName[i]<>':') do
  660.      begin
  661.        if aName[i] = '.' then
  662.          begin
  663.            ExtractFileExt := Copy(aName,i,Length(aName));
  664.            Exit
  665.          end;
  666.        Dec(i)
  667.      end;
  668.    ExtractFileExt := ''
  669. End;
  670. {/////////////////////////////////////////////////////////}
  671.  
  672. Function ExtractFileName(const s: String): String;
  673. {return the filename only - strip path out}
  674. Var
  675. i : Word;
  676. begin
  677.    for i:=Length(s) downto 1 do
  678.      if s[i] in [':','\'] then
  679.      begin
  680.        ExtractFileName := Copy(s,i+1,Length(s));
  681.        Exit
  682.      end; {s[i] in [':','\']}
  683.    ExtractFileName := s
  684. end;
  685. {/////////////////////////////////////////////////////////}
  686.  
  687. Function FileExists(Const S: String): Boolean;
  688. {does filename "S" exist?}
  689. var
  690.   f:    file;
  691.   Attr: word;
  692. begin
  693.   Assign(f, s);
  694.   GetFAttr(f,Attr);
  695.   FileExists := (DosError = 0)
  696. end;
  697. {$endif Delphi}
  698.  
  699. {$ifDef Windows}
  700. {////////////////////////////////////////////////////////}
  701. {$ifdef Win32}
  702. function FileVersionInfo(const fName, StringToGet: string): string;
  703. {get the version information from inside a Win32 binary}
  704. var
  705.   VSize           : LongInt;
  706.   VHandle         : THandle;
  707.   Buffer          : Pointer;
  708.   TranslationInfo : Pointer;
  709.   LangCharSetID   : LongRec;
  710.   Length          : DWORD;
  711.   StringFileInfo  : string;
  712.   aResult         : PChar;
  713. const
  714.   DefaultLangInfo : LongRec = (Lo: $0409;  
  715.                                Hi: $04E4); 
  716. begin
  717.   FileVersionInfo := '';
  718.   { Get size of version info }
  719.   VSize := GetFileVersionInfoSize(PChar(fName), VHandle);
  720.   if VSize > 0 then
  721.     begin
  722.     {$IFDEF Debug}
  723.       if VHandle <> 0 then
  724.         raise EChiefLZDebug.Create('FileVersionInfo() has failed!');
  725.     {$ENDIF}
  726.   { Allocate version info buffer }
  727.       GetMem(Buffer, VSize);
  728.       try { finally }
  729.   { Get version info }
  730.         if GetFileVersionInfo(PChar(fName), VHandle, VSize, Buffer) then
  731.           try { except }
  732.   { Get translation info for Language / CharSet IDs }
  733.             if not VerQueryValue(Buffer,
  734.                                 '\VarFileInfo\Translation',
  735.                                  TranslationInfo,
  736.                                  Length) then
  737.               LangCharSetID := DefaultLangInfo {no translation info - use defaults}
  738.             else
  739.               LangCharSetID := LongRec(TranslationInfo^);
  740. {
  741.   N.B. If cannot get Translation info, (because there ISN'T any ...???)
  742.        will the default values mean anything anyway ...?
  743. }
  744.             with LangCharSetID do
  745.               StringFileInfo :=
  746.                     Format( '\StringFileInfo\%4.4x%4.4x\'+StringToGet,
  747.                             [ Lo, Hi ] );
  748.             if VerQueryValue(Buffer, PChar(StringFileInfo),
  749.                              Pointer(aResult), Length) then
  750.               SetString(Result, aResult, Length)
  751.           except
  752. {
  753.   WinNT does not support the version-information functions for 16 bit
  754.   executable files (although Win95 seems to). Therefore we `handle'
  755.   any EAccessViolation exceptions that VerQueryValue() might raise,
  756.   ensuring that FileVersionInfo() returns an empty string-value ...
  757. }
  758.             on EAccessViolation do;
  759.           end
  760.       finally
  761.         FreeMem(Buffer, VSize)
  762.       end
  763.     end
  764. end;
  765. {$else Win32}
  766. {$ifndef DPMI}
  767. Function FileVersionInfo(const Fname, StringToGet:PChar): String;
  768. {get the version information from inside a Windows binary}
  769. type
  770.   TLangArray = array[1..2] of Word;
  771. var
  772.   VSize, VHandle: LongInt;
  773.   Buffer: PChar;
  774.   Length: Word;
  775.   TranslationInfo, aResult: Pointer;
  776.   StringFileInfo: array[0..255] of Char;
  777.   LangCharSetIDArray: TLangArray;
  778. const
  779.   DefaultLangInfo: TLangArray = ($0409,$04E4);
  780.  
  781. begin
  782.   FileVersionInfo:= '';
  783.   StrCopy(StringFileInfo, '\StringFileInfo\%04x%04x\');
  784.   { Get size of version info }
  785.   VSize := GetFileVersionInfoSize(fName, VHandle);
  786.   { Allocate version info buffer }
  787.   GetMem(Buffer, VSize + 1);
  788.   { Get version info }
  789.   if Buffer <> nil then
  790.   begin
  791.     if GetFileVersionInfo(fName, VHandle, VSize, Buffer) then
  792.     begin
  793.       { Get translation info for Language / CharSet IDs }
  794.       if not VerQueryValue(Buffer, '\VarFileInfo\Translation',
  795.                                           TranslationInfo, Length) then
  796.         LangCharSetIDArray := DefaultLangInfo {no translation info - use defaults}
  797.       else
  798.         begin
  799.           LangCharSetIDArray[1] := LoWord(Longint(TranslationInfo^));
  800.           LangCharSetIDArray[2] := HiWord(Longint(TranslationInfo^))
  801.         end;
  802.  
  803.       wvsPrintf(StringFileInfo, StrCat(StringFileInfo,StringToGet),
  804.                                                     LangCharSetIDArray);
  805.       if VerQueryValue(Buffer, StringFileInfo, aResult, Length) then
  806.         FileVersionInfo := StrPas(PChar(aResult))
  807.     end;
  808.     FreeMem(Buffer, VSize + 1)
  809.   end
  810. end;
  811. {$endif DPMI}
  812. {$endif Win32}
  813. {///////////////////////////////////////////////}
  814. {$endif Windows}
  815.  
  816. end.
  817.