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 < sizeof(value_name)/sizeof(value_name[0]); 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_extra_block(void)
582 {
583     WORD extra_block[] = {
584         72, 0, 0, 'W', 'i', 'n', 'e', 'T', 'e', 's', 't', '\0',
585         24, 4, 0, 'B', 'i', 'n', 'a', 'r', 'y', '\0', 0xbeef, 0xdead,
586         24, 4, 1, 'T', 'e', 'x', 't', '\0', 'B', '-', ')', '\0',
587     };
588     char buf[MAX_PATH];
589     UINT len, ret;
590     ULONG w;
591     char *ver, *p;
592     WORD *length;
593 
594     ret = GetModuleFileNameA(NULL, buf, sizeof(buf));
595     ok(ret, "GetModuleFileNameA failed\n");
596 
597     len = GetFileVersionInfoSizeA(buf, NULL);
598     ok(len, "GetFileVersionInfoSizeA(%s) error %u\n", buf, GetLastError());
599 
600     ver = HeapAlloc(GetProcessHeap(), 0, len + sizeof(extra_block) * 2);
601     ok(ver != NULL, "Can't allocate memory\n");
602 
603     ret = GetFileVersionInfoA(buf, 0, len, ver);
604     ok(ret, "GetFileVersionInfoA error %u\n", GetLastError());
605 
606     /* forge the string table, as windres dislike an extra block */
607     length = (WORD *)ver; /* see VS_VERSION_INFO_STRUCT32 for details */
608     memcpy(ver + *length, extra_block, sizeof(extra_block));
609     *length += sizeof(extra_block);
610 
611     p = (char *)0xdeadbeef;
612     len = 0xdeadbeef;
613     w = 0xdeadbeef;
614     ret = VerQueryValueA(ver, "WineTest\\Binary", (LPVOID*)&p, &len);
615     ok(ret, "VerQueryValue error %u\n", GetLastError());
616     ok(len == 4, "VerQueryValue returned %u, expected 4\n", len);
617     ok(p != (char *)0xdeadbeef, "not expected 0xdeadbeef\n");
618     ok(memcmp(p, &w, sizeof(w)) == 0, "got 0x%08x, expected 0x%08x\n", *(PULONG)p, w);
619 
620     p = (char *)0xdeadbeef;
621     len = 0xdeadbeef;
622     ret = VerQueryValueA(ver, "WineTest\\Text", (LPVOID*)&p, &len);
623     ok(ret, "VerQueryValue error %u\n", GetLastError());
624     ok(len == 4, "VerQueryValue returned %u, expected 4\n", len);
625     ok(p != (char *)0xdeadbeef, "not expected 0xdeadbeef\n");
626     ok(strcmp(p, "B-)") == 0, "got '%s', expected '%s'\n", p, "B-)");
627 
628     HeapFree(GetProcessHeap(), 0, ver);
629 }
630 
631 static void test_GetFileVersionInfoEx(void)
632 {
633     char *ver, *p;
634     BOOL ret;
635     UINT size, translation, i;
636     HMODULE mod;
637     BOOL (WINAPI *pGetFileVersionInfoExW)(DWORD, LPCWSTR, DWORD, DWORD, LPVOID);
638     DWORD (WINAPI *pGetFileVersionInfoSizeExW)(DWORD, LPCWSTR, LPDWORD);
639     const LANGID lang = GetUserDefaultUILanguage();
640     const LANGID english = MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT);
641     const WORD unicode = 1200; /* = UNICODE */
642     const WCHAR kernel32W[] = {'k','e','r','n','e','l','3','2','.','d','l','l',0};
643     const DWORD test_flags[] = {
644         0, FILE_VER_GET_LOCALISED, FILE_VER_GET_NEUTRAL,
645         FILE_VER_GET_LOCALISED | FILE_VER_GET_NEUTRAL,
646         0xdeadbeef, /* invalid value (ignored) */
647     };
648     char desc[MAX_PATH];
649 
650     mod = GetModuleHandleA("kernel32.dll");
651     assert(mod);
652 
653     if (!FindResourceExA(mod, (LPCSTR)RT_VERSION, (LPCSTR)VS_VERSION_INFO, lang) &&
654         !FindResourceExA(mod, (LPCSTR)RT_VERSION, (LPCSTR)VS_VERSION_INFO,
655                          MAKELANGID(PRIMARYLANGID(lang),SUBLANG_NEUTRAL)))
656     {
657         skip("Translation is not available\n");
658         return;
659     }
660 
661     size = GetFileVersionInfoSizeW(kernel32W, NULL);
662     ok(size, "GetFileVersionInfoSize(kernel32) error %u\n", GetLastError());
663 
664     ver = HeapAlloc(GetProcessHeap(), 0, size);
665     assert(ver);
666 
667     ret = GetFileVersionInfoW(kernel32W, 0, size, ver);
668     ok(ret, "GetFileVersionInfo error %u\n", GetLastError());
669 
670     ret = VerQueryValueA(ver, "\\VarFileInfo\\Translation", (void **)&p, &size);
671     translation = *(UINT *)p;
672     ok(ret, "VerQueryValue error %u\n", GetLastError());
673     ok(size == 4, "VerQueryValue returned %u, expected 4\n", size);
674 
675     /* test default version resource */
676     ok(LOWORD(translation) == lang, "got %u, expected lang is %u\n",
677        LOWORD(translation), lang);
678     ok(HIWORD(translation) == unicode, "got %u, expected codepage is %u\n",
679        HIWORD(translation), unicode);
680 
681     HeapFree(GetProcessHeap(), 0, ver);
682 
683     mod = GetModuleHandleA("version.dll");
684     assert(mod);
685 
686     /* prefer W-version as A-version is not available on Windows 7 */
687     pGetFileVersionInfoExW = (void *)GetProcAddress(mod, "GetFileVersionInfoExW");
688     pGetFileVersionInfoSizeExW = (void *)GetProcAddress(mod, "GetFileVersionInfoSizeExW");
689     if (!pGetFileVersionInfoExW && !pGetFileVersionInfoSizeExW)
690     {
691         win_skip("GetFileVersionInfoEx family is not available\n");
692         return;
693     }
694 
695     for (i = 0; i < sizeof(test_flags)/sizeof(test_flags[0]); i++)
696     {
697         size = pGetFileVersionInfoSizeExW(test_flags[i], kernel32W, NULL);
698         ok(size, "[%u] GetFileVersionInfoSizeEx(kernel32) error %u\n", i, GetLastError());
699 
700         ver = HeapAlloc(GetProcessHeap(), 0, size);
701         assert(ver);
702 
703         ret = pGetFileVersionInfoExW(test_flags[i], kernel32W, 0, size, ver);
704         ok(ret, "[%u] GetFileVersionInfoEx error %u\n", i, GetLastError());
705 
706         ret = VerQueryValueA(ver, "\\VarFileInfo\\Translation", (void **)&p, &size);
707         ok(ret, "[%u] VerQueryValue error %u\n", i, GetLastError());
708         ok(size == 4, "[%u] VerQueryValue returned %u, expected 4\n", i, size);
709         translation = *(UINT *)p;
710 
711         /* test MUI version resource */
712         if (test_flags[i] & FILE_VER_GET_LOCALISED)
713             ok(LOWORD(translation) == lang, "[%u] got %u, expected lang is %u\n",
714                i, LOWORD(translation), lang);
715         else
716             ok(LOWORD(translation) == english, "[%u] got %u, expected lang is %u\n",
717                i, LOWORD(translation), english);
718         ok(HIWORD(translation) == unicode, "[%u] got %u, expected codepage is %u\n",
719            i, HIWORD(translation), unicode);
720 
721         /* test string info using translation info */
722         size = 0;
723         sprintf(desc, "\\StringFileInfo\\%04x%04x\\FileDescription",
724                 LOWORD(translation), HIWORD(translation));
725         ret = VerQueryValueA(ver, desc, (void **)&p, &size);
726         ok(ret, "[%u] VerQueryValue error %u\n", i, GetLastError());
727         ok(size == strlen(p) + 1, "[%u] VerQueryValue returned %u\n", i, size);
728 
729         HeapFree(GetProcessHeap(), 0, ver);
730     }
731 
732     return;
733 }
734 
735 START_TEST(info)
736 {
737     test_info_size();
738     test_info();
739     test_32bit_win();
740     test_VerQueryValueA();
741     test_extra_block();
742     test_GetFileVersionInfoEx();
743 }
744