I'll start this by stating that I'm by no means an expert with Delphi (using
v4.0 Pro SP1), but I discovered an issue. It's probably just poor code on
my part, but I need your help to continue to learn and try and resolve. It
seems that when I check for version information on certain .exe's I
consistently get the following error:
EAccessViolation with message 'Access violation at address 00403967. Read
of address 00740074
But yet on other .exe files I'm able to successfully retrieve the version
information. I've checked the files that failed on a couple of machines
(both were NT v4.0 SP5) and the problem is consistant. I've not had a
chance to check the problem on a 95 machine. I've included the code below
that's producing the problem and documented the names of the files that are
failing. Help from any one would be appreciated, and thanks to those in
this group that I was able to learn from to even begin to put (okay I copied
too) this snippet together.
Thanks...
Mark Bos
function FileVersion(const FileName: string): string;
var
Size, Size2: DWord;
Pt, Pt2: Pointer;
begin
Size := GetFileVersionInfoSize (PChar(FileName), Size2);
if Size > 0 then
begin
GetMem (Pt, Size);
GetFileVersionInfo (PChar(FileName), 0, Size, Pt);
VerQueryValue(Pt,'\StringFileInfo\040904E4\FileVersion', Pt2, Size2);
result := PChar(Pt2);
Freemem(Pt, Size);
end else
begin
result := 'Not Available';
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
FilePath: String;
strFVer: String;
begin
// FilePath := 'C:\Program Files\Microsoft Office\Office\msaccess.exe'; //
Failed
// FilePath := 'C:\Program Files\Microsoft Office\Office\binder.exe'; //
Passed
// FilePath := 'C:\Program Files\Microsoft Office\Office\excel.exe'; //
Passed
// FilePath := 'C:\Program Files\Microsoft Office\Office\powerpnt.exe'; //
Passed
// FilePath := 'C:\Program Files\Microsoft Office\Office\winword.exe'; //
Passed
// FilePath := 'C:\Program Files\Microsoft Office\Office\outlook.exe'; //
Failed
// FilePath := 'C:\Program Files\Microsoft Office\Office\finder.exe'; //
Failed
// FilePath := 'C:\Program Files\Microsoft Office\Office\findfast.exe'; //
Passed
// FilePath := 'C:\Program Files\Microsoft Office\Office\graflink.exe'; //
Passed
// FilePath := 'C:\Program Files\Microsoft Office\Office\graph8.exe'; //
Passed
// FilePath := 'C:\Program Files\Microsoft Office\Office\msoffice.exe'; //
Passed
// FilePath := 'C:\Program Files\Microsoft Office\Office\osa.exe'; //
Passed
begin
strFVer := FileVersion(FilePath);
ShowMessage(strFVer);
end;
end;
Problem also occurs under Delphi 3.
> seems that when I check for version information on certain .exe's I
> consistently get the following error:
>
> EAccessViolation with message 'Access violation at address 00403967.
Read
> of address 00740074
>
> But yet on other .exe files I'm able to successfully retrieve the
version
> information. I've checked the files that failed on a couple of
machines
> (both were NT v4.0 SP5) and the problem is consistant. I've not had a
> chance to check the problem on a 95 machine. I've included the code
below
Problem also occurs under Win95.
> that's producing the problem and documented the names of the files
that are
> failing. Help from any one would be appreciated, and thanks to those
in
> this group that I was able to learn from to even begin to put (okay I
copied
> too) this snippet together.
>
> Thanks...
>
> Mark Bos
>
> function FileVersion(const FileName: string): string;
> var
> Size, Size2: DWord;
> Pt, Pt2: Pointer;
> begin
> Size := GetFileVersionInfoSize (PChar(FileName), Size2);
> if Size > 0 then
> begin
> GetMem (Pt, Size);
> GetFileVersionInfo (PChar(FileName), 0, Size, Pt);
Replace:
> VerQueryValue(Pt,'\StringFileInfo\040904E4\FileVersion', Pt2,
Size2);
With:
if VerQueryValue(Pt,'\StringFileInfo\040904E4\FileVersion', Pt2,
Size2) then begin
result := PChar(Pt2);
end else begin
result := 'Not Available';
end;
> result := PChar(Pt2);
> Freemem(Pt, Size);
> end else
> begin
> result := 'Not Available';
> end;
> end;
Here's a replacement function that works with all Win32 files that do
have version info included:
function GetFileVersion(const FileName : String) : String;
{
Obtains the specified file's Version string.
Returns an empty string if the specified file
isn't a Win32 executable (or DLL, etc.) or if it doesn't
include a version string.
For an executable to obtain it's own version info,
it should pass in ParamStr(0).
}
var
VersionInfoSize,
VersionInfoValueSize,
Zero : DWord;
VersionInfo,
VersionInfoValue : Pointer;
begin
Result := '';
{ Obtain size of version info structure }
VersionInfoSize := GetFileVersionInfoSize(PChar(FileName), Zero);
if VersionInfoSize = 0 then exit;
{ Allocate memory for the version info structure }
{ This could raise an EOutOfMemory exception }
GetMem(VersionInfo, VersionInfoSize);
try
if GetFileVersionInfo(PChar(FileName), 0, VersionInfoSize,
VersionInfo) and
VerQueryValue(VersionInfo, '\' { root block },
VersionInfoValue, VersionInfoValueSize) and
(0 <> LongInt(VersionInfoValueSize)) then begin
with TVSFixedFileInfo(VersionInfoValue^) do begin
Result := IntToStr(HiWord(dwFileVersionMS));
Result := Result + '.' + IntToStr(LoWord(dwFileVersionMS));
Result := Result + '.' + IntToStr(HiWord(dwFileVersionLS));
Result := Result + '.' + IntToStr(LoWord(dwFileVersionLS));
end; { with }
end; { then }
finally
FreeMem(VersionInfo);
end; { try }
end; { GetFileVersion }
As you see, the key difference lies in fetching the version info from
the root, not from a sometimes-non-existent StringFileInfo
sub-node...MSAccess.exe's Language/Character Set code is 000004E4
instead of 040904E4, which is why your VerQueryValue fails.
Example of properly extracting a FileDescription string:
type
TLongInt = packed record
LoWord,
HiWord : WORD;
end; { TLongInt }
PTLongInt = ^TLongInt;
procedure TForm1.Button1Click(Sender: TObject);
var
LangCharSet,
Result : String;
VISize,
VIValueSize,
Zero : DWord;
VI,
VIValue : Pointer;
begin
strFVer := '';
//Obtain size of version info structure
VISize := GetFileVersionInfoSize(PChar(FilePath), Zero);
if VISize = 0 then exit;
//Allocate memory for the version info structure
//This could raise an EOutOfMemory exception
GetMem(VI, VISize);
try
//First, obtain the language and character set ID ('040904E4',
typically)
if GetFileVersionInfo(PChar(FilePath), 0, VISize, VI) and
VerQueryValue(VI, '\VarFileInfo\Translation', VIValue,
VIValueSize) and
(0 <> LongInt(VIValueSize))
then begin
//There may be several LangCharSet entries; here we just use
the first one
LangCharSet := Format('%.4x%.4x', [PTLongInt(VIValue)^.LoWord,
PTLongInt(VIValue)^.HiWord]);
//Must Copy the String, otherwise strFVer would point to
//an invalid block of memory once VI is FreeMem'ed
if VerQueryValue(VI, PChar('\StringFileInfo\' + LangCharSet +
'\FileDescription'), VIValue, VIValueSize) and
(0 <> LongInt(VIValueSize))
then begin
strFVer := Copy(String(VIValue), 1, VIValueSize);
end; { then }
end; { then }
finally
FreeMem(VI);
end; { try }
if strFVer <> '' then MessageDlg(strFVer, mtCustom, [mbOK], 0);
end;
Daniel U. Thibault
a.k.a. Urhixidur
a.k.a. Sire Bohémond de Nicée
Sent via Deja.com http://www.deja.com/
Share what you know. Learn what you don't.
Thank you, thank you... The snippet you posted works perfectly and give me
a much better understanding of what's happening, or should I say what wasn't
happening?! :-o
Thanks again...
Mark Bos
[snip]