如何获取正在运行的应用程序的版本?
我一直在使用GetFileVersionInfo(ParamStr(0), ...)
:
filename := PChar(ExtractShortPathName(ParamStr(0)));
//Get the number of bytes he have to allocate for the file information structure
dwInfoLength := GetFileVersionInfoSize(lptstrFilename, {var}dwHandle);
//Get version info
GetMem(pInfoData, dwInfoLength);
GetFileVersionInfo(lptstrFilename, dwHandle, dwInfoLength, pInfoData);
//Set what information we want to extract from pInfoData
lpSubBlock := PChar(Chr(92)+Chr(0));
//Extract the desired data from pInfoData into the FileInformation structure
VerQueryValue(pInfoData, lpSubBlock, PFileInformation, LengthOfReturned);
此技术的问题在于它需要Windows加载程序load the image before the resources can be read。我使用IMAGE_FILE_NET_RUN_FROM_SWAP
图像标记(in order to avoid in-page exceptions on a fiddly network)构建我的应用程序。
这会导致Windows加载程序再次通过网络加载整个图像,而不仅仅是查看 “我” 。由于我在启动时检查并保存了我自己的版本,因此6秒的应用程序启动会变成10秒的应用程序启动。
我如何阅读 me 的版本,我的正在运行的应用程序?
我认为Windows没有API来读取正在运行的进程的版本,只有我加载的文件(如果文件不再存在,那么它就无法读取任何版本信息)。
但我也假设有可能从我的进程自己的内存中读取版本资源(当然不是Administrators或Debuggers组的成员)。
我可以阅读我的流程的版本吗?
相关红利问题:如何从 me 而不是通过网络加载PE Image
资源?
答案 0 :(得分:8)
您可能需要尝试FindResource
/ LockResource
API才能访问正在运行的模块的VERSIONINFO
资源。 MSDN文章链接了示例部分中的示例,并且还有一个C++ sample code的社区评论,它正是这样做的。这从已经加载的模块开始,而不是从文件名开始(假设它单独加载了标志,表示“仅加载资源”,因此可能忽略了图像已经映射到进程中的事实)。
请注意 - 根据提供的代码段 - 您可以找到模块的资源,然后重用标准VerQueryValue
API以继续从那里进行资源解析。
答案 1 :(得分:8)
在Stackoverflow上找到它:
How to determine Delphi Application Version
我已经知道如何确定应用程序版本,但@StijnSanders提出了“更好”的方式,正是因为我正在打击的原因:
当您想知道当前正在运行的可执行文件的版本时,我强烈建议您不要使用GetFileVersion!我有两个很好的理由这样做:
- 可执行文件可能无法访问(已断开连接的驱动器/共享)或已更改(.exe已重命名为.bak并由新的.exe替换,而不会停止正在运行的进程)。
- 您尝试读取的版本数据实际上已经加载到内存中,并且通过加载此资源可以使用,这总是比执行额外(相对较慢)的磁盘操作更好。
我改编成:
function GetModuleVersion(Instance: THandle; out iMajor, iMinor, iRelease, iBuild: Integer): Boolean;
var
fileInformation: PVSFIXEDFILEINFO;
verlen: Cardinal;
rs: TResourceStream;
m: TMemoryStream;
resource: HRSRC;
begin
//You said zero, but you mean "us"
if Instance = 0 then
Instance := HInstance;
//UPDATE: Workaround bug in Delphi if resource doesn't exist
resource := FindResource(Instance, 1, RT_VERSION);
if (resource = 0)
begin
iMajor := 0;
iMinor := 0;
iRelease := 0;
iBuild := 0;
Result := False;
Exit;
end;
m := TMemoryStream.Create;
try
rs := TResourceStream.CreateFromID(Instance, 1, RT_VERSION);
try
m.CopyFrom(rs, rs.Size);
finally
rs.Free;
end;
m.Position:=0;
if not VerQueryValue(m.Memory, '\', /*var*/Pointer(fileInformation), /*var*/verlen) then
begin
iMajor := 0;
iMinor := 0;
iRelease := 0;
iBuild := 0;
Exit;
end;
iMajor := fileInformation.dwFileVersionMS shr 16;
iMinor := fileInformation.dwFileVersionMS and $FFFF;
iRelease := fileInformation.dwFileVersionLS shr 16;
iBuild := fileInformation.dwFileVersionLS and $FFFF;
finally
m.Free;
end;
Result := True;
end;
警告:由于Delphi中的错误,上面的代码有时会崩溃:
rs := TResourceStream.CreateFromID(Instance, 1, RT_VERSION);
如果没有版本信息,Delphi会尝试引发异常:
procedure TResourceStream.Initialize(Instance: THandle; Name, ResType: PChar);
procedure Error;
begin
raise EResNotFound.CreateFmt(SResNotFound, [Name]);
end;
begin
HResInfo := FindResource(Instance, Name, ResType);
if HResInfo = 0 then Error;
...
end;
当然,错误是PChar
并不总是指向ansi char 的指针。对于非命名资源,它们是整数常量,强制转换为PChar
。在这种情况下:
Name: PChar = PChar(1);
当Delphi尝试构建异常字符串,并取消引用指针0x00000001
时,它会触发并访问违规。
修复方法是首先手动调用FindResource(Instance, 1, RT_VERSION)
:
var
...
resource: HRSRC;
begin
...
resource := FindResource(Instance, 1, RT_VERSION);
if (resource = 0)
begin
iMajor := 0;
iMinor := 0;
iRelease := 0;
iBuild := 0;
Result := False;
Exit;
end;
m := TMemoryStream.Create;
...
注意:任何代码都会发布到公共域中。无需归属。
答案 2 :(得分:2)
我建议你从JCL读取代码JclFileUtils.pas。 “TJclFileVersionInfo”类有一些重载的构造函数,允许来自文件和模块句柄的工作来获取版本信息。
您可以使用JCL类,或者至少阅读它以查看其工作原理。
答案 3 :(得分:0)
Delphi 10.3.3提供了一种从运行exe读取ProductVersion的合理方法。在项目exe的[选项] [产品版本]中设置此值。
将变量设置为:
var
major, minor, build: Cardinal;
Version: String;
将代码设置为:
// [GetProductVersion] uses 1st 3 values separated by decimal (.)
// as: major, minor, build. 4th optional parameter is ignored.
//
// Gui may display -- Major, Minor, Release, Build
// but reports as -- Major, Minor, Build
if GetProductVersion(Application.ExeName, major, minor, build) then
Version := 'v' + major.ToString() + '.' + minor.ToString()
+ ' Beta Build ' + build.ToString();