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