1 /*
2  * Copyright (C) 2004 Stefan Leichter
3  * Copyright (C) 2017 Akihiro Sagawa
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19 
20 #include <stdarg.h>
21 #include <stdio.h>
22 #include <assert.h>
23 
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winerror.h"
27 #include "winnls.h"
28 #include "winuser.h"
29 #include "winver.h"
30 #include "verrsrc.h"
31 #include "wine/test.h"
32 
33 #define MY_LAST_ERROR ((DWORD)-1)
34 #define EXPECT_BAD_PATH__NOT_FOUND \
35     ok( (ERROR_PATH_NOT_FOUND == GetLastError()) || \
36 	(ERROR_RESOURCE_DATA_NOT_FOUND == GetLastError()) || \
37 	(ERROR_FILE_NOT_FOUND == GetLastError()) || \
38 	(ERROR_BAD_PATHNAME == GetLastError()) || \
39         (ERROR_SUCCESS == GetLastError()), \
40 	"Last error wrong! ERROR_RESOURCE_DATA_NOT_FOUND/ERROR_BAD_PATHNAME (98)/" \
41 	"ERROR_PATH_NOT_FOUND (NT4)/ERROR_FILE_NOT_FOUND (2k3) " \
42         "ERROR_SUCCESS (2k) expected, got %u\n", GetLastError());
43 #define EXPECT_INVALID__NOT_FOUND \
44     ok( (ERROR_PATH_NOT_FOUND == GetLastError()) || \
45 	(ERROR_RESOURCE_DATA_NOT_FOUND == GetLastError()) || \
46 	(ERROR_FILE_NOT_FOUND == GetLastError()) || \
47 	(ERROR_INVALID_PARAMETER == GetLastError()) || \
48         (ERROR_SUCCESS == GetLastError()), \
49 	"Last error wrong! ERROR_RESOURCE_DATA_NOT_FOUND/ERROR_INVALID_PARAMETER (98)/" \
50 	"ERROR_PATH_NOT_FOUND (NT4)/ERROR_FILE_NOT_FOUND (2k3) " \
51 	"ERROR_SUCCESS (2k) expected, got %u\n", GetLastError());
52 
53 static void create_file(const CHAR *name)
54 {
55     HANDLE file;
56     DWORD written;
57 
58     file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
59     ok(file != INVALID_HANDLE_VALUE, "Failure to open file %s\n", name);
60     WriteFile(file, name, strlen(name), &written, NULL);
61     WriteFile(file, "\n", strlen("\n"), &written, NULL);
62     CloseHandle(file);
63 }
64 
65 static void test_info_size(void)
66 {   DWORD hdl, retval;
67     char mypath[MAX_PATH] = "";
68 
69     SetLastError(MY_LAST_ERROR);
70     retval = GetFileVersionInfoSizeA( NULL, NULL);
71     ok( !retval,
72 	"GetFileVersionInfoSizeA result wrong! 0L expected, got 0x%08x\n",
73 	retval);
74     EXPECT_INVALID__NOT_FOUND;
75 
76     hdl = 0x55555555;
77     SetLastError(MY_LAST_ERROR);
78     retval = GetFileVersionInfoSizeA( NULL, &hdl);
79     ok( !retval,
80 	"GetFileVersionInfoSizeA result wrong! 0L expected, got 0x%08x\n",
81 	retval);
82     EXPECT_INVALID__NOT_FOUND;
83     ok( hdl == 0L,
84 	"Handle wrong! 0L expected, got 0x%08x\n", hdl);
85 
86     SetLastError(MY_LAST_ERROR);
87     retval = GetFileVersionInfoSizeA( "", NULL);
88     ok( !retval,
89 	"GetFileVersionInfoSizeA result wrong! 0L expected, got 0x%08x\n",
90 	retval);
91     EXPECT_BAD_PATH__NOT_FOUND;
92 
93     hdl = 0x55555555;
94     SetLastError(MY_LAST_ERROR);
95     retval = GetFileVersionInfoSizeA( "", &hdl);
96     ok( !retval,
97 	"GetFileVersionInfoSizeA result wrong! 0L expected, got 0x%08x\n",
98 	retval);
99     EXPECT_BAD_PATH__NOT_FOUND;
100     ok( hdl == 0L,
101 	"Handle wrong! 0L expected, got 0x%08x\n", hdl);
102 
103     SetLastError(MY_LAST_ERROR);
104     retval = GetFileVersionInfoSizeA( "kernel32.dll", NULL);
105     ok( retval,
106 	"GetFileVersionInfoSizeA result wrong! <> 0L expected, got 0x%08x\n",
107 	retval);
108     ok((NO_ERROR == GetLastError()) || (MY_LAST_ERROR == GetLastError()),
109 	"Last error wrong! NO_ERROR/0x%08x (NT4)  expected, got %u\n",
110 	MY_LAST_ERROR, GetLastError());
111 
112     hdl = 0x55555555;
113     SetLastError(MY_LAST_ERROR);
114     retval = GetFileVersionInfoSizeA( "kernel32.dll", &hdl);
115     ok( retval,
116 	"GetFileVersionInfoSizeA result wrong! <> 0L expected, got 0x%08x\n",
117 	retval);
118     ok((NO_ERROR == GetLastError()) || (MY_LAST_ERROR == GetLastError()),
119 	"Last error wrong! NO_ERROR/0x%08x (NT4)  expected, got %u\n",
120 	MY_LAST_ERROR, GetLastError());
121     ok( hdl == 0L,
122 	"Handle wrong! 0L expected, got 0x%08x\n", hdl);
123 
124     SetLastError(MY_LAST_ERROR);
125     retval = GetFileVersionInfoSizeA( "notexist.dll", NULL);
126     ok( !retval,
127 	"GetFileVersionInfoSizeA result wrong! 0L expected, got 0x%08x\n",
128 	retval);
129     ok( (ERROR_FILE_NOT_FOUND == GetLastError()) ||
130 	(ERROR_RESOURCE_DATA_NOT_FOUND == GetLastError()) ||
131 	(MY_LAST_ERROR == GetLastError()) ||
132 	(ERROR_SUCCESS == GetLastError()), /* win2k */
133 	"Last error wrong! ERROR_FILE_NOT_FOUND/ERROR_RESOURCE_DATA_NOT_FOUND "
134 	"(XP)/0x%08x (NT4) expected, got %u\n", MY_LAST_ERROR, GetLastError());
135 
136     /* test a currently loaded executable */
137     if(GetModuleFileNameA(NULL, mypath, MAX_PATH)) {
138 	hdl = 0x55555555;
139 	SetLastError(MY_LAST_ERROR);
140 	retval = GetFileVersionInfoSizeA( mypath, &hdl);
141 	ok( retval,
142             "GetFileVersionInfoSizeA result wrong! <> 0L expected, got 0x%08x\n",
143 	    retval);
144 	ok((NO_ERROR == GetLastError()) || (MY_LAST_ERROR == GetLastError()),
145             "Last error wrong! NO_ERROR/0x%08x (NT4)  expected, got %u\n",
146 	    MY_LAST_ERROR, GetLastError());
147 	ok( hdl == 0L,
148             "Handle wrong! 0L expected, got 0x%08x\n", hdl);
149     }
150     else
151 	trace("skipping GetModuleFileNameA(NULL,..) failed\n");
152 
153     /* test a not loaded executable */
154     if(GetSystemDirectoryA(mypath, MAX_PATH)) {
155 	lstrcatA(mypath, "\\regsvr32.exe");
156 
157 	if(INVALID_FILE_ATTRIBUTES == GetFileAttributesA(mypath))
158 	    trace("GetFileAttributesA(%s) failed\n", mypath);
159 	else {
160 	    hdl = 0x55555555;
161 	    SetLastError(MY_LAST_ERROR);
162 	    retval = GetFileVersionInfoSizeA( mypath, &hdl);
163 	    ok( retval,
164 		"GetFileVersionInfoSizeA result wrong! <> 0L expected, got 0x%08x\n",
165 		retval);
166 	    ok((NO_ERROR == GetLastError()) || (MY_LAST_ERROR == GetLastError()),
167 		"Last error wrong! NO_ERROR/0x%08x (NT4)  expected, got %u\n",
168 		MY_LAST_ERROR, GetLastError());
169 	    ok( hdl == 0L,
170 		"Handle wrong! 0L expected, got 0x%08x\n", hdl);
171 	}
172     }
173     else
174 	trace("skipping GetSystemDirectoryA(mypath,..) failed\n");
175 
176     create_file("test.txt");
177 
178     /* no version info */
179     SetLastError(0xdeadbeef);
180     hdl = 0xcafe;
181     retval = GetFileVersionInfoSizeA("test.txt", &hdl);
182     ok(retval == 0, "Expected 0, got %d\n", retval);
183     ok(hdl == 0, "Expected 0, got %d\n", hdl);
184     ok(GetLastError() == ERROR_RESOURCE_DATA_NOT_FOUND ||
185        GetLastError() == ERROR_SUCCESS, /* win2k */
186        "Expected ERROR_RESOURCE_DATA_NOT_FOUND, got %d\n", GetLastError());
187 
188     DeleteFileA("test.txt");
189 }
190 
191 static void VersionDwordLong2String(DWORDLONG Version, LPSTR lpszVerString)
192 {
193     WORD a, b, c, d;
194 
195     a = (WORD)(Version >> 48);
196     b = (WORD)((Version >> 32) & 0xffff);
197     c = (WORD)((Version >> 16) & 0xffff);
198     d = (WORD)(Version & 0xffff);
199 
200     sprintf(lpszVerString, "%d.%d.%d.%d", a, b, c, d);
201 }
202 
203 static void test_info(void)
204 {
205     DWORD hdl, retval;
206     PVOID pVersionInfo = NULL;
207     BOOL boolret;
208     VS_FIXEDFILEINFO *pFixedVersionInfo;
209     UINT uiLength;
210     char VersionString[MAX_PATH];
211     static const char backslash[] = "\\";
212     DWORDLONG dwlVersion;
213 
214     hdl = 0x55555555;
215     SetLastError(MY_LAST_ERROR);
216     retval = GetFileVersionInfoSizeA( "kernel32.dll", &hdl);
217     ok( retval,
218 	"GetFileVersionInfoSizeA result wrong! <> 0L expected, got 0x%08x\n",
219 	retval);
220     ok((NO_ERROR == GetLastError()) || (MY_LAST_ERROR == GetLastError()),
221 	"Last error wrong! NO_ERROR/0x%08x (NT4)  expected, got %u\n",
222 	MY_LAST_ERROR, GetLastError());
223     ok( hdl == 0L,
224 	"Handle wrong! 0L expected, got 0x%08x\n", hdl);
225 
226     if ( retval == 0 || hdl != 0)
227         return;
228 
229     pVersionInfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, retval );
230     ok(pVersionInfo != 0, "HeapAlloc failed\n" );
231     if (pVersionInfo == 0)
232         return;
233 
234     if (0)
235     {
236     /* this test crashes on WinNT4
237      */
238     boolret = GetFileVersionInfoA( "kernel32.dll", 0, retval, 0);
239     ok (!boolret, "GetFileVersionInfoA should have failed: GetLastError = %u\n", GetLastError());
240     ok ((GetLastError() == ERROR_INVALID_DATA) || (GetLastError() == ERROR_BAD_PATHNAME) ||
241 	(GetLastError() == NO_ERROR),
242         "Last error wrong! ERROR_INVALID_DATA/ERROR_BAD_PATHNAME (ME)/"
243 	"NO_ERROR (95) expected, got %u\n",
244         GetLastError());
245     }
246 
247     boolret = GetFileVersionInfoA( "kernel32.dll", 0, retval, pVersionInfo );
248     ok (boolret, "GetFileVersionInfoA failed: GetLastError = %u\n", GetLastError());
249     if (!boolret)
250         goto cleanup;
251 
252     boolret = VerQueryValueA( pVersionInfo, NULL, (LPVOID *)&pFixedVersionInfo, &uiLength );
253     ok (boolret || GetLastError() == NO_ERROR /* Win98 */,
254        "VerQueryValueA failed: GetLastError = %u\n", GetLastError());
255 
256     boolret = VerQueryValueA( pVersionInfo, "", (LPVOID *)&pFixedVersionInfo, &uiLength );
257     ok (boolret, "VerQueryValueA failed: GetLastError = %u\n", GetLastError());
258 
259     boolret = VerQueryValueA( pVersionInfo, backslash, (LPVOID *)&pFixedVersionInfo, &uiLength );
260     ok (boolret, "VerQueryValueA failed: GetLastError = %u\n", GetLastError());
261     if (!boolret)
262         goto cleanup;
263 
264     dwlVersion = (((DWORDLONG)pFixedVersionInfo->dwFileVersionMS) << 32) +
265         pFixedVersionInfo->dwFileVersionLS;
266 
267     VersionDwordLong2String(dwlVersion, VersionString);
268 
269     trace("kernel32.dll version: %s\n", VersionString);
270 
271     if (0)
272     {
273     /* this test crashes on WinNT4
274      */
275     boolret = VerQueryValueA( pVersionInfo, backslash, (LPVOID *)&pFixedVersionInfo, 0);
276     ok (boolret, "VerQueryValue failed: GetLastError = %u\n", GetLastError());
277     }
278 
279 cleanup:
280     HeapFree( GetProcessHeap(), 0, pVersionInfo);
281 }
282 
283 static void test_32bit_win(void)
284 {
285     DWORD hdlA, retvalA;
286     DWORD hdlW, retvalW = 0;
287     BOOL retA,retW;
288     PVOID pVersionInfoA = NULL;
289     PVOID pVersionInfoW = NULL;
290     char *pBufA;
291     WCHAR *pBufW;
292     UINT uiLengthA, uiLengthW;
293     char mypathA[MAX_PATH];
294     WCHAR mypathW[MAX_PATH];
295     char rootA[] = "\\";
296     WCHAR rootW[] = { '\\', 0 };
297     WCHAR emptyW[] = { 0 };
298     char varfileinfoA[] = "\\VarFileInfo\\Translation";
299     WCHAR varfileinfoW[]    = { '\\','V','a','r','F','i','l','e','I','n','f','o',
300                                 '\\','T','r','a','n','s','l','a','t','i','o','n', 0 };
301     char WineVarFileInfoA[] = { 0x09, 0x04, 0xE4, 0x04 };
302     char FileDescriptionA[] = "\\StringFileInfo\\040904E4\\FileDescription";
303     WCHAR FileDescriptionW[] = { '\\','S','t','r','i','n','g','F','i','l','e','I','n','f','o',
304                                 '\\','0','4','0','9','0','4','E','4',
305                                 '\\','F','i','l','e','D','e','s','c','r','i','p','t','i','o','n', 0 };
306     char WineFileDescriptionA[] = "FileDescription";
307     WCHAR WineFileDescriptionW[] = { 'F','i','l','e','D','e','s','c','r','i','p','t','i','o','n', 0 };
308     BOOL is_unicode_enabled = TRUE;
309 
310     /* A copy from dlls/version/info.c */
311     typedef struct
312     {
313         WORD  wLength;
314         WORD  wValueLength;
315         WORD  wType;
316         WCHAR szKey[1];
317 #if 0   /* variable length structure */
318         /* DWORD aligned */
319         BYTE  Value[];
320         /* DWORD aligned */
321         VS_VERSION_INFO_STRUCT32 Children[];
322 #endif
323     } VS_VERSION_INFO_STRUCT32;
324 
325     /* If we call GetFileVersionInfoA on a system that supports Unicode, NT/W2K/XP/W2K3 (by default) and Wine,
326      * the versioninfo will contain Unicode strings.
327      * Part of the test is to call both the A and W versions, which should have the same Version Information
328      * for some requests, on systems that support both calls.
329      */
330 
331     /* First get the versioninfo via the W versions */
332     SetLastError(0xdeadbeef);
333     GetModuleFileNameW(NULL, mypathW, MAX_PATH);
334     if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
335     {
336         win_skip("GetModuleFileNameW not existing on this platform, skipping comparison between A- and W-calls\n");
337         is_unicode_enabled = FALSE;
338     }
339 
340     if (is_unicode_enabled)
341     {
342         retvalW = GetFileVersionInfoSizeW( mypathW, &hdlW);
343         pVersionInfoW = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, retvalW );
344         retW = GetFileVersionInfoW( mypathW, 0, retvalW, pVersionInfoW );
345         ok(retW, "GetFileVersionInfo failed: GetLastError = %u\n", GetLastError());
346     }
347 
348     GetModuleFileNameA(NULL, mypathA, MAX_PATH);
349     retvalA = GetFileVersionInfoSizeA( mypathA, &hdlA);
350     pVersionInfoA = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, retvalA );
351     retA = GetFileVersionInfoA( mypathA, 0, retvalA, pVersionInfoA );
352     ok(retA, "GetFileVersionInfo failed: GetLastError = %u\n", GetLastError());
353 
354     if (is_unicode_enabled)
355     {
356         ok( retvalA == retvalW, "The size of the struct should be the same for both A/W calls, it is (%d) vs. (%d)\n",
357                                 retvalA, retvalW);
358         ok( !memcmp(pVersionInfoA, pVersionInfoW, retvalA), "Both structs should be the same, they aren't\n");
359     }
360 
361     /* The structs on Windows are bigger than just the struct for the basic information. The total struct
362      * contains also an empty part, which is used for converted strings. The converted strings are a result
363      * of calling VerQueryValueA on a 32bit resource and calling VerQueryValueW on a 16bit resource.
364      * The first WORD of the structure (wLength) shows the size of the base struct. The total struct size depends
365      * on the Windows version:
366      *
367      * 16bits resource (numbers are from a sample app):
368      *
369      * Windows Version    Retrieved with A/W    wLength        StructSize
370      * ====================================================================================
371      * Win98              A                     0x01B4 (436)   436
372      * NT4                A/W                   0x01B4 (436)   2048 ???
373      * W2K/XP/W2K3        A/W                   0x01B4 (436)   1536 which is (436 - sizeof(VS_FIXEDFILEINFO)) * 4
374      *
375      * 32bits resource (numbers are from this test executable version_crosstest.exe):
376      * Windows Version    Retrieved with A/W    wLength        StructSize
377      * =============================================================
378      * Win98              A                     0x01E0 (480)   848 (structure data doesn't seem correct)
379      * NT4                A/W                   0x0350 (848)   1272 (848 * 1.5)
380      * W2K/XP/W2K3        A/W                   0x0350 (848)   1700 which is (848 * 2) + 4
381      *
382      * Wine will follow the implementation (eventually) of W2K/XP/W2K3
383      */
384 
385     /* Now some tests for the above (only if we are unicode enabled) */
386 
387     if (is_unicode_enabled)
388     {
389         VS_VERSION_INFO_STRUCT32 *vvis = pVersionInfoW;
390         ok ( retvalW == ((vvis->wLength * 2) + 4) || retvalW == (vvis->wLength * 1.5),
391              "Structure is not of the correct size\n");
392     }
393 
394     /* Although the 32bit resource structures contain Unicode strings, VerQueryValueA will always return normal strings,
395      * VerQueryValueW will always return Unicode ones. (That means everything returned for StringFileInfo requests).
396      */
397 
398     /* Get the VS_FIXEDFILEINFO information, this must be the same for both A- and W-Calls */
399 
400     retA = VerQueryValueA( pVersionInfoA, rootA, (LPVOID *)&pBufA, &uiLengthA );
401     ok (retA, "VerQueryValueA failed: GetLastError = %u\n", GetLastError());
402     ok ( uiLengthA == sizeof(VS_FIXEDFILEINFO), "Size (%d) doesn't match the size of the VS_FIXEDFILEINFO struct\n", uiLengthA);
403 
404     if (is_unicode_enabled)
405     {
406         if(0)
407         {   /* This causes Vista and w2k8 to crash */
408             retW = VerQueryValueW( pVersionInfoW, NULL, (LPVOID *)&pBufW, &uiLengthW );
409             ok (retW, "VerQueryValueW failed: GetLastError = %u\n", GetLastError());
410         }
411 
412         retW = VerQueryValueW( pVersionInfoW, emptyW, (LPVOID *)&pBufW, &uiLengthW );
413         ok (retW, "VerQueryValueW failed: GetLastError = %u\n", GetLastError());
414 
415         retW = VerQueryValueW( pVersionInfoW, rootW, (LPVOID *)&pBufW, &uiLengthW );
416         ok (retW, "VerQueryValueW failed: GetLastError = %u\n", GetLastError());
417         ok ( uiLengthW == sizeof(VS_FIXEDFILEINFO), "Size (%d) doesn't match the size of the VS_FIXEDFILEINFO struct\n", uiLengthW );
418 
419         ok( uiLengthA == uiLengthW, "The size of VS_FIXEDFILEINFO should be the same for both A/W calls, it is (%d) vs. (%d)\n",
420                                     uiLengthA, uiLengthW);
421         ok( !memcmp(pBufA, pBufW, uiLengthA), "Both values should be the same, they aren't\n");
422     }
423 
424     /* Get some VarFileInfo information, this must be the same for both A- and W-Calls */
425 
426     retA = VerQueryValueA( pVersionInfoA, varfileinfoA, (LPVOID *)&pBufA, &uiLengthA );
427     ok (retA, "VerQueryValueA failed: GetLastError = %u\n", GetLastError());
428     ok( !memcmp(pBufA, WineVarFileInfoA, uiLengthA), "The VarFileInfo should have matched 0904e404 (non case sensitive)\n");
429 
430     if (is_unicode_enabled)
431     {
432         retW = VerQueryValueW( pVersionInfoW, varfileinfoW, (LPVOID *)&pBufW, &uiLengthW );
433         ok (retW, "VerQueryValueW failed: GetLastError = %u\n", GetLastError());
434         ok( uiLengthA == uiLengthW, "The size of the VarFileInfo information should be the same for both A/W calls, it is (%d) vs. (%d)\n",
435                                     uiLengthA, uiLengthW);
436         ok( !memcmp(pBufA, pBufW, uiLengthA), "Both values should be the same, they aren't\n");
437     }
438 
439     /* Get some StringFileInfo information, this will be ANSI for A-Calls and Unicode for W-Calls */
440 
441     retA = VerQueryValueA( pVersionInfoA, FileDescriptionA, (LPVOID *)&pBufA, &uiLengthA );
442     ok (retA, "VerQueryValueA failed: GetLastError = %u\n", GetLastError());
443     ok( !lstrcmpA(WineFileDescriptionA, pBufA), "expected '%s' got '%s'\n",
444         WineFileDescriptionA, pBufA);
445 
446     /* Test a second time */
447     retA = VerQueryValueA( pVersionInfoA, FileDescriptionA, (LPVOID *)&pBufA, &uiLengthA );
448     ok (retA, "VerQueryValueA failed: GetLastError = %u\n", GetLastError());
449     ok( !lstrcmpA(WineFileDescriptionA, pBufA), "expected '%s' got '%s'\n",
450         WineFileDescriptionA, pBufA);
451 
452     if (is_unicode_enabled)
453     {
454         retW = VerQueryValueW( pVersionInfoW, FileDescriptionW, (LPVOID *)&pBufW, &uiLengthW );
455         ok (retW, "VerQueryValueW failed: GetLastError = %u\n", GetLastError());
456         ok( !lstrcmpW(WineFileDescriptionW, pBufW), "FileDescription should have been '%s'\n", WineFileDescriptionA);
457     }
458 
459     HeapFree( GetProcessHeap(), 0, pVersionInfoA);
460     if (is_unicode_enabled)
461         HeapFree( GetProcessHeap(), 0, pVersionInfoW);
462 }
463 
464 static void test_VerQueryValueA(void)
465 {
466     static const char * const value_name[] = {
467         "Product", "CompanyName", "FileDescription", "Internal",
468         "ProductVersion", "InternalName", "File", "LegalCopyright",
469         "FileVersion", "Legal", "OriginalFilename", "ProductName",
470         "Company", "Original" };
471     char *ver, *p;
472     UINT len, ret, translation, i;
473     char buf[MAX_PATH];
474 
475     ret = GetModuleFileNameA(NULL, buf, sizeof(buf));
476     assert(ret);
477 
478     SetLastError(0xdeadbeef);
479     len = GetFileVersionInfoSizeA(buf, NULL);
480     ok(len, "GetFileVersionInfoSizeA(%s) error %u\n", buf, GetLastError());
481 
482     ver = HeapAlloc(GetProcessHeap(), 0, len);
483     assert(ver);
484 
485     SetLastError(0xdeadbeef);
486     ret = GetFileVersionInfoA(buf, 0, len, ver);
487     ok(ret, "GetFileVersionInfoA error %u\n", GetLastError());
488 
489     p = (char *)0xdeadbeef;
490     len = 0xdeadbeef;
491     SetLastError(0xdeadbeef);
492     ret = VerQueryValueA(ver, "\\VarFileInfo\\Translation", (LPVOID*)&p, &len);
493     ok(ret, "VerQueryValue error %u\n", GetLastError());
494     ok(len == 4, "VerQueryValue returned %u, expected 4\n", len);
495 
496     translation = *(UINT *)p;
497     translation = MAKELONG(HIWORD(translation), LOWORD(translation));
498 
499     p = (char *)0xdeadbeef;
500     len = 0xdeadbeef;
501     SetLastError(0xdeadbeef);
502     ret = VerQueryValueA(ver, "String", (LPVOID*)&p, &len);
503     ok(!ret, "VerQueryValue should fail\n");
504     ok(GetLastError() == ERROR_RESOURCE_TYPE_NOT_FOUND ||
505        GetLastError() == 0xdeadbeef /* NT4, W2K */,
506        "VerQueryValue returned %u\n", GetLastError());
507     ok(p == (char *)0xdeadbeef, "expected 0xdeadbeef got %p\n", p);
508     ok(len == 0, "expected 0 got %x\n", len);
509 
510     p = (char *)0xdeadbeef;
511     len = 0xdeadbeef;
512     SetLastError(0xdeadbeef);
513     ret = VerQueryValueA(ver, "StringFileInfo", (LPVOID*)&p, &len);
514     ok(ret, "VerQueryValue error %u\n", GetLastError());
515     ok(len == 0, "VerQueryValue returned %u, expected 0\n", len);
516     ok(p != (char *)0xdeadbeef, "not expected 0xdeadbeef\n");
517 
518     p = (char *)0xdeadbeef;
519     len = 0xdeadbeef;
520     SetLastError(0xdeadbeef);
521     ret = VerQueryValueA(ver, "\\StringFileInfo", (LPVOID*)&p, &len);
522     ok(ret, "VerQueryValue error %u\n", GetLastError());
523     ok(len == 0, "VerQueryValue returned %u, expected 0\n", len);
524     ok(p != (char *)0xdeadbeef, "not expected 0xdeadbeef\n");
525 
526     p = (char *)0xdeadbeef;
527     len = 0xdeadbeef;
528     SetLastError(0xdeadbeef);
529     ret = VerQueryValueA(ver, "\\\\StringFileInfo", (LPVOID*)&p, &len);
530     ok(ret, "VerQueryValue error %u\n", GetLastError());
531     ok(len == 0, "VerQueryValue returned %u, expected 0\n", len);
532     ok(p != (char *)0xdeadbeef, "not expected 0xdeadbeef\n");
533 
534     p = (char *)0xdeadbeef;
535     len = 0xdeadbeef;
536     SetLastError(0xdeadbeef);
537     ret = VerQueryValueA(ver, "\\StringFileInfo\\\\", (LPVOID*)&p, &len);
538     ok(ret, "VerQueryValue error %u\n", GetLastError());
539     ok(len == 0, "VerQueryValue returned %u, expected 0\n", len);
540     ok(p != (char *)0xdeadbeef, "not expected 0xdeadbeef\n");
541 
542     sprintf(buf, "\\StringFileInfo\\%08x", translation);
543     p = (char *)0xdeadbeef;
544     len = 0xdeadbeef;
545     SetLastError(0xdeadbeef);
546     ret = VerQueryValueA(ver, buf, (LPVOID*)&p, &len);
547     ok(ret, "VerQueryValue error %u\n", GetLastError());
548     ok(len == 0, "VerQueryValue returned %u, expected 0\n", len);
549     ok(p != (char *)0xdeadbeef, "not expected 0xdeadbeef\n");
550 
551     for (i = 0; i < ARRAY_SIZE(value_name); i++)
552     {
553 	sprintf(buf, "\\StringFileInfo\\%08x\\%s", translation, value_name[i]);
554         p = (char *)0xdeadbeef;
555         len = 0xdeadbeef;
556         SetLastError(0xdeadbeef);
557         ret = VerQueryValueA(ver, buf, (LPVOID*)&p, &len);
558         ok(ret, "VerQueryValueA(%s) error %u\n", buf, GetLastError());
559         ok(len == strlen(value_name[i]) + 1, "VerQueryValue returned %u\n", len);
560         ok(!strcmp(value_name[i], p), "expected \"%s\", got \"%s\"\n",
561            value_name[i], p);
562 
563         /* test partial value names */
564         len = lstrlenA(buf);
565         buf[len - 2] = 0;
566         p = (char *)0xdeadbeef;
567         len = 0xdeadbeef;
568         SetLastError(0xdeadbeef);
569         ret = VerQueryValueA(ver, buf, (LPVOID*)&p, &len);
570         ok(!ret, "VerQueryValueA(%s) succeeded\n", buf);
571         ok(GetLastError() == ERROR_RESOURCE_TYPE_NOT_FOUND ||
572            GetLastError() == 0xdeadbeef /* NT4, W2K */,
573            "VerQueryValue returned %u\n", GetLastError());
574         ok(p == (char *)0xdeadbeef, "expected 0xdeadbeef got %p\n", p);
575         ok(len == 0, "expected 0 or 0xbeef, got %x\n", len);
576     }
577 
578     HeapFree(GetProcessHeap(), 0, ver);
579 }
580 
581 static void test_VerQueryValue_InvalidLength(void)
582 {
583     /* this buffer is created with the reactos resource compiler from this resource:
584 #include "winver.h"
585 
586 VS_VERSION_INFO VERSIONINFO
587 FILEVERSION    1,0,0,0
588 PRODUCTVERSION 1,0,0,0
589 FILEFLAGSMASK  63
590 FILEFLAGS      0
591 FILEOS         VOS_UNKNOWN
592 FILETYPE       VFT_APP
593 FILESUBTYPE    VFT2_UNKNOWN
594 {
595     BLOCK "StringFileInfo"
596     {
597     }
598 }
599 */
600     char preparedbuffer[] = {
601         /* VS_VERSION_INFO_STRUCT32 */
602         0x80, 0x00,     /* wLength */
603         0x34, 0x00,     /* wValueLength */
604         0x00, 0x00,     /* wType */
605         /* L"VS_VERSION_INFO" + DWORD alignment */
606         0x56, 0x00, 0x53, 0x00, 0x5f, 0x00, 0x56, 0x00, 0x45, 0x00, 0x52, 0x00, 0x53, 0x00, 0x49, 0x00, 0x4f,
607         0x00, 0x4e, 0x00, 0x5f, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x46, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00,
608 
609         /* VS_FIXEDFILEINFO */
610         0xbd, 0x04, 0xef, 0xfe,     /* dwSignature */
611         0x00, 0x00, 0x01, 0x00,     /* dwStrucVersion */
612         0x00, 0x00, 0x01, 0x00,     /* dwFileVersionMS */
613         0x00, 0x00, 0x00, 0x00,     /* dwFileVersionLS */
614         0x00, 0x00, 0x01, 0x00,     /* dwProductVersionMS */
615         0x00, 0x00, 0x00, 0x00,     /* dwProductVersionLS */
616         0x3f, 0x00, 0x00, 0x00,     /* dwFileFlagsMask */
617         0x00, 0x00, 0x00, 0x00,     /* dwFileFlags */
618         0x00, 0x00, 0x00, 0x00,     /* dwFileOS */
619         0x01, 0x00, 0x00, 0x00,     /* dwFileType */
620         0x00, 0x00, 0x00, 0x00,     /* dwFileSubtype */
621         0x00, 0x00, 0x00, 0x00,     /* dwFileDateMS */
622         0x00, 0x00, 0x00, 0x00,     /* dwFileDateLS */
623 
624         /* first child: */
625             0x24, 0x00,     /* wLength */
626             0x00, 0x00,     /* wValueLength */
627             0x01, 0x00,     /* wType */
628             /* L"StringFileInfo" + DWORD alignment */
629             0x53, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x46, 0x00, 0x69, 0x00,
630             0x6c, 0x00, 0x65, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x66, 0x00, 0x6f, 0x00, 0x00, 0x00,
631             /* "FE2X" */
632             0x46, 0x45, 0x32, 0x58,
633 
634             /* Extra bytes allocated for W->A conversions. */
635             0x0d, 0xf0, 0xad, 0xba, 0x0d, 0xf0, 0xad, 0xba, 0x0d, 0xf0, 0xad, 0xba, 0x0d, 0xf0, 0xad, 0xba,
636             0x0d, 0xf0, 0xad, 0xba, 0x0d, 0xf0, 0xad, 0xba, 0x0d, 0xf0, 0xad, 0xba, 0x0d, 0xf0, 0xad, 0xba,
637             0x0d, 0xf0, 0xad, 0xba, 0x0d, 0xf0, 0xad, 0xba, 0x0d, 0xf0, 0xad, 0xba, 0x0d, 0xf0, 0xad, 0xba,
638             0x0d, 0xf0, 0xad, 0xba, 0x0d, 0xf0, 0xad, 0xba, 0x0d, 0xf0, 0xad, 0xba, 0x0d, 0xf0, 0xad, 0xba,
639             0x0d, 0xf0, 0xad, 0xba, 0x0d, 0xf0, 0xad, 0xba, 0x0d, 0xf0, 0xad, 0xba, 0x0d, 0xf0, 0xad, 0xba,
640             0x0d, 0xf0, 0xad, 0xba, 0x0d, 0xf0, 0xad, 0xba, 0x0d, 0xf0, 0xad, 0xba, 0x0d, 0xf0, 0xad, 0xba,
641             0x0d, 0xf0, 0xad, 0xba, 0x0d, 0xf0, 0xad, 0xba, 0x0d, 0xf0, 0xad, 0xba, 0x0d, 0xf0, 0xad, 0xba,
642             0x0d, 0xf0, 0xad, 0xba, 0x0d, 0xf0, 0xad, 0xba, 0x0d, 0xf0, 0xad, 0xba, 0x0d, 0xf0, 0xad, 0xba,
643     };
644     char *p;
645     UINT len, ret;
646     WCHAR FileDescriptionW[] = { '\\', '\\', 'S', 't', 'r', 'i', 'n', 'g', 'F', 'i', 'l', 'e', 'I', 'n', 'f', 'o', 0 };
647 
648     p = (char *)0xdeadbeef;
649     len = 0xdeadbeef;
650     SetLastError(0xdeadbeef);
651     ret = VerQueryValueA(preparedbuffer, "StringFileInfo", (LPVOID *)&p, &len);
652     ok(ret, "VerQueryValueA error %u\n", GetLastError());
653     ok(len == 0, "VerQueryValueA returned %u, expected 0\n", len);
654     todo_wine
655     ok(p == preparedbuffer + 0x7e, "p was %p, expected %p\n", p, preparedbuffer + 0x7e);
656 
657     p = (char *)0xdeadbeef;
658     len = 0xdeadbeef;
659     SetLastError(0xdeadbeef);
660     ret = VerQueryValueA(preparedbuffer, "\\StringFileInfo", (LPVOID *)&p, &len);
661     ok(ret, "VerQueryValueA error %u\n", GetLastError());
662     ok(len == 0, "VerQueryValueA returned %u, expected 0\n", len);
663     todo_wine
664     ok(p == preparedbuffer + 0x7e, "p was %p, expected %p\n", p, preparedbuffer + 0x7e);
665 
666     p = (char *)0xdeadbeef;
667     len = 0xdeadbeef;
668     SetLastError(0xdeadbeef);
669     ret = VerQueryValueA(preparedbuffer, "\\\\StringFileInfo", (LPVOID *)&p, &len);
670     ok(ret, "VerQueryValueA error %u\n", GetLastError());
671     ok(len == 0, "VerQueryValueA returned %u, expected 0\n", len);
672     todo_wine
673     ok(p == preparedbuffer + 0x7e, "p was %p, expected %p\n", p, preparedbuffer + 0x7e);
674 
675     /* also test the W versions. */
676     p = (char *)0xdeadbeef;
677     len = 0xdeadbeef;
678     SetLastError(0xdeadbeef);
679     ret = VerQueryValueW(preparedbuffer, FileDescriptionW + 2, (LPVOID *)&p, &len);
680     ok(ret, "VerQueryValueW error %u\n", GetLastError());
681     ok(len == 0, "VerQueryValueW returned %u, expected 0\n", len);
682     todo_wine
683     ok(p == preparedbuffer + 0x7e, "p was %p, expected %p\n", p, preparedbuffer + 0x7e);
684 
685     p = (char *)0xdeadbeef;
686     len = 0xdeadbeef;
687     SetLastError(0xdeadbeef);
688     ret = VerQueryValueW(preparedbuffer, FileDescriptionW + 1, (LPVOID *)&p, &len);
689     ok(ret, "VerQueryValueW error %u\n", GetLastError());
690     ok(len == 0, "VerQueryValueW returned %u, expected 0\n", len);
691     todo_wine
692     ok(p == preparedbuffer + 0x7e, "p was %p, expected %p\n", p, preparedbuffer + 0x7e);
693 
694     p = (char *)0xdeadbeef;
695     len = 0xdeadbeef;
696     SetLastError(0xdeadbeef);
697     ret = VerQueryValueW(preparedbuffer, FileDescriptionW, (LPVOID *)&p, &len);
698     ok(ret, "VerQueryValueW error %u\n", GetLastError());
699     ok(len == 0, "VerQueryValueW returned %u, expected 0\n", len);
700     todo_wine
701     ok(p == preparedbuffer + 0x7e, "p was %p, expected %p\n", p, preparedbuffer + 0x7e);
702 }
703 
704 static void test_extra_block(void)
705 {
706     WORD extra_block[] = {
707         72, 0, 0, 'W', 'i', 'n', 'e', 'T', 'e', 's', 't', '\0',
708         24, 4, 0, 'B', 'i', 'n', 'a', 'r', 'y', '\0', 0xbeef, 0xdead,
709         24, 4, 1, 'T', 'e', 'x', 't', '\0', 'B', '-', ')', '\0',
710     };
711     char buf[MAX_PATH];
712     UINT len, ret;
713     ULONG w;
714     char *ver, *p;
715     WORD *length;
716 
717     ret = GetModuleFileNameA(NULL, buf, sizeof(buf));
718     ok(ret, "GetModuleFileNameA failed\n");
719 
720     len = GetFileVersionInfoSizeA(buf, NULL);
721     ok(len, "GetFileVersionInfoSizeA(%s) error %u\n", buf, GetLastError());
722 
723     ver = HeapAlloc(GetProcessHeap(), 0, len + sizeof(extra_block) * 2);
724     ok(ver != NULL, "Can't allocate memory\n");
725 
726     ret = GetFileVersionInfoA(buf, 0, len, ver);
727     ok(ret, "GetFileVersionInfoA error %u\n", GetLastError());
728 
729     /* forge the string table, as windres dislike an extra block */
730     length = (WORD *)ver; /* see VS_VERSION_INFO_STRUCT32 for details */
731     memcpy(ver + *length, extra_block, sizeof(extra_block));
732     *length += sizeof(extra_block);
733 
734     p = (char *)0xdeadbeef;
735     len = 0xdeadbeef;
736     w = 0xdeadbeef;
737     ret = VerQueryValueA(ver, "WineTest\\Binary", (LPVOID*)&p, &len);
738     ok(ret, "VerQueryValue error %u\n", GetLastError());
739     ok(len == 4, "VerQueryValue returned %u, expected 4\n", len);
740     ok(p != (char *)0xdeadbeef, "not expected 0xdeadbeef\n");
741     ok(memcmp(p, &w, sizeof(w)) == 0, "got 0x%08x, expected 0x%08x\n", *(PULONG)p, w);
742 
743     p = (char *)0xdeadbeef;
744     len = 0xdeadbeef;
745     ret = VerQueryValueA(ver, "WineTest\\Text", (LPVOID*)&p, &len);
746     ok(ret, "VerQueryValue error %u\n", GetLastError());
747     ok(len == 4, "VerQueryValue returned %u, expected 4\n", len);
748     ok(p != (char *)0xdeadbeef, "not expected 0xdeadbeef\n");
749     ok(strcmp(p, "B-)") == 0, "got '%s', expected '%s'\n", p, "B-)");
750 
751     HeapFree(GetProcessHeap(), 0, ver);
752 }
753 
754 static void test_GetFileVersionInfoEx(void)
755 {
756     char *ver, *p;
757     BOOL ret;
758     UINT size, translation, i;
759     HMODULE mod;
760     BOOL (WINAPI *pGetFileVersionInfoExW)(DWORD, LPCWSTR, DWORD, DWORD, LPVOID);
761     DWORD (WINAPI *pGetFileVersionInfoSizeExW)(DWORD, LPCWSTR, LPDWORD);
762     const LANGID lang = GetUserDefaultUILanguage();
763     const LANGID english = MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT);
764     const WORD unicode = 1200; /* = UNICODE */
765     const WCHAR kernel32W[] = {'k','e','r','n','e','l','3','2','.','d','l','l',0};
766     const DWORD test_flags[] = {
767         0, FILE_VER_GET_LOCALISED, FILE_VER_GET_NEUTRAL,
768         FILE_VER_GET_LOCALISED | FILE_VER_GET_NEUTRAL,
769         0xdeadbeef, /* invalid value (ignored) */
770     };
771     char desc[MAX_PATH];
772 
773     mod = GetModuleHandleA("kernel32.dll");
774     assert(mod);
775 
776     if (!FindResourceExA(mod, (LPCSTR)RT_VERSION, (LPCSTR)VS_VERSION_INFO, lang) &&
777         !FindResourceExA(mod, (LPCSTR)RT_VERSION, (LPCSTR)VS_VERSION_INFO,
778                          MAKELANGID(PRIMARYLANGID(lang),SUBLANG_NEUTRAL)))
779     {
780         skip("Translation is not available\n");
781         return;
782     }
783 
784     size = GetFileVersionInfoSizeW(kernel32W, NULL);
785     ok(size, "GetFileVersionInfoSize(kernel32) error %u\n", GetLastError());
786 
787     ver = HeapAlloc(GetProcessHeap(), 0, size);
788     assert(ver);
789 
790     ret = GetFileVersionInfoW(kernel32W, 0, size, ver);
791     ok(ret, "GetFileVersionInfo error %u\n", GetLastError());
792 
793     ret = VerQueryValueA(ver, "\\VarFileInfo\\Translation", (void **)&p, &size);
794     translation = *(UINT *)p;
795     ok(ret, "VerQueryValue error %u\n", GetLastError());
796     ok(size == 4, "VerQueryValue returned %u, expected 4\n", size);
797 
798     /* test default version resource */
799     ok(LOWORD(translation) == lang, "got %u, expected lang is %u\n",
800        LOWORD(translation), lang);
801     ok(HIWORD(translation) == unicode, "got %u, expected codepage is %u\n",
802        HIWORD(translation), unicode);
803 
804     HeapFree(GetProcessHeap(), 0, ver);
805 
806     mod = GetModuleHandleA("version.dll");
807     assert(mod);
808 
809     /* prefer W-version as A-version is not available on Windows 7 */
810     pGetFileVersionInfoExW = (void *)GetProcAddress(mod, "GetFileVersionInfoExW");
811     pGetFileVersionInfoSizeExW = (void *)GetProcAddress(mod, "GetFileVersionInfoSizeExW");
812     if (!pGetFileVersionInfoExW && !pGetFileVersionInfoSizeExW)
813     {
814         win_skip("GetFileVersionInfoEx family is not available\n");
815         return;
816     }
817 
818     for (i = 0; i < ARRAY_SIZE(test_flags); i++)
819     {
820         size = pGetFileVersionInfoSizeExW(test_flags[i], kernel32W, NULL);
821         ok(size, "[%u] GetFileVersionInfoSizeEx(kernel32) error %u\n", i, GetLastError());
822 
823         ver = HeapAlloc(GetProcessHeap(), 0, size);
824         assert(ver);
825 
826         ret = pGetFileVersionInfoExW(test_flags[i], kernel32W, 0, size, ver);
827         ok(ret, "[%u] GetFileVersionInfoEx error %u\n", i, GetLastError());
828 
829         ret = VerQueryValueA(ver, "\\VarFileInfo\\Translation", (void **)&p, &size);
830         ok(ret, "[%u] VerQueryValue error %u\n", i, GetLastError());
831         ok(size == 4, "[%u] VerQueryValue returned %u, expected 4\n", i, size);
832         translation = *(UINT *)p;
833 
834         /* test MUI version resource */
835         if (test_flags[i] & FILE_VER_GET_LOCALISED)
836             ok(LOWORD(translation) == lang, "[%u] got %u, expected lang is %u\n",
837                i, LOWORD(translation), lang);
838         else
839             ok(LOWORD(translation) == english, "[%u] got %u, expected lang is %u\n",
840                i, LOWORD(translation), english);
841         ok(HIWORD(translation) == unicode, "[%u] got %u, expected codepage is %u\n",
842            i, HIWORD(translation), unicode);
843 
844         /* test string info using translation info */
845         size = 0;
846         sprintf(desc, "\\StringFileInfo\\%04x%04x\\FileDescription",
847                 LOWORD(translation), HIWORD(translation));
848         ret = VerQueryValueA(ver, desc, (void **)&p, &size);
849         ok(ret, "[%u] VerQueryValue error %u\n", i, GetLastError());
850         ok(size == strlen(p) + 1, "[%u] VerQueryValue returned %u\n", i, size);
851 
852         HeapFree(GetProcessHeap(), 0, ver);
853     }
854 
855     return;
856 }
857 
858 START_TEST(info)
859 {
860     test_info_size();
861     test_info();
862     test_32bit_win();
863     test_VerQueryValueA();
864     test_VerQueryValue_InvalidLength();
865     test_extra_block();
866     test_GetFileVersionInfoEx();
867 }
868