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 SetLastError( lzfd == HFILE_ERROR ? ofs.nErrCode : 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 (!strncasecmp( 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 (!strncmpiW( 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 nullW[] = { 0 }; 1053 static const WCHAR rootW[] = { '\\', 0 }; 1054 static const WCHAR varfileinfoW[] = { '\\','V','a','r','F','i','l','e','I','n','f','o', 1055 '\\','T','r','a','n','s','l','a','t','i','o','n', 0 }; 1056 1057 const VS_VERSION_INFO_STRUCT32 *info = pBlock; 1058 1059 TRACE("(%p,%s,%p,%p)\n", 1060 pBlock, debugstr_w(lpSubBlock), lplpBuffer, puLen ); 1061 1062 if (!pBlock) 1063 return FALSE; 1064 1065 if (lpSubBlock == NULL || lpSubBlock[0] == nullW[0]) 1066 lpSubBlock = rootW; 1067 1068 if ( VersionInfoIs16( info ) ) 1069 { 1070 BOOL ret; 1071 int len; 1072 LPSTR lpSubBlockA; 1073 1074 len = WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, NULL, 0, NULL, NULL); 1075 lpSubBlockA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char)); 1076 1077 if (!lpSubBlockA) 1078 return FALSE; 1079 1080 WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, lpSubBlockA, len, NULL, NULL); 1081 1082 ret = VersionInfo16_QueryValue(pBlock, lpSubBlockA, lplpBuffer, puLen); 1083 1084 HeapFree(GetProcessHeap(), 0, lpSubBlockA); 1085 1086 if (ret && strcmpiW( lpSubBlock, rootW ) && strcmpiW( lpSubBlock, varfileinfoW )) 1087 { 1088 /* Set lpBuffer so it points to the 'empty' area where we store 1089 * the converted strings 1090 */ 1091 LPWSTR lpBufferW = (LPWSTR)((LPSTR)pBlock + info->wLength); 1092 DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock; 1093 DWORD max = (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4 - info->wLength; 1094 1095 len = MultiByteToWideChar(CP_ACP, 0, *lplpBuffer, -1, 1096 lpBufferW + pos, max/sizeof(WCHAR) - pos ); 1097 *lplpBuffer = lpBufferW + pos; 1098 if (puLen) *puLen = len; 1099 } 1100 return ret; 1101 } 1102 1103 return VersionInfo32_QueryValue(info, lpSubBlock, lplpBuffer, puLen, NULL); 1104 } 1105 1106 1107 /****************************************************************************** 1108 * testFileExistenceA 1109 * 1110 * Tests whether a given path/file combination exists. If the file does 1111 * not exist, the return value is zero. If it does exist, the return 1112 * value is non-zero. 1113 * 1114 * Revision history 1115 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu) 1116 * Original implementation 1117 * 1118 */ 1119 static int testFileExistenceA( char const * path, char const * file, BOOL excl ) 1120 { 1121 char filename[1024]; 1122 int filenamelen; 1123 OFSTRUCT fileinfo; 1124 1125 fileinfo.cBytes = sizeof(OFSTRUCT); 1126 1127 strcpy(filename, path); 1128 filenamelen = strlen(filename); 1129 1130 /* Add a trailing \ if necessary */ 1131 if(filenamelen) { 1132 if(filename[filenamelen - 1] != '\\') 1133 strcat(filename, "\\"); 1134 } 1135 else /* specify the current directory */ 1136 strcpy(filename, ".\\"); 1137 1138 /* Create the full pathname */ 1139 strcat(filename, file); 1140 1141 return (OpenFile(filename, &fileinfo, 1142 OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR); 1143 } 1144 1145 /****************************************************************************** 1146 * testFileExistenceW 1147 */ 1148 static int testFileExistenceW( const WCHAR *path, const WCHAR *file, BOOL excl ) 1149 { 1150 char *filename; 1151 DWORD pathlen, filelen; 1152 int ret; 1153 OFSTRUCT fileinfo; 1154 1155 fileinfo.cBytes = sizeof(OFSTRUCT); 1156 1157 pathlen = WideCharToMultiByte( CP_ACP, 0, path, -1, NULL, 0, NULL, NULL ); 1158 filelen = WideCharToMultiByte( CP_ACP, 0, file, -1, NULL, 0, NULL, NULL ); 1159 filename = HeapAlloc( GetProcessHeap(), 0, pathlen+filelen+2 ); 1160 1161 WideCharToMultiByte( CP_ACP, 0, path, -1, filename, pathlen, NULL, NULL ); 1162 /* Add a trailing \ if necessary */ 1163 if (pathlen > 1) 1164 { 1165 if (filename[pathlen-2] != '\\') strcpy( &filename[pathlen-1], "\\" ); 1166 } 1167 else /* specify the current directory */ 1168 strcpy(filename, ".\\"); 1169 1170 WideCharToMultiByte( CP_ACP, 0, file, -1, filename+strlen(filename), filelen, NULL, NULL ); 1171 1172 ret = (OpenFile(filename, &fileinfo, 1173 OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR); 1174 HeapFree( GetProcessHeap(), 0, filename ); 1175 return ret; 1176 } 1177 1178 /***************************************************************************** 1179 * VerFindFileA [VERSION.@] 1180 * 1181 * Determines where to install a file based on whether it locates another 1182 * version of the file in the system. The values VerFindFile returns are 1183 * used in a subsequent call to the VerInstallFile function. 1184 * 1185 * Revision history: 1186 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu) 1187 * Reimplementation of VerFindFile from original stub. 1188 */ 1189 DWORD WINAPI VerFindFileA( 1190 DWORD flags, 1191 LPCSTR lpszFilename, 1192 LPCSTR lpszWinDir, 1193 LPCSTR lpszAppDir, 1194 LPSTR lpszCurDir, 1195 PUINT lpuCurDirLen, 1196 LPSTR lpszDestDir, 1197 PUINT lpuDestDirLen ) 1198 { 1199 DWORD retval = 0; 1200 const char *curDir; 1201 const char *destDir; 1202 unsigned int curDirSizeReq; 1203 unsigned int destDirSizeReq; 1204 char winDir[MAX_PATH], systemDir[MAX_PATH]; 1205 1206 /* Print out debugging information */ 1207 TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n", 1208 flags, debugstr_a(lpszFilename), debugstr_a(lpszWinDir), debugstr_a(lpszAppDir), 1209 lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0, 1210 lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 ); 1211 1212 /* Figure out where the file should go; shared files default to the 1213 system directory */ 1214 1215 GetSystemDirectoryA(systemDir, sizeof(systemDir)); 1216 curDir = ""; 1217 1218 if(flags & VFFF_ISSHAREDFILE) 1219 { 1220 destDir = systemDir; 1221 /* Were we given a filename? If so, try to find the file. */ 1222 if(lpszFilename) 1223 { 1224 if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir; 1225 else if(lpszAppDir && testFileExistenceA(lpszAppDir, lpszFilename, FALSE)) 1226 { 1227 curDir = lpszAppDir; 1228 retval |= VFF_CURNEDEST; 1229 } 1230 } 1231 } 1232 else /* not a shared file */ 1233 { 1234 destDir = lpszAppDir ? lpszAppDir : ""; 1235 if(lpszFilename) 1236 { 1237 GetWindowsDirectoryA( winDir, MAX_PATH ); 1238 if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir; 1239 else if(testFileExistenceA(winDir, lpszFilename, FALSE)) 1240 { 1241 curDir = winDir; 1242 retval |= VFF_CURNEDEST; 1243 } 1244 else if(testFileExistenceA(systemDir, lpszFilename, FALSE)) 1245 { 1246 curDir = systemDir; 1247 retval |= VFF_CURNEDEST; 1248 } 1249 } 1250 } 1251 1252 /* Check to see if the file exists and is in use by another application */ 1253 if (lpszFilename && testFileExistenceA(curDir, lpszFilename, FALSE)) { 1254 if (lpszFilename && !testFileExistenceA(curDir, lpszFilename, TRUE)) 1255 retval |= VFF_FILEINUSE; 1256 } 1257 1258 curDirSizeReq = strlen(curDir) + 1; 1259 destDirSizeReq = strlen(destDir) + 1; 1260 1261 /* Make sure that the pointers to the size of the buffers are 1262 valid; if not, do NOTHING with that buffer. If that pointer 1263 is valid, then make sure that the buffer pointer is valid, too! */ 1264 1265 if(lpuDestDirLen && lpszDestDir) 1266 { 1267 if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL; 1268 lstrcpynA(lpszDestDir, destDir, *lpuDestDirLen); 1269 *lpuDestDirLen = destDirSizeReq; 1270 } 1271 if(lpuCurDirLen && lpszCurDir) 1272 { 1273 if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL; 1274 lstrcpynA(lpszCurDir, curDir, *lpuCurDirLen); 1275 *lpuCurDirLen = curDirSizeReq; 1276 } 1277 1278 TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval, 1279 (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "", 1280 (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "", 1281 (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "", 1282 debugstr_a(lpszCurDir), debugstr_a(lpszDestDir)); 1283 1284 return retval; 1285 } 1286 1287 /***************************************************************************** 1288 * VerFindFileW [VERSION.@] 1289 */ 1290 DWORD WINAPI VerFindFileW( DWORD flags,LPCWSTR lpszFilename,LPCWSTR lpszWinDir, 1291 LPCWSTR lpszAppDir, LPWSTR lpszCurDir,PUINT lpuCurDirLen, 1292 LPWSTR lpszDestDir,PUINT lpuDestDirLen ) 1293 { 1294 static const WCHAR emptyW; 1295 DWORD retval = 0; 1296 const WCHAR *curDir; 1297 const WCHAR *destDir; 1298 unsigned int curDirSizeReq; 1299 unsigned int destDirSizeReq; 1300 WCHAR winDir[MAX_PATH], systemDir[MAX_PATH]; 1301 1302 /* Print out debugging information */ 1303 TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n", 1304 flags, debugstr_w(lpszFilename), debugstr_w(lpszWinDir), debugstr_w(lpszAppDir), 1305 lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0, 1306 lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 ); 1307 1308 /* Figure out where the file should go; shared files default to the 1309 system directory */ 1310 1311 GetSystemDirectoryW(systemDir, sizeof(systemDir)/sizeof(WCHAR)); 1312 curDir = &emptyW; 1313 1314 if(flags & VFFF_ISSHAREDFILE) 1315 { 1316 destDir = systemDir; 1317 /* Were we given a filename? If so, try to find the file. */ 1318 if(lpszFilename) 1319 { 1320 if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir; 1321 else if(lpszAppDir && testFileExistenceW(lpszAppDir, lpszFilename, FALSE)) 1322 { 1323 curDir = lpszAppDir; 1324 retval |= VFF_CURNEDEST; 1325 } 1326 } 1327 } 1328 else /* not a shared file */ 1329 { 1330 destDir = lpszAppDir ? lpszAppDir : &emptyW; 1331 if(lpszFilename) 1332 { 1333 GetWindowsDirectoryW( winDir, MAX_PATH ); 1334 if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir; 1335 else if(testFileExistenceW(winDir, lpszFilename, FALSE)) 1336 { 1337 curDir = winDir; 1338 retval |= VFF_CURNEDEST; 1339 } 1340 else if(testFileExistenceW(systemDir, lpszFilename, FALSE)) 1341 { 1342 curDir = systemDir; 1343 retval |= VFF_CURNEDEST; 1344 } 1345 } 1346 } 1347 1348 if (lpszFilename && !testFileExistenceW(curDir, lpszFilename, TRUE)) 1349 retval |= VFF_FILEINUSE; 1350 1351 curDirSizeReq = strlenW(curDir) + 1; 1352 destDirSizeReq = strlenW(destDir) + 1; 1353 1354 /* Make sure that the pointers to the size of the buffers are 1355 valid; if not, do NOTHING with that buffer. If that pointer 1356 is valid, then make sure that the buffer pointer is valid, too! */ 1357 1358 if(lpuDestDirLen && lpszDestDir) 1359 { 1360 if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL; 1361 lstrcpynW(lpszDestDir, destDir, *lpuDestDirLen); 1362 *lpuDestDirLen = destDirSizeReq; 1363 } 1364 if(lpuCurDirLen && lpszCurDir) 1365 { 1366 if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL; 1367 lstrcpynW(lpszCurDir, curDir, *lpuCurDirLen); 1368 *lpuCurDirLen = curDirSizeReq; 1369 } 1370 1371 TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval, 1372 (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "", 1373 (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "", 1374 (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "", 1375 debugstr_w(lpszCurDir), debugstr_w(lpszDestDir)); 1376 return retval; 1377 } 1378 1379 static LPBYTE 1380 _fetch_versioninfo(LPSTR fn,VS_FIXEDFILEINFO **vffi) { 1381 DWORD alloclen; 1382 LPBYTE buf; 1383 DWORD ret; 1384 1385 alloclen = 1000; 1386 buf=HeapAlloc(GetProcessHeap(), 0, alloclen); 1387 if(buf == NULL) { 1388 WARN("Memory exhausted while fetching version info!\n"); 1389 return NULL; 1390 } 1391 while (1) { 1392 ret = GetFileVersionInfoA(fn,0,alloclen,buf); 1393 if (!ret) { 1394 HeapFree(GetProcessHeap(), 0, buf); 1395 return NULL; 1396 } 1397 if (alloclen<*(WORD*)buf) { 1398 alloclen = *(WORD*)buf; 1399 HeapFree(GetProcessHeap(), 0, buf); 1400 buf = HeapAlloc(GetProcessHeap(), 0, alloclen); 1401 if(buf == NULL) { 1402 WARN("Memory exhausted while fetching version info!\n"); 1403 return NULL; 1404 } 1405 } else { 1406 *vffi = (VS_FIXEDFILEINFO*)(buf+0x14); 1407 if ((*vffi)->dwSignature == 0x004f0049) /* hack to detect unicode */ 1408 *vffi = (VS_FIXEDFILEINFO*)(buf+0x28); 1409 if ((*vffi)->dwSignature != VS_FFI_SIGNATURE) 1410 WARN("Bad VS_FIXEDFILEINFO signature 0x%08x\n",(*vffi)->dwSignature); 1411 return buf; 1412 } 1413 } 1414 } 1415 1416 static DWORD 1417 _error2vif(DWORD error) { 1418 switch (error) { 1419 case ERROR_ACCESS_DENIED: 1420 return VIF_ACCESSVIOLATION; 1421 case ERROR_SHARING_VIOLATION: 1422 return VIF_SHARINGVIOLATION; 1423 default: 1424 return 0; 1425 } 1426 } 1427 1428 1429 /****************************************************************************** 1430 * VerInstallFileA [VERSION.@] 1431 */ 1432 DWORD WINAPI VerInstallFileA( 1433 DWORD flags,LPCSTR srcfilename,LPCSTR destfilename,LPCSTR srcdir, 1434 LPCSTR destdir,LPCSTR curdir,LPSTR tmpfile,PUINT tmpfilelen ) 1435 { 1436 LPCSTR pdest; 1437 char destfn[260],tmpfn[260],srcfn[260]; 1438 HFILE hfsrc,hfdst; 1439 DWORD attr,xret,tmplast; 1440 LONG ret; 1441 LPBYTE buf1,buf2; 1442 OFSTRUCT ofs; 1443 1444 TRACE("(%x,%s,%s,%s,%s,%s,%p,%d)\n", 1445 flags,debugstr_a(srcfilename),debugstr_a(destfilename), 1446 debugstr_a(srcdir),debugstr_a(destdir),debugstr_a(curdir), 1447 tmpfile,*tmpfilelen); 1448 xret = 0; 1449 if (!srcdir || !srcfilename) return VIF_CANNOTREADSRC; 1450 sprintf(srcfn,"%s\\%s",srcdir,srcfilename); 1451 if (!destdir || !*destdir) pdest = srcdir; 1452 else pdest = destdir; 1453 sprintf(destfn,"%s\\%s",pdest,destfilename); 1454 hfsrc=LZOpenFileA(srcfn,&ofs,OF_READ); 1455 if (hfsrc < 0) 1456 return VIF_CANNOTREADSRC; 1457 sprintf(tmpfn,"%s\\%s",pdest,destfilename); 1458 tmplast=strlen(pdest)+1; 1459 attr = GetFileAttributesA(tmpfn); 1460 if (attr != INVALID_FILE_ATTRIBUTES) { 1461 if (attr & FILE_ATTRIBUTE_READONLY) { 1462 LZClose(hfsrc); 1463 return VIF_WRITEPROT; 1464 } 1465 /* FIXME: check if file currently in use and return VIF_FILEINUSE */ 1466 } 1467 attr = INVALID_FILE_ATTRIBUTES; 1468 if (flags & VIFF_FORCEINSTALL) { 1469 if (tmpfile[0]) { 1470 sprintf(tmpfn,"%s\\%s",pdest,tmpfile); 1471 tmplast = strlen(pdest)+1; 1472 attr = GetFileAttributesA(tmpfn); 1473 /* if it exists, it has been copied by the call before. 1474 * we jump over the copy part... 1475 */ 1476 } 1477 } 1478 if (attr == INVALID_FILE_ATTRIBUTES) { 1479 char *s; 1480 1481 GetTempFileNameA(pdest,"ver",0,tmpfn); /* should not fail ... */ 1482 s=strrchr(tmpfn,'\\'); 1483 if (s) 1484 tmplast = s-tmpfn; 1485 else 1486 tmplast = 0; 1487 hfdst = OpenFile(tmpfn,&ofs,OF_CREATE); 1488 if (hfdst == HFILE_ERROR) { 1489 LZClose(hfsrc); 1490 return VIF_CANNOTCREATE; /* | translated dos error */ 1491 } 1492 ret = LZCopy(hfsrc,hfdst); 1493 _lclose(hfdst); 1494 if (ret < 0) { 1495 /* translate LZ errors into VIF_xxx */ 1496 switch (ret) { 1497 case LZERROR_BADINHANDLE: 1498 case LZERROR_READ: 1499 case LZERROR_BADVALUE: 1500 case LZERROR_UNKNOWNALG: 1501 xret = VIF_CANNOTREADSRC; 1502 break; 1503 case LZERROR_BADOUTHANDLE: 1504 case LZERROR_WRITE: 1505 xret = VIF_OUTOFSPACE; 1506 break; 1507 case LZERROR_GLOBALLOC: 1508 case LZERROR_GLOBLOCK: 1509 xret = VIF_OUTOFMEMORY; 1510 break; 1511 default: /* unknown error, should not happen */ 1512 FIXME("Unknown LZCopy error %d, ignoring.\n", ret); 1513 xret = 0; 1514 break; 1515 } 1516 if (xret) { 1517 LZClose(hfsrc); 1518 return xret; 1519 } 1520 } 1521 } 1522 if (!(flags & VIFF_FORCEINSTALL)) { 1523 VS_FIXEDFILEINFO *destvffi,*tmpvffi; 1524 buf1 = _fetch_versioninfo(destfn,&destvffi); 1525 if (buf1) { 1526 buf2 = _fetch_versioninfo(tmpfn,&tmpvffi); 1527 if (buf2) { 1528 char *tbuf1,*tbuf2; 1529 static const CHAR trans_array[] = "\\VarFileInfo\\Translation"; 1530 UINT len1,len2; 1531 1532 len1=len2=40; 1533 1534 /* compare file versions */ 1535 if ((destvffi->dwFileVersionMS > tmpvffi->dwFileVersionMS)|| 1536 ((destvffi->dwFileVersionMS==tmpvffi->dwFileVersionMS)&& 1537 (destvffi->dwFileVersionLS > tmpvffi->dwFileVersionLS) 1538 ) 1539 ) 1540 xret |= VIF_MISMATCH|VIF_SRCOLD; 1541 /* compare filetypes and filesubtypes */ 1542 if ((destvffi->dwFileType!=tmpvffi->dwFileType) || 1543 (destvffi->dwFileSubtype!=tmpvffi->dwFileSubtype) 1544 ) 1545 xret |= VIF_MISMATCH|VIF_DIFFTYPE; 1546 if (VerQueryValueA(buf1,trans_array,(LPVOID*)&tbuf1,&len1) && 1547 VerQueryValueA(buf2,trans_array,(LPVOID*)&tbuf2,&len2) 1548 ) { 1549 /* Do something with tbuf1 and tbuf2 1550 * generates DIFFLANG|MISMATCH 1551 */ 1552 } 1553 HeapFree(GetProcessHeap(), 0, buf2); 1554 } else 1555 xret=VIF_MISMATCH|VIF_SRCOLD; 1556 HeapFree(GetProcessHeap(), 0, buf1); 1557 } 1558 } 1559 if (xret) { 1560 if (*tmpfilelen<strlen(tmpfn+tmplast)) { 1561 xret|=VIF_BUFFTOOSMALL; 1562 DeleteFileA(tmpfn); 1563 } else { 1564 strcpy(tmpfile,tmpfn+tmplast); 1565 *tmpfilelen = strlen(tmpfn+tmplast)+1; 1566 xret|=VIF_TEMPFILE; 1567 } 1568 } else { 1569 if (INVALID_FILE_ATTRIBUTES!=GetFileAttributesA(destfn)) 1570 if (!DeleteFileA(destfn)) { 1571 xret|=_error2vif(GetLastError())|VIF_CANNOTDELETE; 1572 DeleteFileA(tmpfn); 1573 LZClose(hfsrc); 1574 return xret; 1575 } 1576 if ((!(flags & VIFF_DONTDELETEOLD)) && 1577 curdir && 1578 *curdir && 1579 lstrcmpiA(curdir,pdest) 1580 ) { 1581 char curfn[260]; 1582 1583 sprintf(curfn,"%s\\%s",curdir,destfilename); 1584 if (INVALID_FILE_ATTRIBUTES != GetFileAttributesA(curfn)) { 1585 /* FIXME: check if in use ... if it is, VIF_CANNOTDELETECUR */ 1586 if (!DeleteFileA(curfn)) 1587 xret|=_error2vif(GetLastError())|VIF_CANNOTDELETECUR; 1588 } 1589 } 1590 if (!MoveFileA(tmpfn,destfn)) { 1591 xret|=_error2vif(GetLastError())|VIF_CANNOTRENAME; 1592 DeleteFileA(tmpfn); 1593 } 1594 } 1595 LZClose(hfsrc); 1596 return xret; 1597 } 1598 1599 1600 /****************************************************************************** 1601 * VerInstallFileW [VERSION.@] 1602 */ 1603 DWORD WINAPI VerInstallFileW( 1604 DWORD flags,LPCWSTR srcfilename,LPCWSTR destfilename,LPCWSTR srcdir, 1605 LPCWSTR destdir,LPCWSTR curdir,LPWSTR tmpfile,PUINT tmpfilelen ) 1606 { 1607 LPSTR wsrcf = NULL, wsrcd = NULL, wdestf = NULL, wdestd = NULL, wtmpf = NULL, wcurd = NULL; 1608 DWORD ret = 0; 1609 UINT len; 1610 1611 if (srcfilename) 1612 { 1613 len = WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, NULL, 0, NULL, NULL ); 1614 if ((wsrcf = HeapAlloc( GetProcessHeap(), 0, len ))) 1615 WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, wsrcf, len, NULL, NULL ); 1616 else 1617 ret = VIF_OUTOFMEMORY; 1618 } 1619 if (srcdir && !ret) 1620 { 1621 len = WideCharToMultiByte( CP_ACP, 0, srcdir, -1, NULL, 0, NULL, NULL ); 1622 if ((wsrcd = HeapAlloc( GetProcessHeap(), 0, len ))) 1623 WideCharToMultiByte( CP_ACP, 0, srcdir, -1, wsrcd, len, NULL, NULL ); 1624 else 1625 ret = VIF_OUTOFMEMORY; 1626 } 1627 if (destfilename && !ret) 1628 { 1629 len = WideCharToMultiByte( CP_ACP, 0, destfilename, -1, NULL, 0, NULL, NULL ); 1630 if ((wdestf = HeapAlloc( GetProcessHeap(), 0, len ))) 1631 WideCharToMultiByte( CP_ACP, 0, destfilename, -1, wdestf, len, NULL, NULL ); 1632 else 1633 ret = VIF_OUTOFMEMORY; 1634 } 1635 if (destdir && !ret) 1636 { 1637 len = WideCharToMultiByte( CP_ACP, 0, destdir, -1, NULL, 0, NULL, NULL ); 1638 if ((wdestd = HeapAlloc( GetProcessHeap(), 0, len ))) 1639 WideCharToMultiByte( CP_ACP, 0, destdir, -1, wdestd, len, NULL, NULL ); 1640 else 1641 ret = VIF_OUTOFMEMORY; 1642 } 1643 if (curdir && !ret) 1644 { 1645 len = WideCharToMultiByte( CP_ACP, 0, curdir, -1, NULL, 0, NULL, NULL ); 1646 if ((wcurd = HeapAlloc( GetProcessHeap(), 0, len ))) 1647 WideCharToMultiByte( CP_ACP, 0, curdir, -1, wcurd, len, NULL, NULL ); 1648 else 1649 ret = VIF_OUTOFMEMORY; 1650 } 1651 if (!ret) 1652 { 1653 len = *tmpfilelen * sizeof(WCHAR); 1654 wtmpf = HeapAlloc( GetProcessHeap(), 0, len ); 1655 if (!wtmpf) 1656 ret = VIF_OUTOFMEMORY; 1657 } 1658 if (!ret) 1659 ret = VerInstallFileA(flags,wsrcf,wdestf,wsrcd,wdestd,wcurd,wtmpf,&len); 1660 if (!ret) 1661 *tmpfilelen = MultiByteToWideChar( CP_ACP, 0, wtmpf, -1, tmpfile, *tmpfilelen ); 1662 else if (ret & VIF_BUFFTOOSMALL) 1663 *tmpfilelen = len; /* FIXME: not correct */ 1664 1665 HeapFree( GetProcessHeap(), 0, wsrcf ); 1666 HeapFree( GetProcessHeap(), 0, wsrcd ); 1667 HeapFree( GetProcessHeap(), 0, wdestf ); 1668 HeapFree( GetProcessHeap(), 0, wdestd ); 1669 HeapFree( GetProcessHeap(), 0, wtmpf ); 1670 HeapFree( GetProcessHeap(), 0, wcurd ); 1671 return ret; 1672 } 1673