|
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.
|
|
|
|
|
Test app only displays information from string tables, but you can get
access to FixedFile Info. As you know version number is stored in two
places (VS_FIXEDFILEINFO, and string tables).
As the example on the top of the article shows:
vi.SetFileVersion(4, 5, 0, 3, <br />
TRUE );
Use SetFileVersion function to update VS_FIXEDFILEINFO, if you specify
TRUE for the last argument, it will also update all string tables.
You can get access to other VS_FIXEDFILEINFO structure elements by calling
VS_FIXEDFILEINFO& GetFixedFileInfo();
Just assign values like this if you need to modify values other than product version or file version:
vi.GetFixedFileInfo().dwFileDateMS = dwMyFileDataMS;
Don't forget to call Save()
Happy coding!
-- No project was ever completed on time and within budget. Cheops Law
|
|
|
|
|
Thanks Denis! I'll have a look.
|
|
|
|
|
Hi Denis,
This is great and exactly what we were looking for. We needed something to fix up the FileVersion and ProductVersion (strings and words) after VB6 compiles a project, because VB6 only provides 3 of the 4 version words - e.g. 1.2.0.4 - 3rd word (Build number) is always zero.
Unfortunately your version library has a problem with VB6 compiled resources. It generates the following error:
---------------------------
Microsoft Visual C++ Debug Library
---------------------------
Debug Assertion Failed!
Program: H:\Misc\VerInfoLib\Output\debug\VerInfoLibTest.exe
File: h:\misc\verinfolib\tools\mainline\verinfolib\versioninfo.cpp
Line: 153
For information on how your program can cause an assertion
failure, see the Visual C++ documentation on asserts.
(Press Retry to debug the application)
---------------------------
Abort Retry Ignore
---------------------------
The assertion line is:
ASSERT(1 == pVarInfo->wType);
According to the VarInfoFile help, wType can be 1 or 0, which is what VB6 uses (zero). I tried changing the code to allow zero:
ASSERT(0 == pVarInfo->wType || 1 == pVarInfo->wType);
However it then failed a little furthter on, perhaps because CVersionInfo::WriteVarInfo always writes Type=1?
Anyway, I'm not real familiar with these API's, so I was wondering if you knew how to get this working for a file generated by VB6?
Thanks, Dean.
|
|
|
|
|
Hi,
Let me compile something in VB6 and try it out.
If you tried running test application compiled in Debug mode, with "-system" command line param, you will see A LOT of ASSERTs. Because some of the DLLs have extra VarFileInfo and things like that.
I'll get back with more information soon.
Thanks for your comment.
Denis
-- No project was ever completed on time and within budget. Cheops Law
|
|
|
|
|
Well, the whole part about reading VarFileInfo CVersionInfo::FromFile() is just a sanity check. As well as evetything inside
#ifdef _DEBUG <br />
...<br />
#endif
(was added to verify that code is capable of writing exactly same data as it just read).
VarFileInfo structure that is being loaded in CVersionInfo::FromFile() is discarded. It is being recreated from StringFileInfo tables (translations) in WriteVarInfo().
Please make a Release build of the library, or strip the ASSERTs that you don't need.
I have no idea what that wType for VarFileInfo and Var structures is used for. Regardless of what you specify in wType the translation table must still be binary (array of DWORDs). So I guess I can strip those asserts out of the code. You should definitely use Release build.
Excerpt from MSDN about Var structure:
Remarks
If you use the Var structure to list the languages your application or DLL supports instead of using multiple version resources, use the Value member to contain an array of DWORD values indicating the language and code page combinations supported by this file. The low-order word of each DWORD must contain a Microsoft language identifier, and the high-order word must contain the IBM® code page number. Either high-order or low-order word can be zero, indicating that the file is language or code page independent. If the Var structure is omitted, the file will be interpreted as both language and code page independent.
-- No project was ever completed on time and within budget. Cheops Law
|
|
|
|
|