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