1 /* 2 * Implementation of VERSION.DLL 3 * 4 * Copyright 1996,1997 Marcus Meissner 5 * Copyright 1997 David Cuthbert 6 * Copyright 1999 Ulrich Weigand 7 * Copyright 2005 Paul Vriens 8 * 9 * This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Lesser General Public 11 * License as published by the Free Software Foundation; either 12 * version 2.1 of the License, or (at your option) any later version. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Lesser General Public License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public 20 * License along with this library; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 22 * 23 */ 24 25 #include <stdarg.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include <stdio.h> 29 30 #include <sys/types.h> 31 32 #define NONAMELESSUNION 33 #define NONAMELESSSTRUCT 34 #include "windef.h" 35 #include "winbase.h" 36 #include "winver.h" 37 #include "winuser.h" 38 #include "winnls.h" 39 #include "wine/winternl.h" 40 #include "lzexpand.h" 41 #include "winerror.h" 42 #include "wine/debug.h" 43 44 45 WINE_DEFAULT_DEBUG_CHANNEL(ver); 46 47 typedef struct 48 { 49 WORD offset; 50 WORD length; 51 WORD flags; 52 WORD id; 53 WORD handle; 54 WORD usage; 55 } NE_NAMEINFO; 56 57 typedef struct 58 { 59 WORD type_id; 60 WORD count; 61 DWORD resloader; 62 } NE_TYPEINFO; 63 64 /********************************************************************** 65 * find_entry_by_id 66 * 67 * Find an entry by id in a resource directory 68 * Copied from loader/pe_resource.c 69 */ 70 static const IMAGE_RESOURCE_DIRECTORY *find_entry_by_id( const IMAGE_RESOURCE_DIRECTORY *dir, 71 WORD id, const void *root ) 72 { 73 const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry; 74 int min, max, pos; 75 76 entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1); 77 min = dir->NumberOfNamedEntries; 78 max = min + dir->NumberOfIdEntries - 1; 79 while (min <= max) 80 { 81 pos = (min + max) / 2; 82 if (entry[pos].u.Id == id) 83 return (const IMAGE_RESOURCE_DIRECTORY *)((const char *)root + entry[pos].u2.s2.OffsetToDirectory); 84 if (entry[pos].u.Id > id) max = pos - 1; 85 else min = pos + 1; 86 } 87 return NULL; 88 } 89 90 91 /********************************************************************** 92 * find_entry_default 93 * 94 * Find a default entry in a resource directory 95 * Copied from loader/pe_resource.c 96 */ 97 static const IMAGE_RESOURCE_DIRECTORY *find_entry_default( const IMAGE_RESOURCE_DIRECTORY *dir, 98 const void *root ) 99 { 100 const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry; 101 102 entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1); 103 return (const IMAGE_RESOURCE_DIRECTORY *)((const char *)root + entry->u2.s2.OffsetToDirectory); 104 } 105 106 107 /********************************************************************** 108 * push_language 109 * 110 * push a language onto the list of languages to try 111 */ 112 static inline int push_language( WORD *list, int pos, WORD lang ) 113 { 114 int i; 115 for (i = 0; i < pos; i++) if (list[i] == lang) return pos; 116 list[pos++] = lang; 117 return pos; 118 } 119 120 121 /********************************************************************** 122 * find_entry_language 123 */ 124 static const IMAGE_RESOURCE_DIRECTORY *find_entry_language( const IMAGE_RESOURCE_DIRECTORY *dir, 125 const void *root, DWORD flags ) 126 { 127 const IMAGE_RESOURCE_DIRECTORY *ret; 128 WORD list[9]; 129 int i, pos = 0; 130 131 if (flags & FILE_VER_GET_LOCALISED) 132 { 133 /* cf. LdrFindResource_U */ 134 pos = push_language( list, pos, MAKELANGID( LANG_NEUTRAL, SUBLANG_NEUTRAL ) ); 135 pos = push_language( list, pos, LANGIDFROMLCID( NtCurrentTeb()->CurrentLocale ) ); 136 pos = push_language( list, pos, GetUserDefaultLangID() ); 137 pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(GetUserDefaultLangID()), SUBLANG_NEUTRAL )); 138 pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(GetUserDefaultLangID()), SUBLANG_DEFAULT )); 139 pos = push_language( list, pos, GetSystemDefaultLangID() ); 140 pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(GetSystemDefaultLangID()), SUBLANG_NEUTRAL )); 141 pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(GetSystemDefaultLangID()), SUBLANG_DEFAULT )); 142 pos = push_language( list, pos, MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT ) ); 143 } 144 else 145 { 146 /* FIXME: resolve LN file here */ 147 pos = push_language( list, pos, MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT ) ); 148 } 149 150 for (i = 0; i < pos; i++) if ((ret = find_entry_by_id( dir, list[i], root ))) return ret; 151 return find_entry_default( dir, root ); 152 } 153 154 155 /*********************************************************************** 156 * read_xx_header [internal] 157 */ 158 static int read_xx_header( HFILE lzfd ) 159 { 160 IMAGE_DOS_HEADER mzh; 161 char magic[3]; 162 163 LZSeek( lzfd, 0, SEEK_SET ); 164 if ( sizeof(mzh) != LZRead( lzfd, (LPSTR)&mzh, sizeof(mzh) ) ) 165 return 0; 166 if ( mzh.e_magic != IMAGE_DOS_SIGNATURE ) 167 { 168 if (!memcmp( &mzh, "\177ELF", 4 )) return 1; /* ELF */ 169 if (*(UINT *)&mzh == 0xfeedface || *(UINT *)&mzh == 0xcefaedfe) return 1; /* Mach-O */ 170 return 0; 171 } 172 173 LZSeek( lzfd, mzh.e_lfanew, SEEK_SET ); 174 if ( 2 != LZRead( lzfd, magic, 2 ) ) 175 return 0; 176 177 LZSeek( lzfd, mzh.e_lfanew, SEEK_SET ); 178 179 if ( magic[0] == 'N' && magic[1] == 'E' ) 180 return IMAGE_OS2_SIGNATURE; 181 if ( magic[0] == 'P' && magic[1] == 'E' ) 182 return IMAGE_NT_SIGNATURE; 183 184 magic[2] = '\0'; 185 WARN("Can't handle %s files.\n", magic ); 186 return 0; 187 } 188 189 /*********************************************************************** 190 * find_ne_resource [internal] 191 */ 192 static BOOL find_ne_resource( HFILE lzfd, DWORD *resLen, DWORD *resOff ) 193 { 194 const WORD typeid = VS_FILE_INFO | 0x8000; 195 const WORD resid = VS_VERSION_INFO | 0x8000; 196 IMAGE_OS2_HEADER nehd; 197 NE_TYPEINFO *typeInfo; 198 NE_NAMEINFO *nameInfo; 199 DWORD nehdoffset; 200 LPBYTE resTab; 201 DWORD resTabSize; 202 int count; 203 204 /* Read in NE header */ 205 nehdoffset = LZSeek( lzfd, 0, SEEK_CUR ); 206 if ( sizeof(nehd) != LZRead( lzfd, (LPSTR)&nehd, sizeof(nehd) ) ) return FALSE; 207 208 resTabSize = nehd.ne_restab - nehd.ne_rsrctab; 209 if ( !resTabSize ) 210 { 211 TRACE("No resources in NE dll\n" ); 212 return FALSE; 213 } 214 215 /* Read in resource table */ 216 resTab = HeapAlloc( GetProcessHeap(), 0, resTabSize ); 217 if ( !resTab ) return FALSE; 218 219 LZSeek( lzfd, nehd.ne_rsrctab + nehdoffset, SEEK_SET ); 220 if ( resTabSize != LZRead( lzfd, (char*)resTab, resTabSize ) ) 221 { 222 HeapFree( GetProcessHeap(), 0, resTab ); 223 return FALSE; 224 } 225 226 /* Find resource */ 227 typeInfo = (NE_TYPEINFO *)(resTab + 2); 228 while (typeInfo->type_id) 229 { 230 if (typeInfo->type_id == typeid) goto found_type; 231 typeInfo = (NE_TYPEINFO *)((char *)(typeInfo + 1) + 232 typeInfo->count * sizeof(NE_NAMEINFO)); 233 } 234 TRACE("No typeid entry found\n" ); 235 HeapFree( GetProcessHeap(), 0, resTab ); 236 return FALSE; 237 238 found_type: 239 nameInfo = (NE_NAMEINFO *)(typeInfo + 1); 240 241 for (count = typeInfo->count; count > 0; count--, nameInfo++) 242 if (nameInfo->id == resid) goto found_name; 243 244 TRACE("No resid entry found\n" ); 245 HeapFree( GetProcessHeap(), 0, resTab ); 246 return FALSE; 247 248 found_name: 249 /* Return resource data */ 250 if ( resLen ) *resLen = nameInfo->length << *(WORD *)resTab; 251 if ( resOff ) *resOff = nameInfo->offset << *(WORD *)resTab; 252 253 HeapFree( GetProcessHeap(), 0, resTab ); 254 return TRUE; 255 } 256 257 /*********************************************************************** 258 * find_pe_resource [internal] 259 */ 260 static BOOL find_pe_resource( HFILE lzfd, DWORD *resLen, DWORD *resOff, DWORD flags ) 261 { 262 union 263 { 264 IMAGE_NT_HEADERS32 nt32; 265 IMAGE_NT_HEADERS64 nt64; 266 } pehd; 267 DWORD pehdoffset; 268 PIMAGE_DATA_DIRECTORY resDataDir; 269 PIMAGE_SECTION_HEADER sections; 270 LPBYTE resSection; 271 DWORD section_size, data_size; 272 const void *resDir; 273 const IMAGE_RESOURCE_DIRECTORY *resPtr; 274 const IMAGE_RESOURCE_DATA_ENTRY *resData; 275 int i, len, nSections; 276 BOOL ret = FALSE; 277 278 /* Read in PE header */ 279 pehdoffset = LZSeek( lzfd, 0, SEEK_CUR ); 280 len = LZRead( lzfd, (LPSTR)&pehd, sizeof(pehd) ); 281 if (len < sizeof(pehd.nt32.FileHeader)) return FALSE; 282 if (len < sizeof(pehd)) memset( (char *)&pehd + len, 0, sizeof(pehd) - len ); 283 284 switch (pehd.nt32.OptionalHeader.Magic) 285 { 286 case IMAGE_NT_OPTIONAL_HDR32_MAGIC: 287 resDataDir = pehd.nt32.OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_RESOURCE; 288 break; 289 case IMAGE_NT_OPTIONAL_HDR64_MAGIC: 290 resDataDir = pehd.nt64.OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_RESOURCE; 291 break; 292 default: 293 return FALSE; 294 } 295 296 if ( !resDataDir->Size ) 297 { 298 TRACE("No resources in PE dll\n" ); 299 return FALSE; 300 } 301 302 /* Read in section table */ 303 nSections = pehd.nt32.FileHeader.NumberOfSections; 304 sections = HeapAlloc( GetProcessHeap(), 0, 305 nSections * sizeof(IMAGE_SECTION_HEADER) ); 306 if ( !sections ) return FALSE; 307 308 len = FIELD_OFFSET( IMAGE_NT_HEADERS32, OptionalHeader ) + pehd.nt32.FileHeader.SizeOfOptionalHeader; 309 LZSeek( lzfd, pehdoffset + len, SEEK_SET ); 310 311 if ( nSections * sizeof(IMAGE_SECTION_HEADER) != 312 LZRead( lzfd, (LPSTR)sections, nSections * sizeof(IMAGE_SECTION_HEADER) ) ) 313 { 314 HeapFree( GetProcessHeap(), 0, sections ); 315 return FALSE; 316 } 317 318 /* Find resource section */ 319 for ( i = 0; i < nSections; i++ ) 320 if ( resDataDir->VirtualAddress >= sections[i].VirtualAddress 321 && resDataDir->VirtualAddress < sections[i].VirtualAddress + 322 sections[i].SizeOfRawData ) 323 break; 324 325 if ( i == nSections ) 326 { 327 HeapFree( GetProcessHeap(), 0, sections ); 328 TRACE("Couldn't find resource section\n" ); 329 return FALSE; 330 } 331 332 /* Read in resource section */ 333 data_size = sections[i].SizeOfRawData; 334 section_size = max( data_size, sections[i].Misc.VirtualSize ); 335 resSection = HeapAlloc( GetProcessHeap(), 0, section_size ); 336 if ( !resSection ) 337 { 338 HeapFree( GetProcessHeap(), 0, sections ); 339 return FALSE; 340 } 341 342 LZSeek( lzfd, sections[i].PointerToRawData, SEEK_SET ); 343 if (data_size != LZRead( lzfd, (char*)resSection, data_size )) goto done; 344 if (data_size < section_size) memset( (char *)resSection + data_size, 0, section_size - data_size ); 345 346 /* Find resource */ 347 resDir = resSection + (resDataDir->VirtualAddress - sections[i].VirtualAddress); 348 349 resPtr = resDir; 350 resPtr = find_entry_by_id( resPtr, VS_FILE_INFO, resDir ); 351 if ( !resPtr ) 352 { 353 TRACE("No typeid entry found\n" ); 354 goto done; 355 } 356 resPtr = find_entry_by_id( resPtr, VS_VERSION_INFO, resDir ); 357 if ( !resPtr ) 358 { 359 TRACE("No resid entry found\n" ); 360 goto done; 361 } 362 resPtr = find_entry_language( resPtr, resDir, flags ); 363 if ( !resPtr ) 364 { 365 TRACE("No default language entry found\n" ); 366 goto done; 367 } 368 369 /* Find resource data section */ 370 resData = (const IMAGE_RESOURCE_DATA_ENTRY*)resPtr; 371 for ( i = 0; i < nSections; i++ ) 372 if ( resData->OffsetToData >= sections[i].VirtualAddress 373 && resData->OffsetToData < sections[i].VirtualAddress + 374 sections[i].SizeOfRawData ) 375 break; 376 377 if ( i == nSections ) 378 { 379 TRACE("Couldn't find resource data section\n" ); 380 goto done; 381 } 382 383 /* Return resource data */ 384 if ( resLen ) *resLen = resData->Size; 385 if ( resOff ) *resOff = resData->OffsetToData - sections[i].VirtualAddress 386 + sections[i].PointerToRawData; 387 ret = TRUE; 388 389 done: 390 HeapFree( GetProcessHeap(), 0, resSection ); 391 HeapFree( GetProcessHeap(), 0, sections ); 392 return ret; 393 } 394 395 396 /*********************************************************************** 397 * find_version_resource [internal] 398 */ 399 static DWORD find_version_resource( HFILE lzfd, DWORD *reslen, DWORD *offset, DWORD flags ) 400 { 401 DWORD magic = read_xx_header( lzfd ); 402 403 switch (magic) 404 { 405 case IMAGE_OS2_SIGNATURE: 406 if (!find_ne_resource( lzfd, reslen, offset )) magic = 0; 407 break; 408 case IMAGE_NT_SIGNATURE: 409 if (!find_pe_resource( lzfd, reslen, offset, flags )) magic = 0; 410 break; 411 } 412 return magic; 413 } 414 415 /****************************************************************************** 416 * 417 * This function will print via standard TRACE, debug info regarding 418 * the file info structure vffi. 419 * 15-Feb-1998 Dimitrie Paun (dimi@cs.toronto.edu) 420 * Added this function to clean up the code. 421 * 422 *****************************************************************************/ 423 static void print_vffi_debug(const VS_FIXEDFILEINFO *vffi) 424 { 425 BOOL versioned_printer = FALSE; 426 427 if((vffi->dwFileType == VFT_DLL) || (vffi->dwFileType == VFT_DRV)) 428 { 429 if(vffi->dwFileSubtype == VFT2_DRV_VERSIONED_PRINTER) 430 /* this is documented for newer w2k Drivers and up */ 431 versioned_printer = TRUE; 432 else if( (vffi->dwFileSubtype == VFT2_DRV_PRINTER) && 433 (vffi->dwFileVersionMS != vffi->dwProductVersionMS) && 434 (vffi->dwFileVersionMS > 0) && 435 (vffi->dwFileVersionMS <= 3) ) 436 /* found this on NT 3.51, NT4.0 and old w2k Drivers */ 437 versioned_printer = TRUE; 438 } 439 440 TRACE("structversion=%u.%u, ", 441 HIWORD(vffi->dwStrucVersion),LOWORD(vffi->dwStrucVersion)); 442 if(versioned_printer) 443 { 444 WORD mode = LOWORD(vffi->dwFileVersionMS); 445 WORD ver_rev = HIWORD(vffi->dwFileVersionLS); 446 TRACE("fileversion=%u.%u.%u.%u (%s.major.minor.release), ", 447 (vffi->dwFileVersionMS), 448 HIBYTE(ver_rev), LOBYTE(ver_rev), LOWORD(vffi->dwFileVersionLS), 449 (mode == 3) ? "Usermode" : ((mode <= 2) ? "Kernelmode" : "?") ); 450 } 451 else 452 { 453 TRACE("fileversion=%u.%u.%u.%u, ", 454 HIWORD(vffi->dwFileVersionMS),LOWORD(vffi->dwFileVersionMS), 455 HIWORD(vffi->dwFileVersionLS),LOWORD(vffi->dwFileVersionLS)); 456 } 457 TRACE("productversion=%u.%u.%u.%u\n", 458 HIWORD(vffi->dwProductVersionMS),LOWORD(vffi->dwProductVersionMS), 459 HIWORD(vffi->dwProductVersionLS),LOWORD(vffi->dwProductVersionLS)); 460 461 TRACE("flagmask=0x%x, flags=0x%x %s%s%s%s%s%s\n", 462 vffi->dwFileFlagsMask, vffi->dwFileFlags, 463 (vffi->dwFileFlags & VS_FF_DEBUG) ? "DEBUG," : "", 464 (vffi->dwFileFlags & VS_FF_PRERELEASE) ? "PRERELEASE," : "", 465 (vffi->dwFileFlags & VS_FF_PATCHED) ? "PATCHED," : "", 466 (vffi->dwFileFlags & VS_FF_PRIVATEBUILD) ? "PRIVATEBUILD," : "", 467 (vffi->dwFileFlags & VS_FF_INFOINFERRED) ? "INFOINFERRED," : "", 468 (vffi->dwFileFlags & VS_FF_SPECIALBUILD) ? "SPECIALBUILD," : ""); 469 470 TRACE("("); 471 472 TRACE("OS=0x%x.0x%x ", HIWORD(vffi->dwFileOS), LOWORD(vffi->dwFileOS)); 473 474 switch (vffi->dwFileOS&0xFFFF0000) 475 { 476 case VOS_DOS:TRACE("DOS,");break; 477 case VOS_OS216:TRACE("OS/2-16,");break; 478 case VOS_OS232:TRACE("OS/2-32,");break; 479 case VOS_NT:TRACE("NT,");break; 480 case VOS_UNKNOWN: 481 default: 482 TRACE("UNKNOWN(0x%x),",vffi->dwFileOS&0xFFFF0000);break; 483 } 484 485 switch (LOWORD(vffi->dwFileOS)) 486 { 487 case VOS__BASE:TRACE("BASE");break; 488 case VOS__WINDOWS16:TRACE("WIN16");break; 489 case VOS__WINDOWS32:TRACE("WIN32");break; 490 case VOS__PM16:TRACE("PM16");break; 491 case VOS__PM32:TRACE("PM32");break; 492 default: 493 TRACE("UNKNOWN(0x%x)",LOWORD(vffi->dwFileOS));break; 494 } 495 496 TRACE(")\n"); 497 498 switch (vffi->dwFileType) 499 { 500 case VFT_APP:TRACE("filetype=APP");break; 501 case VFT_DLL: 502 TRACE("filetype=DLL"); 503 if(vffi->dwFileSubtype != 0) 504 { 505 if(versioned_printer) /* NT3.x/NT4.0 or old w2k Driver */ 506 TRACE(",PRINTER"); 507 TRACE(" (subtype=0x%x)", vffi->dwFileSubtype); 508 } 509 break; 510 case VFT_DRV: 511 TRACE("filetype=DRV,"); 512 switch(vffi->dwFileSubtype) 513 { 514 case VFT2_DRV_PRINTER:TRACE("PRINTER");break; 515 case VFT2_DRV_KEYBOARD:TRACE("KEYBOARD");break; 516 case VFT2_DRV_LANGUAGE:TRACE("LANGUAGE");break; 517 case VFT2_DRV_DISPLAY:TRACE("DISPLAY");break; 518 case VFT2_DRV_MOUSE:TRACE("MOUSE");break; 519 case VFT2_DRV_NETWORK:TRACE("NETWORK");break; 520 case VFT2_DRV_SYSTEM:TRACE("SYSTEM");break; 521 case VFT2_DRV_INSTALLABLE:TRACE("INSTALLABLE");break; 522 case VFT2_DRV_SOUND:TRACE("SOUND");break; 523 case VFT2_DRV_COMM:TRACE("COMM");break; 524 case VFT2_DRV_INPUTMETHOD:TRACE("INPUTMETHOD");break; 525 case VFT2_DRV_VERSIONED_PRINTER:TRACE("VERSIONED_PRINTER");break; 526 case VFT2_UNKNOWN: 527 default: 528 TRACE("UNKNOWN(0x%x)",vffi->dwFileSubtype);break; 529 } 530 break; 531 case VFT_FONT: 532 TRACE("filetype=FONT,"); 533 switch (vffi->dwFileSubtype) 534 { 535 case VFT2_FONT_RASTER:TRACE("RASTER");break; 536 case VFT2_FONT_VECTOR:TRACE("VECTOR");break; 537 case VFT2_FONT_TRUETYPE:TRACE("TRUETYPE");break; 538 default:TRACE("UNKNOWN(0x%x)",vffi->dwFileSubtype);break; 539 } 540 break; 541 case VFT_VXD:TRACE("filetype=VXD");break; 542 case VFT_STATIC_LIB:TRACE("filetype=STATIC_LIB");break; 543 case VFT_UNKNOWN: 544 default: 545 TRACE("filetype=Unknown(0x%x)",vffi->dwFileType);break; 546 } 547 548 TRACE("\n"); 549 TRACE("filedate=0x%x.0x%x\n",vffi->dwFileDateMS,vffi->dwFileDateLS); 550 } 551 552 /*********************************************************************** 553 * Version Info Structure 554 */ 555 556 typedef struct 557 { 558 WORD wLength; 559 WORD wValueLength; 560 CHAR szKey[1]; 561 #if 0 /* variable length structure */ 562 /* DWORD aligned */ 563 BYTE Value[]; 564 /* DWORD aligned */ 565 VS_VERSION_INFO_STRUCT16 Children[]; 566 #endif 567 } VS_VERSION_INFO_STRUCT16; 568 569 typedef struct 570 { 571 WORD wLength; 572 WORD wValueLength; 573 WORD wType; /* 1:Text, 0:Binary */ 574 WCHAR szKey[1]; 575 #if 0 /* variable length structure */ 576 /* DWORD aligned */ 577 BYTE Value[]; 578 /* DWORD aligned */ 579 VS_VERSION_INFO_STRUCT32 Children[]; 580 #endif 581 } VS_VERSION_INFO_STRUCT32; 582 583 #define VersionInfoIs16( ver ) \ 584 ( ((const VS_VERSION_INFO_STRUCT16 *)ver)->szKey[0] >= ' ' ) 585 586 #define DWORD_ALIGN( base, ptr ) \ 587 ( (LPBYTE)(base) + ((((LPBYTE)(ptr) - (LPBYTE)(base)) + 3) & ~3) ) 588 589 #define VersionInfo16_Value( ver ) \ 590 DWORD_ALIGN( (ver), (ver)->szKey + strlen((ver)->szKey) + 1 ) 591 #define VersionInfo32_Value( ver ) \ 592 DWORD_ALIGN( (ver), (ver)->szKey + lstrlenW((ver)->szKey) + 1 ) 593 594 #define VersionInfo16_Children( ver ) \ 595 (const VS_VERSION_INFO_STRUCT16 *)( VersionInfo16_Value( ver ) + \ 596 ( ( (ver)->wValueLength + 3 ) & ~3 ) ) 597 #define VersionInfo32_Children( ver ) \ 598 (const VS_VERSION_INFO_STRUCT32 *)( VersionInfo32_Value( ver ) + \ 599 ( ( (ver)->wValueLength * \ 600 ((ver)->wType? 2 : 1) + 3 ) & ~3 ) ) 601 602 #define VersionInfo16_Next( ver ) \ 603 (VS_VERSION_INFO_STRUCT16 *)( (LPBYTE)ver + (((ver)->wLength + 3) & ~3) ) 604 #define VersionInfo32_Next( ver ) \ 605 (VS_VERSION_INFO_STRUCT32 *)( (LPBYTE)ver + (((ver)->wLength + 3) & ~3) ) 606 607 608 /*********************************************************************** 609 * GetFileVersionInfoSizeW [VERSION.@] 610 */ 611 DWORD WINAPI GetFileVersionInfoSizeW( LPCWSTR filename, LPDWORD handle ) 612 { 613 return GetFileVersionInfoSizeExW( FILE_VER_GET_LOCALISED, filename, handle ); 614 } 615 616 /*********************************************************************** 617 * GetFileVersionInfoSizeA [VERSION.@] 618 */ 619 DWORD WINAPI GetFileVersionInfoSizeA( LPCSTR filename, LPDWORD handle ) 620 { 621 return GetFileVersionInfoSizeExA( FILE_VER_GET_LOCALISED, filename, handle ); 622 } 623 624 /****************************************************************************** 625 * GetFileVersionInfoSizeExW [VERSION.@] 626 */ 627 DWORD WINAPI GetFileVersionInfoSizeExW( DWORD flags, LPCWSTR filename, LPDWORD handle ) 628 { 629 DWORD len, offset, magic = 1; 630 HFILE lzfd; 631 HMODULE hModule; 632 OFSTRUCT ofs; 633 634 TRACE("(0x%x,%s,%p)\n", flags, debugstr_w(filename), handle ); 635 636 if (handle) *handle = 0; 637 638 if (!filename) 639 { 640 SetLastError(ERROR_INVALID_PARAMETER); 641 return 0; 642 } 643 if (!*filename) 644 { 645 SetLastError(ERROR_BAD_PATHNAME); 646 return 0; 647 } 648 if (flags & ~FILE_VER_GET_LOCALISED) 649 FIXME("flags 0x%x ignored\n", flags & ~FILE_VER_GET_LOCALISED); 650 651 if ((lzfd = LZOpenFileW( (LPWSTR)filename, &ofs, OF_READ )) != HFILE_ERROR) 652 { 653 magic = find_version_resource( lzfd, &len, &offset, flags ); 654 LZClose( lzfd ); 655 } 656 657 if ((magic == 1) && (hModule = LoadLibraryExW( filename, 0, LOAD_LIBRARY_AS_DATAFILE ))) 658 { 659 HRSRC hRsrc = NULL; 660 if (!(flags & FILE_VER_GET_LOCALISED)) 661 { 662 LANGID english = MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT ); 663 hRsrc = FindResourceExW( hModule, MAKEINTRESOURCEW(VS_VERSION_INFO), 664 (LPWSTR)VS_FILE_INFO, english ); 665 } 666 if (!hRsrc) 667 hRsrc = FindResourceW( hModule, MAKEINTRESOURCEW(VS_VERSION_INFO), 668 (LPWSTR)VS_FILE_INFO ); 669 if (hRsrc) 670 { 671 magic = IMAGE_NT_SIGNATURE; 672 len = SizeofResource( hModule, hRsrc ); 673 } 674 FreeLibrary( hModule ); 675 } 676 677 switch (magic) 678 { 679 case IMAGE_OS2_SIGNATURE: 680 /* We have a 16bit resource. 681 * 682 * XP/W2K/W2K3 uses a buffer which is more than the actual needed space: 683 * 684 * (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4 685 * 686 * This extra buffer is used for ANSI to Unicode conversions in W-Calls. 687 * info->wLength should be the same as len. Currently it isn't but that 688 * doesn't seem to be a problem (len is bigger than info->wLength). 689 */ 690 SetLastError(0); 691 return (len - sizeof(VS_FIXEDFILEINFO)) * 4; 692 693 case IMAGE_NT_SIGNATURE: 694 /* We have a 32bit resource. 695 * 696 * XP/W2K/W2K3 uses a buffer which is 2 times the actual needed space + 4 bytes "FE2X" 697 * This extra buffer is used for Unicode to ANSI conversions in A-Calls 698 */ 699 SetLastError(0); 700 return (len * 2) + 4; 701 702 default: 703 if (lzfd == HFILE_ERROR) 704 SetLastError(ofs.nErrCode); 705 else if (GetVersion() & 0x80000000) /* Windows 95/98 */ 706 SetLastError(ERROR_FILE_NOT_FOUND); 707 else 708 SetLastError(ERROR_RESOURCE_DATA_NOT_FOUND); 709 return 0; 710 } 711 } 712 713 /****************************************************************************** 714 * GetFileVersionInfoSizeExA [VERSION.@] 715 */ 716 DWORD WINAPI GetFileVersionInfoSizeExA( DWORD flags, LPCSTR filename, LPDWORD handle ) 717 { 718 UNICODE_STRING filenameW; 719 DWORD retval; 720 721 TRACE("(0x%x,%s,%p)\n", flags, debugstr_a(filename), handle ); 722 723 if(filename) 724 RtlCreateUnicodeStringFromAsciiz(&filenameW, filename); 725 else 726 filenameW.Buffer = NULL; 727 728 retval = GetFileVersionInfoSizeExW(flags, filenameW.Buffer, handle); 729 730 RtlFreeUnicodeString(&filenameW); 731 732 return retval; 733 } 734 735 /*********************************************************************** 736 * GetFileVersionInfoExW [VERSION.@] 737 */ 738 BOOL WINAPI GetFileVersionInfoExW( DWORD flags, LPCWSTR filename, DWORD handle, DWORD datasize, LPVOID data ) 739 { 740 static const char signature[4] = "FE2X"; 741 DWORD len, offset, magic = 1; 742 HFILE lzfd; 743 OFSTRUCT ofs; 744 HMODULE hModule; 745 VS_VERSION_INFO_STRUCT32* vvis = data; 746 747 TRACE("(0x%x,%s,%d,size=%d,data=%p)\n", 748 flags, debugstr_w(filename), handle, datasize, data ); 749 750 if (!data) 751 { 752 SetLastError(ERROR_INVALID_DATA); 753 return FALSE; 754 } 755 if (flags & ~FILE_VER_GET_LOCALISED) 756 FIXME("flags 0x%x ignored\n", flags & ~FILE_VER_GET_LOCALISED); 757 758 if ((lzfd = LZOpenFileW( (LPWSTR)filename, &ofs, OF_READ )) != HFILE_ERROR) 759 { 760 if ((magic = find_version_resource( lzfd, &len, &offset, flags )) > 1) 761 { 762 LZSeek( lzfd, offset, 0 /* SEEK_SET */ ); 763 len = LZRead( lzfd, data, min( len, datasize ) ); 764 } 765 LZClose( lzfd ); 766 } 767 768 if ((magic == 1) && (hModule = LoadLibraryExW( filename, 0, LOAD_LIBRARY_AS_DATAFILE ))) 769 { 770 HRSRC hRsrc = NULL; 771 if (!(flags & FILE_VER_GET_LOCALISED)) 772 { 773 LANGID english = MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT ); 774 hRsrc = FindResourceExW( hModule, MAKEINTRESOURCEW(VS_VERSION_INFO), 775 (LPWSTR)VS_FILE_INFO, english ); 776 } 777 if (!hRsrc) 778 hRsrc = FindResourceW( hModule, MAKEINTRESOURCEW(VS_VERSION_INFO), 779 (LPWSTR)VS_FILE_INFO ); 780 if (hRsrc) 781 { 782 HGLOBAL hMem = LoadResource( hModule, hRsrc ); 783 magic = IMAGE_NT_SIGNATURE; 784 len = min( SizeofResource(hModule, hRsrc), datasize ); 785 memcpy( data, LockResource( hMem ), len ); 786 FreeResource( hMem ); 787 } 788 FreeLibrary( hModule ); 789 } 790 791 switch (magic) 792 { 793 case IMAGE_OS2_SIGNATURE: 794 /* We have a 16bit resource. */ 795 if (TRACE_ON(ver)) 796 print_vffi_debug( (VS_FIXEDFILEINFO *)VersionInfo16_Value( (VS_VERSION_INFO_STRUCT16 *)data )); 797 SetLastError(0); 798 return TRUE; 799 800 case IMAGE_NT_SIGNATURE: 801 /* We have a 32bit resource. 802 * 803 * XP/W2K/W2K3 uses a buffer which is 2 times the actual needed space + 4 bytes "FE2X" 804 * This extra buffer is used for Unicode to ANSI conversions in A-Calls 805 */ 806 len = vvis->wLength + sizeof(signature); 807 if (datasize >= len) memcpy( (char*)data + vvis->wLength, signature, sizeof(signature) ); 808 if (TRACE_ON(ver)) 809 print_vffi_debug( (VS_FIXEDFILEINFO *)VersionInfo32_Value( vvis )); 810 SetLastError(0); 811 return TRUE; 812 813 default: 814 SetLastError( lzfd == HFILE_ERROR ? ofs.nErrCode : ERROR_RESOURCE_DATA_NOT_FOUND ); 815 return FALSE; 816 } 817 } 818 819 /*********************************************************************** 820 * GetFileVersionInfoExA [VERSION.@] 821 */ 822 BOOL WINAPI GetFileVersionInfoExA( DWORD flags, LPCSTR filename, DWORD handle, DWORD datasize, LPVOID data ) 823 { 824 UNICODE_STRING filenameW; 825 BOOL retval; 826 827 TRACE("(0x%x,%s,%d,size=%d,data=%p)\n", 828 flags, debugstr_a(filename), handle, datasize, data ); 829 830 if(filename) 831 RtlCreateUnicodeStringFromAsciiz(&filenameW, filename); 832 else 833 filenameW.Buffer = NULL; 834 835 retval = GetFileVersionInfoExW(flags, filenameW.Buffer, handle, datasize, data); 836 837 RtlFreeUnicodeString(&filenameW); 838 839 return retval; 840 } 841 842 /*********************************************************************** 843 * GetFileVersionInfoW [VERSION.@] 844 */ 845 BOOL WINAPI GetFileVersionInfoW( LPCWSTR filename, DWORD handle, DWORD datasize, LPVOID data ) 846 { 847 return GetFileVersionInfoExW(FILE_VER_GET_LOCALISED, filename, handle, datasize, data); 848 } 849 850 /*********************************************************************** 851 * GetFileVersionInfoA [VERSION.@] 852 */ 853 BOOL WINAPI GetFileVersionInfoA( LPCSTR filename, DWORD handle, DWORD datasize, LPVOID data ) 854 { 855 return GetFileVersionInfoExA(FILE_VER_GET_LOCALISED, filename, handle, datasize, data); 856 } 857 858 /*********************************************************************** 859 * VersionInfo16_FindChild [internal] 860 */ 861 static const VS_VERSION_INFO_STRUCT16 *VersionInfo16_FindChild( const VS_VERSION_INFO_STRUCT16 *info, 862 LPCSTR szKey, UINT cbKey ) 863 { 864 const VS_VERSION_INFO_STRUCT16 *child = VersionInfo16_Children( info ); 865 866 while ((char *)child < (char *)info + info->wLength ) 867 { 868 if (!_strnicmp( child->szKey, szKey, cbKey ) && !child->szKey[cbKey]) 869 return child; 870 871 if (!(child->wLength)) return NULL; 872 child = VersionInfo16_Next( child ); 873 } 874 875 return NULL; 876 } 877 878 /*********************************************************************** 879 * VersionInfo32_FindChild [internal] 880 */ 881 static const VS_VERSION_INFO_STRUCT32 *VersionInfo32_FindChild( const VS_VERSION_INFO_STRUCT32 *info, 882 LPCWSTR szKey, UINT cbKey ) 883 { 884 const VS_VERSION_INFO_STRUCT32 *child = VersionInfo32_Children( info ); 885 886 while ((char *)child < (char *)info + info->wLength ) 887 { 888 if (!_wcsnicmp( child->szKey, szKey, cbKey ) && !child->szKey[cbKey]) 889 return child; 890 891 if (!(child->wLength)) return NULL; 892 child = VersionInfo32_Next( child ); 893 } 894 895 return NULL; 896 } 897 898 /*********************************************************************** 899 * VersionInfo16_QueryValue [internal] 900 * 901 * Gets a value from a 16-bit NE resource 902 */ 903 static BOOL VersionInfo16_QueryValue( const VS_VERSION_INFO_STRUCT16 *info, LPCSTR lpSubBlock, 904 LPVOID *lplpBuffer, UINT *puLen ) 905 { 906 while ( *lpSubBlock ) 907 { 908 /* Find next path component */ 909 LPCSTR lpNextSlash; 910 for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ ) 911 if ( *lpNextSlash == '\\' ) 912 break; 913 914 /* Skip empty components */ 915 if ( lpNextSlash == lpSubBlock ) 916 { 917 lpSubBlock++; 918 continue; 919 } 920 921 /* We have a non-empty component: search info for key */ 922 info = VersionInfo16_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock ); 923 if ( !info ) 924 { 925 if (puLen) *puLen = 0 ; 926 SetLastError( ERROR_RESOURCE_TYPE_NOT_FOUND ); 927 return FALSE; 928 } 929 930 /* Skip path component */ 931 lpSubBlock = lpNextSlash; 932 } 933 934 /* Return value */ 935 *lplpBuffer = VersionInfo16_Value( info ); 936 if (puLen) 937 *puLen = info->wValueLength; 938 939 return TRUE; 940 } 941 942 /*********************************************************************** 943 * VersionInfo32_QueryValue [internal] 944 * 945 * Gets a value from a 32-bit PE resource 946 */ 947 static BOOL VersionInfo32_QueryValue( const VS_VERSION_INFO_STRUCT32 *info, LPCWSTR lpSubBlock, 948 LPVOID *lplpBuffer, UINT *puLen, BOOL *pbText ) 949 { 950 TRACE("lpSubBlock : (%s)\n", debugstr_w(lpSubBlock)); 951 952 while ( *lpSubBlock ) 953 { 954 /* Find next path component */ 955 LPCWSTR lpNextSlash; 956 for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ ) 957 if ( *lpNextSlash == '\\' ) 958 break; 959 960 /* Skip empty components */ 961 if ( lpNextSlash == lpSubBlock ) 962 { 963 lpSubBlock++; 964 continue; 965 } 966 967 /* We have a non-empty component: search info for key */ 968 info = VersionInfo32_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock ); 969 if ( !info ) 970 { 971 if (puLen) *puLen = 0 ; 972 SetLastError( ERROR_RESOURCE_TYPE_NOT_FOUND ); 973 return FALSE; 974 } 975 976 /* Skip path component */ 977 lpSubBlock = lpNextSlash; 978 } 979 980 /* Return value */ 981 *lplpBuffer = VersionInfo32_Value( info ); 982 if (puLen) 983 *puLen = info->wValueLength; 984 if (pbText) 985 *pbText = info->wType; 986 987 return TRUE; 988 } 989 990 /*********************************************************************** 991 * VerQueryValueA [VERSION.@] 992 */ 993 BOOL WINAPI VerQueryValueA( LPCVOID pBlock, LPCSTR lpSubBlock, 994 LPVOID *lplpBuffer, PUINT puLen ) 995 { 996 static const char rootA[] = "\\"; 997 const VS_VERSION_INFO_STRUCT16 *info = pBlock; 998 999 TRACE("(%p,%s,%p,%p)\n", 1000 pBlock, debugstr_a(lpSubBlock), lplpBuffer, puLen ); 1001 1002 if (!pBlock) 1003 return FALSE; 1004 1005 if (lpSubBlock == NULL || lpSubBlock[0] == '\0') 1006 lpSubBlock = rootA; 1007 1008 if ( !VersionInfoIs16( info ) ) 1009 { 1010 BOOL ret, isText; 1011 INT len; 1012 LPWSTR lpSubBlockW; 1013 UINT value_len; 1014 1015 len = MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, NULL, 0); 1016 lpSubBlockW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); 1017 1018 if (!lpSubBlockW) 1019 return FALSE; 1020 1021 MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, lpSubBlockW, len); 1022 1023 ret = VersionInfo32_QueryValue(pBlock, lpSubBlockW, lplpBuffer, &value_len, &isText); 1024 if (puLen) *puLen = value_len; 1025 1026 HeapFree(GetProcessHeap(), 0, lpSubBlockW); 1027 1028 if (ret && isText) 1029 { 1030 /* Set lpBuffer so it points to the 'empty' area where we store 1031 * the converted strings 1032 */ 1033 LPSTR lpBufferA = (LPSTR)pBlock + info->wLength + 4; 1034 DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock; 1035 len = WideCharToMultiByte(CP_ACP, 0, *lplpBuffer, value_len, 1036 lpBufferA + pos, info->wLength - pos, NULL, NULL); 1037 *lplpBuffer = lpBufferA + pos; 1038 if (puLen) *puLen = len; 1039 } 1040 return ret; 1041 } 1042 1043 return VersionInfo16_QueryValue(info, lpSubBlock, lplpBuffer, puLen); 1044 } 1045 1046 /*********************************************************************** 1047 * VerQueryValueW [VERSION.@] 1048 */ 1049 BOOL WINAPI VerQueryValueW( LPCVOID pBlock, LPCWSTR lpSubBlock, 1050 LPVOID *lplpBuffer, PUINT puLen ) 1051 { 1052 static const WCHAR rootW[] = { '\\', 0 }; 1053 static const WCHAR varfileinfoW[] = { '\\','V','a','r','F','i','l','e','I','n','f','o', 1054 '\\','T','r','a','n','s','l','a','t','i','o','n', 0 }; 1055 1056 const VS_VERSION_INFO_STRUCT32 *info = pBlock; 1057 1058 TRACE("(%p,%s,%p,%p)\n", 1059 pBlock, debugstr_w(lpSubBlock), lplpBuffer, puLen ); 1060 1061 if (!pBlock) 1062 return FALSE; 1063 1064 if (!lpSubBlock || !lpSubBlock[0]) 1065 lpSubBlock = rootW; 1066 1067 if ( VersionInfoIs16( info ) ) 1068 { 1069 BOOL ret; 1070 int len; 1071 LPSTR lpSubBlockA; 1072 1073 len = WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, NULL, 0, NULL, NULL); 1074 lpSubBlockA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char)); 1075 1076 if (!lpSubBlockA) 1077 return FALSE; 1078 1079 WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, lpSubBlockA, len, NULL, NULL); 1080 1081 ret = VersionInfo16_QueryValue(pBlock, lpSubBlockA, lplpBuffer, puLen); 1082 1083 HeapFree(GetProcessHeap(), 0, lpSubBlockA); 1084 1085 if (ret && wcsicmp( lpSubBlock, rootW ) && wcsicmp( lpSubBlock, varfileinfoW )) 1086 { 1087 /* Set lpBuffer so it points to the 'empty' area where we store 1088 * the converted strings 1089 */ 1090 LPWSTR lpBufferW = (LPWSTR)((LPSTR)pBlock + info->wLength); 1091 DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock; 1092 DWORD max = (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4 - info->wLength; 1093 1094 len = MultiByteToWideChar(CP_ACP, 0, *lplpBuffer, -1, 1095 lpBufferW + pos, max/sizeof(WCHAR) - pos ); 1096 *lplpBuffer = lpBufferW + pos; 1097 if (puLen) *puLen = len; 1098 } 1099 return ret; 1100 } 1101 1102 return VersionInfo32_QueryValue(info, lpSubBlock, lplpBuffer, puLen, NULL); 1103 } 1104 1105 1106 /****************************************************************************** 1107 * testFileExistenceA 1108 * 1109 * Tests whether a given path/file combination exists. If the file does 1110 * not exist, the return value is zero. If it does exist, the return 1111 * value is non-zero. 1112 * 1113 * Revision history 1114 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu) 1115 * Original implementation 1116 * 1117 */ 1118 static int testFileExistenceA( char const * path, char const * file, BOOL excl ) 1119 { 1120 char filename[1024]; 1121 int filenamelen; 1122 OFSTRUCT fileinfo; 1123 1124 fileinfo.cBytes = sizeof(OFSTRUCT); 1125 1126 if (path) 1127 { 1128 strcpy(filename, path); 1129 filenamelen = strlen(filename); 1130 1131 /* Add a trailing \ if necessary */ 1132 if(filenamelen) 1133 { 1134 if(filename[filenamelen - 1] != '\\') 1135 strcat(filename, "\\"); 1136 } 1137 else /* specify the current directory */ 1138 strcpy(filename, ".\\"); 1139 } 1140 else 1141 filename[0] = 0; 1142 1143 /* Create the full pathname */ 1144 strcat(filename, file); 1145 1146 return (OpenFile(filename, &fileinfo, 1147 OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR); 1148 } 1149 1150 /****************************************************************************** 1151 * testFileExistenceW 1152 */ 1153 static int testFileExistenceW( const WCHAR *path, const WCHAR *file, BOOL excl ) 1154 { 1155 char *filename; 1156 DWORD pathlen, filelen; 1157 int ret; 1158 OFSTRUCT fileinfo; 1159 1160 fileinfo.cBytes = sizeof(OFSTRUCT); 1161 1162 pathlen = WideCharToMultiByte( CP_ACP, 0, path, -1, NULL, 0, NULL, NULL ); 1163 filelen = WideCharToMultiByte( CP_ACP, 0, file, -1, NULL, 0, NULL, NULL ); 1164 filename = HeapAlloc( GetProcessHeap(), 0, pathlen+filelen+2 ); 1165 1166 WideCharToMultiByte( CP_ACP, 0, path, -1, filename, pathlen, NULL, NULL ); 1167 /* Add a trailing \ if necessary */ 1168 if (pathlen > 1) 1169 { 1170 if (filename[pathlen-2] != '\\') strcpy( &filename[pathlen-1], "\\" ); 1171 } 1172 else /* specify the current directory */ 1173 strcpy(filename, ".\\"); 1174 1175 WideCharToMultiByte( CP_ACP, 0, file, -1, filename+strlen(filename), filelen, NULL, NULL ); 1176 1177 ret = (OpenFile(filename, &fileinfo, 1178 OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR); 1179 HeapFree( GetProcessHeap(), 0, filename ); 1180 return ret; 1181 } 1182 1183 /***************************************************************************** 1184 * VerFindFileA [VERSION.@] 1185 * 1186 * Determines where to install a file based on whether it locates another 1187 * version of the file in the system. The values VerFindFile returns are 1188 * used in a subsequent call to the VerInstallFile function. 1189 * 1190 * Revision history: 1191 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu) 1192 * Reimplementation of VerFindFile from original stub. 1193 */ 1194 DWORD WINAPI VerFindFileA( 1195 DWORD flags, 1196 LPCSTR lpszFilename, 1197 LPCSTR lpszWinDir, 1198 LPCSTR lpszAppDir, 1199 LPSTR lpszCurDir, 1200 PUINT lpuCurDirLen, 1201 LPSTR lpszDestDir, 1202 PUINT lpuDestDirLen ) 1203 { 1204 DWORD retval = 0; 1205 const char *curDir; 1206 const char *destDir; 1207 unsigned int curDirSizeReq; 1208 unsigned int destDirSizeReq; 1209 char winDir[MAX_PATH], systemDir[MAX_PATH]; 1210 1211 /* Print out debugging information */ 1212 TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n", 1213 flags, debugstr_a(lpszFilename), debugstr_a(lpszWinDir), debugstr_a(lpszAppDir), 1214 lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0, 1215 lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 ); 1216 1217 /* Figure out where the file should go; shared files default to the 1218 system directory */ 1219 1220 GetSystemDirectoryA(systemDir, sizeof(systemDir)); 1221 curDir = ""; 1222 1223 if(flags & VFFF_ISSHAREDFILE) 1224 { 1225 destDir = systemDir; 1226 /* Were we given a filename? If so, try to find the file. */ 1227 if(lpszFilename) 1228 { 1229 if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir; 1230 else if(lpszAppDir && testFileExistenceA(lpszAppDir, lpszFilename, FALSE)) 1231 curDir = lpszAppDir; 1232 1233 if(!testFileExistenceA(systemDir, lpszFilename, FALSE)) 1234 retval |= VFF_CURNEDEST; 1235 } 1236 } 1237 else /* not a shared file */ 1238 { 1239 destDir = lpszAppDir ? lpszAppDir : ""; 1240 if(lpszFilename) 1241 { 1242 GetWindowsDirectoryA( winDir, MAX_PATH ); 1243 if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir; 1244 else if(testFileExistenceA(winDir, lpszFilename, FALSE)) 1245 curDir = winDir; 1246 else if(testFileExistenceA(systemDir, lpszFilename, FALSE)) 1247 curDir = systemDir; 1248 1249 if (lpszAppDir && lpszAppDir[0]) 1250 { 1251 if(!testFileExistenceA(lpszAppDir, lpszFilename, FALSE)) 1252 retval |= VFF_CURNEDEST; 1253 } 1254 else if(testFileExistenceA(NULL, lpszFilename, FALSE)) 1255 retval |= VFF_CURNEDEST; 1256 } 1257 } 1258 1259 /* Check to see if the file exists and is in use by another application */ 1260 if (lpszFilename && testFileExistenceA(curDir, lpszFilename, FALSE)) { 1261 if (lpszFilename && !testFileExistenceA(curDir, lpszFilename, TRUE)) 1262 retval |= VFF_FILEINUSE; 1263 } 1264 1265 curDirSizeReq = strlen(curDir) + 1; 1266 destDirSizeReq = strlen(destDir) + 1; 1267 1268 /* Make sure that the pointers to the size of the buffers are 1269 valid; if not, do NOTHING with that buffer. If that pointer 1270 is valid, then make sure that the buffer pointer is valid, too! */ 1271 1272 if(lpuDestDirLen && lpszDestDir) 1273 { 1274 if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL; 1275 lstrcpynA(lpszDestDir, destDir, *lpuDestDirLen); 1276 *lpuDestDirLen = destDirSizeReq; 1277 } 1278 if(lpuCurDirLen && lpszCurDir) 1279 { 1280 if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL; 1281 lstrcpynA(lpszCurDir, curDir, *lpuCurDirLen); 1282 *lpuCurDirLen = curDirSizeReq; 1283 } 1284 1285 TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval, 1286 (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "", 1287 (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "", 1288 (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "", 1289 debugstr_a(lpszCurDir), debugstr_a(lpszDestDir)); 1290 1291 return retval; 1292 } 1293 1294 /***************************************************************************** 1295 * VerFindFileW [VERSION.@] 1296 */ 1297 DWORD WINAPI VerFindFileW( DWORD flags,LPCWSTR lpszFilename,LPCWSTR lpszWinDir, 1298 LPCWSTR lpszAppDir, LPWSTR lpszCurDir,PUINT lpuCurDirLen, 1299 LPWSTR lpszDestDir,PUINT lpuDestDirLen ) 1300 { 1301 static const WCHAR emptyW; 1302 DWORD retval = 0; 1303 const WCHAR *curDir; 1304 const WCHAR *destDir; 1305 unsigned int curDirSizeReq; 1306 unsigned int destDirSizeReq; 1307 WCHAR winDir[MAX_PATH], systemDir[MAX_PATH]; 1308 1309 /* Print out debugging information */ 1310 TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n", 1311 flags, debugstr_w(lpszFilename), debugstr_w(lpszWinDir), debugstr_w(lpszAppDir), 1312 lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0, 1313 lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 ); 1314 1315 /* Figure out where the file should go; shared files default to the 1316 system directory */ 1317 1318 GetSystemDirectoryW(systemDir, ARRAY_SIZE(systemDir)); 1319 curDir = &emptyW; 1320 1321 if(flags & VFFF_ISSHAREDFILE) 1322 { 1323 destDir = systemDir; 1324 /* Were we given a filename? If so, try to find the file. */ 1325 if(lpszFilename) 1326 { 1327 if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir; 1328 else if(lpszAppDir && testFileExistenceW(lpszAppDir, lpszFilename, FALSE)) 1329 { 1330 curDir = lpszAppDir; 1331 retval |= VFF_CURNEDEST; 1332 } 1333 } 1334 } 1335 else /* not a shared file */ 1336 { 1337 destDir = lpszAppDir ? lpszAppDir : &emptyW; 1338 if(lpszFilename) 1339 { 1340 GetWindowsDirectoryW( winDir, MAX_PATH ); 1341 if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir; 1342 else if(testFileExistenceW(winDir, lpszFilename, FALSE)) 1343 { 1344 curDir = winDir; 1345 retval |= VFF_CURNEDEST; 1346 } 1347 else if(testFileExistenceW(systemDir, lpszFilename, FALSE)) 1348 { 1349 curDir = systemDir; 1350 retval |= VFF_CURNEDEST; 1351 } 1352 } 1353 } 1354 1355 if (lpszFilename && !testFileExistenceW(curDir, lpszFilename, TRUE)) 1356 retval |= VFF_FILEINUSE; 1357 1358 curDirSizeReq = lstrlenW(curDir) + 1; 1359 destDirSizeReq = lstrlenW(destDir) + 1; 1360 1361 /* Make sure that the pointers to the size of the buffers are 1362 valid; if not, do NOTHING with that buffer. If that pointer 1363 is valid, then make sure that the buffer pointer is valid, too! */ 1364 1365 if(lpuDestDirLen && lpszDestDir) 1366 { 1367 if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL; 1368 lstrcpynW(lpszDestDir, destDir, *lpuDestDirLen); 1369 *lpuDestDirLen = destDirSizeReq; 1370 } 1371 if(lpuCurDirLen && lpszCurDir) 1372 { 1373 if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL; 1374 lstrcpynW(lpszCurDir, curDir, *lpuCurDirLen); 1375 *lpuCurDirLen = curDirSizeReq; 1376 } 1377 1378 TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval, 1379 (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "", 1380 (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "", 1381 (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "", 1382 debugstr_w(lpszCurDir), debugstr_w(lpszDestDir)); 1383 return retval; 1384 } 1385 1386 static LPBYTE 1387 _fetch_versioninfo(LPSTR fn,VS_FIXEDFILEINFO **vffi) { 1388 DWORD alloclen; 1389 LPBYTE buf; 1390 DWORD ret; 1391 1392 alloclen = 1000; 1393 buf=HeapAlloc(GetProcessHeap(), 0, alloclen); 1394 if(buf == NULL) { 1395 WARN("Memory exhausted while fetching version info!\n"); 1396 return NULL; 1397 } 1398 while (1) { 1399 ret = GetFileVersionInfoA(fn,0,alloclen,buf); 1400 if (!ret) { 1401 HeapFree(GetProcessHeap(), 0, buf); 1402 return NULL; 1403 } 1404 if (alloclen<*(WORD*)buf) { 1405 alloclen = *(WORD*)buf; 1406 HeapFree(GetProcessHeap(), 0, buf); 1407 buf = HeapAlloc(GetProcessHeap(), 0, alloclen); 1408 if(buf == NULL) { 1409 WARN("Memory exhausted while fetching version info!\n"); 1410 return NULL; 1411 } 1412 } else { 1413 *vffi = (VS_FIXEDFILEINFO*)(buf+0x14); 1414 if ((*vffi)->dwSignature == 0x004f0049) /* hack to detect unicode */ 1415 *vffi = (VS_FIXEDFILEINFO*)(buf+0x28); 1416 if ((*vffi)->dwSignature != VS_FFI_SIGNATURE) 1417 WARN("Bad VS_FIXEDFILEINFO signature 0x%08x\n",(*vffi)->dwSignature); 1418 return buf; 1419 } 1420 } 1421 } 1422 1423 static DWORD 1424 _error2vif(DWORD error) { 1425 switch (error) { 1426 case ERROR_ACCESS_DENIED: 1427 return VIF_ACCESSVIOLATION; 1428 case ERROR_SHARING_VIOLATION: 1429 return VIF_SHARINGVIOLATION; 1430 default: 1431 return 0; 1432 } 1433 } 1434 1435 1436 /****************************************************************************** 1437 * VerInstallFileA [VERSION.@] 1438 */ 1439 DWORD WINAPI VerInstallFileA( 1440 DWORD flags,LPCSTR srcfilename,LPCSTR destfilename,LPCSTR srcdir, 1441 LPCSTR destdir,LPCSTR curdir,LPSTR tmpfile,PUINT tmpfilelen ) 1442 { 1443 LPCSTR pdest; 1444 char destfn[260],tmpfn[260],srcfn[260]; 1445 HFILE hfsrc,hfdst; 1446 DWORD attr,xret,tmplast; 1447 LONG ret; 1448 LPBYTE buf1,buf2; 1449 OFSTRUCT ofs; 1450 1451 TRACE("(%x,%s,%s,%s,%s,%s,%p,%d)\n", 1452 flags,debugstr_a(srcfilename),debugstr_a(destfilename), 1453 debugstr_a(srcdir),debugstr_a(destdir),debugstr_a(curdir), 1454 tmpfile,*tmpfilelen); 1455 xret = 0; 1456 if (!srcdir || !srcfilename) return VIF_CANNOTREADSRC; 1457 sprintf(srcfn,"%s\\%s",srcdir,srcfilename); 1458 if (!destdir || !*destdir) pdest = srcdir; 1459 else pdest = destdir; 1460 sprintf(destfn,"%s\\%s",pdest,destfilename); 1461 hfsrc=LZOpenFileA(srcfn,&ofs,OF_READ); 1462 if (hfsrc < 0) 1463 return VIF_CANNOTREADSRC; 1464 sprintf(tmpfn,"%s\\%s",pdest,destfilename); 1465 tmplast=strlen(pdest)+1; 1466 attr = GetFileAttributesA(tmpfn); 1467 if (attr != INVALID_FILE_ATTRIBUTES) { 1468 if (attr & FILE_ATTRIBUTE_READONLY) { 1469 LZClose(hfsrc); 1470 return VIF_WRITEPROT; 1471 } 1472 /* FIXME: check if file currently in use and return VIF_FILEINUSE */ 1473 } 1474 attr = INVALID_FILE_ATTRIBUTES; 1475 if (flags & VIFF_FORCEINSTALL) { 1476 if (tmpfile[0]) { 1477 sprintf(tmpfn,"%s\\%s",pdest,tmpfile); 1478 tmplast = strlen(pdest)+1; 1479 attr = GetFileAttributesA(tmpfn); 1480 /* if it exists, it has been copied by the call before. 1481 * we jump over the copy part... 1482 */ 1483 } 1484 } 1485 if (attr == INVALID_FILE_ATTRIBUTES) { 1486 char *s; 1487 1488 GetTempFileNameA(pdest,"ver",0,tmpfn); /* should not fail ... */ 1489 s=strrchr(tmpfn,'\\'); 1490 if (s) 1491 tmplast = s-tmpfn; 1492 else 1493 tmplast = 0; 1494 hfdst = OpenFile(tmpfn,&ofs,OF_CREATE); 1495 if (hfdst == HFILE_ERROR) { 1496 LZClose(hfsrc); 1497 return VIF_CANNOTCREATE; /* | translated dos error */ 1498 } 1499 ret = LZCopy(hfsrc,hfdst); 1500 _lclose(hfdst); 1501 if (ret < 0) { 1502 /* translate LZ errors into VIF_xxx */ 1503 switch (ret) { 1504 case LZERROR_BADINHANDLE: 1505 case LZERROR_READ: 1506 case LZERROR_BADVALUE: 1507 case LZERROR_UNKNOWNALG: 1508 xret = VIF_CANNOTREADSRC; 1509 break; 1510 case LZERROR_BADOUTHANDLE: 1511 case LZERROR_WRITE: 1512 xret = VIF_OUTOFSPACE; 1513 break; 1514 case LZERROR_GLOBALLOC: 1515 case LZERROR_GLOBLOCK: 1516 xret = VIF_OUTOFMEMORY; 1517 break; 1518 default: /* unknown error, should not happen */ 1519 FIXME("Unknown LZCopy error %d, ignoring.\n", ret); 1520 xret = 0; 1521 break; 1522 } 1523 if (xret) { 1524 LZClose(hfsrc); 1525 return xret; 1526 } 1527 } 1528 } 1529 if (!(flags & VIFF_FORCEINSTALL)) { 1530 VS_FIXEDFILEINFO *destvffi,*tmpvffi; 1531 buf1 = _fetch_versioninfo(destfn,&destvffi); 1532 if (buf1) { 1533 buf2 = _fetch_versioninfo(tmpfn,&tmpvffi); 1534 if (buf2) { 1535 char *tbuf1,*tbuf2; 1536 static const CHAR trans_array[] = "\\VarFileInfo\\Translation"; 1537 UINT len1,len2; 1538 1539 len1=len2=40; 1540 1541 /* compare file versions */ 1542 if ((destvffi->dwFileVersionMS > tmpvffi->dwFileVersionMS)|| 1543 ((destvffi->dwFileVersionMS==tmpvffi->dwFileVersionMS)&& 1544 (destvffi->dwFileVersionLS > tmpvffi->dwFileVersionLS) 1545 ) 1546 ) 1547 xret |= VIF_MISMATCH|VIF_SRCOLD; 1548 /* compare filetypes and filesubtypes */ 1549 if ((destvffi->dwFileType!=tmpvffi->dwFileType) || 1550 (destvffi->dwFileSubtype!=tmpvffi->dwFileSubtype) 1551 ) 1552 xret |= VIF_MISMATCH|VIF_DIFFTYPE; 1553 if (VerQueryValueA(buf1,trans_array,(LPVOID*)&tbuf1,&len1) && 1554 VerQueryValueA(buf2,trans_array,(LPVOID*)&tbuf2,&len2) 1555 ) { 1556 /* Do something with tbuf1 and tbuf2 1557 * generates DIFFLANG|MISMATCH 1558 */ 1559 } 1560 HeapFree(GetProcessHeap(), 0, buf2); 1561 } else 1562 xret=VIF_MISMATCH|VIF_SRCOLD; 1563 HeapFree(GetProcessHeap(), 0, buf1); 1564 } 1565 } 1566 if (xret) { 1567 if (*tmpfilelen<strlen(tmpfn+tmplast)) { 1568 xret|=VIF_BUFFTOOSMALL; 1569 DeleteFileA(tmpfn); 1570 } else { 1571 strcpy(tmpfile,tmpfn+tmplast); 1572 *tmpfilelen = strlen(tmpfn+tmplast)+1; 1573 xret|=VIF_TEMPFILE; 1574 } 1575 } else { 1576 if (INVALID_FILE_ATTRIBUTES!=GetFileAttributesA(destfn)) 1577 if (!DeleteFileA(destfn)) { 1578 xret|=_error2vif(GetLastError())|VIF_CANNOTDELETE; 1579 DeleteFileA(tmpfn); 1580 LZClose(hfsrc); 1581 return xret; 1582 } 1583 if ((!(flags & VIFF_DONTDELETEOLD)) && 1584 curdir && 1585 *curdir && 1586 lstrcmpiA(curdir,pdest) 1587 ) { 1588 char curfn[260]; 1589 1590 sprintf(curfn,"%s\\%s",curdir,destfilename); 1591 if (INVALID_FILE_ATTRIBUTES != GetFileAttributesA(curfn)) { 1592 /* FIXME: check if in use ... if it is, VIF_CANNOTDELETECUR */ 1593 if (!DeleteFileA(curfn)) 1594 xret|=_error2vif(GetLastError())|VIF_CANNOTDELETECUR; 1595 } 1596 } 1597 if (!MoveFileA(tmpfn,destfn)) { 1598 xret|=_error2vif(GetLastError())|VIF_CANNOTRENAME; 1599 DeleteFileA(tmpfn); 1600 } 1601 } 1602 LZClose(hfsrc); 1603 return xret; 1604 } 1605 1606 1607 /****************************************************************************** 1608 * VerInstallFileW [VERSION.@] 1609 */ 1610 DWORD WINAPI VerInstallFileW( 1611 DWORD flags,LPCWSTR srcfilename,LPCWSTR destfilename,LPCWSTR srcdir, 1612 LPCWSTR destdir,LPCWSTR curdir,LPWSTR tmpfile,PUINT tmpfilelen ) 1613 { 1614 LPSTR wsrcf = NULL, wsrcd = NULL, wdestf = NULL, wdestd = NULL, wtmpf = NULL, wcurd = NULL; 1615 DWORD ret = 0; 1616 UINT len; 1617 1618 if (srcfilename) 1619 { 1620 len = WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, NULL, 0, NULL, NULL ); 1621 if ((wsrcf = HeapAlloc( GetProcessHeap(), 0, len ))) 1622 WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, wsrcf, len, NULL, NULL ); 1623 else 1624 ret = VIF_OUTOFMEMORY; 1625 } 1626 if (srcdir && !ret) 1627 { 1628 len = WideCharToMultiByte( CP_ACP, 0, srcdir, -1, NULL, 0, NULL, NULL ); 1629 if ((wsrcd = HeapAlloc( GetProcessHeap(), 0, len ))) 1630 WideCharToMultiByte( CP_ACP, 0, srcdir, -1, wsrcd, len, NULL, NULL ); 1631 else 1632 ret = VIF_OUTOFMEMORY; 1633 } 1634 if (destfilename && !ret) 1635 { 1636 len = WideCharToMultiByte( CP_ACP, 0, destfilename, -1, NULL, 0, NULL, NULL ); 1637 if ((wdestf = HeapAlloc( GetProcessHeap(), 0, len ))) 1638 WideCharToMultiByte( CP_ACP, 0, destfilename, -1, wdestf, len, NULL, NULL ); 1639 else 1640 ret = VIF_OUTOFMEMORY; 1641 } 1642 if (destdir && !ret) 1643 { 1644 len = WideCharToMultiByte( CP_ACP, 0, destdir, -1, NULL, 0, NULL, NULL ); 1645 if ((wdestd = HeapAlloc( GetProcessHeap(), 0, len ))) 1646 WideCharToMultiByte( CP_ACP, 0, destdir, -1, wdestd, len, NULL, NULL ); 1647 else 1648 ret = VIF_OUTOFMEMORY; 1649 } 1650 if (curdir && !ret) 1651 { 1652 len = WideCharToMultiByte( CP_ACP, 0, curdir, -1, NULL, 0, NULL, NULL ); 1653 if ((wcurd = HeapAlloc( GetProcessHeap(), 0, len ))) 1654 WideCharToMultiByte( CP_ACP, 0, curdir, -1, wcurd, len, NULL, NULL ); 1655 else 1656 ret = VIF_OUTOFMEMORY; 1657 } 1658 if (!ret) 1659 { 1660 len = *tmpfilelen * sizeof(WCHAR); 1661 wtmpf = HeapAlloc( GetProcessHeap(), 0, len ); 1662 if (!wtmpf) 1663 ret = VIF_OUTOFMEMORY; 1664 } 1665 if (!ret) 1666 ret = VerInstallFileA(flags,wsrcf,wdestf,wsrcd,wdestd,wcurd,wtmpf,&len); 1667 if (!ret) 1668 *tmpfilelen = MultiByteToWideChar( CP_ACP, 0, wtmpf, -1, tmpfile, *tmpfilelen ); 1669 else if (ret & VIF_BUFFTOOSMALL) 1670 *tmpfilelen = len; /* FIXME: not correct */ 1671 1672 HeapFree( GetProcessHeap(), 0, wsrcf ); 1673 HeapFree( GetProcessHeap(), 0, wsrcd ); 1674 HeapFree( GetProcessHeap(), 0, wdestf ); 1675 HeapFree( GetProcessHeap(), 0, wdestd ); 1676 HeapFree( GetProcessHeap(), 0, wtmpf ); 1677 HeapFree( GetProcessHeap(), 0, wcurd ); 1678 return ret; 1679 } 1680