vbscript: Added support for title and type arguments of MsgBox.
[wine.git] / dlls / version / version.c
blob9bc6ad133af55be719344cef915776c56cc7a66b
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 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, BOOL *pbText )
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;
926 if (pbText)
927 *pbText = info->wType;
929 return TRUE;
932 /***********************************************************************
933 * VerQueryValueA [VERSION.@]
935 BOOL WINAPI VerQueryValueA( LPCVOID pBlock, LPCSTR lpSubBlock,
936 LPVOID *lplpBuffer, PUINT puLen )
938 static const char rootA[] = "\\";
939 const VS_VERSION_INFO_STRUCT16 *info = pBlock;
941 TRACE("(%p,%s,%p,%p)\n",
942 pBlock, debugstr_a(lpSubBlock), lplpBuffer, puLen );
944 if (!pBlock)
945 return FALSE;
947 if (lpSubBlock == NULL || lpSubBlock[0] == '\0')
948 lpSubBlock = rootA;
950 if ( !VersionInfoIs16( info ) )
952 BOOL ret, isText;
953 INT len;
954 LPWSTR lpSubBlockW;
956 len = MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, NULL, 0);
957 lpSubBlockW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
959 if (!lpSubBlockW)
960 return FALSE;
962 MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, lpSubBlockW, len);
964 ret = VersionInfo32_QueryValue(pBlock, lpSubBlockW, lplpBuffer, puLen, &isText);
966 HeapFree(GetProcessHeap(), 0, lpSubBlockW);
968 if (ret && isText)
970 /* Set lpBuffer so it points to the 'empty' area where we store
971 * the converted strings
973 LPSTR lpBufferA = (LPSTR)pBlock + info->wLength + 4;
974 DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock;
976 len = WideCharToMultiByte(CP_ACP, 0, *lplpBuffer, -1,
977 lpBufferA + pos, info->wLength - pos, NULL, NULL);
978 *lplpBuffer = lpBufferA + pos;
979 *puLen = len;
981 return ret;
984 return VersionInfo16_QueryValue(info, lpSubBlock, lplpBuffer, puLen);
987 /***********************************************************************
988 * VerQueryValueW [VERSION.@]
990 BOOL WINAPI VerQueryValueW( LPCVOID pBlock, LPCWSTR lpSubBlock,
991 LPVOID *lplpBuffer, PUINT puLen )
993 static const WCHAR nullW[] = { 0 };
994 static const WCHAR rootW[] = { '\\', 0 };
995 static const WCHAR varfileinfoW[] = { '\\','V','a','r','F','i','l','e','I','n','f','o',
996 '\\','T','r','a','n','s','l','a','t','i','o','n', 0 };
998 const VS_VERSION_INFO_STRUCT32 *info = pBlock;
1000 TRACE("(%p,%s,%p,%p)\n",
1001 pBlock, debugstr_w(lpSubBlock), lplpBuffer, puLen );
1003 if (!pBlock)
1004 return FALSE;
1006 if (lpSubBlock == NULL || lpSubBlock[0] == nullW[0])
1007 lpSubBlock = rootW;
1009 if ( VersionInfoIs16( info ) )
1011 BOOL ret;
1012 int len;
1013 LPSTR lpSubBlockA;
1015 len = WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, NULL, 0, NULL, NULL);
1016 lpSubBlockA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
1018 if (!lpSubBlockA)
1019 return FALSE;
1021 WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, lpSubBlockA, len, NULL, NULL);
1023 ret = VersionInfo16_QueryValue(pBlock, lpSubBlockA, lplpBuffer, puLen);
1025 HeapFree(GetProcessHeap(), 0, lpSubBlockA);
1027 if (ret && strcmpiW( lpSubBlock, rootW ) && strcmpiW( lpSubBlock, varfileinfoW ))
1029 /* Set lpBuffer so it points to the 'empty' area where we store
1030 * the converted strings
1032 LPWSTR lpBufferW = (LPWSTR)((LPSTR)pBlock + info->wLength);
1033 DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock;
1034 DWORD max = (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4 - info->wLength;
1036 len = MultiByteToWideChar(CP_ACP, 0, *lplpBuffer, -1,
1037 lpBufferW + pos, max/sizeof(WCHAR) - pos );
1038 *lplpBuffer = lpBufferW + pos;
1039 *puLen = len;
1041 return ret;
1044 return VersionInfo32_QueryValue(info, lpSubBlock, lplpBuffer, puLen, NULL);
1048 /******************************************************************************
1049 * testFileExistenceA
1051 * Tests whether a given path/file combination exists. If the file does
1052 * not exist, the return value is zero. If it does exist, the return
1053 * value is non-zero.
1055 * Revision history
1056 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
1057 * Original implementation
1060 static int testFileExistenceA( char const * path, char const * file, BOOL excl )
1062 char filename[1024];
1063 int filenamelen;
1064 OFSTRUCT fileinfo;
1066 fileinfo.cBytes = sizeof(OFSTRUCT);
1068 strcpy(filename, path);
1069 filenamelen = strlen(filename);
1071 /* Add a trailing \ if necessary */
1072 if(filenamelen) {
1073 if(filename[filenamelen - 1] != '\\')
1074 strcat(filename, "\\");
1076 else /* specify the current directory */
1077 strcpy(filename, ".\\");
1079 /* Create the full pathname */
1080 strcat(filename, file);
1082 return (OpenFile(filename, &fileinfo,
1083 OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR);
1086 /******************************************************************************
1087 * testFileExistenceW
1089 static int testFileExistenceW( const WCHAR *path, const WCHAR *file, BOOL excl )
1091 char *filename;
1092 DWORD pathlen, filelen;
1093 int ret;
1094 OFSTRUCT fileinfo;
1096 fileinfo.cBytes = sizeof(OFSTRUCT);
1098 pathlen = WideCharToMultiByte( CP_ACP, 0, path, -1, NULL, 0, NULL, NULL );
1099 filelen = WideCharToMultiByte( CP_ACP, 0, file, -1, NULL, 0, NULL, NULL );
1100 filename = HeapAlloc( GetProcessHeap(), 0, pathlen+filelen+2 );
1102 WideCharToMultiByte( CP_ACP, 0, path, -1, filename, pathlen, NULL, NULL );
1103 /* Add a trailing \ if necessary */
1104 if (pathlen > 1)
1106 if (filename[pathlen-2] != '\\') strcpy( &filename[pathlen-1], "\\" );
1108 else /* specify the current directory */
1109 strcpy(filename, ".\\");
1111 WideCharToMultiByte( CP_ACP, 0, file, -1, filename+strlen(filename), filelen, NULL, NULL );
1113 ret = (OpenFile(filename, &fileinfo,
1114 OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR);
1115 HeapFree( GetProcessHeap(), 0, filename );
1116 return ret;
1119 /*****************************************************************************
1120 * VerFindFileA [VERSION.@]
1122 * Determines where to install a file based on whether it locates another
1123 * version of the file in the system. The values VerFindFile returns are
1124 * used in a subsequent call to the VerInstallFile function.
1126 * Revision history:
1127 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
1128 * Reimplementation of VerFindFile from original stub.
1130 DWORD WINAPI VerFindFileA(
1131 DWORD flags,
1132 LPCSTR lpszFilename,
1133 LPCSTR lpszWinDir,
1134 LPCSTR lpszAppDir,
1135 LPSTR lpszCurDir,
1136 PUINT lpuCurDirLen,
1137 LPSTR lpszDestDir,
1138 PUINT lpuDestDirLen )
1140 DWORD retval = 0;
1141 const char *curDir;
1142 const char *destDir;
1143 unsigned int curDirSizeReq;
1144 unsigned int destDirSizeReq;
1145 char winDir[MAX_PATH], systemDir[MAX_PATH];
1147 /* Print out debugging information */
1148 TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n",
1149 flags, debugstr_a(lpszFilename), debugstr_a(lpszWinDir), debugstr_a(lpszAppDir),
1150 lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0,
1151 lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 );
1153 /* Figure out where the file should go; shared files default to the
1154 system directory */
1156 GetSystemDirectoryA(systemDir, sizeof(systemDir));
1157 curDir = "";
1158 destDir = "";
1160 if(flags & VFFF_ISSHAREDFILE)
1162 destDir = systemDir;
1163 /* Were we given a filename? If so, try to find the file. */
1164 if(lpszFilename)
1166 if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir;
1167 else if(lpszAppDir && testFileExistenceA(lpszAppDir, lpszFilename, FALSE))
1169 curDir = lpszAppDir;
1170 retval |= VFF_CURNEDEST;
1174 else /* not a shared file */
1176 destDir = lpszAppDir ? lpszAppDir : "";
1177 if(lpszFilename)
1179 GetWindowsDirectoryA( winDir, MAX_PATH );
1180 if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir;
1181 else if(testFileExistenceA(winDir, lpszFilename, FALSE))
1183 curDir = winDir;
1184 retval |= VFF_CURNEDEST;
1186 else if(testFileExistenceA(systemDir, lpszFilename, FALSE))
1188 curDir = systemDir;
1189 retval |= VFF_CURNEDEST;
1194 /* Check to see if the file exists and is in use by another application */
1195 if (lpszFilename && testFileExistenceA(curDir, lpszFilename, FALSE)) {
1196 if (lpszFilename && !testFileExistenceA(curDir, lpszFilename, TRUE))
1197 retval |= VFF_FILEINUSE;
1200 curDirSizeReq = strlen(curDir) + 1;
1201 destDirSizeReq = strlen(destDir) + 1;
1203 /* Make sure that the pointers to the size of the buffers are
1204 valid; if not, do NOTHING with that buffer. If that pointer
1205 is valid, then make sure that the buffer pointer is valid, too! */
1207 if(lpuDestDirLen && lpszDestDir)
1209 if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1210 lstrcpynA(lpszDestDir, destDir, *lpuDestDirLen);
1211 *lpuDestDirLen = destDirSizeReq;
1213 if(lpuCurDirLen && lpszCurDir)
1215 if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1216 lstrcpynA(lpszCurDir, curDir, *lpuCurDirLen);
1217 *lpuCurDirLen = curDirSizeReq;
1220 TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval,
1221 (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "",
1222 (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "",
1223 (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "",
1224 debugstr_a(lpszCurDir), debugstr_a(lpszDestDir));
1226 return retval;
1229 /*****************************************************************************
1230 * VerFindFileW [VERSION.@]
1232 DWORD WINAPI VerFindFileW( DWORD flags,LPCWSTR lpszFilename,LPCWSTR lpszWinDir,
1233 LPCWSTR lpszAppDir, LPWSTR lpszCurDir,PUINT lpuCurDirLen,
1234 LPWSTR lpszDestDir,PUINT lpuDestDirLen )
1236 static const WCHAR emptyW;
1237 DWORD retval = 0;
1238 const WCHAR *curDir;
1239 const WCHAR *destDir;
1240 unsigned int curDirSizeReq;
1241 unsigned int destDirSizeReq;
1242 WCHAR winDir[MAX_PATH], systemDir[MAX_PATH];
1244 /* Print out debugging information */
1245 TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n",
1246 flags, debugstr_w(lpszFilename), debugstr_w(lpszWinDir), debugstr_w(lpszAppDir),
1247 lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0,
1248 lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 );
1250 /* Figure out where the file should go; shared files default to the
1251 system directory */
1253 GetSystemDirectoryW(systemDir, sizeof(systemDir)/sizeof(WCHAR));
1254 curDir = &emptyW;
1255 destDir = &emptyW;
1257 if(flags & VFFF_ISSHAREDFILE)
1259 destDir = systemDir;
1260 /* Were we given a filename? If so, try to find the file. */
1261 if(lpszFilename)
1263 if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir;
1264 else if(lpszAppDir && testFileExistenceW(lpszAppDir, lpszFilename, FALSE))
1266 curDir = lpszAppDir;
1267 retval |= VFF_CURNEDEST;
1271 else /* not a shared file */
1273 destDir = lpszAppDir ? lpszAppDir : &emptyW;
1274 if(lpszFilename)
1276 GetWindowsDirectoryW( winDir, MAX_PATH );
1277 if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir;
1278 else if(testFileExistenceW(winDir, lpszFilename, FALSE))
1280 curDir = winDir;
1281 retval |= VFF_CURNEDEST;
1283 else if(testFileExistenceW(systemDir, lpszFilename, FALSE))
1285 curDir = systemDir;
1286 retval |= VFF_CURNEDEST;
1291 if (lpszFilename && !testFileExistenceW(curDir, lpszFilename, TRUE))
1292 retval |= VFF_FILEINUSE;
1294 curDirSizeReq = strlenW(curDir) + 1;
1295 destDirSizeReq = strlenW(destDir) + 1;
1297 /* Make sure that the pointers to the size of the buffers are
1298 valid; if not, do NOTHING with that buffer. If that pointer
1299 is valid, then make sure that the buffer pointer is valid, too! */
1301 if(lpuDestDirLen && lpszDestDir)
1303 if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1304 lstrcpynW(lpszDestDir, destDir, *lpuDestDirLen);
1305 *lpuDestDirLen = destDirSizeReq;
1307 if(lpuCurDirLen && lpszCurDir)
1309 if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1310 lstrcpynW(lpszCurDir, curDir, *lpuCurDirLen);
1311 *lpuCurDirLen = curDirSizeReq;
1314 TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval,
1315 (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "",
1316 (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "",
1317 (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "",
1318 debugstr_w(lpszCurDir), debugstr_w(lpszDestDir));
1319 return retval;
1322 static LPBYTE
1323 _fetch_versioninfo(LPSTR fn,VS_FIXEDFILEINFO **vffi) {
1324 DWORD alloclen;
1325 LPBYTE buf;
1326 DWORD ret;
1328 alloclen = 1000;
1329 buf=HeapAlloc(GetProcessHeap(), 0, alloclen);
1330 if(buf == NULL) {
1331 WARN("Memory exausted while fetching version info!\n");
1332 return NULL;
1334 while (1) {
1335 ret = GetFileVersionInfoA(fn,0,alloclen,buf);
1336 if (!ret) {
1337 HeapFree(GetProcessHeap(), 0, buf);
1338 return NULL;
1340 if (alloclen<*(WORD*)buf) {
1341 alloclen = *(WORD*)buf;
1342 HeapFree(GetProcessHeap(), 0, buf);
1343 buf = HeapAlloc(GetProcessHeap(), 0, alloclen);
1344 if(buf == NULL) {
1345 WARN("Memory exausted while fetching version info!\n");
1346 return NULL;
1348 } else {
1349 *vffi = (VS_FIXEDFILEINFO*)(buf+0x14);
1350 if ((*vffi)->dwSignature == 0x004f0049) /* hack to detect unicode */
1351 *vffi = (VS_FIXEDFILEINFO*)(buf+0x28);
1352 if ((*vffi)->dwSignature != VS_FFI_SIGNATURE)
1353 WARN("Bad VS_FIXEDFILEINFO signature 0x%08x\n",(*vffi)->dwSignature);
1354 return buf;
1359 static DWORD
1360 _error2vif(DWORD error) {
1361 switch (error) {
1362 case ERROR_ACCESS_DENIED:
1363 return VIF_ACCESSVIOLATION;
1364 case ERROR_SHARING_VIOLATION:
1365 return VIF_SHARINGVIOLATION;
1366 default:
1367 return 0;
1372 /******************************************************************************
1373 * VerInstallFileA [VERSION.@]
1375 DWORD WINAPI VerInstallFileA(
1376 DWORD flags,LPCSTR srcfilename,LPCSTR destfilename,LPCSTR srcdir,
1377 LPCSTR destdir,LPCSTR curdir,LPSTR tmpfile,PUINT tmpfilelen )
1379 LPCSTR pdest;
1380 char destfn[260],tmpfn[260],srcfn[260];
1381 HFILE hfsrc,hfdst;
1382 DWORD attr,xret,tmplast;
1383 LONG ret;
1384 LPBYTE buf1,buf2;
1385 OFSTRUCT ofs;
1387 TRACE("(%x,%s,%s,%s,%s,%s,%p,%d)\n",
1388 flags,debugstr_a(srcfilename),debugstr_a(destfilename),
1389 debugstr_a(srcdir),debugstr_a(destdir),debugstr_a(curdir),
1390 tmpfile,*tmpfilelen);
1391 xret = 0;
1392 if (!srcdir || !srcfilename) return VIF_CANNOTREADSRC;
1393 sprintf(srcfn,"%s\\%s",srcdir,srcfilename);
1394 if (!destdir || !*destdir) pdest = srcdir;
1395 else pdest = destdir;
1396 sprintf(destfn,"%s\\%s",pdest,destfilename);
1397 hfsrc=LZOpenFileA(srcfn,&ofs,OF_READ);
1398 if (hfsrc < 0)
1399 return VIF_CANNOTREADSRC;
1400 sprintf(tmpfn,"%s\\%s",pdest,destfilename);
1401 tmplast=strlen(pdest)+1;
1402 attr = GetFileAttributesA(tmpfn);
1403 if (attr != INVALID_FILE_ATTRIBUTES) {
1404 if (attr & FILE_ATTRIBUTE_READONLY) {
1405 LZClose(hfsrc);
1406 return VIF_WRITEPROT;
1408 /* FIXME: check if file currently in use and return VIF_FILEINUSE */
1410 attr = INVALID_FILE_ATTRIBUTES;
1411 if (flags & VIFF_FORCEINSTALL) {
1412 if (tmpfile[0]) {
1413 sprintf(tmpfn,"%s\\%s",pdest,tmpfile);
1414 tmplast = strlen(pdest)+1;
1415 attr = GetFileAttributesA(tmpfn);
1416 /* if it exists, it has been copied by the call before.
1417 * we jump over the copy part...
1421 if (attr == INVALID_FILE_ATTRIBUTES) {
1422 char *s;
1424 GetTempFileNameA(pdest,"ver",0,tmpfn); /* should not fail ... */
1425 s=strrchr(tmpfn,'\\');
1426 if (s)
1427 tmplast = s-tmpfn;
1428 else
1429 tmplast = 0;
1430 hfdst = OpenFile(tmpfn,&ofs,OF_CREATE);
1431 if (hfdst == HFILE_ERROR) {
1432 LZClose(hfsrc);
1433 return VIF_CANNOTCREATE; /* | translated dos error */
1435 ret = LZCopy(hfsrc,hfdst);
1436 _lclose(hfdst);
1437 if (ret < 0) {
1438 /* translate LZ errors into VIF_xxx */
1439 switch (ret) {
1440 case LZERROR_BADINHANDLE:
1441 case LZERROR_READ:
1442 case LZERROR_BADVALUE:
1443 case LZERROR_UNKNOWNALG:
1444 xret = VIF_CANNOTREADSRC;
1445 break;
1446 case LZERROR_BADOUTHANDLE:
1447 case LZERROR_WRITE:
1448 xret = VIF_OUTOFSPACE;
1449 break;
1450 case LZERROR_GLOBALLOC:
1451 case LZERROR_GLOBLOCK:
1452 xret = VIF_OUTOFMEMORY;
1453 break;
1454 default: /* unknown error, should not happen */
1455 FIXME("Unknown LZCopy error %d, ignoring.\n", ret);
1456 xret = 0;
1457 break;
1459 if (xret) {
1460 LZClose(hfsrc);
1461 return xret;
1465 if (!(flags & VIFF_FORCEINSTALL)) {
1466 VS_FIXEDFILEINFO *destvffi,*tmpvffi;
1467 buf1 = _fetch_versioninfo(destfn,&destvffi);
1468 if (buf1) {
1469 buf2 = _fetch_versioninfo(tmpfn,&tmpvffi);
1470 if (buf2) {
1471 char *tbuf1,*tbuf2;
1472 static const CHAR trans_array[] = "\\VarFileInfo\\Translation";
1473 UINT len1,len2;
1475 len1=len2=40;
1477 /* compare file versions */
1478 if ((destvffi->dwFileVersionMS > tmpvffi->dwFileVersionMS)||
1479 ((destvffi->dwFileVersionMS==tmpvffi->dwFileVersionMS)&&
1480 (destvffi->dwFileVersionLS > tmpvffi->dwFileVersionLS)
1483 xret |= VIF_MISMATCH|VIF_SRCOLD;
1484 /* compare filetypes and filesubtypes */
1485 if ((destvffi->dwFileType!=tmpvffi->dwFileType) ||
1486 (destvffi->dwFileSubtype!=tmpvffi->dwFileSubtype)
1488 xret |= VIF_MISMATCH|VIF_DIFFTYPE;
1489 if (VerQueryValueA(buf1,trans_array,(LPVOID*)&tbuf1,&len1) &&
1490 VerQueryValueA(buf2,trans_array,(LPVOID*)&tbuf2,&len2)
1492 /* Do something with tbuf1 and tbuf2
1493 * generates DIFFLANG|MISMATCH
1496 HeapFree(GetProcessHeap(), 0, buf2);
1497 } else
1498 xret=VIF_MISMATCH|VIF_SRCOLD;
1499 HeapFree(GetProcessHeap(), 0, buf1);
1502 if (xret) {
1503 if (*tmpfilelen<strlen(tmpfn+tmplast)) {
1504 xret|=VIF_BUFFTOOSMALL;
1505 DeleteFileA(tmpfn);
1506 } else {
1507 strcpy(tmpfile,tmpfn+tmplast);
1508 *tmpfilelen = strlen(tmpfn+tmplast)+1;
1509 xret|=VIF_TEMPFILE;
1511 } else {
1512 if (INVALID_FILE_ATTRIBUTES!=GetFileAttributesA(destfn))
1513 if (!DeleteFileA(destfn)) {
1514 xret|=_error2vif(GetLastError())|VIF_CANNOTDELETE;
1515 DeleteFileA(tmpfn);
1516 LZClose(hfsrc);
1517 return xret;
1519 if ((!(flags & VIFF_DONTDELETEOLD)) &&
1520 curdir &&
1521 *curdir &&
1522 lstrcmpiA(curdir,pdest)
1524 char curfn[260];
1526 sprintf(curfn,"%s\\%s",curdir,destfilename);
1527 if (INVALID_FILE_ATTRIBUTES != GetFileAttributesA(curfn)) {
1528 /* FIXME: check if in use ... if it is, VIF_CANNOTDELETECUR */
1529 if (!DeleteFileA(curfn))
1530 xret|=_error2vif(GetLastError())|VIF_CANNOTDELETECUR;
1533 if (!MoveFileA(tmpfn,destfn)) {
1534 xret|=_error2vif(GetLastError())|VIF_CANNOTRENAME;
1535 DeleteFileA(tmpfn);
1538 LZClose(hfsrc);
1539 return xret;
1543 /******************************************************************************
1544 * VerInstallFileW [VERSION.@]
1546 DWORD WINAPI VerInstallFileW(
1547 DWORD flags,LPCWSTR srcfilename,LPCWSTR destfilename,LPCWSTR srcdir,
1548 LPCWSTR destdir,LPCWSTR curdir,LPWSTR tmpfile,PUINT tmpfilelen )
1550 LPSTR wsrcf = NULL, wsrcd = NULL, wdestf = NULL, wdestd = NULL, wtmpf = NULL, wcurd = NULL;
1551 DWORD ret = 0;
1552 UINT len;
1554 if (srcfilename)
1556 len = WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, NULL, 0, NULL, NULL );
1557 if ((wsrcf = HeapAlloc( GetProcessHeap(), 0, len )))
1558 WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, wsrcf, len, NULL, NULL );
1559 else
1560 ret = VIF_OUTOFMEMORY;
1562 if (srcdir && !ret)
1564 len = WideCharToMultiByte( CP_ACP, 0, srcdir, -1, NULL, 0, NULL, NULL );
1565 if ((wsrcd = HeapAlloc( GetProcessHeap(), 0, len )))
1566 WideCharToMultiByte( CP_ACP, 0, srcdir, -1, wsrcd, len, NULL, NULL );
1567 else
1568 ret = VIF_OUTOFMEMORY;
1570 if (destfilename && !ret)
1572 len = WideCharToMultiByte( CP_ACP, 0, destfilename, -1, NULL, 0, NULL, NULL );
1573 if ((wdestf = HeapAlloc( GetProcessHeap(), 0, len )))
1574 WideCharToMultiByte( CP_ACP, 0, destfilename, -1, wdestf, len, NULL, NULL );
1575 else
1576 ret = VIF_OUTOFMEMORY;
1578 if (destdir && !ret)
1580 len = WideCharToMultiByte( CP_ACP, 0, destdir, -1, NULL, 0, NULL, NULL );
1581 if ((wdestd = HeapAlloc( GetProcessHeap(), 0, len )))
1582 WideCharToMultiByte( CP_ACP, 0, destdir, -1, wdestd, len, NULL, NULL );
1583 else
1584 ret = VIF_OUTOFMEMORY;
1586 if (curdir && !ret)
1588 len = WideCharToMultiByte( CP_ACP, 0, curdir, -1, NULL, 0, NULL, NULL );
1589 if ((wcurd = HeapAlloc( GetProcessHeap(), 0, len )))
1590 WideCharToMultiByte( CP_ACP, 0, curdir, -1, wcurd, len, NULL, NULL );
1591 else
1592 ret = VIF_OUTOFMEMORY;
1594 if (!ret)
1596 len = *tmpfilelen * sizeof(WCHAR);
1597 wtmpf = HeapAlloc( GetProcessHeap(), 0, len );
1598 if (!wtmpf)
1599 ret = VIF_OUTOFMEMORY;
1601 if (!ret)
1602 ret = VerInstallFileA(flags,wsrcf,wdestf,wsrcd,wdestd,wcurd,wtmpf,&len);
1603 if (!ret)
1604 *tmpfilelen = MultiByteToWideChar( CP_ACP, 0, wtmpf, -1, tmpfile, *tmpfilelen );
1605 else if (ret & VIF_BUFFTOOSMALL)
1606 *tmpfilelen = len; /* FIXME: not correct */
1608 HeapFree( GetProcessHeap(), 0, wsrcf );
1609 HeapFree( GetProcessHeap(), 0, wsrcd );
1610 HeapFree( GetProcessHeap(), 0, wdestf );
1611 HeapFree( GetProcessHeap(), 0, wdestd );
1612 HeapFree( GetProcessHeap(), 0, wtmpf );
1613 HeapFree( GetProcessHeap(), 0, wcurd );
1614 return ret;