version: Use boolean return value in boolean function.
[wine/multimedia.git] / dlls / version / version.c
blobd5d84526a718e962f5cff1487951c2a985d7f4f1
1 /*
2 * Implementation of VERSION.DLL
4 * Copyright 1996,1997 Marcus Meissner
5 * Copyright 1997 David Cuthbert
6 * Copyright 1999 Ulrich Weigand
7 * Copyright 2005 Paul Vriens
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.
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.
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
24 #include "config.h"
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdio.h>
31 #include <sys/types.h>
32 #ifdef HAVE_UNISTD_H
33 # include <unistd.h>
34 #endif
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"
50 WINE_DEFAULT_DEBUG_CHANNEL(ver);
52 typedef struct
54 WORD offset;
55 WORD length;
56 WORD flags;
57 WORD id;
58 WORD handle;
59 WORD usage;
60 } NE_NAMEINFO;
62 typedef struct
64 WORD type_id;
65 WORD count;
66 DWORD resloader;
67 } NE_TYPEINFO;
69 /**********************************************************************
70 * find_entry_by_id
72 * Find an entry by id in a resource directory
73 * Copied from loader/pe_resource.c
75 static const IMAGE_RESOURCE_DIRECTORY *find_entry_by_id( const IMAGE_RESOURCE_DIRECTORY *dir,
76 WORD id, const void *root )
78 const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
79 int min, max, pos;
81 entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
82 min = dir->NumberOfNamedEntries;
83 max = min + dir->NumberOfIdEntries - 1;
84 while (min <= max)
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;
92 return NULL;
96 /**********************************************************************
97 * find_entry_default
99 * Find a default entry in a resource directory
100 * Copied from loader/pe_resource.c
102 static const IMAGE_RESOURCE_DIRECTORY *find_entry_default( const IMAGE_RESOURCE_DIRECTORY *dir,
103 const void *root )
105 const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
107 entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
108 return (const IMAGE_RESOURCE_DIRECTORY *)((const char *)root + entry->u2.s2.OffsetToDirectory);
112 /**********************************************************************
113 * push_language
115 * push a language onto the list of languages to try
117 static inline int push_language( WORD *list, int pos, WORD lang )
119 int i;
120 for (i = 0; i < pos; i++) if (list[i] == lang) return pos;
121 list[pos++] = lang;
122 return pos;
126 /**********************************************************************
127 * find_entry_language
129 static const IMAGE_RESOURCE_DIRECTORY *find_entry_language( const IMAGE_RESOURCE_DIRECTORY *dir,
130 const void *root )
132 const IMAGE_RESOURCE_DIRECTORY *ret;
133 WORD list[9];
134 int i, pos = 0;
136 /* cf. LdrFindResource_U */
137 pos = push_language( list, pos, MAKELANGID( LANG_NEUTRAL, SUBLANG_NEUTRAL ) );
138 pos = push_language( list, pos, LANGIDFROMLCID( NtCurrentTeb()->CurrentLocale ) );
139 pos = push_language( list, pos, GetUserDefaultLangID() );
140 pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(GetUserDefaultLangID()), SUBLANG_NEUTRAL ));
141 pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(GetUserDefaultLangID()), SUBLANG_DEFAULT ));
142 pos = push_language( list, pos, GetSystemDefaultLangID() );
143 pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(GetSystemDefaultLangID()), SUBLANG_NEUTRAL ));
144 pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(GetSystemDefaultLangID()), SUBLANG_DEFAULT ));
145 pos = push_language( list, pos, MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT ) );
147 for (i = 0; i < pos; i++) if ((ret = find_entry_by_id( dir, list[i], root ))) return ret;
148 return find_entry_default( dir, root );
152 /***********************************************************************
153 * read_xx_header [internal]
155 static int read_xx_header( HFILE lzfd )
157 IMAGE_DOS_HEADER mzh;
158 char magic[3];
160 LZSeek( lzfd, 0, SEEK_SET );
161 if ( sizeof(mzh) != LZRead( lzfd, (LPSTR)&mzh, sizeof(mzh) ) )
162 return 0;
163 if ( mzh.e_magic != IMAGE_DOS_SIGNATURE )
165 if (!memcmp( &mzh, "\177ELF", 4 )) return 1; /* ELF */
166 if (*(UINT *)&mzh == 0xfeedface || *(UINT *)&mzh == 0xcefaedfe) return 1; /* Mach-O */
167 return 0;
170 LZSeek( lzfd, mzh.e_lfanew, SEEK_SET );
171 if ( 2 != LZRead( lzfd, magic, 2 ) )
172 return 0;
174 LZSeek( lzfd, mzh.e_lfanew, SEEK_SET );
176 if ( magic[0] == 'N' && magic[1] == 'E' )
177 return IMAGE_OS2_SIGNATURE;
178 if ( magic[0] == 'P' && magic[1] == 'E' )
179 return IMAGE_NT_SIGNATURE;
181 magic[2] = '\0';
182 WARN("Can't handle %s files.\n", magic );
183 return 0;
186 /***********************************************************************
187 * find_ne_resource [internal]
189 static BOOL find_ne_resource( HFILE lzfd, DWORD *resLen, DWORD *resOff )
191 const WORD typeid = VS_FILE_INFO | 0x8000;
192 const WORD resid = VS_VERSION_INFO | 0x8000;
193 IMAGE_OS2_HEADER nehd;
194 NE_TYPEINFO *typeInfo;
195 NE_NAMEINFO *nameInfo;
196 DWORD nehdoffset;
197 LPBYTE resTab;
198 DWORD resTabSize;
199 int count;
201 /* Read in NE header */
202 nehdoffset = LZSeek( lzfd, 0, SEEK_CUR );
203 if ( sizeof(nehd) != LZRead( lzfd, (LPSTR)&nehd, sizeof(nehd) ) ) return FALSE;
205 resTabSize = nehd.ne_restab - nehd.ne_rsrctab;
206 if ( !resTabSize )
208 TRACE("No resources in NE dll\n" );
209 return FALSE;
212 /* Read in resource table */
213 resTab = HeapAlloc( GetProcessHeap(), 0, resTabSize );
214 if ( !resTab ) return FALSE;
216 LZSeek( lzfd, nehd.ne_rsrctab + nehdoffset, SEEK_SET );
217 if ( resTabSize != LZRead( lzfd, (char*)resTab, resTabSize ) )
219 HeapFree( GetProcessHeap(), 0, resTab );
220 return FALSE;
223 /* Find resource */
224 typeInfo = (NE_TYPEINFO *)(resTab + 2);
225 while (typeInfo->type_id)
227 if (typeInfo->type_id == typeid) goto found_type;
228 typeInfo = (NE_TYPEINFO *)((char *)(typeInfo + 1) +
229 typeInfo->count * sizeof(NE_NAMEINFO));
231 TRACE("No typeid entry found\n" );
232 HeapFree( GetProcessHeap(), 0, resTab );
233 return FALSE;
235 found_type:
236 nameInfo = (NE_NAMEINFO *)(typeInfo + 1);
238 for (count = typeInfo->count; count > 0; count--, nameInfo++)
239 if (nameInfo->id == resid) goto found_name;
241 TRACE("No resid entry found\n" );
242 HeapFree( GetProcessHeap(), 0, resTab );
243 return FALSE;
245 found_name:
246 /* Return resource data */
247 if ( resLen ) *resLen = nameInfo->length << *(WORD *)resTab;
248 if ( resOff ) *resOff = nameInfo->offset << *(WORD *)resTab;
250 HeapFree( GetProcessHeap(), 0, resTab );
251 return TRUE;
254 /***********************************************************************
255 * find_pe_resource [internal]
257 static BOOL find_pe_resource( HFILE lzfd, DWORD *resLen, DWORD *resOff )
259 union
261 IMAGE_NT_HEADERS32 nt32;
262 IMAGE_NT_HEADERS64 nt64;
263 } pehd;
264 DWORD pehdoffset;
265 PIMAGE_DATA_DIRECTORY resDataDir;
266 PIMAGE_SECTION_HEADER sections;
267 LPBYTE resSection;
268 DWORD section_size, data_size;
269 const void *resDir;
270 const IMAGE_RESOURCE_DIRECTORY *resPtr;
271 const IMAGE_RESOURCE_DATA_ENTRY *resData;
272 int i, len, nSections;
273 BOOL ret = FALSE;
275 /* Read in PE header */
276 pehdoffset = LZSeek( lzfd, 0, SEEK_CUR );
277 len = LZRead( lzfd, (LPSTR)&pehd, sizeof(pehd) );
278 if (len < sizeof(pehd.nt32.FileHeader)) return FALSE;
279 if (len < sizeof(pehd)) memset( (char *)&pehd + len, 0, sizeof(pehd) - len );
281 switch (pehd.nt32.OptionalHeader.Magic)
283 case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
284 resDataDir = pehd.nt32.OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_RESOURCE;
285 break;
286 case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
287 resDataDir = pehd.nt64.OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_RESOURCE;
288 break;
289 default:
290 return FALSE;
293 if ( !resDataDir->Size )
295 TRACE("No resources in PE dll\n" );
296 return FALSE;
299 /* Read in section table */
300 nSections = pehd.nt32.FileHeader.NumberOfSections;
301 sections = HeapAlloc( GetProcessHeap(), 0,
302 nSections * sizeof(IMAGE_SECTION_HEADER) );
303 if ( !sections ) return FALSE;
305 len = FIELD_OFFSET( IMAGE_NT_HEADERS32, OptionalHeader ) + pehd.nt32.FileHeader.SizeOfOptionalHeader;
306 LZSeek( lzfd, pehdoffset + len, SEEK_SET );
308 if ( nSections * sizeof(IMAGE_SECTION_HEADER) !=
309 LZRead( lzfd, (LPSTR)sections, nSections * sizeof(IMAGE_SECTION_HEADER) ) )
311 HeapFree( GetProcessHeap(), 0, sections );
312 return FALSE;
315 /* Find resource section */
316 for ( i = 0; i < nSections; i++ )
317 if ( resDataDir->VirtualAddress >= sections[i].VirtualAddress
318 && resDataDir->VirtualAddress < sections[i].VirtualAddress +
319 sections[i].SizeOfRawData )
320 break;
322 if ( i == nSections )
324 HeapFree( GetProcessHeap(), 0, sections );
325 TRACE("Couldn't find resource section\n" );
326 return FALSE;
329 /* Read in resource section */
330 data_size = sections[i].SizeOfRawData;
331 section_size = max( data_size, sections[i].Misc.VirtualSize );
332 resSection = HeapAlloc( GetProcessHeap(), 0, section_size );
333 if ( !resSection )
335 HeapFree( GetProcessHeap(), 0, sections );
336 return FALSE;
339 LZSeek( lzfd, sections[i].PointerToRawData, SEEK_SET );
340 if (data_size != LZRead( lzfd, (char*)resSection, data_size )) goto done;
341 if (data_size < section_size) memset( (char *)resSection + data_size, 0, section_size - data_size );
343 /* Find resource */
344 resDir = resSection + (resDataDir->VirtualAddress - sections[i].VirtualAddress);
346 resPtr = resDir;
347 resPtr = find_entry_by_id( resPtr, VS_FILE_INFO, resDir );
348 if ( !resPtr )
350 TRACE("No typeid entry found\n" );
351 goto done;
353 resPtr = find_entry_by_id( resPtr, VS_VERSION_INFO, resDir );
354 if ( !resPtr )
356 TRACE("No resid entry found\n" );
357 goto done;
359 resPtr = find_entry_language( resPtr, resDir );
360 if ( !resPtr )
362 TRACE("No default language entry found\n" );
363 goto done;
366 /* Find resource data section */
367 resData = (const IMAGE_RESOURCE_DATA_ENTRY*)resPtr;
368 for ( i = 0; i < nSections; i++ )
369 if ( resData->OffsetToData >= sections[i].VirtualAddress
370 && resData->OffsetToData < sections[i].VirtualAddress +
371 sections[i].SizeOfRawData )
372 break;
374 if ( i == nSections )
376 TRACE("Couldn't find resource data section\n" );
377 goto done;
380 /* Return resource data */
381 if ( resLen ) *resLen = resData->Size;
382 if ( resOff ) *resOff = resData->OffsetToData - sections[i].VirtualAddress
383 + sections[i].PointerToRawData;
384 ret = TRUE;
386 done:
387 HeapFree( GetProcessHeap(), 0, resSection );
388 HeapFree( GetProcessHeap(), 0, sections );
389 return ret;
393 /***********************************************************************
394 * find_version_resource [internal]
396 static DWORD find_version_resource( HFILE lzfd, DWORD *reslen, DWORD *offset )
398 DWORD magic = read_xx_header( lzfd );
400 switch (magic)
402 case IMAGE_OS2_SIGNATURE:
403 if (!find_ne_resource( lzfd, reslen, offset )) magic = 0;
404 break;
405 case IMAGE_NT_SIGNATURE:
406 if (!find_pe_resource( lzfd, reslen, offset )) magic = 0;
407 break;
409 return magic;
412 /******************************************************************************
414 * This function will print via standard TRACE, debug info regarding
415 * the file info structure vffi.
416 * 15-Feb-1998 Dimitrie Paun (dimi@cs.toronto.edu)
417 * Added this function to clean up the code.
419 *****************************************************************************/
420 static void print_vffi_debug(const VS_FIXEDFILEINFO *vffi)
422 BOOL versioned_printer = FALSE;
424 if((vffi->dwFileType == VFT_DLL) || (vffi->dwFileType == VFT_DRV))
426 if(vffi->dwFileSubtype == VFT2_DRV_VERSIONED_PRINTER)
427 /* this is documented for newer w2k Drivers and up */
428 versioned_printer = TRUE;
429 else if( (vffi->dwFileSubtype == VFT2_DRV_PRINTER) &&
430 (vffi->dwFileVersionMS != vffi->dwProductVersionMS) &&
431 (vffi->dwFileVersionMS > 0) &&
432 (vffi->dwFileVersionMS <= 3) )
433 /* found this on NT 3.51, NT4.0 and old w2k Drivers */
434 versioned_printer = TRUE;
437 TRACE("structversion=%u.%u, ",
438 HIWORD(vffi->dwStrucVersion),LOWORD(vffi->dwStrucVersion));
439 if(versioned_printer)
441 WORD mode = LOWORD(vffi->dwFileVersionMS);
442 WORD ver_rev = HIWORD(vffi->dwFileVersionLS);
443 TRACE("fileversion=%u.%u.%u.%u (%s.major.minor.release), ",
444 (vffi->dwFileVersionMS),
445 HIBYTE(ver_rev), LOBYTE(ver_rev), LOWORD(vffi->dwFileVersionLS),
446 (mode == 3) ? "Usermode" : ((mode <= 2) ? "Kernelmode" : "?") );
448 else
450 TRACE("fileversion=%u.%u.%u.%u, ",
451 HIWORD(vffi->dwFileVersionMS),LOWORD(vffi->dwFileVersionMS),
452 HIWORD(vffi->dwFileVersionLS),LOWORD(vffi->dwFileVersionLS));
454 TRACE("productversion=%u.%u.%u.%u\n",
455 HIWORD(vffi->dwProductVersionMS),LOWORD(vffi->dwProductVersionMS),
456 HIWORD(vffi->dwProductVersionLS),LOWORD(vffi->dwProductVersionLS));
458 TRACE("flagmask=0x%x, flags=0x%x %s%s%s%s%s%s\n",
459 vffi->dwFileFlagsMask, vffi->dwFileFlags,
460 (vffi->dwFileFlags & VS_FF_DEBUG) ? "DEBUG," : "",
461 (vffi->dwFileFlags & VS_FF_PRERELEASE) ? "PRERELEASE," : "",
462 (vffi->dwFileFlags & VS_FF_PATCHED) ? "PATCHED," : "",
463 (vffi->dwFileFlags & VS_FF_PRIVATEBUILD) ? "PRIVATEBUILD," : "",
464 (vffi->dwFileFlags & VS_FF_INFOINFERRED) ? "INFOINFERRED," : "",
465 (vffi->dwFileFlags & VS_FF_SPECIALBUILD) ? "SPECIALBUILD," : "");
467 TRACE("(");
469 TRACE("OS=0x%x.0x%x ", HIWORD(vffi->dwFileOS), LOWORD(vffi->dwFileOS));
471 switch (vffi->dwFileOS&0xFFFF0000)
473 case VOS_DOS:TRACE("DOS,");break;
474 case VOS_OS216:TRACE("OS/2-16,");break;
475 case VOS_OS232:TRACE("OS/2-32,");break;
476 case VOS_NT:TRACE("NT,");break;
477 case VOS_UNKNOWN:
478 default:
479 TRACE("UNKNOWN(0x%x),",vffi->dwFileOS&0xFFFF0000);break;
482 switch (LOWORD(vffi->dwFileOS))
484 case VOS__BASE:TRACE("BASE");break;
485 case VOS__WINDOWS16:TRACE("WIN16");break;
486 case VOS__WINDOWS32:TRACE("WIN32");break;
487 case VOS__PM16:TRACE("PM16");break;
488 case VOS__PM32:TRACE("PM32");break;
489 default:
490 TRACE("UNKNOWN(0x%x)",LOWORD(vffi->dwFileOS));break;
493 TRACE(")\n");
495 switch (vffi->dwFileType)
497 case VFT_APP:TRACE("filetype=APP");break;
498 case VFT_DLL:
499 TRACE("filetype=DLL");
500 if(vffi->dwFileSubtype != 0)
502 if(versioned_printer) /* NT3.x/NT4.0 or old w2k Driver */
503 TRACE(",PRINTER");
504 TRACE(" (subtype=0x%x)", vffi->dwFileSubtype);
506 break;
507 case VFT_DRV:
508 TRACE("filetype=DRV,");
509 switch(vffi->dwFileSubtype)
511 case VFT2_DRV_PRINTER:TRACE("PRINTER");break;
512 case VFT2_DRV_KEYBOARD:TRACE("KEYBOARD");break;
513 case VFT2_DRV_LANGUAGE:TRACE("LANGUAGE");break;
514 case VFT2_DRV_DISPLAY:TRACE("DISPLAY");break;
515 case VFT2_DRV_MOUSE:TRACE("MOUSE");break;
516 case VFT2_DRV_NETWORK:TRACE("NETWORK");break;
517 case VFT2_DRV_SYSTEM:TRACE("SYSTEM");break;
518 case VFT2_DRV_INSTALLABLE:TRACE("INSTALLABLE");break;
519 case VFT2_DRV_SOUND:TRACE("SOUND");break;
520 case VFT2_DRV_COMM:TRACE("COMM");break;
521 case VFT2_DRV_INPUTMETHOD:TRACE("INPUTMETHOD");break;
522 case VFT2_DRV_VERSIONED_PRINTER:TRACE("VERSIONED_PRINTER");break;
523 case VFT2_UNKNOWN:
524 default:
525 TRACE("UNKNOWN(0x%x)",vffi->dwFileSubtype);break;
527 break;
528 case VFT_FONT:
529 TRACE("filetype=FONT,");
530 switch (vffi->dwFileSubtype)
532 case VFT2_FONT_RASTER:TRACE("RASTER");break;
533 case VFT2_FONT_VECTOR:TRACE("VECTOR");break;
534 case VFT2_FONT_TRUETYPE:TRACE("TRUETYPE");break;
535 default:TRACE("UNKNOWN(0x%x)",vffi->dwFileSubtype);break;
537 break;
538 case VFT_VXD:TRACE("filetype=VXD");break;
539 case VFT_STATIC_LIB:TRACE("filetype=STATIC_LIB");break;
540 case VFT_UNKNOWN:
541 default:
542 TRACE("filetype=Unknown(0x%x)",vffi->dwFileType);break;
545 TRACE("\n");
546 TRACE("filedate=0x%x.0x%x\n",vffi->dwFileDateMS,vffi->dwFileDateLS);
549 /***********************************************************************
550 * Version Info Structure
553 typedef struct
555 WORD wLength;
556 WORD wValueLength;
557 CHAR szKey[1];
558 #if 0 /* variable length structure */
559 /* DWORD aligned */
560 BYTE Value[];
561 /* DWORD aligned */
562 VS_VERSION_INFO_STRUCT16 Children[];
563 #endif
564 } VS_VERSION_INFO_STRUCT16;
566 typedef struct
568 WORD wLength;
569 WORD wValueLength;
570 WORD wType;
571 WCHAR szKey[1];
572 #if 0 /* variable length structure */
573 /* DWORD aligned */
574 BYTE Value[];
575 /* DWORD aligned */
576 VS_VERSION_INFO_STRUCT32 Children[];
577 #endif
578 } VS_VERSION_INFO_STRUCT32;
580 #define VersionInfoIs16( ver ) \
581 ( ((const VS_VERSION_INFO_STRUCT16 *)ver)->szKey[0] >= ' ' )
583 #define DWORD_ALIGN( base, ptr ) \
584 ( (LPBYTE)(base) + ((((LPBYTE)(ptr) - (LPBYTE)(base)) + 3) & ~3) )
586 #define VersionInfo16_Value( ver ) \
587 DWORD_ALIGN( (ver), (ver)->szKey + strlen((ver)->szKey) + 1 )
588 #define VersionInfo32_Value( ver ) \
589 DWORD_ALIGN( (ver), (ver)->szKey + strlenW((ver)->szKey) + 1 )
591 #define VersionInfo16_Children( ver ) \
592 (const VS_VERSION_INFO_STRUCT16 *)( VersionInfo16_Value( ver ) + \
593 ( ( (ver)->wValueLength + 3 ) & ~3 ) )
594 #define VersionInfo32_Children( ver ) \
595 (const VS_VERSION_INFO_STRUCT32 *)( VersionInfo32_Value( ver ) + \
596 ( ( (ver)->wValueLength * \
597 ((ver)->wType? 2 : 1) + 3 ) & ~3 ) )
599 #define VersionInfo16_Next( ver ) \
600 (VS_VERSION_INFO_STRUCT16 *)( (LPBYTE)ver + (((ver)->wLength + 3) & ~3) )
601 #define VersionInfo32_Next( ver ) \
602 (VS_VERSION_INFO_STRUCT32 *)( (LPBYTE)ver + (((ver)->wLength + 3) & ~3) )
605 /***********************************************************************
606 * GetFileVersionInfoSizeW [VERSION.@]
608 DWORD WINAPI GetFileVersionInfoSizeW( LPCWSTR filename, LPDWORD handle )
610 DWORD len, offset, magic = 1;
611 HFILE lzfd;
612 HMODULE hModule;
613 OFSTRUCT ofs;
615 TRACE("(%s,%p)\n", debugstr_w(filename), handle );
617 if (handle) *handle = 0;
619 if (!filename)
621 SetLastError(ERROR_INVALID_PARAMETER);
622 return 0;
624 if (!*filename)
626 SetLastError(ERROR_BAD_PATHNAME);
627 return 0;
630 if ((lzfd = LZOpenFileW( (LPWSTR)filename, &ofs, OF_READ )) != HFILE_ERROR)
632 magic = find_version_resource( lzfd, &len, &offset );
633 LZClose( lzfd );
636 if ((magic == 1) && (hModule = LoadLibraryExW( filename, 0, LOAD_LIBRARY_AS_DATAFILE )))
638 HRSRC hRsrc = FindResourceW( hModule, MAKEINTRESOURCEW(VS_VERSION_INFO),
639 MAKEINTRESOURCEW(VS_FILE_INFO) );
640 if (hRsrc)
642 magic = IMAGE_NT_SIGNATURE;
643 len = SizeofResource( hModule, hRsrc );
645 FreeLibrary( hModule );
648 switch (magic)
650 case IMAGE_OS2_SIGNATURE:
651 /* We have a 16bit resource.
653 * XP/W2K/W2K3 uses a buffer which is more than the actual needed space:
655 * (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4
657 * This extra buffer is used for ANSI to Unicode conversions in W-Calls.
658 * info->wLength should be the same as len. Currently it isn't but that
659 * doesn't seem to be a problem (len is bigger than info->wLength).
661 SetLastError(0);
662 return (len - sizeof(VS_FIXEDFILEINFO)) * 4;
664 case IMAGE_NT_SIGNATURE:
665 /* We have a 32bit resource.
667 * XP/W2K/W2K3 uses a buffer which is 2 times the actual needed space + 4 bytes "FE2X"
668 * This extra buffer is used for Unicode to ANSI conversions in A-Calls
670 SetLastError(0);
671 return (len * 2) + 4;
673 default:
674 SetLastError( lzfd == HFILE_ERROR ? ofs.nErrCode : ERROR_RESOURCE_DATA_NOT_FOUND );
675 return 0;
679 /***********************************************************************
680 * GetFileVersionInfoSizeA [VERSION.@]
682 DWORD WINAPI GetFileVersionInfoSizeA( LPCSTR filename, LPDWORD handle )
684 UNICODE_STRING filenameW;
685 DWORD retval;
687 TRACE("(%s,%p)\n", debugstr_a(filename), handle );
689 if(filename)
690 RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
691 else
692 filenameW.Buffer = NULL;
694 retval = GetFileVersionInfoSizeW(filenameW.Buffer, handle);
696 RtlFreeUnicodeString(&filenameW);
698 return retval;
701 /***********************************************************************
702 * GetFileVersionInfoW [VERSION.@]
704 BOOL WINAPI GetFileVersionInfoW( LPCWSTR filename, DWORD handle,
705 DWORD datasize, LPVOID data )
707 static const char signature[4] = "FE2X";
708 DWORD len, offset, magic = 1;
709 HFILE lzfd;
710 OFSTRUCT ofs;
711 HMODULE hModule;
712 VS_VERSION_INFO_STRUCT32* vvis = data;
714 TRACE("(%s,%d,size=%d,data=%p)\n",
715 debugstr_w(filename), handle, datasize, data );
717 if (!data)
719 SetLastError(ERROR_INVALID_DATA);
720 return FALSE;
723 if ((lzfd = LZOpenFileW( (LPWSTR)filename, &ofs, OF_READ )) != HFILE_ERROR)
725 if ((magic = find_version_resource( lzfd, &len, &offset )) > 1)
727 LZSeek( lzfd, offset, 0 /* SEEK_SET */ );
728 len = LZRead( lzfd, data, min( len, datasize ) );
730 LZClose( lzfd );
733 if ((magic == 1) && (hModule = LoadLibraryExW( filename, 0, LOAD_LIBRARY_AS_DATAFILE )))
735 HRSRC hRsrc = FindResourceW( hModule, MAKEINTRESOURCEW(VS_VERSION_INFO),
736 MAKEINTRESOURCEW(VS_FILE_INFO) );
737 if (hRsrc)
739 HGLOBAL hMem = LoadResource( hModule, hRsrc );
740 magic = IMAGE_NT_SIGNATURE;
741 len = min( SizeofResource(hModule, hRsrc), datasize );
742 memcpy( data, LockResource( hMem ), len );
743 FreeResource( hMem );
745 FreeLibrary( hModule );
748 switch (magic)
750 case IMAGE_OS2_SIGNATURE:
751 /* We have a 16bit resource. */
752 if (TRACE_ON(ver))
753 print_vffi_debug( (VS_FIXEDFILEINFO *)VersionInfo16_Value( (VS_VERSION_INFO_STRUCT16 *)data ));
754 SetLastError(0);
755 return TRUE;
757 case IMAGE_NT_SIGNATURE:
758 /* We have a 32bit resource.
760 * XP/W2K/W2K3 uses a buffer which is 2 times the actual needed space + 4 bytes "FE2X"
761 * This extra buffer is used for Unicode to ANSI conversions in A-Calls
763 len = vvis->wLength + sizeof(signature);
764 if (datasize >= len) memcpy( (char*)data + vvis->wLength, signature, sizeof(signature) );
765 if (TRACE_ON(ver))
766 print_vffi_debug( (VS_FIXEDFILEINFO *)VersionInfo32_Value( vvis ));
767 SetLastError(0);
768 return TRUE;
770 default:
771 SetLastError( lzfd == HFILE_ERROR ? ofs.nErrCode : ERROR_RESOURCE_DATA_NOT_FOUND );
772 return FALSE;
776 /***********************************************************************
777 * GetFileVersionInfoA [VERSION.@]
779 BOOL WINAPI GetFileVersionInfoA( LPCSTR filename, DWORD handle,
780 DWORD datasize, LPVOID data )
782 UNICODE_STRING filenameW;
783 BOOL retval;
785 TRACE("(%s,%d,size=%d,data=%p)\n",
786 debugstr_a(filename), handle, datasize, data );
788 if(filename)
789 RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
790 else
791 filenameW.Buffer = NULL;
793 retval = GetFileVersionInfoW(filenameW.Buffer, handle, datasize, data);
795 RtlFreeUnicodeString(&filenameW);
797 return retval;
800 /***********************************************************************
801 * VersionInfo16_FindChild [internal]
803 static const VS_VERSION_INFO_STRUCT16 *VersionInfo16_FindChild( const VS_VERSION_INFO_STRUCT16 *info,
804 LPCSTR szKey, UINT cbKey )
806 const VS_VERSION_INFO_STRUCT16 *child = VersionInfo16_Children( info );
808 while ((char *)child < (char *)info + info->wLength )
810 if (!strncasecmp( child->szKey, szKey, cbKey ) && !child->szKey[cbKey])
811 return child;
813 if (!(child->wLength)) return NULL;
814 child = VersionInfo16_Next( child );
817 return NULL;
820 /***********************************************************************
821 * VersionInfo32_FindChild [internal]
823 static const VS_VERSION_INFO_STRUCT32 *VersionInfo32_FindChild( const VS_VERSION_INFO_STRUCT32 *info,
824 LPCWSTR szKey, UINT cbKey )
826 const VS_VERSION_INFO_STRUCT32 *child = VersionInfo32_Children( info );
828 while ((char *)child < (char *)info + info->wLength )
830 if (!strncmpiW( child->szKey, szKey, cbKey ) && !child->szKey[cbKey])
831 return child;
833 if (!(child->wLength)) return NULL;
834 child = VersionInfo32_Next( child );
837 return NULL;
840 /***********************************************************************
841 * VersionInfo16_QueryValue [internal]
843 * Gets a value from a 16-bit NE resource
845 static BOOL VersionInfo16_QueryValue( const VS_VERSION_INFO_STRUCT16 *info, LPCSTR lpSubBlock,
846 LPVOID *lplpBuffer, UINT *puLen )
848 while ( *lpSubBlock )
850 /* Find next path component */
851 LPCSTR lpNextSlash;
852 for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ )
853 if ( *lpNextSlash == '\\' )
854 break;
856 /* Skip empty components */
857 if ( lpNextSlash == lpSubBlock )
859 lpSubBlock++;
860 continue;
863 /* We have a non-empty component: search info for key */
864 info = VersionInfo16_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock );
865 if ( !info )
867 if (puLen) *puLen = 0 ;
868 SetLastError( ERROR_RESOURCE_TYPE_NOT_FOUND );
869 return FALSE;
872 /* Skip path component */
873 lpSubBlock = lpNextSlash;
876 /* Return value */
877 *lplpBuffer = VersionInfo16_Value( info );
878 if (puLen)
879 *puLen = info->wValueLength;
881 return TRUE;
884 /***********************************************************************
885 * VersionInfo32_QueryValue [internal]
887 * Gets a value from a 32-bit PE resource
889 static BOOL VersionInfo32_QueryValue( const VS_VERSION_INFO_STRUCT32 *info, LPCWSTR lpSubBlock,
890 LPVOID *lplpBuffer, UINT *puLen )
892 TRACE("lpSubBlock : (%s)\n", debugstr_w(lpSubBlock));
894 while ( *lpSubBlock )
896 /* Find next path component */
897 LPCWSTR lpNextSlash;
898 for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ )
899 if ( *lpNextSlash == '\\' )
900 break;
902 /* Skip empty components */
903 if ( lpNextSlash == lpSubBlock )
905 lpSubBlock++;
906 continue;
909 /* We have a non-empty component: search info for key */
910 info = VersionInfo32_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock );
911 if ( !info )
913 if (puLen) *puLen = 0 ;
914 SetLastError( ERROR_RESOURCE_TYPE_NOT_FOUND );
915 return FALSE;
918 /* Skip path component */
919 lpSubBlock = lpNextSlash;
922 /* Return value */
923 *lplpBuffer = VersionInfo32_Value( info );
924 if (puLen)
925 *puLen = info->wValueLength;
927 return TRUE;
930 /***********************************************************************
931 * VerQueryValueA [VERSION.@]
933 BOOL WINAPI VerQueryValueA( LPCVOID pBlock, LPCSTR lpSubBlock,
934 LPVOID *lplpBuffer, PUINT puLen )
936 static const char rootA[] = "\\";
937 static const char varfileinfoA[] = "\\VarFileInfo\\Translation";
938 const VS_VERSION_INFO_STRUCT16 *info = pBlock;
940 TRACE("(%p,%s,%p,%p)\n",
941 pBlock, debugstr_a(lpSubBlock), lplpBuffer, puLen );
943 if (!pBlock)
944 return FALSE;
946 if (lpSubBlock == NULL || lpSubBlock[0] == '\0')
947 lpSubBlock = rootA;
949 if ( !VersionInfoIs16( info ) )
951 BOOL ret;
952 INT len;
953 LPWSTR lpSubBlockW;
955 len = MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, NULL, 0);
956 lpSubBlockW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
958 if (!lpSubBlockW)
959 return FALSE;
961 MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, lpSubBlockW, len);
963 ret = VersionInfo32_QueryValue(pBlock, lpSubBlockW, lplpBuffer, puLen);
965 HeapFree(GetProcessHeap(), 0, lpSubBlockW);
967 if (ret && strcasecmp( lpSubBlock, rootA ) && strcasecmp( lpSubBlock, varfileinfoA ))
969 /* Set lpBuffer so it points to the 'empty' area where we store
970 * the converted strings
972 LPSTR lpBufferA = (LPSTR)pBlock + info->wLength + 4;
973 DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock;
975 len = WideCharToMultiByte(CP_ACP, 0, *lplpBuffer, -1,
976 lpBufferA + pos, info->wLength - pos, NULL, NULL);
977 *lplpBuffer = lpBufferA + pos;
978 *puLen = len;
980 return ret;
983 return VersionInfo16_QueryValue(info, lpSubBlock, lplpBuffer, puLen);
986 /***********************************************************************
987 * VerQueryValueW [VERSION.@]
989 BOOL WINAPI VerQueryValueW( LPCVOID pBlock, LPCWSTR lpSubBlock,
990 LPVOID *lplpBuffer, PUINT puLen )
992 static const WCHAR nullW[] = { 0 };
993 static const WCHAR rootW[] = { '\\', 0 };
994 static const WCHAR varfileinfoW[] = { '\\','V','a','r','F','i','l','e','I','n','f','o',
995 '\\','T','r','a','n','s','l','a','t','i','o','n', 0 };
997 const VS_VERSION_INFO_STRUCT32 *info = pBlock;
999 TRACE("(%p,%s,%p,%p)\n",
1000 pBlock, debugstr_w(lpSubBlock), lplpBuffer, puLen );
1002 if (!pBlock)
1003 return FALSE;
1005 if (lpSubBlock == NULL || lpSubBlock[0] == nullW[0])
1006 lpSubBlock = rootW;
1008 if ( VersionInfoIs16( info ) )
1010 BOOL ret;
1011 int len;
1012 LPSTR lpSubBlockA;
1014 len = WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, NULL, 0, NULL, NULL);
1015 lpSubBlockA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
1017 if (!lpSubBlockA)
1018 return FALSE;
1020 WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, lpSubBlockA, len, NULL, NULL);
1022 ret = VersionInfo16_QueryValue(pBlock, lpSubBlockA, lplpBuffer, puLen);
1024 HeapFree(GetProcessHeap(), 0, lpSubBlockA);
1026 if (ret && strcmpiW( lpSubBlock, rootW ) && strcmpiW( lpSubBlock, varfileinfoW ))
1028 /* Set lpBuffer so it points to the 'empty' area where we store
1029 * the converted strings
1031 LPWSTR lpBufferW = (LPWSTR)((LPSTR)pBlock + info->wLength);
1032 DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock;
1033 DWORD max = (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4 - info->wLength;
1035 len = MultiByteToWideChar(CP_ACP, 0, *lplpBuffer, -1,
1036 lpBufferW + pos, max/sizeof(WCHAR) - pos );
1037 *lplpBuffer = lpBufferW + pos;
1038 *puLen = len;
1040 return ret;
1043 return VersionInfo32_QueryValue(info, lpSubBlock, lplpBuffer, puLen);
1047 /******************************************************************************
1048 * testFileExistenceA
1050 * Tests whether a given path/file combination exists. If the file does
1051 * not exist, the return value is zero. If it does exist, the return
1052 * value is non-zero.
1054 * Revision history
1055 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
1056 * Original implementation
1059 static int testFileExistenceA( char const * path, char const * file, BOOL excl )
1061 char filename[1024];
1062 int filenamelen;
1063 OFSTRUCT fileinfo;
1065 fileinfo.cBytes = sizeof(OFSTRUCT);
1067 strcpy(filename, path);
1068 filenamelen = strlen(filename);
1070 /* Add a trailing \ if necessary */
1071 if(filenamelen) {
1072 if(filename[filenamelen - 1] != '\\')
1073 strcat(filename, "\\");
1075 else /* specify the current directory */
1076 strcpy(filename, ".\\");
1078 /* Create the full pathname */
1079 strcat(filename, file);
1081 return (OpenFile(filename, &fileinfo,
1082 OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR);
1085 /******************************************************************************
1086 * testFileExistenceW
1088 static int testFileExistenceW( const WCHAR *path, const WCHAR *file, BOOL excl )
1090 char *filename;
1091 DWORD pathlen, filelen;
1092 int ret;
1093 OFSTRUCT fileinfo;
1095 fileinfo.cBytes = sizeof(OFSTRUCT);
1097 pathlen = WideCharToMultiByte( CP_ACP, 0, path, -1, NULL, 0, NULL, NULL );
1098 filelen = WideCharToMultiByte( CP_ACP, 0, file, -1, NULL, 0, NULL, NULL );
1099 filename = HeapAlloc( GetProcessHeap(), 0, pathlen+filelen+2 );
1101 WideCharToMultiByte( CP_ACP, 0, path, -1, filename, pathlen, NULL, NULL );
1102 /* Add a trailing \ if necessary */
1103 if (pathlen > 1)
1105 if (filename[pathlen-2] != '\\') strcpy( &filename[pathlen-1], "\\" );
1107 else /* specify the current directory */
1108 strcpy(filename, ".\\");
1110 WideCharToMultiByte( CP_ACP, 0, file, -1, filename+strlen(filename), filelen, NULL, NULL );
1112 ret = (OpenFile(filename, &fileinfo,
1113 OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR);
1114 HeapFree( GetProcessHeap(), 0, filename );
1115 return ret;
1118 /*****************************************************************************
1119 * VerFindFileA [VERSION.@]
1121 * Determines where to install a file based on whether it locates another
1122 * version of the file in the system. The values VerFindFile returns are
1123 * used in a subsequent call to the VerInstallFile function.
1125 * Revision history:
1126 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
1127 * Reimplementation of VerFindFile from original stub.
1129 DWORD WINAPI VerFindFileA(
1130 DWORD flags,
1131 LPCSTR lpszFilename,
1132 LPCSTR lpszWinDir,
1133 LPCSTR lpszAppDir,
1134 LPSTR lpszCurDir,
1135 PUINT lpuCurDirLen,
1136 LPSTR lpszDestDir,
1137 PUINT lpuDestDirLen )
1139 DWORD retval = 0;
1140 const char *curDir;
1141 const char *destDir;
1142 unsigned int curDirSizeReq;
1143 unsigned int destDirSizeReq;
1144 char winDir[MAX_PATH], systemDir[MAX_PATH];
1146 /* Print out debugging information */
1147 TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n",
1148 flags, debugstr_a(lpszFilename), debugstr_a(lpszWinDir), debugstr_a(lpszAppDir),
1149 lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0,
1150 lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 );
1152 /* Figure out where the file should go; shared files default to the
1153 system directory */
1155 GetSystemDirectoryA(systemDir, sizeof(systemDir));
1156 curDir = "";
1157 destDir = "";
1159 if(flags & VFFF_ISSHAREDFILE)
1161 destDir = systemDir;
1162 /* Were we given a filename? If so, try to find the file. */
1163 if(lpszFilename)
1165 if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir;
1166 else if(lpszAppDir && testFileExistenceA(lpszAppDir, lpszFilename, FALSE))
1168 curDir = lpszAppDir;
1169 retval |= VFF_CURNEDEST;
1173 else /* not a shared file */
1175 destDir = lpszAppDir ? lpszAppDir : "";
1176 if(lpszFilename)
1178 GetWindowsDirectoryA( winDir, MAX_PATH );
1179 if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir;
1180 else if(testFileExistenceA(winDir, lpszFilename, FALSE))
1182 curDir = winDir;
1183 retval |= VFF_CURNEDEST;
1185 else if(testFileExistenceA(systemDir, lpszFilename, FALSE))
1187 curDir = systemDir;
1188 retval |= VFF_CURNEDEST;
1193 /* Check to see if the file exists and is in use by another application */
1194 if (lpszFilename && testFileExistenceA(curDir, lpszFilename, FALSE)) {
1195 if (lpszFilename && !testFileExistenceA(curDir, lpszFilename, TRUE))
1196 retval |= VFF_FILEINUSE;
1199 curDirSizeReq = strlen(curDir) + 1;
1200 destDirSizeReq = strlen(destDir) + 1;
1202 /* Make sure that the pointers to the size of the buffers are
1203 valid; if not, do NOTHING with that buffer. If that pointer
1204 is valid, then make sure that the buffer pointer is valid, too! */
1206 if(lpuDestDirLen && lpszDestDir)
1208 if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1209 lstrcpynA(lpszDestDir, destDir, *lpuDestDirLen);
1210 *lpuDestDirLen = destDirSizeReq;
1212 if(lpuCurDirLen && lpszCurDir)
1214 if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1215 lstrcpynA(lpszCurDir, curDir, *lpuCurDirLen);
1216 *lpuCurDirLen = curDirSizeReq;
1219 TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval,
1220 (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "",
1221 (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "",
1222 (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "",
1223 debugstr_a(lpszCurDir), debugstr_a(lpszDestDir));
1225 return retval;
1228 /*****************************************************************************
1229 * VerFindFileW [VERSION.@]
1231 DWORD WINAPI VerFindFileW( DWORD flags,LPCWSTR lpszFilename,LPCWSTR lpszWinDir,
1232 LPCWSTR lpszAppDir, LPWSTR lpszCurDir,PUINT lpuCurDirLen,
1233 LPWSTR lpszDestDir,PUINT lpuDestDirLen )
1235 static const WCHAR emptyW;
1236 DWORD retval = 0;
1237 const WCHAR *curDir;
1238 const WCHAR *destDir;
1239 unsigned int curDirSizeReq;
1240 unsigned int destDirSizeReq;
1241 WCHAR winDir[MAX_PATH], systemDir[MAX_PATH];
1243 /* Print out debugging information */
1244 TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n",
1245 flags, debugstr_w(lpszFilename), debugstr_w(lpszWinDir), debugstr_w(lpszAppDir),
1246 lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0,
1247 lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 );
1249 /* Figure out where the file should go; shared files default to the
1250 system directory */
1252 GetSystemDirectoryW(systemDir, sizeof(systemDir)/sizeof(WCHAR));
1253 curDir = &emptyW;
1254 destDir = &emptyW;
1256 if(flags & VFFF_ISSHAREDFILE)
1258 destDir = systemDir;
1259 /* Were we given a filename? If so, try to find the file. */
1260 if(lpszFilename)
1262 if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir;
1263 else if(lpszAppDir && testFileExistenceW(lpszAppDir, lpszFilename, FALSE))
1265 curDir = lpszAppDir;
1266 retval |= VFF_CURNEDEST;
1270 else /* not a shared file */
1272 destDir = lpszAppDir ? lpszAppDir : &emptyW;
1273 if(lpszFilename)
1275 GetWindowsDirectoryW( winDir, MAX_PATH );
1276 if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir;
1277 else if(testFileExistenceW(winDir, lpszFilename, FALSE))
1279 curDir = winDir;
1280 retval |= VFF_CURNEDEST;
1282 else if(testFileExistenceW(systemDir, lpszFilename, FALSE))
1284 curDir = systemDir;
1285 retval |= VFF_CURNEDEST;
1290 if (lpszFilename && !testFileExistenceW(curDir, lpszFilename, TRUE))
1291 retval |= VFF_FILEINUSE;
1293 curDirSizeReq = strlenW(curDir) + 1;
1294 destDirSizeReq = strlenW(destDir) + 1;
1296 /* Make sure that the pointers to the size of the buffers are
1297 valid; if not, do NOTHING with that buffer. If that pointer
1298 is valid, then make sure that the buffer pointer is valid, too! */
1300 if(lpuDestDirLen && lpszDestDir)
1302 if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1303 lstrcpynW(lpszDestDir, destDir, *lpuDestDirLen);
1304 *lpuDestDirLen = destDirSizeReq;
1306 if(lpuCurDirLen && lpszCurDir)
1308 if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1309 lstrcpynW(lpszCurDir, curDir, *lpuCurDirLen);
1310 *lpuCurDirLen = curDirSizeReq;
1313 TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval,
1314 (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "",
1315 (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "",
1316 (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "",
1317 debugstr_w(lpszCurDir), debugstr_w(lpszDestDir));
1318 return retval;
1321 static LPBYTE
1322 _fetch_versioninfo(LPSTR fn,VS_FIXEDFILEINFO **vffi) {
1323 DWORD alloclen;
1324 LPBYTE buf;
1325 DWORD ret;
1327 alloclen = 1000;
1328 buf=HeapAlloc(GetProcessHeap(), 0, alloclen);
1329 if(buf == NULL) {
1330 WARN("Memory exausted while fetching version info!\n");
1331 return NULL;
1333 while (1) {
1334 ret = GetFileVersionInfoA(fn,0,alloclen,buf);
1335 if (!ret) {
1336 HeapFree(GetProcessHeap(), 0, buf);
1337 return NULL;
1339 if (alloclen<*(WORD*)buf) {
1340 alloclen = *(WORD*)buf;
1341 HeapFree(GetProcessHeap(), 0, buf);
1342 buf = HeapAlloc(GetProcessHeap(), 0, alloclen);
1343 if(buf == NULL) {
1344 WARN("Memory exausted while fetching version info!\n");
1345 return NULL;
1347 } else {
1348 *vffi = (VS_FIXEDFILEINFO*)(buf+0x14);
1349 if ((*vffi)->dwSignature == 0x004f0049) /* hack to detect unicode */
1350 *vffi = (VS_FIXEDFILEINFO*)(buf+0x28);
1351 if ((*vffi)->dwSignature != VS_FFI_SIGNATURE)
1352 WARN("Bad VS_FIXEDFILEINFO signature 0x%08x\n",(*vffi)->dwSignature);
1353 return buf;
1358 static DWORD
1359 _error2vif(DWORD error) {
1360 switch (error) {
1361 case ERROR_ACCESS_DENIED:
1362 return VIF_ACCESSVIOLATION;
1363 case ERROR_SHARING_VIOLATION:
1364 return VIF_SHARINGVIOLATION;
1365 default:
1366 return 0;
1371 /******************************************************************************
1372 * VerInstallFileA [VERSION.@]
1374 DWORD WINAPI VerInstallFileA(
1375 DWORD flags,LPCSTR srcfilename,LPCSTR destfilename,LPCSTR srcdir,
1376 LPCSTR destdir,LPCSTR curdir,LPSTR tmpfile,PUINT tmpfilelen )
1378 LPCSTR pdest;
1379 char destfn[260],tmpfn[260],srcfn[260];
1380 HFILE hfsrc,hfdst;
1381 DWORD attr,xret,tmplast;
1382 LONG ret;
1383 LPBYTE buf1,buf2;
1384 OFSTRUCT ofs;
1386 TRACE("(%x,%s,%s,%s,%s,%s,%p,%d)\n",
1387 flags,debugstr_a(srcfilename),debugstr_a(destfilename),
1388 debugstr_a(srcdir),debugstr_a(destdir),debugstr_a(curdir),
1389 tmpfile,*tmpfilelen);
1390 xret = 0;
1391 if (!srcdir || !srcfilename) return VIF_CANNOTREADSRC;
1392 sprintf(srcfn,"%s\\%s",srcdir,srcfilename);
1393 if (!destdir || !*destdir) pdest = srcdir;
1394 else pdest = destdir;
1395 sprintf(destfn,"%s\\%s",pdest,destfilename);
1396 hfsrc=LZOpenFileA(srcfn,&ofs,OF_READ);
1397 if (hfsrc < 0)
1398 return VIF_CANNOTREADSRC;
1399 sprintf(tmpfn,"%s\\%s",pdest,destfilename);
1400 tmplast=strlen(pdest)+1;
1401 attr = GetFileAttributesA(tmpfn);
1402 if (attr != INVALID_FILE_ATTRIBUTES) {
1403 if (attr & FILE_ATTRIBUTE_READONLY) {
1404 LZClose(hfsrc);
1405 return VIF_WRITEPROT;
1407 /* FIXME: check if file currently in use and return VIF_FILEINUSE */
1409 attr = INVALID_FILE_ATTRIBUTES;
1410 if (flags & VIFF_FORCEINSTALL) {
1411 if (tmpfile[0]) {
1412 sprintf(tmpfn,"%s\\%s",pdest,tmpfile);
1413 tmplast = strlen(pdest)+1;
1414 attr = GetFileAttributesA(tmpfn);
1415 /* if it exists, it has been copied by the call before.
1416 * we jump over the copy part...
1420 if (attr == INVALID_FILE_ATTRIBUTES) {
1421 char *s;
1423 GetTempFileNameA(pdest,"ver",0,tmpfn); /* should not fail ... */
1424 s=strrchr(tmpfn,'\\');
1425 if (s)
1426 tmplast = s-tmpfn;
1427 else
1428 tmplast = 0;
1429 hfdst = OpenFile(tmpfn,&ofs,OF_CREATE);
1430 if (hfdst == HFILE_ERROR) {
1431 LZClose(hfsrc);
1432 return VIF_CANNOTCREATE; /* | translated dos error */
1434 ret = LZCopy(hfsrc,hfdst);
1435 _lclose(hfdst);
1436 if (ret < 0) {
1437 /* translate LZ errors into VIF_xxx */
1438 switch (ret) {
1439 case LZERROR_BADINHANDLE:
1440 case LZERROR_READ:
1441 case LZERROR_BADVALUE:
1442 case LZERROR_UNKNOWNALG:
1443 xret = VIF_CANNOTREADSRC;
1444 break;
1445 case LZERROR_BADOUTHANDLE:
1446 case LZERROR_WRITE:
1447 xret = VIF_OUTOFSPACE;
1448 break;
1449 case LZERROR_GLOBALLOC:
1450 case LZERROR_GLOBLOCK:
1451 xret = VIF_OUTOFMEMORY;
1452 break;
1453 default: /* unknown error, should not happen */
1454 FIXME("Unknown LZCopy error %d, ignoring.\n", ret);
1455 xret = 0;
1456 break;
1458 if (xret) {
1459 LZClose(hfsrc);
1460 return xret;
1464 if (!(flags & VIFF_FORCEINSTALL)) {
1465 VS_FIXEDFILEINFO *destvffi,*tmpvffi;
1466 buf1 = _fetch_versioninfo(destfn,&destvffi);
1467 if (buf1) {
1468 buf2 = _fetch_versioninfo(tmpfn,&tmpvffi);
1469 if (buf2) {
1470 char *tbuf1,*tbuf2;
1471 static const CHAR trans_array[] = "\\VarFileInfo\\Translation";
1472 UINT len1,len2;
1474 len1=len2=40;
1476 /* compare file versions */
1477 if ((destvffi->dwFileVersionMS > tmpvffi->dwFileVersionMS)||
1478 ((destvffi->dwFileVersionMS==tmpvffi->dwFileVersionMS)&&
1479 (destvffi->dwFileVersionLS > tmpvffi->dwFileVersionLS)
1482 xret |= VIF_MISMATCH|VIF_SRCOLD;
1483 /* compare filetypes and filesubtypes */
1484 if ((destvffi->dwFileType!=tmpvffi->dwFileType) ||
1485 (destvffi->dwFileSubtype!=tmpvffi->dwFileSubtype)
1487 xret |= VIF_MISMATCH|VIF_DIFFTYPE;
1488 if (VerQueryValueA(buf1,trans_array,(LPVOID*)&tbuf1,&len1) &&
1489 VerQueryValueA(buf2,trans_array,(LPVOID*)&tbuf2,&len2)
1491 /* Do something with tbuf1 and tbuf2
1492 * generates DIFFLANG|MISMATCH
1495 HeapFree(GetProcessHeap(), 0, buf2);
1496 } else
1497 xret=VIF_MISMATCH|VIF_SRCOLD;
1498 HeapFree(GetProcessHeap(), 0, buf1);
1501 if (xret) {
1502 if (*tmpfilelen<strlen(tmpfn+tmplast)) {
1503 xret|=VIF_BUFFTOOSMALL;
1504 DeleteFileA(tmpfn);
1505 } else {
1506 strcpy(tmpfile,tmpfn+tmplast);
1507 *tmpfilelen = strlen(tmpfn+tmplast)+1;
1508 xret|=VIF_TEMPFILE;
1510 } else {
1511 if (INVALID_FILE_ATTRIBUTES!=GetFileAttributesA(destfn))
1512 if (!DeleteFileA(destfn)) {
1513 xret|=_error2vif(GetLastError())|VIF_CANNOTDELETE;
1514 DeleteFileA(tmpfn);
1515 LZClose(hfsrc);
1516 return xret;
1518 if ((!(flags & VIFF_DONTDELETEOLD)) &&
1519 curdir &&
1520 *curdir &&
1521 lstrcmpiA(curdir,pdest)
1523 char curfn[260];
1525 sprintf(curfn,"%s\\%s",curdir,destfilename);
1526 if (INVALID_FILE_ATTRIBUTES != GetFileAttributesA(curfn)) {
1527 /* FIXME: check if in use ... if it is, VIF_CANNOTDELETECUR */
1528 if (!DeleteFileA(curfn))
1529 xret|=_error2vif(GetLastError())|VIF_CANNOTDELETECUR;
1532 if (!MoveFileA(tmpfn,destfn)) {
1533 xret|=_error2vif(GetLastError())|VIF_CANNOTRENAME;
1534 DeleteFileA(tmpfn);
1537 LZClose(hfsrc);
1538 return xret;
1542 /******************************************************************************
1543 * VerInstallFileW [VERSION.@]
1545 DWORD WINAPI VerInstallFileW(
1546 DWORD flags,LPCWSTR srcfilename,LPCWSTR destfilename,LPCWSTR srcdir,
1547 LPCWSTR destdir,LPCWSTR curdir,LPWSTR tmpfile,PUINT tmpfilelen )
1549 LPSTR wsrcf = NULL, wsrcd = NULL, wdestf = NULL, wdestd = NULL, wtmpf = NULL, wcurd = NULL;
1550 DWORD ret = 0;
1551 UINT len;
1553 if (srcfilename)
1555 len = WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, NULL, 0, NULL, NULL );
1556 if ((wsrcf = HeapAlloc( GetProcessHeap(), 0, len )))
1557 WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, wsrcf, len, NULL, NULL );
1558 else
1559 ret = VIF_OUTOFMEMORY;
1561 if (srcdir && !ret)
1563 len = WideCharToMultiByte( CP_ACP, 0, srcdir, -1, NULL, 0, NULL, NULL );
1564 if ((wsrcd = HeapAlloc( GetProcessHeap(), 0, len )))
1565 WideCharToMultiByte( CP_ACP, 0, srcdir, -1, wsrcd, len, NULL, NULL );
1566 else
1567 ret = VIF_OUTOFMEMORY;
1569 if (destfilename && !ret)
1571 len = WideCharToMultiByte( CP_ACP, 0, destfilename, -1, NULL, 0, NULL, NULL );
1572 if ((wdestf = HeapAlloc( GetProcessHeap(), 0, len )))
1573 WideCharToMultiByte( CP_ACP, 0, destfilename, -1, wdestf, len, NULL, NULL );
1574 else
1575 ret = VIF_OUTOFMEMORY;
1577 if (destdir && !ret)
1579 len = WideCharToMultiByte( CP_ACP, 0, destdir, -1, NULL, 0, NULL, NULL );
1580 if ((wdestd = HeapAlloc( GetProcessHeap(), 0, len )))
1581 WideCharToMultiByte( CP_ACP, 0, destdir, -1, wdestd, len, NULL, NULL );
1582 else
1583 ret = VIF_OUTOFMEMORY;
1585 if (curdir && !ret)
1587 len = WideCharToMultiByte( CP_ACP, 0, curdir, -1, NULL, 0, NULL, NULL );
1588 if ((wcurd = HeapAlloc( GetProcessHeap(), 0, len )))
1589 WideCharToMultiByte( CP_ACP, 0, curdir, -1, wcurd, len, NULL, NULL );
1590 else
1591 ret = VIF_OUTOFMEMORY;
1593 if (!ret)
1595 len = *tmpfilelen * sizeof(WCHAR);
1596 wtmpf = HeapAlloc( GetProcessHeap(), 0, len );
1597 if (!wtmpf)
1598 ret = VIF_OUTOFMEMORY;
1600 if (!ret)
1601 ret = VerInstallFileA(flags,wsrcf,wdestf,wsrcd,wdestd,wcurd,wtmpf,&len);
1602 if (!ret)
1603 *tmpfilelen = MultiByteToWideChar( CP_ACP, 0, wtmpf, -1, tmpfile, *tmpfilelen );
1604 else if (ret & VIF_BUFFTOOSMALL)
1605 *tmpfilelen = len; /* FIXME: not correct */
1607 HeapFree( GetProcessHeap(), 0, wsrcf );
1608 HeapFree( GetProcessHeap(), 0, wsrcd );
1609 HeapFree( GetProcessHeap(), 0, wdestf );
1610 HeapFree( GetProcessHeap(), 0, wdestd );
1611 HeapFree( GetProcessHeap(), 0, wtmpf );
1612 HeapFree( GetProcessHeap(), 0, wcurd );
1613 return ret;