3

I have the Version Info in resources declared:

100 VERSIONINFO
FILEVERSION 1,0,0,2
PRODUCTVERSION 1,0,0,2
FILEOS VOS_NT
FILETYPE VFT_APP
{
    BLOCK "StringFileInfo"
    {
        BLOCK "000004b0"
        {
            VALUE "FileDescription", "My application"
            VALUE "FileVersion", "1.0.0.2"
            VALUE "InternalName", "app.exe"
            VALUE "LegalCopyright", "Copyright ©  2012 by David."
            VALUE "OriginalFilename", "app.exe"
            VALUE "ProductName", "app"
            VALUE "ProductVersion", "1.0.0.2"
            VALUE "Assembly Version", "1.0.0.2"
        }
    }

    BLOCK "VarFileInfo"
    {
        VALUE "Translation", 0x0000 0x04B0
    }
}

I am getting Version Info this way:

HRSRC hResInfo;
HGLOBAL hResData;
LPCVOID pRes;
UINT uLen;
VS_FIXEDFILEINFO *lpFfi;

hResInfo = FindResource(hInst, MAKEINTRESOURCE(100), RT_VERSION);
MessageBox(0, "FindResource", 0,0);

hResData = LoadResource(hInst, hResInfo);
MessageBox(0, "LoadResource", 0,0);

pRes = LockResource(hResData);
MessageBox(0, "LockResource", 0,0);

VerQueryValue(pRes, "\\" ,(LPVOID*)&lpFfi, &uLen);
MessageBox(0, "VerQueryValue", 0,0);

FreeResource(hResData);

DWORD dwFileVersionMS = lpFfi->dwFileVersionMS;
DWORD dwFileVersionLS = lpFfi->dwFileVersionLS;

DWORD dwLeftMost     = HIWORD(dwFileVersionMS);
DWORD dwSecondLeft   = LOWORD(dwFileVersionMS);
DWORD dwSecondRight  = HIWORD(dwFileVersionLS);
DWORD dwRightMost    = LOWORD(dwFileVersionLS);

In function VerQueryValue is an error, because program breaks (MessageBox with text "VerQueryValue" doesn't show) and Visual C++ shows me the following message:

First-chance exception at 0x77bf15a5 in ProxyCU.exe: 0xC0000005: Access violation writing location 0x00483192.

How to repair this code?

Regards, David

David
  • 417
  • 1
  • 9
  • 18
  • I usually factor out where the version information is defined, [as shown here](http://stackoverflow.com/questions/13607986/how-can-i-share-same-product-version-between-two-visual-c-projects/13608454#13608454), so when it comes time to use it in code, it is a compiler define rather than a call to `LoadResource()`. If you cannot get your code to work, you may consider it an option. – Sean Cline Dec 18 '12 at 21:37
  • I suggest check error code after LoadResource() (@ERR watch in debugger, or GetLastError). Also, check that this address, 0x00483192, is inside memory pointed by pRes. – Pavel Radzivilovsky Dec 19 '12 at 09:02
  • Completely unrelated to the question, but why is your version resource at id 100? As far as I can tell it is expected to be at id 1 which is VS_VERSION_INFO – poizan42 Apr 27 '16 at 14:24

2 Answers2

16

VerQueryValue() cannot access version info from the original resource directly. You must make a copy of the resource in memory, then pass that memory to VerQueryValue() instead. The reason is because VerQueryValue() is designed to work with GetFileVersionInfo(), which requires a user-allocated block of writable memory and performs certain fixups within that memory. Accessing the VS_FIXEDFILEINFO struct does not require the fixups, but the memory block must still be writable. You cannot pass the original resource directly to VerQueryValue() because it is read-only memory.

Try this instead:

HRSRC hResInfo;
DWORD dwSize;
HGLOBAL hResData;
LPVOID pRes, pResCopy;
UINT uLen;
VS_FIXEDFILEINFO *lpFfi;

hResInfo = FindResource(hInst, MAKEINTRESOURCE(100), RT_VERSION);
dwSize = SizeofResource(hInst, hResInfo);
hResData = LoadResource(hInst, hResInfo);
pRes = LockResource(hResData);
pResCopy = LocalAlloc(LMEM_FIXED, dwSize);
CopyMemory(pResCopy, pRes, dwSize);
FreeResource(hResData);

VerQueryValue(pResCopy, TEXT("\\"), (LPVOID*)&lpFfi, &uLen);

DWORD dwFileVersionMS = lpFfi->dwFileVersionMS;
DWORD dwFileVersionLS = lpFfi->dwFileVersionLS;

DWORD dwLeftMost     = HIWORD(dwFileVersionMS);
DWORD dwSecondLeft   = LOWORD(dwFileVersionMS);
DWORD dwSecondRight  = HIWORD(dwFileVersionLS);
DWORD dwRightMost    = LOWORD(dwFileVersionLS);

LocalFree(pResCopy);

UPDATE: this only works if you access the VS_FIXEDFILEINFO struct only. If you need to access any other values, you must use GetFileVersionInfo(). Per Raymond Chen's blog:

The first parameter to VerQueryValue really must be a buffer you obtained from GetFileVersionInfo

The documentation says that the first parameter to VerQueryValue must be a buffer returned by the GetFileVersionInfo function for a reason. The buffer returned by GetFileVersionInfo is an opaque data block specifically formatted so that VerQueryValue will work. You're not supposed to look inside that buffer, and you certainly can't try to "obtain the data some other way". Because if you do, VerQueryValue will look for something in a buffer that is not formatted in the manner the function expects.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • +1 I just needed and used this. Thank you. One very minor comment: `FreeResource` call is not needed and returns `FALSE`, according to the docs. – David Heffernan Sep 28 '13 at 14:26
  • Is there any official documentation of the fact that you have to take a copy of the block? – M.M Jun 09 '14 at 04:49
  • The documentation for [FreeResource](https://msdn.microsoft.com/en-us/library/windows/desktop/ms648044(v=vs.85).aspx) suggests that it should not be called – M.M Nov 19 '15 at 07:07
  • Version resources aren’t necessarily 100, can be any number. In the legacy project I’m working on it’s `MAKEINTRESOURCE( 1 )` – Soonts Jan 12 '19 at 20:53
  • @Soonts true, but in the example given by the OP, it is 100. – Remy Lebeau Jan 12 '19 at 23:10
  • @M.M [The first parameter to VerQueryValue really must be a buffer you obtained from GetFileVersionInfo](https://blogs.msdn.microsoft.com/oldnewthing/20061226-06/?p=28603) – Remy Lebeau Jan 12 '19 at 23:14
  • Do you really have to `LocalAlloc` the memory? Could you use a `std::unique_ptr` instead ? – Spencer Dec 08 '22 at 13:52
  • @Spencer It doesn't matter how the memory is allocated, as long as it is writable memory. – Remy Lebeau Dec 08 '22 at 19:04
  • That should be part of the answer, then. `LocalAlloc` has to be balanced with a call to `LocalFree` so maybe a `unique_ptr` is preferred here in C++. – Spencer Dec 08 '22 at 19:20
  • @Spencer I already stated in my answer that the allocated memory has to be writable. Using `unique_ptr` is simply a matter of good memory management. Whether the memory is allocated with `LocalAlloc()`, `malloc()`, `new[]`, etc has no bearing whatsoever on the behavior of `VerQueryValue()` itself, which is the root of the question. – Remy Lebeau Dec 08 '22 at 19:41
  • Perhaps, but people will interpret your code to think you have to use `LocalAlloc`. – Spencer Dec 08 '22 at 20:01
  • @Spencer anyone who makes that assumption clearly lacks adequate experience with the Win32 API and how it interacts with user-provided memory buffers. – Remy Lebeau Dec 08 '22 at 20:11
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/250274/discussion-between-spencer-and-remy-lebeau). – Spencer Dec 08 '22 at 20:15
-1

The above would create a heap corruption error message in debug mode, such as "Free Heap block b753e70 modified at b753ed4 after it was freed". Someone has posted this problem many years ago at http://microsoft.public.win32.programmer.kernel.narkive.com/mqoHgVwM/verqueryvalue-bug. It is still hapenning today. One can make the message disappear by making dwSize sufficiently large, such as multiplying it by 4.

YS Yang
  • 1
  • 1