bradgrafelman;10905316 wrote:Here's a quick-and-dirty way to do it:
$data = file_get_contents($path_to_file, FILE_BINARY);
if(preg_match('/\0F\0i\0l\0e\0V\0e\0r\0s\0i\0o\0n\0([0-9\.\0]+)/', $data, $matches)) {
echo 'File Version: ' . str_replace("\0", "", $matches[1]);
}
Also note that "FileVersion" could be replaced with "ProductVersion".
EDIT: This approach obviously assumes that the version will consist only of numbers and periods.
Unfortunately, that won't work for what I'm trying to do. (As .NET doesn't require you to input a product version, or file version for that matter.) Developers would have a choice as to what version they choose to use as their 'check for updates' version. So I really needed to pull all 3 if I could. (Plus actual company names in case there were duplicate files. Though, typically, company names aren't for 'real companies', just individual developers.)
I was forced to read the PE header, and the StringFileInfo entries manually in order to get the info I needed. (I only grabbed a few things from the PE header, one being the optional CLR header [which is required by .NET] so I'm not hosting any native code.)
Just dumping all the fields from the StringFileInfo entries gives me much more (useful?) information.
E.g, a typical request for a file will output the following text for my updater implementation (this is in the actual product that I ship out):
FileType=32-bit UNICODE
FileVersion=0.1.2.2
ProductVersion=0.2.0.0
OriginalFilename=MyDLL.dll
InternalName=MyDLL.dll
FileDescription=MyDLL
LegalCopyright=Copyright © MyDLL.NET 2009
CompanyName=MyDLL.NET
Assembly Version=0.1.3.2
ProductName=MyDLL
Then I just parse this info back in the application, and do any necessary things there.
Sadly, since I couldn't figure out how to get PHP to read only certain offsets of the file, and do conversions from<->to unicode/ASCII, I had to default back to Perl. (Which I really don't like using.)
I attempted to read the PE header in full (as then I could just keep around a general Win32PE class in PHP) but quickly found out, it was not something that could easily be done in PHP. (Too much pointer arithmetic I guess.)
Thanks for the help though, if you can come up with an easier method than something similar to
if (!file_exists($path)) die("File does not exist");
$out = shell_exec("perl fileinfo.pl ".$path);
echo nl2br($out);
Please do let me know.
P.S. Below is the Perl code I'm using. (Credits in the header)
#!perl -w
# A modified version of Max Maischein's (aka Corion) script found at
# http://www.perlmonks.org/index.pl?node_id=15871 on Perl Monks.
die "Specify a .exe/.dll-file to get information on.\n" unless @ARGV == 1;
my $filename = shift;
get_version($filename);
sub MakeUnicode
{
my ($S) = @_;
$S =~ s/(.)/$1\x00/g;
return $S;
};
sub MakeASCII
{
my ($S) = @_;
$S =~ s/(.)\x00/$1/g;
return $S;
};
sub dumpfile
{
my $filename = shift;
my $exedata;
my %Result;
# Regex definitions
# For files that are 32-bit
my $StringFileInfoU = "..\x00\x00[\x00\x01]\x00" . MakeUnicode("StringFileInfo") . "(?:\x00\x00)+" . # StringFileInfo header
"((..)\x00\x00[\x00\x01]\x00(?:[0-9A-Fa-f]\x00){8}(?:\x00\x00)+)" . # StringTable header
"(.*)"; # Strings
open EXE, "<$filename" or die "Cannot open $filename\n";
binmode EXE;
local $/;
undef $/;
$exedata = <EXE>; # sluuuuurp
close EXE;
# 32bit Windows Unicode format
if ($exedata =~ /$StringFileInfoU/gms)
{
my ($STHeader, $Len, $Info) = ($1,$2,$3);
undef $exedata;
$Result{"FileType"} = "32-bit UNICODE";
$Len = unpack( "v", $Len );
$Len -= length( $STHeader );
$Info = substr( $Info, 0, $Len );
while ($Info)
{
my $Sublen;
my ($Next, $Value, $Type) = unpack("vvv", substr( $Info, 0, 6 ));
$Sublen = $Next - 6;
while ($Next % 4)
{
$Next++
};
last unless $Next;
my $Item = substr($Info, 6, $Sublen);
my (@Info) = ();
# Extract the key :
$Item =~ s/^((?:..)+?)(\x00\x00)+//sm;
my ($Key) = MakeASCII( $1 );
while ($Item =~ s/^((?:..)+?)(\x00\x00)+//sm)
{
push @Info, MakeASCII( $1 );
}
$Result{$Key} = $Info[0];
shift @Info;
if ($Next < length( $Info ))
{ # != ?
$Info = substr( $Info, $Next );
}
else
{
$Info = "";
}
}
}
else
{
print "[StringFileInfo not found in $filename]\n";
}
return \%Result;
}
sub get_version
{
my $data = dumpfile($_[0]);
while(my($key,$value) = each(%{$data}))
{
print "$key=$value\n";
}
}