|
Hi,
There are two places for both product version and file version:
1st is in the fixed (basic) version information VS_FIXEDFILEINFO which is all binary (file version and product version are made of two 32 bit values (64 bits for four parts of the version number, thus allowing 16 bits per part).
2nd is in StringFileInfo - that is a collection of name-value pairs, where values are unicode strings.
Notice the parameter TRUE /*Update StringTables*/ in your call to SetFileVersion. It was implemented for convenience, because most of the time
you want to update version number in both places, and do it as MS Visual Studio does - no leading zeroes.
Windows explorer is using information from String Tables when you are looking at files' properties, so if you want it to display 1.02 you need to update the file version in string table.
See the sample at the top of the article:
vi.SetFileVersion(4, 5, 0, 3, <br />
TRUE );<br />
<br />
You need to modify your code - set FileVersion separately. For that replace one line with these two:
vi.SetFileVersion(1, 2, 0, 0, FALSE );<br />
vi["FileVersion"] = "1.02"
Don't forget to save update version info.
Regards,
Denis
P.S. This does not guarantee that some applications will not display version as 1.2.0.0 for your module. Such applications may disregard what is in string tables and obtain the version number(s) from VS_FIXEDFILEINFO and format it as it needs, which will in most cases not have trailing zeros
-- No project was ever completed on time and within budget. Cheops Law
|
|
|
|
|
Thanks so much Denis for your reply to this!
So what I am hearing you saying is that a fileversion as found in VS_FIXEDFILEINFO can not appear as 1.02.0.0 because of the way it is constructed, however the string fileversion can do this (because it can show whatever you want).
I just always thought that there was a way for VS_FIXEDFILEINFO to do this and that other programs did it, but I guess they don't. I certainly couldn't make it do it.
Anyway, thanks.
Chris
|
|
|
|
|
chrislong2 wrote: So what I am hearing you saying is that a fileversion as found in VS_FIXEDFILEINFO can not appear as 1.02.0.0 because of the way it is constructed
That's an incorrect question
Value vs. Representation!
Your version number will be stored in VS_FIXEDFILEINFO's member variables
DWORD dwFileVersionMS;
DWORD dwFileVersionLS;
In hex it will look like this:
dwFileVersionMS = 0x00010002;
dwFileVersionLS = 0x00000000;
One may interprete/format the version number like this
strVersion.Format(_T("%d.%02d.%d.%d"), HIWORD(dwFileVersionMS), LOWORD(dwFileVersionMS), HIWORD(dwFileVersionLS), LOWORD(dwFileVersionLS));
strVersion for your number will look like 1.02.0.0
Somebody may use _T("%d.%02d.%04d.%04d") which will result in 1.02.0000.0000.
Microsoft chose %d.%d.%d.%d format when displaying "File version:" on the "Version" tab of file properties (first line). In the "Other version information" you see the list of items, one of them is File Version. The representation/format of a string is string itself (the only formatting is word wrapping).
So if you need to display 1.02.0.0 or 1.02 somewhere from your own code, all you need to do is format binary data from VS_FIXEDFILEINFO to your liking, or get File Version from string table. In later case you have a lot of flexibility - you can have any text in version number (i.e. File Version for XP SP2 package: 5.5.1005.0 (SRV03_QFE.031113-0918)
If you want version number for your DLL/EXE to be displayed as 1.02.0.0 in the first line on the Version tab (Right click/Properties/Version) - you can't do that.
-- No project was ever completed on time and within budget. Cheops Law
|
|
|
|
|
Hi Denis,
Is there a similar way to update resource strings in STRINGTABLE?
Is there an easy way to port these libraries for use in non-MFC apps?
Thanks!!
|
|
|
|
|
Hi,
Yes, there is a way to update string resources.
See this KB article http://support.microsoft.com/kb/q196774/.
As for MFC, you can convert it to non MFC project. Replace/remove CString, and container classes (maps, etc.) with similar from other libraries.
Regards,
Denis
-- No project was ever completed on time and within budget. Cheops Law
|
|
|
|
|
Hi Denis,
Not tested yet but I'm planning to use it under Windows98. Any info about it?
Also, what about limited user rights under Windows 2000/XP?
Thanks,
Pavel;)
|
|
|
|
|
Very good tool and I am going to use a modified version (one executable, no DLL, update the FileDescription if it has a particular string).
However, I can't find out if there is a restriction on the length of the FileDescription string. Do you know of one (and any other strings in the Version String Table)??
David
|
|
|
|
|
According to MSDN (String structure description):
This structure is not a true C-language structure because it contains variable-length members. This structure was created solely to depict the organization of data in a version resource and does not appear in any of the header files shipped with the Microsoft® Platform Software Development Kit (SDK).
struct String {
WORD wLength;
WORD wValueLength;
WORD wType;
WCHAR szKey[];
WORD Padding[];
WORD Value[];
};
Members
wLength - Specifies the length, in bytes, of this String structure.
wValueLength - Specifies the size, in words, of the Value member.
Since wLength is a WORD and wValueLength is a WORD maximum String structure size is 64K. I didn't try writing long strings into the version resources, but if I had to I would never have the Value longer than:
65535 - (sizeof(WORD)*3 /* size of fixed size members */ + (wcslen(szKey) + sizeof(WCHAR) /*zero term for szKey*/ + 3 /*max padding*/) & (~3 /*alignment) ) bytes
Which leaves you enough space for about 32740 (or so) unicode characters for the value of FileDescription.
(Keep in mind that as any other modification to executable it will invalidate any strong name digital signatures, if module has been signed)
Best regards,
Denis
-- No project was ever completed on time and within budget. Cheops Law
|
|
|
|
|
Thanks but I knew that.
However, if I change the "File Description" to a string longer than about 37-48 characters (depending on whether it is Windows XP/Windows 2000), it gets truncated in MS's version display (right click file in Windows Explorer, select Properties, select Version tab).
Note MS put "File Description" in the top part of the file's properties display with limited width along with the version and copyright information, rather than under the heading of "Other version information", which seems to allow the string size you mention.
Apart from using your program, I don't know how a "general user" would be able to view this "File Descritpion" except in this properties page and, if it is always truncated, then there is no point in putting anything there longer than can be displayed i.e. ~40 characters.
David
|
|
|
|
|
I would just put all extra information into Comment. Because of sorting it will show up on the top of the list, and it's value will be displayed in multi-line edit box.
Regards,
Denis
-- No project was ever completed on time and within budget. Cheops Law
|
|
|
|
|
OK thanks.
Great code. I have already made a single executable with it to do the modifications I need.
Any thoughts about an easy way to replace Accelerator Key combinations?
David
PS. Re: String replacements: have you see the "ResText" program used by TortoiseSVN (http://tortoisesvn.net/), a Subversion client(http://subversion.tigris.org/)? They use it to implement localization (i18n) by allowing easy update of strings in resource-only DLLs. Source is available.
|
|
|
|
|
Thanks.
Accelerators should be really easy, since it's a flat table of ACCELTABLEENTRY (see http://msdn2.microsoft.com/en-us/library/ms648010.aspx), which is pretty much ACCEL structure but different member names, sizes + padding).
You can probably reuse the padding from the VerInfoLib and streaming. Create accelerator table in memory, and then use BeginUpdateResource UpdateResource EndUpdateResource as illustrated in the library and described well in MSDN to update the resouce of a PE module.
Re: PS. Re: String replacements
You are talking about RT_STRING type resources. Yep. There is nothing much there, just a little bit awkward way of accessing strings by ID, and bucketing. There is source code and samples available on MSDN to update string tables.
Regards,
Denis.
PS. 0K. Never actually thought about looking at OK as 0K. Nice!
-- No project was ever completed on time and within budget. Cheops Law
|
|
|
|
|
Hi,
When using your tool with a compiled DLL (with the -u argument) that has no version table it crashes after a minute or so of hanging. Can you make it create a version table and inject the new value to it instead? If not programmatically, how can I make that manually (I don't have the source for the DLL, and I need it versioned)?
Thanks in advance,
Stilgar.
|
|
|
|
|
Hi,
The simpliest way is to load version resource from some other DLL, and save it to your dll with ToFile(). Or you can create version resource from scratch and then save it to that file.
Here is sample code (demonstraits copying version info from one DLL to another).
CVersionInfo vi;<br />
vi.FromFile("DllWithVersionInfo.dll");<br />
vi.ToFile("YourDllWithoutVersionInfo.dll");
Regards,
Denis
-- No project was ever completed on time and within budget. Cheops Law
|
|
|
|
|
Hi,
Thanks for your quick reply.
I changed the sample application you provided to look like this:
void TestVersionInfoLib(int argc, TCHAR* argv[], TCHAR* envp[])<br />
{<br />
CString strFilePath(argv[1]);<br />
CString strFilePath2(argv[2]);<br />
CVersionInfo vi;<br />
vi.FromFile(strFilePath);<br />
vi.SetFileVersion(7, 15, 5, 0, <br />
TRUE );<br />
vi.ToFile(strFilePath2);<br />
}
The file, however, still does not contain any version information, nor other fields which I was expecting the app to copy.
Let me say this again - the DLL probably has no resources with it. Maybe this is the cause. Any idea how to overcome this?
Stilgar.
|
|
|
|
|
Dear Denis,
It looks like there is a bug in the CVersionInfo::Write method.
This function contains following code:
if (m_bRegularInfoOrder)
{
//Write string file info, it will pad as needed
m_stringFileInfo.Write(viBuf);
WriteVarInfo(viBuf);
}
else
{
//Write string file info, it will pad as needed
m_stringFileInfo.Write(viBuf);
WriteVarInfo(viBuf);
}
It seems to me you meant following:
if (m_bRegularInfoOrder)
{
//Write string file info, it will pad as needed
m_stringFileInfo.Write(viBuf);
WriteVarInfo(viBuf);
}
else
{
WriteVarInfo(viBuf);
//Write string file info, it will pad as needed
m_stringFileInfo.Write(viBuf);
}
In addtion, from my point of view, it will be better to support 2 formats for string representation of file and product version ", " delimited and "." delimited.
WBR
Sergey
|
|
|
|
|
Hi Sergey,
Khan Solo wrote: It looks like there is a bug in the CVersionInfo::Write method.
You are right! Good catch!
Regular order is StringInfo first, then VarInfo, but it was kind of ignoring the flag and always writing in that order.
Khan Solo wrote: In addtion, from my point of view, it will be better to support 2 formats for string representation of file and product version ", " delimited and "." delimited.
There is a workaround. Actually these functions:
void SetFileVersion(WORD dwFileVersionMSHi, WORD dwFileVersionMSLo, WORD dwFileVersionLSHi, WORD dwFileVersionLSLo, BOOL bUpdateStringTables = TRUE);<br />
void SetFileVersion(DWORD dwFileVersionMS, DWORD dwFileVersionLS, BOOL bUpdateStringTables = TRUE);<br />
<br />
void SetProductVersion(WORD dwProductVersionMSHi, WORD dwProductVersionMSLo, WORD dwProductVersionLSHi, WORD dwProductVersionLSLo, BOOL bUpdateStringTables = TRUE);<br />
void SetProductVersion(DWORD dwProductVersionMS, DWORD dwProductVersionLS, BOOL bUpdateStringTables = TRUE);
Were only implemented for conveinience. You can always set product and file version in StringFileInfo to arbitrary string:
vi["FileVersion"] = "1.2.0.3456 RC2"
But I agree it would probably make sence to be able to specify delimiter:
void SetFileVersion(WORD dwFileVersionMSHi, WORD dwFileVersionMSLo, WORD dwFileVersionLSHi, WORD dwFileVersionLSLo, BOOL bUpdateStringTables = TRUE, TCHAR chDelim = _T(','));<br />
void SetFileVersion(DWORD dwFileVersionMS, DWORD dwFileVersionLS, BOOL bUpdateStringTables = TRUE, TCHAR chDelim = _T(','));
Actually, I just found two more bugs:
void CVersionInfo::SetFileVersion(WORD dwFileVersionMSHi, WORD dwFileVersionMSLo, WORD dwFileVersionLSHi, WORD dwFileVersionLSLo, BOOL bUpdateStringTables )<br />
{<br />
SetFileVersion((dwFileVersionMSHi << 16) | dwFileVersionMSLo, (dwFileVersionLSHi << 16) | dwFileVersionLSLo);<br />
}
bUpdateStringTables was not passed to the "worker" SetFileVersion. So it would always update String Tables!
(same for SetProductVersion pair).
Thanks. I will update the code and republish.
Regards,
Denis
-- No project was ever completed on time and within budget. Cheops Law
|
|
|
|
|
Article code for the article updated.
Thanks for enchancement suggestion. You can now specify the delimiter:
void SetFileVersion(WORD dwFileVersionMSHi, WORD dwFileVersionMSLo, WORD dwFileVersionLSHi, WORD dwFileVersionLSLo, BOOL bUpdateStringTables = TRUE, LPCTSTR lpszDelim = _T(", "));<br />
void SetProductVersion(WORD dwProductVersionMSHi, WORD dwProductVersionMSLo, WORD dwProductVersionLSHi, WORD dwProductVersionLSLo, BOOL bUpdateStringTables = TRUE, LPCTSTR lpszDelim = _T(", "));
Denis
-- No project was ever completed on time and within budget. Cheops Law
|
|
|
|
|
Dear Denis Zabavchik, When i use this libray to change the version info resource of some mixed mode(c++ and .net framework CLR) exes, I lost the strong name of those exe, so as a result i have to re-sign them again, any clue about that, why it is happening.
Warm Regards,
Mushq
-- modified at 1:35 Saturday 30th September, 2006
|
|
|
|
|
Hi,
Well, that's what digital signing is for. Once you signed the file, you can not change the contents of the file (even one bit anywhere in the file) because the signature will become invalid. Signature fails when verifying that file is authentic and has not changed since the moment of signing. (WinVerifyTrust as I remember, or something like that). Any version info modification must be done before signing (as well as other steps that may modify the contents of the file (i.e. packing)).
Regards,
Denis
-- No project was ever completed on time and within budget. Cheops Law
|
|
|
|
|
"There is another article that will be published soon that will allow to modify string tables (RT_STRING) and icons (RT_GROUP_ICON and RT_ICON). Vote for the this article to see the other one sooner"
I hope that article will be coming soon.
|
|
|
|
|
Well, there is this article about icons:Icons in Win32 by John Hornick (Microsoft Corporation)[^]
It doesn't update the resources in binaries though, so it's probably worth writing.
And this one about strings: Stablupd.exe Manipulate String Resources in an Executable[^]
Both samples are GUI apps, not really libraries that you can use to write your own apps. But if you're looking for something to start with, these two are very good.
Hopefully I'll have some time for pet projects in near future.
-- No project was ever completed on time and within budget. Cheops Law
|
|
|
|
|
Its a very good idea, and wonderful piece of work as well.
|
|
|
|
|
Thanks Denis, for a really clean and useful bit of code. I need to update string info without recompiling and this does the job perfectly. Brilliant stuff.
FayeWilliams.com
|
|
|
|
|
Hi there,
When I ran the test program it did not display the FILEVERSION (e.g. 2,0,0,5), even though the text above shows that it can do that.
If you run
VerInfoLibTest -u App.exe FileVersion "2.0.0.2"
It will only update the StringFileInfo section, but not the FILEVERSION, which leads to some confusion when the Properties->Version on an .EXE. You see the original FILEVERSION, and an updated FileVersion.
Is it possible to be able to edit FILEVERSION?
Thanks in Advance!
Corey.
|
|
|
|
|