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