|
Does anyone have a simple DLL version for this?
It would be nice to have a DLL that has one function,
named 'SetV' that accepts a file, field-name and value
as in the example executable.
I cannot get this to compile in code blocks (obviously)
So I'm kinda lost.
* Life would be so much easier if we just had the sourcecode *
|
|
|
|
|
Hi - still a great article.
I have a new problem. I already change the 'OriginalFilename' and 'ProductName' but now I want to change the Language ID in the existing 'VarFileInfo\Translation' section and the 'StringFileInfo' area of a resource-only DLL.
Basically, we have a resource-only DLL with a language ID = 0x0409 (English US), CodePage = 1200 (Unicode - 0x04b0). We use PO files and TortoiseSVN's ResText program to update it with a different language's strings. However, the 'StringFileInfo' and the 'VarFileInfo\Translation' still have '0x0409, 0x04b0'.
I then run a program using your code to set the 'OriginalFilename' based on the new language DLL and update the 'ProductName' to have the language in the text.
However, when looking at the properties of the DLL with Windows Explorer, it still says that the language is 'English (US)'. If I use a Hex Editor to change the values in the 'StringFileInfo' and the 'VarFileInfo\Translation' from 0x0409 to, say 0x0407, then it says the language is German.
It is this that I want to be able to do using your code.
Many thanks in advance.
David
|
|
|
|
|
I mananged to resolve this myself - as follows:
Firstly, I added a new function to VersionInfo.h to obtain the languageID after the information has been read from a file. I also added an extra parameter to the 'ToFile' routine to specify if we are replacing the version information. This parameter is passed on unchanged to the 'UpdateModuleResource' routine.
Changes to: VersionInfo.h
WORD GetLangID() const {return m_wLangId;}
BOOL ToFile(const CString& strModulePath = "", LPCTSTR lpszResourceId = NULL ,
WORD wLangId = 0xFFFF , const bool bReplace = false);
BOOL UpdateModuleResource(const CString &strFilePath, LPCTSTR lpszResourceId, WORD wLangId,
LPVOID lpData, DWORD dwDataLength, const bool bReplace = false);
The if the new bReplace parameter is true, then the UpdateModuleResource routine deletes the existing version information before adding the new one.
Changes to: VersionInfo.cpp
BOOL CVersionInfo::UpdateModuleResource(const CString &strFilePath, LPCTSTR lpszResourceId,
WORD wLangId, LPVOID lpData, DWORD dwDataLength,
const bool bReplace)
{
HANDLE hUpdate = ::BeginUpdateResource(strFilePath, FALSE);
if (hUpdate == NULL)
return FALSE;
BOOL bUpdateResult = TRUE;
if (bReplace)
bUpdateResult = UpdateResource(hUpdate, RT_VERSION, lpszResourceId, m_wLangId, NULL, 0);
if (bUpdateResult)
bUpdateResult = UpdateResource(hUpdate, RT_VERSION, lpszResourceId, wLangId, lpData, dwDataLength);
return EndUpdateResource(hUpdate, FALSE) && bUpdateResult;
}
My program now has:
....
CVersionInfo vi(filename);
.....
CStringFileInfo &stringFileInfo = vi.GetStringFileInfo();
CString strOldLangID, strNewLangID, strCodePage(_T("04b0"));
strNewLangID.Format(_T("%04x"), wNewLangID);
WORD wOldLangID = vi.GetLangID();
strOldLangID.Format(_T("%04x"), wOldLangID);
stringFileInfo.SetStringTableKey(strOldLangID + strCodePage, strNewLangID + strCodePage);
vi.ToFile(_T(""), NULL, wNewLangID, true);
I hope this is of use to others.
David
|
|
|
|
|
First of all I want to thank you for the source code provided. It is a great and useful article.
I have done small modifications:
- only one exe file SetVersion.exe (no dll)
- supporting of the new -v parameter -it sets fixed version numbers
- new -all parameter - it allows to set all basic parameters together
If someone of you wants to send "my" exe or source code, feel free. Email: michal (.) zeman (@) centrum.cz
Michal
|
|
|
|
|
I traced in Debug mode to see how this code works, and I got assert failure.
I compared the memory from the VC6 IDE and both memory were not the same!
Please check this possibility of bug.
I used "-u" option to modify the resource.
The call stack was:
CStringFileInfo::Write(CVersionInfoBuffer & {CVersionInfoBuffer}) line 82
CVersionInfo::Write(CVersionInfoBuffer & {CVersionInfoBuffer}) line 307
CVersionInfo::FromFile(const CString & {"C:\Sample.exe"}, const char * 0x00000000, unsigned short 65535) line 209
CVersionInfo::CVersionInfo(const CString & {"C:\Sample.exe"}, const char * 0x00000000, unsigned short 65535) line 39
TestVersionInfoLib(int 5, char * * 0x003830f0, char * * 0x003832a0) line 120 + 28 bytes
main(int 5, char * * 0x003830f0, char * * 0x003832a0) line 148 + 17 bytes
BOOL CVersionInfo::FromFile(const CString &strModulePath, LPCTSTR lpszResourceId, WORD wLangId)
{
...
//Verify Write
CVersionInfoBuffer viSaveBuf;
Write(viSaveBuf);
ASSERT(viSaveBuf.GetPosition() == viLoadBuf.GetPosition());
ASSERT(!memcmp(viSaveBuf.GetData(), viLoadBuf.GetData(), viSaveBuf.GetPosition())); // GOT ASSERT HERE!!!
...
}
void CVersionInfo::Write(CVersionInfoBuffer & viBuf)
{
//Pad to DWORD and save position for wLength
DWORD pos = viBuf.PadToDWORD();
//Skip size for now;
viBuf.Pad(sizeof WORD);
//Write wValueLength
viBuf.WriteWord(sizeof VS_FIXEDFILEINFO);
//Write wType
viBuf.WriteWord(0);
//Write key
viBuf.WriteString(L"VS_VERSION_INFO");
//Pad Fixed info
viBuf.PadToDWORD();
//Write Fixed file info
viBuf.Write(&m_vsFixedFileInfo, sizeof VS_FIXEDFILEINFO);
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);
}
//Set the size of the Version Info
viBuf.WriteStructSize(pos); // This line wrote 06 02 in memory, but the load buffer had 08 02. See below memory snapshot comparison.
}
The memory snapshot of viSaveBuf.m_lpData after viBuf.WriteStructSize(pos):
003865D8 00 00 34 00 00 00 56 00 ..4...V.
003865E0 53 00 5F 00 56 00 45 00 S._.V.E.
003865E8 52 00 53 00 49 00 4F 00 R.S.I.O.
003865F0 4E 00 5F 00 49 00 4E 00 N._.I.N.
003865F8 46 00 4F 00 00 00 00 00 F.O.....
00386600 BD 04 EF FE 00 00 01 00 ..靖....
00386608 00 00 01 00 CD 00 97 08 ........
00386610 00 00 05 00 CD 00 97 08 ........
00386618 3F 00 00 00 00 00 00 00 ?.......
00386620 04 00 00 00 02 00 00 00 ........
00386628 00 00 00 00 00 00 00 00 ........
00386630 00 00 00 00 06 02 00 00 ........
00386638 01 00 53 00 74 00 72 00 ..S.t.r.
0o386640 69 00 6E 00 67 00 46 00 i.n.g.F.
00386648 69 00 6C 00 65 00 49 00 i.l.e.I.
00386650 6E 00 66 00 6F 00 00 00 n.f.o...
00386658 E2 01 00 00 01 00 30 00 ......0.
00386660 34 00 30 00 39 00 30 00 4.0.9.0.
00386668 34 00 62 00 30 00 00 00 4.b.0...
The snapshot of viLoadBuf.m_lpData :
003851A8 A8 02 34 00 00 00 56 00 ..4...V.
003851B0 53 00 5F 00 56 00 45 00 S._.V.E.
003851B8 52 00 53 00 49 00 4F 00 R.S.I.O.
003851C0 4E 00 5F 00 49 00 4E 00 N._.I.N.
003851C8 46 00 4F 00 00 00 00 00 F.O.....
003851D0 BD 04 EF FE 00 00 01 00 ..靖....
003851D8 00 00 01 00 CD 00 97 08 ........
003851E0 00 00 05 00 CD 00 97 08 ........
003851E8 3F 00 00 00 00 00 00 00 ?.......
003851F0 04 00 00 00 02 00 00 00 ........
003851F8 00 00 00 00 00 00 00 00 ........
00385200 00 00 00 00 08 02 00 00 ........
00385208 01 00 53 00 74 00 72 00 ..S.t.r.
00385210 69 00 6E 00 67 00 46 00 i.n.g.F.
00385218 69 00 6C 00 65 00 49 00 i.l.e.I.
00385220 6E 00 66 00 6F 00 00 00 n.f.o...
00385228 E4 01 00 00 01 00 30 00 ......0.
00385230 34 00 30 00 39 00 30 00 4.0.9.0.
00385238 34 00 62 00 30 00 00 00 4.b.0...
HR
modified on Thursday, April 23, 2009 4:43 PM
|
|
|
|
|
You can ignore that assert, sometimes Version resources are not authored correctly, like there would be a translation specified, but no values for it. It was a part of broad testing, in release mode it doesn't attempt to "reconstruct" VI data when it reads one
-- No project was ever completed on time and within budget. Cheops Law
|
|
|
|
|
Hi,
The implementation seems to be missing the methods for getting the file & product versions from the fixed structure. Although I can easily implement this in my copy of the code, I'd like to see it public too
Thanks,
Gil.
|
|
|
|
|
|
Hi, as said numerous times, great article!
I am currently using this library in a program I am writing to update the file version information to include an arbitrary build number. It is working fine apart from if the build number is too large it seems an arbitrary number is put in its place. For example,
say "verinfolibtest.exe" is currently 1.0.0.1, now I run my program with a build number of say, a large value 999999999:
%>UpdateBuildNumber.exe -f verinfolibtest.exe -n 999999999
Then it will say the current file version is now 1.0.51711.1
Is there a way of finding out when this will occur so I can catch and handle the error?
Cheers..
|
|
|
|
|
Hi,
Keep in mind that VS_FIXEDFILEINFO structure hast two values:
<br />
DWORD dwFileVersionMS<br />
DWORD dwFileVersionLS<br />
Both are double-words, each their WORD (high and low) are corresponding parts of the four part version number.
Internally those two DWORDs make a 64-bit value used for numeric comparisons.
So the limit on each is one word (two bytes). The maximum for each part is 65535 (0xFFFF).
So if you are trying to set one of the members to anything higher than that value, it will be mod-ed by 65536 (0x10000) (or AND-ed with 65535 (0xFFFF).
In your case 999999999 % 65536 = 51711
For setting arbitrary version numbers (alpha-numeric) try using StringFileInfo values like FileVersion, PrivateBuild, SpecialBuild.
Keep in mind that File Version on the top of the Version tab of File Properties always displays standard representation of the version number from VS_FIXEDFILEINFO, and arbitrary (string) version number below (in "Other version information" group).
Regards
-- No project was ever completed on time and within budget. Cheops Law
|
|
|
|
|
Thank you, that answers my question perfectly. However one more question, I am trying to run my program on DLLs which have the resource information, but a assertion is failing where the hasStringTables function is in VersionInfo.cpp, what can I do to resolve this or catch it, etc..
thanks for your time
regards,
Michael
|
|
|
|
|
You can just remove the whole part of the code between
#ifdef _DEBUG
and
#endif
As it is just sanity check and should have been removed (I left it there, so if somebody modifies the library, he would have some code sample to perform sanity check. You don't actually need it to create f1.res and f2.res files.
You can also make a release build, it will "solve" the problem (which is BTW caused by some extra translation(s) registered but no string table for it)
-- No project was ever completed on time and within budget. Cheops Law
modified on Friday, January 11, 2008 12:29:55 AM
|
|
|
|
|
Oh I thought it wasn't necessary. In fact I just discovered that a release build gets rid of the problem all together. My program is completed now!
Thank you very much for your time and speedy responses.
Regards,
Michael
|
|
|
|
|
You might want to add this to the sample code. Updates the fileversion, so you can do VerInfoLibTest.exe -fileversion foobar.dll 1.2.3.4 .
else if (argc == 4)
{
if (!_tcscmp(argv[1], _T("-fileversion")))
{
CString filename(argv[2]);
CString version(argv[3]);
int dwVersion[4];
int i = 0; CString token;
int j = 0;
do
{
token = version.Tokenize(".", i);
dwVersion[j++] = atoi((LPCSTR) token);
} while (token.GetLength() > 0 && j < 4);
_tprintf(_T("Patching %s to version %d.%d.%d.%d\n"), (LPCSTR) filename,
dwVersion[0], dwVersion[1], dwVersion[2], dwVersion[3]);
CVersionInfo vi;
vi.FromFile(filename);
vi.SetFileVersion(dwVersion[0], dwVersion[1], dwVersion[2], dwVersion[3]);
vi.ToFile(filename);
}
}
|
|
|
|
|
Hi,
Thanks. It would probably make sense to implement -productversion in sample code too. I'll update the code when I have more free time.
Denis
-- No project was ever completed on time and within budget. Cheops Law
|
|
|
|
|
Hi,
I got source files and rebuilded it, but i got 2 error:
1.version information resources in compiled binaries\tools\mainline\verinfolib\versioninfo.h(77) : error C2061: syntax error : identifier 'LONG_PTR'
2.version information resources in compiled binaries\tools\mainline\verinfolib\versioninfo.cpp(31) : error C2065: 'IS_INTRESOURCE' : undeclared identifier
I had tried include windows.h, include winuser.h, but it still got there error.
My compter has .net2003, VS6. Winserver2000. and i used VC6 to rebuilded project.
Thanks and regards.
ColdSun
|
|
|
|
|
Hi,
when I try the sample program to update the file version of my InstallShiled setup.exe the setup.exe no longer works after changing. It looks like there was omething embedded in the binary before which is stripped of when using the VerInfoLib library. The setup.exe was 4011 kB before and 248 kB after the modification.
Any ideas?
Anyway, very useful tool...
Bernd
|
|
|
|
|
Wow, that's an odd side effect.
Did you trace it? When exactly does the size change?
Feel free to contact me by e-mail, I really want to see that happening and fix it.
Denis
-- No project was ever completed on time and within budget. Cheops Law
|
|
|
|
|
It is because you are using a self-extracting file. You need to update before you alter the PE header
|
|
|
|
|
Hello, I'm seeing this same problem when trying to update our version information on a Macrovision file. Can you give more detail on how to update before altering the PE header?
thanks,
Charlie
|
|
|
|
|
Hi,
First, I really appreciate this code!! Thanks!
And now a question:
When using vi.SetFileVersion on a version such as 1.02.0.0, it writes it as 1.2.0.0. In other words,
vi.SetFileVersion(1, 02, 0, 0, TRUE /*Update StringTables*/);
doesn't write the file version a 1.02, but rather 1.2.
I'm really only a beginner C++ programmer (I'm a VB guy)...how can I make it write it with the leading 0? Is this something you could fix in a new version?
Thanks,
Chris
|
|
|
|
|
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!!
|
|
|
|
|