msi: Correctly parse double quotes in the token value.
[wine/multimedia.git] / dlls / version / version.c
blobb2b0b08233194c8ed0c5d75b566d023cf42ac404
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; /* 1:Text, 0:Binary */
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 return GetFileVersionInfoSizeExW( 0, filename, handle );
613 /***********************************************************************
614 * GetFileVersionInfoSizeA [VERSION.@]
616 DWORD WINAPI GetFileVersionInfoSizeA( LPCSTR filename, LPDWORD handle )
618 return GetFileVersionInfoSizeExA( 0, filename, handle );
621 /******************************************************************************
622 * GetFileVersionInfoSizeExW [VERSION.@]
624 DWORD WINAPI GetFileVersionInfoSizeExW( DWORD flags, LPCWSTR filename, LPDWORD handle )
626 DWORD len, offset, magic = 1;
627 HFILE lzfd;
628 HMODULE hModule;
629 OFSTRUCT ofs;
631 if (flags)
633 FIXME("stub: %x %s %p\n", flags, wine_dbgstr_w(filename), handle);
634 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
635 return 0;
638 TRACE("(%s,%p)\n", debugstr_w(filename), handle );
640 if (handle) *handle = 0;
642 if (!filename)
644 SetLastError(ERROR_INVALID_PARAMETER);
645 return 0;
647 if (!*filename)
649 SetLastError(ERROR_BAD_PATHNAME);
650 return 0;
653 if ((lzfd = LZOpenFileW( (LPWSTR)filename, &ofs, OF_READ )) != HFILE_ERROR)
655 magic = find_version_resource( lzfd, &len, &offset );
656 LZClose( lzfd );
659 if ((magic == 1) && (hModule = LoadLibraryExW( filename, 0, LOAD_LIBRARY_AS_DATAFILE )))
661 HRSRC hRsrc = FindResourceW( hModule, MAKEINTRESOURCEW(VS_VERSION_INFO),
662 (LPWSTR)VS_FILE_INFO );
663 if (hRsrc)
665 magic = IMAGE_NT_SIGNATURE;
666 len = SizeofResource( hModule, hRsrc );
668 FreeLibrary( hModule );
671 switch (magic)
673 case IMAGE_OS2_SIGNATURE:
674 /* We have a 16bit resource.
676 * XP/W2K/W2K3 uses a buffer which is more than the actual needed space:
678 * (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4
680 * This extra buffer is used for ANSI to Unicode conversions in W-Calls.
681 * info->wLength should be the same as len. Currently it isn't but that
682 * doesn't seem to be a problem (len is bigger than info->wLength).
684 SetLastError(0);
685 return (len - sizeof(VS_FIXEDFILEINFO)) * 4;
687 case IMAGE_NT_SIGNATURE:
688 /* We have a 32bit resource.
690 * XP/W2K/W2K3 uses a buffer which is 2 times the actual needed space + 4 bytes "FE2X"
691 * This extra buffer is used for Unicode to ANSI conversions in A-Calls
693 SetLastError(0);
694 return (len * 2) + 4;
696 default:
697 SetLastError( lzfd == HFILE_ERROR ? ofs.nErrCode : ERROR_RESOURCE_DATA_NOT_FOUND );
698 return 0;
702 /******************************************************************************
703 * GetFileVersionInfoSizeExA [VERSION.@]
705 DWORD WINAPI GetFileVersionInfoSizeExA( DWORD flags, LPCSTR filename, LPDWORD handle )
707 UNICODE_STRING filenameW;
708 DWORD retval;
710 TRACE("(%s,%p)\n", debugstr_a(filename), handle );
712 if(filename)
713 RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
714 else
715 filenameW.Buffer = NULL;
717 retval = GetFileVersionInfoSizeExW(0, filenameW.Buffer, handle);
719 RtlFreeUnicodeString(&filenameW);
721 return retval;
724 /***********************************************************************
725 * GetFileVersionInfoExW [VERSION.@]
727 BOOL WINAPI GetFileVersionInfoExW( DWORD flags, LPCWSTR filename, DWORD handle, DWORD datasize, LPVOID data )
729 static const char signature[4] = "FE2X";
730 DWORD len, offset, magic = 1;
731 HFILE lzfd;
732 OFSTRUCT ofs;
733 HMODULE hModule;
734 VS_VERSION_INFO_STRUCT32* vvis = data;
736 if (flags)
738 FIXME("stub: %x %s %u %u %p\n", flags, wine_dbgstr_w(filename), handle, datasize, data);
739 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
740 return 0;
743 TRACE("(%s,%d,size=%d,data=%p)\n",
744 debugstr_w(filename), handle, datasize, data );
746 if (!data)
748 SetLastError(ERROR_INVALID_DATA);
749 return FALSE;
752 if ((lzfd = LZOpenFileW( (LPWSTR)filename, &ofs, OF_READ )) != HFILE_ERROR)
754 if ((magic = find_version_resource( lzfd, &len, &offset )) > 1)
756 LZSeek( lzfd, offset, 0 /* SEEK_SET */ );
757 len = LZRead( lzfd, data, min( len, datasize ) );
759 LZClose( lzfd );
762 if ((magic == 1) && (hModule = LoadLibraryExW( filename, 0, LOAD_LIBRARY_AS_DATAFILE )))
764 HRSRC hRsrc = FindResourceW( hModule, MAKEINTRESOURCEW(VS_VERSION_INFO),
765 (LPWSTR)VS_FILE_INFO );
766 if (hRsrc)
768 HGLOBAL hMem = LoadResource( hModule, hRsrc );
769 magic = IMAGE_NT_SIGNATURE;
770 len = min( SizeofResource(hModule, hRsrc), datasize );
771 memcpy( data, LockResource( hMem ), len );
772 FreeResource( hMem );
774 FreeLibrary( hModule );
777 switch (magic)
779 case IMAGE_OS2_SIGNATURE:
780 /* We have a 16bit resource. */
781 if (TRACE_ON(ver))
782 print_vffi_debug( (VS_FIXEDFILEINFO *)VersionInfo16_Value( (VS_VERSION_INFO_STRUCT16 *)data ));
783 SetLastError(0);
784 return TRUE;
786 case IMAGE_NT_SIGNATURE:
787 /* We have a 32bit resource.
789 * XP/W2K/W2K3 uses a buffer which is 2 times the actual needed space + 4 bytes "FE2X"
790 * This extra buffer is used for Unicode to ANSI conversions in A-Calls
792 len = vvis->wLength + sizeof(signature);
793 if (datasize >= len) memcpy( (char*)data + vvis->wLength, signature, sizeof(signature) );
794 if (TRACE_ON(ver))
795 print_vffi_debug( (VS_FIXEDFILEINFO *)VersionInfo32_Value( vvis ));
796 SetLastError(0);
797 return TRUE;
799 default:
800 SetLastError( lzfd == HFILE_ERROR ? ofs.nErrCode : ERROR_RESOURCE_DATA_NOT_FOUND );
801 return FALSE;
805 /***********************************************************************
806 * GetFileVersionInfoExA [VERSION.@]
808 BOOL WINAPI GetFileVersionInfoExA( DWORD flags, LPCSTR filename, DWORD handle, DWORD datasize, LPVOID data )
810 UNICODE_STRING filenameW;
811 BOOL retval;
813 TRACE("(%s,%d,size=%d,data=%p)\n",
814 debugstr_a(filename), handle, datasize, data );
816 if(filename)
817 RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
818 else
819 filenameW.Buffer = NULL;
821 retval = GetFileVersionInfoExW(flags, filenameW.Buffer, handle, datasize, data);
823 RtlFreeUnicodeString(&filenameW);
825 return retval;
828 /***********************************************************************
829 * GetFileVersionInfoW [VERSION.@]
831 BOOL WINAPI GetFileVersionInfoW( LPCWSTR filename, DWORD handle, DWORD datasize, LPVOID data )
833 return GetFileVersionInfoExW(0, filename, handle, datasize, data);
836 /***********************************************************************
837 * GetFileVersionInfoA [VERSION.@]
839 BOOL WINAPI GetFileVersionInfoA( LPCSTR filename, DWORD handle, DWORD datasize, LPVOID data )
841 return GetFileVersionInfoExA(0, filename, handle, datasize, data);
844 /***********************************************************************
845 * VersionInfo16_FindChild [internal]
847 static const VS_VERSION_INFO_STRUCT16 *VersionInfo16_FindChild( const VS_VERSION_INFO_STRUCT16 *info,
848 LPCSTR szKey, UINT cbKey )
850 const VS_VERSION_INFO_STRUCT16 *child = VersionInfo16_Children( info );
852 while ((char *)child < (char *)info + info->wLength )
854 if (!strncasecmp( child->szKey, szKey, cbKey ) && !child->szKey[cbKey])
855 return child;
857 if (!(child->wLength)) return NULL;
858 child = VersionInfo16_Next( child );
861 return NULL;
864 /***********************************************************************
865 * VersionInfo32_FindChild [internal]
867 static const VS_VERSION_INFO_STRUCT32 *VersionInfo32_FindChild( const VS_VERSION_INFO_STRUCT32 *info,
868 LPCWSTR szKey, UINT cbKey )
870 const VS_VERSION_INFO_STRUCT32 *child = VersionInfo32_Children( info );
872 while ((char *)child < (char *)info + info->wLength )
874 if (!strncmpiW( child->szKey, szKey, cbKey ) && !child->szKey[cbKey])
875 return child;
877 if (!(child->wLength)) return NULL;
878 child = VersionInfo32_Next( child );
881 return NULL;
884 /***********************************************************************
885 * VersionInfo16_QueryValue [internal]
887 * Gets a value from a 16-bit NE resource
889 static BOOL VersionInfo16_QueryValue( const VS_VERSION_INFO_STRUCT16 *info, LPCSTR lpSubBlock,
890 LPVOID *lplpBuffer, UINT *puLen )
892 while ( *lpSubBlock )
894 /* Find next path component */
895 LPCSTR lpNextSlash;
896 for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ )
897 if ( *lpNextSlash == '\\' )
898 break;
900 /* Skip empty components */
901 if ( lpNextSlash == lpSubBlock )
903 lpSubBlock++;
904 continue;
907 /* We have a non-empty component: search info for key */
908 info = VersionInfo16_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock );
909 if ( !info )
911 if (puLen) *puLen = 0 ;
912 SetLastError( ERROR_RESOURCE_TYPE_NOT_FOUND );
913 return FALSE;
916 /* Skip path component */
917 lpSubBlock = lpNextSlash;
920 /* Return value */
921 *lplpBuffer = VersionInfo16_Value( info );
922 if (puLen)
923 *puLen = info->wValueLength;
925 return TRUE;
928 /***********************************************************************
929 * VersionInfo32_QueryValue [internal]
931 * Gets a value from a 32-bit PE resource
933 static BOOL VersionInfo32_QueryValue( const VS_VERSION_INFO_STRUCT32 *info, LPCWSTR lpSubBlock,
934 LPVOID *lplpBuffer, UINT *puLen, BOOL *pbText )
936 TRACE("lpSubBlock : (%s)\n", debugstr_w(lpSubBlock));
938 while ( *lpSubBlock )
940 /* Find next path component */
941 LPCWSTR lpNextSlash;
942 for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ )
943 if ( *lpNextSlash == '\\' )
944 break;
946 /* Skip empty components */
947 if ( lpNextSlash == lpSubBlock )
949 lpSubBlock++;
950 continue;
953 /* We have a non-empty component: search info for key */
954 info = VersionInfo32_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock );
955 if ( !info )
957 if (puLen) *puLen = 0 ;
958 SetLastError( ERROR_RESOURCE_TYPE_NOT_FOUND );
959 return FALSE;
962 /* Skip path component */
963 lpSubBlock = lpNextSlash;
966 /* Return value */
967 *lplpBuffer = VersionInfo32_Value( info );
968 if (puLen)
969 *puLen = info->wValueLength;
970 if (pbText)
971 *pbText = info->wType;
973 return TRUE;
976 /***********************************************************************
977 * VerQueryValueA [VERSION.@]
979 BOOL WINAPI VerQueryValueA( LPCVOID pBlock, LPCSTR lpSubBlock,
980 LPVOID *lplpBuffer, PUINT puLen )
982 static const char rootA[] = "\\";
983 const VS_VERSION_INFO_STRUCT16 *info = pBlock;
985 TRACE("(%p,%s,%p,%p)\n",
986 pBlock, debugstr_a(lpSubBlock), lplpBuffer, puLen );
988 if (!pBlock)
989 return FALSE;
991 if (lpSubBlock == NULL || lpSubBlock[0] == '\0')
992 lpSubBlock = rootA;
994 if ( !VersionInfoIs16( info ) )
996 BOOL ret, isText;
997 INT len;
998 LPWSTR lpSubBlockW;
1000 len = MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, NULL, 0);
1001 lpSubBlockW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1003 if (!lpSubBlockW)
1004 return FALSE;
1006 MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, lpSubBlockW, len);
1008 ret = VersionInfo32_QueryValue(pBlock, lpSubBlockW, lplpBuffer, puLen, &isText);
1010 HeapFree(GetProcessHeap(), 0, lpSubBlockW);
1012 if (ret && isText)
1014 /* Set lpBuffer so it points to the 'empty' area where we store
1015 * the converted strings
1017 LPSTR lpBufferA = (LPSTR)pBlock + info->wLength + 4;
1018 DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock;
1020 len = WideCharToMultiByte(CP_ACP, 0, *lplpBuffer, -1,
1021 lpBufferA + pos, info->wLength - pos, NULL, NULL);
1022 *lplpBuffer = lpBufferA + pos;
1023 if (puLen) *puLen = len;
1025 return ret;
1028 return VersionInfo16_QueryValue(info, lpSubBlock, lplpBuffer, puLen);
1031 /***********************************************************************
1032 * VerQueryValueW [VERSION.@]
1034 BOOL WINAPI VerQueryValueW( LPCVOID pBlock, LPCWSTR lpSubBlock,
1035 LPVOID *lplpBuffer, PUINT puLen )
1037 static const WCHAR nullW[] = { 0 };
1038 static const WCHAR rootW[] = { '\\', 0 };
1039 static const WCHAR varfileinfoW[] = { '\\','V','a','r','F','i','l','e','I','n','f','o',
1040 '\\','T','r','a','n','s','l','a','t','i','o','n', 0 };
1042 const VS_VERSION_INFO_STRUCT32 *info = pBlock;
1044 TRACE("(%p,%s,%p,%p)\n",
1045 pBlock, debugstr_w(lpSubBlock), lplpBuffer, puLen );
1047 if (!pBlock)
1048 return FALSE;
1050 if (lpSubBlock == NULL || lpSubBlock[0] == nullW[0])
1051 lpSubBlock = rootW;
1053 if ( VersionInfoIs16( info ) )
1055 BOOL ret;
1056 int len;
1057 LPSTR lpSubBlockA;
1059 len = WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, NULL, 0, NULL, NULL);
1060 lpSubBlockA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
1062 if (!lpSubBlockA)
1063 return FALSE;
1065 WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, lpSubBlockA, len, NULL, NULL);
1067 ret = VersionInfo16_QueryValue(pBlock, lpSubBlockA, lplpBuffer, puLen);
1069 HeapFree(GetProcessHeap(), 0, lpSubBlockA);
1071 if (ret && strcmpiW( lpSubBlock, rootW ) && strcmpiW( lpSubBlock, varfileinfoW ))
1073 /* Set lpBuffer so it points to the 'empty' area where we store
1074 * the converted strings
1076 LPWSTR lpBufferW = (LPWSTR)((LPSTR)pBlock + info->wLength);
1077 DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock;
1078 DWORD max = (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4 - info->wLength;
1080 len = MultiByteToWideChar(CP_ACP, 0, *lplpBuffer, -1,
1081 lpBufferW + pos, max/sizeof(WCHAR) - pos );
1082 *lplpBuffer = lpBufferW + pos;
1083 if (puLen) *puLen = len;
1085 return ret;
1088 return VersionInfo32_QueryValue(info, lpSubBlock, lplpBuffer, puLen, NULL);
1092 /******************************************************************************
1093 * testFileExistenceA
1095 * Tests whether a given path/file combination exists. If the file does
1096 * not exist, the return value is zero. If it does exist, the return
1097 * value is non-zero.
1099 * Revision history
1100 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
1101 * Original implementation
1104 static int testFileExistenceA( char const * path, char const * file, BOOL excl )
1106 char filename[1024];
1107 int filenamelen;
1108 OFSTRUCT fileinfo;
1110 fileinfo.cBytes = sizeof(OFSTRUCT);
1112 strcpy(filename, path);
1113 filenamelen = strlen(filename);
1115 /* Add a trailing \ if necessary */
1116 if(filenamelen) {
1117 if(filename[filenamelen - 1] != '\\')
1118 strcat(filename, "\\");
1120 else /* specify the current directory */
1121 strcpy(filename, ".\\");
1123 /* Create the full pathname */
1124 strcat(filename, file);
1126 return (OpenFile(filename, &fileinfo,
1127 OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR);
1130 /******************************************************************************
1131 * testFileExistenceW
1133 static int testFileExistenceW( const WCHAR *path, const WCHAR *file, BOOL excl )
1135 char *filename;
1136 DWORD pathlen, filelen;
1137 int ret;
1138 OFSTRUCT fileinfo;
1140 fileinfo.cBytes = sizeof(OFSTRUCT);
1142 pathlen = WideCharToMultiByte( CP_ACP, 0, path, -1, NULL, 0, NULL, NULL );
1143 filelen = WideCharToMultiByte( CP_ACP, 0, file, -1, NULL, 0, NULL, NULL );
1144 filename = HeapAlloc( GetProcessHeap(), 0, pathlen+filelen+2 );
1146 WideCharToMultiByte( CP_ACP, 0, path, -1, filename, pathlen, NULL, NULL );
1147 /* Add a trailing \ if necessary */
1148 if (pathlen > 1)
1150 if (filename[pathlen-2] != '\\') strcpy( &filename[pathlen-1], "\\" );
1152 else /* specify the current directory */
1153 strcpy(filename, ".\\");
1155 WideCharToMultiByte( CP_ACP, 0, file, -1, filename+strlen(filename), filelen, NULL, NULL );
1157 ret = (OpenFile(filename, &fileinfo,
1158 OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR);
1159 HeapFree( GetProcessHeap(), 0, filename );
1160 return ret;
1163 /*****************************************************************************
1164 * VerFindFileA [VERSION.@]
1166 * Determines where to install a file based on whether it locates another
1167 * version of the file in the system. The values VerFindFile returns are
1168 * used in a subsequent call to the VerInstallFile function.
1170 * Revision history:
1171 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
1172 * Reimplementation of VerFindFile from original stub.
1174 DWORD WINAPI VerFindFileA(
1175 DWORD flags,
1176 LPCSTR lpszFilename,
1177 LPCSTR lpszWinDir,
1178 LPCSTR lpszAppDir,
1179 LPSTR lpszCurDir,
1180 PUINT lpuCurDirLen,
1181 LPSTR lpszDestDir,
1182 PUINT lpuDestDirLen )
1184 DWORD retval = 0;
1185 const char *curDir;
1186 const char *destDir;
1187 unsigned int curDirSizeReq;
1188 unsigned int destDirSizeReq;
1189 char winDir[MAX_PATH], systemDir[MAX_PATH];
1191 /* Print out debugging information */
1192 TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n",
1193 flags, debugstr_a(lpszFilename), debugstr_a(lpszWinDir), debugstr_a(lpszAppDir),
1194 lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0,
1195 lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 );
1197 /* Figure out where the file should go; shared files default to the
1198 system directory */
1200 GetSystemDirectoryA(systemDir, sizeof(systemDir));
1201 curDir = "";
1202 destDir = "";
1204 if(flags & VFFF_ISSHAREDFILE)
1206 destDir = systemDir;
1207 /* Were we given a filename? If so, try to find the file. */
1208 if(lpszFilename)
1210 if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir;
1211 else if(lpszAppDir && testFileExistenceA(lpszAppDir, lpszFilename, FALSE))
1213 curDir = lpszAppDir;
1214 retval |= VFF_CURNEDEST;
1218 else /* not a shared file */
1220 destDir = lpszAppDir ? lpszAppDir : "";
1221 if(lpszFilename)
1223 GetWindowsDirectoryA( winDir, MAX_PATH );
1224 if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir;
1225 else if(testFileExistenceA(winDir, lpszFilename, FALSE))
1227 curDir = winDir;
1228 retval |= VFF_CURNEDEST;
1230 else if(testFileExistenceA(systemDir, lpszFilename, FALSE))
1232 curDir = systemDir;
1233 retval |= VFF_CURNEDEST;
1238 /* Check to see if the file exists and is in use by another application */
1239 if (lpszFilename && testFileExistenceA(curDir, lpszFilename, FALSE)) {
1240 if (lpszFilename && !testFileExistenceA(curDir, lpszFilename, TRUE))
1241 retval |= VFF_FILEINUSE;
1244 curDirSizeReq = strlen(curDir) + 1;
1245 destDirSizeReq = strlen(destDir) + 1;
1247 /* Make sure that the pointers to the size of the buffers are
1248 valid; if not, do NOTHING with that buffer. If that pointer
1249 is valid, then make sure that the buffer pointer is valid, too! */
1251 if(lpuDestDirLen && lpszDestDir)
1253 if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1254 lstrcpynA(lpszDestDir, destDir, *lpuDestDirLen);
1255 *lpuDestDirLen = destDirSizeReq;
1257 if(lpuCurDirLen && lpszCurDir)
1259 if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1260 lstrcpynA(lpszCurDir, curDir, *lpuCurDirLen);
1261 *lpuCurDirLen = curDirSizeReq;
1264 TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval,
1265 (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "",
1266 (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "",
1267 (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "",
1268 debugstr_a(lpszCurDir), debugstr_a(lpszDestDir));
1270 return retval;
1273 /*****************************************************************************
1274 * VerFindFileW [VERSION.@]
1276 DWORD WINAPI VerFindFileW( DWORD flags,LPCWSTR lpszFilename,LPCWSTR lpszWinDir,
1277 LPCWSTR lpszAppDir, LPWSTR lpszCurDir,PUINT lpuCurDirLen,
1278 LPWSTR lpszDestDir,PUINT lpuDestDirLen )
1280 static const WCHAR emptyW;
1281 DWORD retval = 0;
1282 const WCHAR *curDir;
1283 const WCHAR *destDir;
1284 unsigned int curDirSizeReq;
1285 unsigned int destDirSizeReq;
1286 WCHAR winDir[MAX_PATH], systemDir[MAX_PATH];
1288 /* Print out debugging information */
1289 TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n",
1290 flags, debugstr_w(lpszFilename), debugstr_w(lpszWinDir), debugstr_w(lpszAppDir),
1291 lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0,
1292 lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 );
1294 /* Figure out where the file should go; shared files default to the
1295 system directory */
1297 GetSystemDirectoryW(systemDir, sizeof(systemDir)/sizeof(WCHAR));
1298 curDir = &emptyW;
1299 destDir = &emptyW;
1301 if(flags & VFFF_ISSHAREDFILE)
1303 destDir = systemDir;
1304 /* Were we given a filename? If so, try to find the file. */
1305 if(lpszFilename)
1307 if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir;
1308 else if(lpszAppDir && testFileExistenceW(lpszAppDir, lpszFilename, FALSE))
1310 curDir = lpszAppDir;
1311 retval |= VFF_CURNEDEST;
1315 else /* not a shared file */
1317 destDir = lpszAppDir ? lpszAppDir : &emptyW;
1318 if(lpszFilename)
1320 GetWindowsDirectoryW( winDir, MAX_PATH );
1321 if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir;
1322 else if(testFileExistenceW(winDir, lpszFilename, FALSE))
1324 curDir = winDir;
1325 retval |= VFF_CURNEDEST;
1327 else if(testFileExistenceW(systemDir, lpszFilename, FALSE))
1329 curDir = systemDir;
1330 retval |= VFF_CURNEDEST;
1335 if (lpszFilename && !testFileExistenceW(curDir, lpszFilename, TRUE))
1336 retval |= VFF_FILEINUSE;
1338 curDirSizeReq = strlenW(curDir) + 1;
1339 destDirSizeReq = strlenW(destDir) + 1;
1341 /* Make sure that the pointers to the size of the buffers are
1342 valid; if not, do NOTHING with that buffer. If that pointer
1343 is valid, then make sure that the buffer pointer is valid, too! */
1345 if(lpuDestDirLen && lpszDestDir)
1347 if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1348 lstrcpynW(lpszDestDir, destDir, *lpuDestDirLen);
1349 *lpuDestDirLen = destDirSizeReq;
1351 if(lpuCurDirLen && lpszCurDir)
1353 if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1354 lstrcpynW(lpszCurDir, curDir, *lpuCurDirLen);
1355 *lpuCurDirLen = curDirSizeReq;
1358 TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval,
1359 (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "",
1360 (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "",
1361 (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "",
1362 debugstr_w(lpszCurDir), debugstr_w(lpszDestDir));
1363 return retval;
1366 static LPBYTE
1367 _fetch_versioninfo(LPSTR fn,VS_FIXEDFILEINFO **vffi) {
1368 DWORD alloclen;
1369 LPBYTE buf;
1370 DWORD ret;
1372 alloclen = 1000;
1373 buf=HeapAlloc(GetProcessHeap(), 0, alloclen);
1374 if(buf == NULL) {
1375 WARN("Memory exhausted while fetching version info!\n");
1376 return NULL;
1378 while (1) {
1379 ret = GetFileVersionInfoA(fn,0,alloclen,buf);
1380 if (!ret) {
1381 HeapFree(GetProcessHeap(), 0, buf);
1382 return NULL;
1384 if (alloclen<*(WORD*)buf) {
1385 alloclen = *(WORD*)buf;
1386 HeapFree(GetProcessHeap(), 0, buf);
1387 buf = HeapAlloc(GetProcessHeap(), 0, alloclen);
1388 if(buf == NULL) {
1389 WARN("Memory exhausted while fetching version info!\n");
1390 return NULL;
1392 } else {
1393 *vffi = (VS_FIXEDFILEINFO*)(buf+0x14);
1394 if ((*vffi)->dwSignature == 0x004f0049) /* hack to detect unicode */
1395 *vffi = (VS_FIXEDFILEINFO*)(buf+0x28);
1396 if ((*vffi)->dwSignature != VS_FFI_SIGNATURE)
1397 WARN("Bad VS_FIXEDFILEINFO signature 0x%08x\n",(*vffi)->dwSignature);
1398 return buf;
1403 static DWORD
1404 _error2vif(DWORD error) {
1405 switch (error) {
1406 case ERROR_ACCESS_DENIED:
1407 return VIF_ACCESSVIOLATION;
1408 case ERROR_SHARING_VIOLATION:
1409 return VIF_SHARINGVIOLATION;
1410 default:
1411 return 0;
1416 /******************************************************************************
1417 * VerInstallFileA [VERSION.@]
1419 DWORD WINAPI VerInstallFileA(
1420 DWORD flags,LPCSTR srcfilename,LPCSTR destfilename,LPCSTR srcdir,
1421 LPCSTR destdir,LPCSTR curdir,LPSTR tmpfile,PUINT tmpfilelen )
1423 LPCSTR pdest;
1424 char destfn[260],tmpfn[260],srcfn[260];
1425 HFILE hfsrc,hfdst;
1426 DWORD attr,xret,tmplast;
1427 LONG ret;
1428 LPBYTE buf1,buf2;
1429 OFSTRUCT ofs;
1431 TRACE("(%x,%s,%s,%s,%s,%s,%p,%d)\n",
1432 flags,debugstr_a(srcfilename),debugstr_a(destfilename),
1433 debugstr_a(srcdir),debugstr_a(destdir),debugstr_a(curdir),
1434 tmpfile,*tmpfilelen);
1435 xret = 0;
1436 if (!srcdir || !srcfilename) return VIF_CANNOTREADSRC;
1437 sprintf(srcfn,"%s\\%s",srcdir,srcfilename);
1438 if (!destdir || !*destdir) pdest = srcdir;
1439 else pdest = destdir;
1440 sprintf(destfn,"%s\\%s",pdest,destfilename);
1441 hfsrc=LZOpenFileA(srcfn,&ofs,OF_READ);
1442 if (hfsrc < 0)
1443 return VIF_CANNOTREADSRC;
1444 sprintf(tmpfn,"%s\\%s",pdest,destfilename);
1445 tmplast=strlen(pdest)+1;
1446 attr = GetFileAttributesA(tmpfn);
1447 if (attr != INVALID_FILE_ATTRIBUTES) {
1448 if (attr & FILE_ATTRIBUTE_READONLY) {
1449 LZClose(hfsrc);
1450 return VIF_WRITEPROT;
1452 /* FIXME: check if file currently in use and return VIF_FILEINUSE */
1454 attr = INVALID_FILE_ATTRIBUTES;
1455 if (flags & VIFF_FORCEINSTALL) {
1456 if (tmpfile[0]) {
1457 sprintf(tmpfn,"%s\\%s",pdest,tmpfile);
1458 tmplast = strlen(pdest)+1;
1459 attr = GetFileAttributesA(tmpfn);
1460 /* if it exists, it has been copied by the call before.
1461 * we jump over the copy part...
1465 if (attr == INVALID_FILE_ATTRIBUTES) {
1466 char *s;
1468 GetTempFileNameA(pdest,"ver",0,tmpfn); /* should not fail ... */
1469 s=strrchr(tmpfn,'\\');
1470 if (s)
1471 tmplast = s-tmpfn;
1472 else
1473 tmplast = 0;
1474 hfdst = OpenFile(tmpfn,&ofs,OF_CREATE);
1475 if (hfdst == HFILE_ERROR) {
1476 LZClose(hfsrc);
1477 return VIF_CANNOTCREATE; /* | translated dos error */
1479 ret = LZCopy(hfsrc,hfdst);
1480 _lclose(hfdst);
1481 if (ret < 0) {
1482 /* translate LZ errors into VIF_xxx */
1483 switch (ret) {
1484 case LZERROR_BADINHANDLE:
1485 case LZERROR_READ:
1486 case LZERROR_BADVALUE:
1487 case LZERROR_UNKNOWNALG:
1488 xret = VIF_CANNOTREADSRC;
1489 break;
1490 case LZERROR_BADOUTHANDLE:
1491 case LZERROR_WRITE:
1492 xret = VIF_OUTOFSPACE;
1493 break;
1494 case LZERROR_GLOBALLOC:
1495 case LZERROR_GLOBLOCK:
1496 xret = VIF_OUTOFMEMORY;
1497 break;
1498 default: /* unknown error, should not happen */
1499 FIXME("Unknown LZCopy error %d, ignoring.\n", ret);
1500 xret = 0;
1501 break;
1503 if (xret) {
1504 LZClose(hfsrc);
1505 return xret;
1509 if (!(flags & VIFF_FORCEINSTALL)) {
1510 VS_FIXEDFILEINFO *destvffi,*tmpvffi;
1511 buf1 = _fetch_versioninfo(destfn,&destvffi);
1512 if (buf1) {
1513 buf2 = _fetch_versioninfo(tmpfn,&tmpvffi);
1514 if (buf2) {
1515 char *tbuf1,*tbuf2;
1516 static const CHAR trans_array[] = "\\VarFileInfo\\Translation";
1517 UINT len1,len2;
1519 len1=len2=40;
1521 /* compare file versions */
1522 if ((destvffi->dwFileVersionMS > tmpvffi->dwFileVersionMS)||
1523 ((destvffi->dwFileVersionMS==tmpvffi->dwFileVersionMS)&&
1524 (destvffi->dwFileVersionLS > tmpvffi->dwFileVersionLS)
1527 xret |= VIF_MISMATCH|VIF_SRCOLD;
1528 /* compare filetypes and filesubtypes */
1529 if ((destvffi->dwFileType!=tmpvffi->dwFileType) ||
1530 (destvffi->dwFileSubtype!=tmpvffi->dwFileSubtype)
1532 xret |= VIF_MISMATCH|VIF_DIFFTYPE;
1533 if (VerQueryValueA(buf1,trans_array,(LPVOID*)&tbuf1,&len1) &&
1534 VerQueryValueA(buf2,trans_array,(LPVOID*)&tbuf2,&len2)
1536 /* Do something with tbuf1 and tbuf2
1537 * generates DIFFLANG|MISMATCH
1540 HeapFree(GetProcessHeap(), 0, buf2);
1541 } else
1542 xret=VIF_MISMATCH|VIF_SRCOLD;
1543 HeapFree(GetProcessHeap(), 0, buf1);
1546 if (xret) {
1547 if (*tmpfilelen<strlen(tmpfn+tmplast)) {
1548 xret|=VIF_BUFFTOOSMALL;
1549 DeleteFileA(tmpfn);
1550 } else {
1551 strcpy(tmpfile,tmpfn+tmplast);
1552 *tmpfilelen = strlen(tmpfn+tmplast)+1;
1553 xret|=VIF_TEMPFILE;
1555 } else {
1556 if (INVALID_FILE_ATTRIBUTES!=GetFileAttributesA(destfn))
1557 if (!DeleteFileA(destfn)) {
1558 xret|=_error2vif(GetLastError())|VIF_CANNOTDELETE;
1559 DeleteFileA(tmpfn);
1560 LZClose(hfsrc);
1561 return xret;
1563 if ((!(flags & VIFF_DONTDELETEOLD)) &&
1564 curdir &&
1565 *curdir &&
1566 lstrcmpiA(curdir,pdest)
1568 char curfn[260];
1570 sprintf(curfn,"%s\\%s",curdir,destfilename);
1571 if (INVALID_FILE_ATTRIBUTES != GetFileAttributesA(curfn)) {
1572 /* FIXME: check if in use ... if it is, VIF_CANNOTDELETECUR */
1573 if (!DeleteFileA(curfn))
1574 xret|=_error2vif(GetLastError())|VIF_CANNOTDELETECUR;
1577 if (!MoveFileA(tmpfn,destfn)) {
1578 xret|=_error2vif(GetLastError())|VIF_CANNOTRENAME;
1579 DeleteFileA(tmpfn);
1582 LZClose(hfsrc);
1583 return xret;
1587 /******************************************************************************
1588 * VerInstallFileW [VERSION.@]
1590 DWORD WINAPI VerInstallFileW(
1591 DWORD flags,LPCWSTR srcfilename,LPCWSTR destfilename,LPCWSTR srcdir,
1592 LPCWSTR destdir,LPCWSTR curdir,LPWSTR tmpfile,PUINT tmpfilelen )
1594 LPSTR wsrcf = NULL, wsrcd = NULL, wdestf = NULL, wdestd = NULL, wtmpf = NULL, wcurd = NULL;
1595 DWORD ret = 0;
1596 UINT len;
1598 if (srcfilename)
1600 len = WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, NULL, 0, NULL, NULL );
1601 if ((wsrcf = HeapAlloc( GetProcessHeap(), 0, len )))
1602 WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, wsrcf, len, NULL, NULL );
1603 else
1604 ret = VIF_OUTOFMEMORY;
1606 if (srcdir && !ret)
1608 len = WideCharToMultiByte( CP_ACP, 0, srcdir, -1, NULL, 0, NULL, NULL );
1609 if ((wsrcd = HeapAlloc( GetProcessHeap(), 0, len )))
1610 WideCharToMultiByte( CP_ACP, 0, srcdir, -1, wsrcd, len, NULL, NULL );
1611 else
1612 ret = VIF_OUTOFMEMORY;
1614 if (destfilename && !ret)
1616 len = WideCharToMultiByte( CP_ACP, 0, destfilename, -1, NULL, 0, NULL, NULL );
1617 if ((wdestf = HeapAlloc( GetProcessHeap(), 0, len )))
1618 WideCharToMultiByte( CP_ACP, 0, destfilename, -1, wdestf, len, NULL, NULL );
1619 else
1620 ret = VIF_OUTOFMEMORY;
1622 if (destdir && !ret)
1624 len = WideCharToMultiByte( CP_ACP, 0, destdir, -1, NULL, 0, NULL, NULL );
1625 if ((wdestd = HeapAlloc( GetProcessHeap(), 0, len )))
1626 WideCharToMultiByte( CP_ACP, 0, destdir, -1, wdestd, len, NULL, NULL );
1627 else
1628 ret = VIF_OUTOFMEMORY;
1630 if (curdir && !ret)
1632 len = WideCharToMultiByte( CP_ACP, 0, curdir, -1, NULL, 0, NULL, NULL );
1633 if ((wcurd = HeapAlloc( GetProcessHeap(), 0, len )))
1634 WideCharToMultiByte( CP_ACP, 0, curdir, -1, wcurd, len, NULL, NULL );
1635 else
1636 ret = VIF_OUTOFMEMORY;
1638 if (!ret)
1640 len = *tmpfilelen * sizeof(WCHAR);
1641 wtmpf = HeapAlloc( GetProcessHeap(), 0, len );
1642 if (!wtmpf)
1643 ret = VIF_OUTOFMEMORY;
1645 if (!ret)
1646 ret = VerInstallFileA(flags,wsrcf,wdestf,wsrcd,wdestd,wcurd,wtmpf,&len);
1647 if (!ret)
1648 *tmpfilelen = MultiByteToWideChar( CP_ACP, 0, wtmpf, -1, tmpfile, *tmpfilelen );
1649 else if (ret & VIF_BUFFTOOSMALL)
1650 *tmpfilelen = len; /* FIXME: not correct */
1652 HeapFree( GetProcessHeap(), 0, wsrcf );
1653 HeapFree( GetProcessHeap(), 0, wsrcd );
1654 HeapFree( GetProcessHeap(), 0, wdestf );
1655 HeapFree( GetProcessHeap(), 0, wdestd );
1656 HeapFree( GetProcessHeap(), 0, wtmpf );
1657 HeapFree( GetProcessHeap(), 0, wcurd );
1658 return ret;