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