mshtml: Rename fire_event_obj and dispatch_event.
[wine.git] / dlls / version / version.c
blobed060c82b66eab9f480d123f5bc2b673c41b51c6
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, DWORD flags )
132 const IMAGE_RESOURCE_DIRECTORY *ret;
133 WORD list[9];
134 int i, pos = 0;
136 if (flags & FILE_VER_GET_LOCALISED)
138 /* cf. LdrFindResource_U */
139 pos = push_language( list, pos, MAKELANGID( LANG_NEUTRAL, SUBLANG_NEUTRAL ) );
140 pos = push_language( list, pos, LANGIDFROMLCID( NtCurrentTeb()->CurrentLocale ) );
141 pos = push_language( list, pos, GetUserDefaultLangID() );
142 pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(GetUserDefaultLangID()), SUBLANG_NEUTRAL ));
143 pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(GetUserDefaultLangID()), SUBLANG_DEFAULT ));
144 pos = push_language( list, pos, GetSystemDefaultLangID() );
145 pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(GetSystemDefaultLangID()), SUBLANG_NEUTRAL ));
146 pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(GetSystemDefaultLangID()), SUBLANG_DEFAULT ));
147 pos = push_language( list, pos, MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT ) );
149 else
151 /* FIXME: resolve LN file here */
152 pos = push_language( list, pos, MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT ) );
155 for (i = 0; i < pos; i++) if ((ret = find_entry_by_id( dir, list[i], root ))) return ret;
156 return find_entry_default( dir, root );
160 /***********************************************************************
161 * read_xx_header [internal]
163 static int read_xx_header( HFILE lzfd )
165 IMAGE_DOS_HEADER mzh;
166 char magic[3];
168 LZSeek( lzfd, 0, SEEK_SET );
169 if ( sizeof(mzh) != LZRead( lzfd, (LPSTR)&mzh, sizeof(mzh) ) )
170 return 0;
171 if ( mzh.e_magic != IMAGE_DOS_SIGNATURE )
173 if (!memcmp( &mzh, "\177ELF", 4 )) return 1; /* ELF */
174 if (*(UINT *)&mzh == 0xfeedface || *(UINT *)&mzh == 0xcefaedfe) return 1; /* Mach-O */
175 return 0;
178 LZSeek( lzfd, mzh.e_lfanew, SEEK_SET );
179 if ( 2 != LZRead( lzfd, magic, 2 ) )
180 return 0;
182 LZSeek( lzfd, mzh.e_lfanew, SEEK_SET );
184 if ( magic[0] == 'N' && magic[1] == 'E' )
185 return IMAGE_OS2_SIGNATURE;
186 if ( magic[0] == 'P' && magic[1] == 'E' )
187 return IMAGE_NT_SIGNATURE;
189 magic[2] = '\0';
190 WARN("Can't handle %s files.\n", magic );
191 return 0;
194 /***********************************************************************
195 * find_ne_resource [internal]
197 static BOOL find_ne_resource( HFILE lzfd, DWORD *resLen, DWORD *resOff )
199 const WORD typeid = VS_FILE_INFO | 0x8000;
200 const WORD resid = VS_VERSION_INFO | 0x8000;
201 IMAGE_OS2_HEADER nehd;
202 NE_TYPEINFO *typeInfo;
203 NE_NAMEINFO *nameInfo;
204 DWORD nehdoffset;
205 LPBYTE resTab;
206 DWORD resTabSize;
207 int count;
209 /* Read in NE header */
210 nehdoffset = LZSeek( lzfd, 0, SEEK_CUR );
211 if ( sizeof(nehd) != LZRead( lzfd, (LPSTR)&nehd, sizeof(nehd) ) ) return FALSE;
213 resTabSize = nehd.ne_restab - nehd.ne_rsrctab;
214 if ( !resTabSize )
216 TRACE("No resources in NE dll\n" );
217 return FALSE;
220 /* Read in resource table */
221 resTab = HeapAlloc( GetProcessHeap(), 0, resTabSize );
222 if ( !resTab ) return FALSE;
224 LZSeek( lzfd, nehd.ne_rsrctab + nehdoffset, SEEK_SET );
225 if ( resTabSize != LZRead( lzfd, (char*)resTab, resTabSize ) )
227 HeapFree( GetProcessHeap(), 0, resTab );
228 return FALSE;
231 /* Find resource */
232 typeInfo = (NE_TYPEINFO *)(resTab + 2);
233 while (typeInfo->type_id)
235 if (typeInfo->type_id == typeid) goto found_type;
236 typeInfo = (NE_TYPEINFO *)((char *)(typeInfo + 1) +
237 typeInfo->count * sizeof(NE_NAMEINFO));
239 TRACE("No typeid entry found\n" );
240 HeapFree( GetProcessHeap(), 0, resTab );
241 return FALSE;
243 found_type:
244 nameInfo = (NE_NAMEINFO *)(typeInfo + 1);
246 for (count = typeInfo->count; count > 0; count--, nameInfo++)
247 if (nameInfo->id == resid) goto found_name;
249 TRACE("No resid entry found\n" );
250 HeapFree( GetProcessHeap(), 0, resTab );
251 return FALSE;
253 found_name:
254 /* Return resource data */
255 if ( resLen ) *resLen = nameInfo->length << *(WORD *)resTab;
256 if ( resOff ) *resOff = nameInfo->offset << *(WORD *)resTab;
258 HeapFree( GetProcessHeap(), 0, resTab );
259 return TRUE;
262 /***********************************************************************
263 * find_pe_resource [internal]
265 static BOOL find_pe_resource( HFILE lzfd, DWORD *resLen, DWORD *resOff, DWORD flags )
267 union
269 IMAGE_NT_HEADERS32 nt32;
270 IMAGE_NT_HEADERS64 nt64;
271 } pehd;
272 DWORD pehdoffset;
273 PIMAGE_DATA_DIRECTORY resDataDir;
274 PIMAGE_SECTION_HEADER sections;
275 LPBYTE resSection;
276 DWORD section_size, data_size;
277 const void *resDir;
278 const IMAGE_RESOURCE_DIRECTORY *resPtr;
279 const IMAGE_RESOURCE_DATA_ENTRY *resData;
280 int i, len, nSections;
281 BOOL ret = FALSE;
283 /* Read in PE header */
284 pehdoffset = LZSeek( lzfd, 0, SEEK_CUR );
285 len = LZRead( lzfd, (LPSTR)&pehd, sizeof(pehd) );
286 if (len < sizeof(pehd.nt32.FileHeader)) return FALSE;
287 if (len < sizeof(pehd)) memset( (char *)&pehd + len, 0, sizeof(pehd) - len );
289 switch (pehd.nt32.OptionalHeader.Magic)
291 case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
292 resDataDir = pehd.nt32.OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_RESOURCE;
293 break;
294 case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
295 resDataDir = pehd.nt64.OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_RESOURCE;
296 break;
297 default:
298 return FALSE;
301 if ( !resDataDir->Size )
303 TRACE("No resources in PE dll\n" );
304 return FALSE;
307 /* Read in section table */
308 nSections = pehd.nt32.FileHeader.NumberOfSections;
309 sections = HeapAlloc( GetProcessHeap(), 0,
310 nSections * sizeof(IMAGE_SECTION_HEADER) );
311 if ( !sections ) return FALSE;
313 len = FIELD_OFFSET( IMAGE_NT_HEADERS32, OptionalHeader ) + pehd.nt32.FileHeader.SizeOfOptionalHeader;
314 LZSeek( lzfd, pehdoffset + len, SEEK_SET );
316 if ( nSections * sizeof(IMAGE_SECTION_HEADER) !=
317 LZRead( lzfd, (LPSTR)sections, nSections * sizeof(IMAGE_SECTION_HEADER) ) )
319 HeapFree( GetProcessHeap(), 0, sections );
320 return FALSE;
323 /* Find resource section */
324 for ( i = 0; i < nSections; i++ )
325 if ( resDataDir->VirtualAddress >= sections[i].VirtualAddress
326 && resDataDir->VirtualAddress < sections[i].VirtualAddress +
327 sections[i].SizeOfRawData )
328 break;
330 if ( i == nSections )
332 HeapFree( GetProcessHeap(), 0, sections );
333 TRACE("Couldn't find resource section\n" );
334 return FALSE;
337 /* Read in resource section */
338 data_size = sections[i].SizeOfRawData;
339 section_size = max( data_size, sections[i].Misc.VirtualSize );
340 resSection = HeapAlloc( GetProcessHeap(), 0, section_size );
341 if ( !resSection )
343 HeapFree( GetProcessHeap(), 0, sections );
344 return FALSE;
347 LZSeek( lzfd, sections[i].PointerToRawData, SEEK_SET );
348 if (data_size != LZRead( lzfd, (char*)resSection, data_size )) goto done;
349 if (data_size < section_size) memset( (char *)resSection + data_size, 0, section_size - data_size );
351 /* Find resource */
352 resDir = resSection + (resDataDir->VirtualAddress - sections[i].VirtualAddress);
354 resPtr = resDir;
355 resPtr = find_entry_by_id( resPtr, VS_FILE_INFO, resDir );
356 if ( !resPtr )
358 TRACE("No typeid entry found\n" );
359 goto done;
361 resPtr = find_entry_by_id( resPtr, VS_VERSION_INFO, resDir );
362 if ( !resPtr )
364 TRACE("No resid entry found\n" );
365 goto done;
367 resPtr = find_entry_language( resPtr, resDir, flags );
368 if ( !resPtr )
370 TRACE("No default language entry found\n" );
371 goto done;
374 /* Find resource data section */
375 resData = (const IMAGE_RESOURCE_DATA_ENTRY*)resPtr;
376 for ( i = 0; i < nSections; i++ )
377 if ( resData->OffsetToData >= sections[i].VirtualAddress
378 && resData->OffsetToData < sections[i].VirtualAddress +
379 sections[i].SizeOfRawData )
380 break;
382 if ( i == nSections )
384 TRACE("Couldn't find resource data section\n" );
385 goto done;
388 /* Return resource data */
389 if ( resLen ) *resLen = resData->Size;
390 if ( resOff ) *resOff = resData->OffsetToData - sections[i].VirtualAddress
391 + sections[i].PointerToRawData;
392 ret = TRUE;
394 done:
395 HeapFree( GetProcessHeap(), 0, resSection );
396 HeapFree( GetProcessHeap(), 0, sections );
397 return ret;
401 /***********************************************************************
402 * find_version_resource [internal]
404 static DWORD find_version_resource( HFILE lzfd, DWORD *reslen, DWORD *offset, DWORD flags )
406 DWORD magic = read_xx_header( lzfd );
408 switch (magic)
410 case IMAGE_OS2_SIGNATURE:
411 if (!find_ne_resource( lzfd, reslen, offset )) magic = 0;
412 break;
413 case IMAGE_NT_SIGNATURE:
414 if (!find_pe_resource( lzfd, reslen, offset, flags )) magic = 0;
415 break;
417 return magic;
420 /******************************************************************************
422 * This function will print via standard TRACE, debug info regarding
423 * the file info structure vffi.
424 * 15-Feb-1998 Dimitrie Paun (dimi@cs.toronto.edu)
425 * Added this function to clean up the code.
427 *****************************************************************************/
428 static void print_vffi_debug(const VS_FIXEDFILEINFO *vffi)
430 BOOL versioned_printer = FALSE;
432 if((vffi->dwFileType == VFT_DLL) || (vffi->dwFileType == VFT_DRV))
434 if(vffi->dwFileSubtype == VFT2_DRV_VERSIONED_PRINTER)
435 /* this is documented for newer w2k Drivers and up */
436 versioned_printer = TRUE;
437 else if( (vffi->dwFileSubtype == VFT2_DRV_PRINTER) &&
438 (vffi->dwFileVersionMS != vffi->dwProductVersionMS) &&
439 (vffi->dwFileVersionMS > 0) &&
440 (vffi->dwFileVersionMS <= 3) )
441 /* found this on NT 3.51, NT4.0 and old w2k Drivers */
442 versioned_printer = TRUE;
445 TRACE("structversion=%u.%u, ",
446 HIWORD(vffi->dwStrucVersion),LOWORD(vffi->dwStrucVersion));
447 if(versioned_printer)
449 WORD mode = LOWORD(vffi->dwFileVersionMS);
450 WORD ver_rev = HIWORD(vffi->dwFileVersionLS);
451 TRACE("fileversion=%u.%u.%u.%u (%s.major.minor.release), ",
452 (vffi->dwFileVersionMS),
453 HIBYTE(ver_rev), LOBYTE(ver_rev), LOWORD(vffi->dwFileVersionLS),
454 (mode == 3) ? "Usermode" : ((mode <= 2) ? "Kernelmode" : "?") );
456 else
458 TRACE("fileversion=%u.%u.%u.%u, ",
459 HIWORD(vffi->dwFileVersionMS),LOWORD(vffi->dwFileVersionMS),
460 HIWORD(vffi->dwFileVersionLS),LOWORD(vffi->dwFileVersionLS));
462 TRACE("productversion=%u.%u.%u.%u\n",
463 HIWORD(vffi->dwProductVersionMS),LOWORD(vffi->dwProductVersionMS),
464 HIWORD(vffi->dwProductVersionLS),LOWORD(vffi->dwProductVersionLS));
466 TRACE("flagmask=0x%x, flags=0x%x %s%s%s%s%s%s\n",
467 vffi->dwFileFlagsMask, vffi->dwFileFlags,
468 (vffi->dwFileFlags & VS_FF_DEBUG) ? "DEBUG," : "",
469 (vffi->dwFileFlags & VS_FF_PRERELEASE) ? "PRERELEASE," : "",
470 (vffi->dwFileFlags & VS_FF_PATCHED) ? "PATCHED," : "",
471 (vffi->dwFileFlags & VS_FF_PRIVATEBUILD) ? "PRIVATEBUILD," : "",
472 (vffi->dwFileFlags & VS_FF_INFOINFERRED) ? "INFOINFERRED," : "",
473 (vffi->dwFileFlags & VS_FF_SPECIALBUILD) ? "SPECIALBUILD," : "");
475 TRACE("(");
477 TRACE("OS=0x%x.0x%x ", HIWORD(vffi->dwFileOS), LOWORD(vffi->dwFileOS));
479 switch (vffi->dwFileOS&0xFFFF0000)
481 case VOS_DOS:TRACE("DOS,");break;
482 case VOS_OS216:TRACE("OS/2-16,");break;
483 case VOS_OS232:TRACE("OS/2-32,");break;
484 case VOS_NT:TRACE("NT,");break;
485 case VOS_UNKNOWN:
486 default:
487 TRACE("UNKNOWN(0x%x),",vffi->dwFileOS&0xFFFF0000);break;
490 switch (LOWORD(vffi->dwFileOS))
492 case VOS__BASE:TRACE("BASE");break;
493 case VOS__WINDOWS16:TRACE("WIN16");break;
494 case VOS__WINDOWS32:TRACE("WIN32");break;
495 case VOS__PM16:TRACE("PM16");break;
496 case VOS__PM32:TRACE("PM32");break;
497 default:
498 TRACE("UNKNOWN(0x%x)",LOWORD(vffi->dwFileOS));break;
501 TRACE(")\n");
503 switch (vffi->dwFileType)
505 case VFT_APP:TRACE("filetype=APP");break;
506 case VFT_DLL:
507 TRACE("filetype=DLL");
508 if(vffi->dwFileSubtype != 0)
510 if(versioned_printer) /* NT3.x/NT4.0 or old w2k Driver */
511 TRACE(",PRINTER");
512 TRACE(" (subtype=0x%x)", vffi->dwFileSubtype);
514 break;
515 case VFT_DRV:
516 TRACE("filetype=DRV,");
517 switch(vffi->dwFileSubtype)
519 case VFT2_DRV_PRINTER:TRACE("PRINTER");break;
520 case VFT2_DRV_KEYBOARD:TRACE("KEYBOARD");break;
521 case VFT2_DRV_LANGUAGE:TRACE("LANGUAGE");break;
522 case VFT2_DRV_DISPLAY:TRACE("DISPLAY");break;
523 case VFT2_DRV_MOUSE:TRACE("MOUSE");break;
524 case VFT2_DRV_NETWORK:TRACE("NETWORK");break;
525 case VFT2_DRV_SYSTEM:TRACE("SYSTEM");break;
526 case VFT2_DRV_INSTALLABLE:TRACE("INSTALLABLE");break;
527 case VFT2_DRV_SOUND:TRACE("SOUND");break;
528 case VFT2_DRV_COMM:TRACE("COMM");break;
529 case VFT2_DRV_INPUTMETHOD:TRACE("INPUTMETHOD");break;
530 case VFT2_DRV_VERSIONED_PRINTER:TRACE("VERSIONED_PRINTER");break;
531 case VFT2_UNKNOWN:
532 default:
533 TRACE("UNKNOWN(0x%x)",vffi->dwFileSubtype);break;
535 break;
536 case VFT_FONT:
537 TRACE("filetype=FONT,");
538 switch (vffi->dwFileSubtype)
540 case VFT2_FONT_RASTER:TRACE("RASTER");break;
541 case VFT2_FONT_VECTOR:TRACE("VECTOR");break;
542 case VFT2_FONT_TRUETYPE:TRACE("TRUETYPE");break;
543 default:TRACE("UNKNOWN(0x%x)",vffi->dwFileSubtype);break;
545 break;
546 case VFT_VXD:TRACE("filetype=VXD");break;
547 case VFT_STATIC_LIB:TRACE("filetype=STATIC_LIB");break;
548 case VFT_UNKNOWN:
549 default:
550 TRACE("filetype=Unknown(0x%x)",vffi->dwFileType);break;
553 TRACE("\n");
554 TRACE("filedate=0x%x.0x%x\n",vffi->dwFileDateMS,vffi->dwFileDateLS);
557 /***********************************************************************
558 * Version Info Structure
561 typedef struct
563 WORD wLength;
564 WORD wValueLength;
565 CHAR szKey[1];
566 #if 0 /* variable length structure */
567 /* DWORD aligned */
568 BYTE Value[];
569 /* DWORD aligned */
570 VS_VERSION_INFO_STRUCT16 Children[];
571 #endif
572 } VS_VERSION_INFO_STRUCT16;
574 typedef struct
576 WORD wLength;
577 WORD wValueLength;
578 WORD wType; /* 1:Text, 0:Binary */
579 WCHAR szKey[1];
580 #if 0 /* variable length structure */
581 /* DWORD aligned */
582 BYTE Value[];
583 /* DWORD aligned */
584 VS_VERSION_INFO_STRUCT32 Children[];
585 #endif
586 } VS_VERSION_INFO_STRUCT32;
588 #define VersionInfoIs16( ver ) \
589 ( ((const VS_VERSION_INFO_STRUCT16 *)ver)->szKey[0] >= ' ' )
591 #define DWORD_ALIGN( base, ptr ) \
592 ( (LPBYTE)(base) + ((((LPBYTE)(ptr) - (LPBYTE)(base)) + 3) & ~3) )
594 #define VersionInfo16_Value( ver ) \
595 DWORD_ALIGN( (ver), (ver)->szKey + strlen((ver)->szKey) + 1 )
596 #define VersionInfo32_Value( ver ) \
597 DWORD_ALIGN( (ver), (ver)->szKey + strlenW((ver)->szKey) + 1 )
599 #define VersionInfo16_Children( ver ) \
600 (const VS_VERSION_INFO_STRUCT16 *)( VersionInfo16_Value( ver ) + \
601 ( ( (ver)->wValueLength + 3 ) & ~3 ) )
602 #define VersionInfo32_Children( ver ) \
603 (const VS_VERSION_INFO_STRUCT32 *)( VersionInfo32_Value( ver ) + \
604 ( ( (ver)->wValueLength * \
605 ((ver)->wType? 2 : 1) + 3 ) & ~3 ) )
607 #define VersionInfo16_Next( ver ) \
608 (VS_VERSION_INFO_STRUCT16 *)( (LPBYTE)ver + (((ver)->wLength + 3) & ~3) )
609 #define VersionInfo32_Next( ver ) \
610 (VS_VERSION_INFO_STRUCT32 *)( (LPBYTE)ver + (((ver)->wLength + 3) & ~3) )
613 /***********************************************************************
614 * GetFileVersionInfoSizeW [VERSION.@]
616 DWORD WINAPI GetFileVersionInfoSizeW( LPCWSTR filename, LPDWORD handle )
618 return GetFileVersionInfoSizeExW( FILE_VER_GET_LOCALISED, filename, handle );
621 /***********************************************************************
622 * GetFileVersionInfoSizeA [VERSION.@]
624 DWORD WINAPI GetFileVersionInfoSizeA( LPCSTR filename, LPDWORD handle )
626 return GetFileVersionInfoSizeExA( FILE_VER_GET_LOCALISED, filename, handle );
629 /******************************************************************************
630 * GetFileVersionInfoSizeExW [VERSION.@]
632 DWORD WINAPI GetFileVersionInfoSizeExW( DWORD flags, LPCWSTR filename, LPDWORD handle )
634 DWORD len, offset, magic = 1;
635 HFILE lzfd;
636 HMODULE hModule;
637 OFSTRUCT ofs;
639 TRACE("(0x%x,%s,%p)\n", flags, debugstr_w(filename), handle );
641 if (handle) *handle = 0;
643 if (!filename)
645 SetLastError(ERROR_INVALID_PARAMETER);
646 return 0;
648 if (!*filename)
650 SetLastError(ERROR_BAD_PATHNAME);
651 return 0;
653 if (flags & ~FILE_VER_GET_LOCALISED)
654 FIXME("flags 0x%x ignored\n", flags & ~FILE_VER_GET_LOCALISED);
656 if ((lzfd = LZOpenFileW( (LPWSTR)filename, &ofs, OF_READ )) != HFILE_ERROR)
658 magic = find_version_resource( lzfd, &len, &offset, flags );
659 LZClose( lzfd );
662 if ((magic == 1) && (hModule = LoadLibraryExW( filename, 0, LOAD_LIBRARY_AS_DATAFILE )))
664 HRSRC hRsrc = NULL;
665 if (!(flags & FILE_VER_GET_LOCALISED))
667 LANGID english = MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT );
668 hRsrc = FindResourceExW( hModule, MAKEINTRESOURCEW(VS_VERSION_INFO),
669 (LPWSTR)VS_FILE_INFO, english );
671 if (!hRsrc)
672 hRsrc = FindResourceW( hModule, MAKEINTRESOURCEW(VS_VERSION_INFO),
673 (LPWSTR)VS_FILE_INFO );
674 if (hRsrc)
676 magic = IMAGE_NT_SIGNATURE;
677 len = SizeofResource( hModule, hRsrc );
679 FreeLibrary( hModule );
682 switch (magic)
684 case IMAGE_OS2_SIGNATURE:
685 /* We have a 16bit resource.
687 * XP/W2K/W2K3 uses a buffer which is more than the actual needed space:
689 * (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4
691 * This extra buffer is used for ANSI to Unicode conversions in W-Calls.
692 * info->wLength should be the same as len. Currently it isn't but that
693 * doesn't seem to be a problem (len is bigger than info->wLength).
695 SetLastError(0);
696 return (len - sizeof(VS_FIXEDFILEINFO)) * 4;
698 case IMAGE_NT_SIGNATURE:
699 /* We have a 32bit resource.
701 * XP/W2K/W2K3 uses a buffer which is 2 times the actual needed space + 4 bytes "FE2X"
702 * This extra buffer is used for Unicode to ANSI conversions in A-Calls
704 SetLastError(0);
705 return (len * 2) + 4;
707 default:
708 SetLastError( lzfd == HFILE_ERROR ? ofs.nErrCode : ERROR_RESOURCE_DATA_NOT_FOUND );
709 return 0;
713 /******************************************************************************
714 * GetFileVersionInfoSizeExA [VERSION.@]
716 DWORD WINAPI GetFileVersionInfoSizeExA( DWORD flags, LPCSTR filename, LPDWORD handle )
718 UNICODE_STRING filenameW;
719 DWORD retval;
721 TRACE("(0x%x,%s,%p)\n", flags, debugstr_a(filename), handle );
723 if(filename)
724 RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
725 else
726 filenameW.Buffer = NULL;
728 retval = GetFileVersionInfoSizeExW(flags, filenameW.Buffer, handle);
730 RtlFreeUnicodeString(&filenameW);
732 return retval;
735 /***********************************************************************
736 * GetFileVersionInfoExW [VERSION.@]
738 BOOL WINAPI GetFileVersionInfoExW( DWORD flags, LPCWSTR filename, DWORD handle, DWORD datasize, LPVOID data )
740 static const char signature[4] = "FE2X";
741 DWORD len, offset, magic = 1;
742 HFILE lzfd;
743 OFSTRUCT ofs;
744 HMODULE hModule;
745 VS_VERSION_INFO_STRUCT32* vvis = data;
747 TRACE("(0x%x,%s,%d,size=%d,data=%p)\n",
748 flags, debugstr_w(filename), handle, datasize, data );
750 if (!data)
752 SetLastError(ERROR_INVALID_DATA);
753 return FALSE;
755 if (flags & ~FILE_VER_GET_LOCALISED)
756 FIXME("flags 0x%x ignored\n", flags & ~FILE_VER_GET_LOCALISED);
758 if ((lzfd = LZOpenFileW( (LPWSTR)filename, &ofs, OF_READ )) != HFILE_ERROR)
760 if ((magic = find_version_resource( lzfd, &len, &offset, flags )) > 1)
762 LZSeek( lzfd, offset, 0 /* SEEK_SET */ );
763 len = LZRead( lzfd, data, min( len, datasize ) );
765 LZClose( lzfd );
768 if ((magic == 1) && (hModule = LoadLibraryExW( filename, 0, LOAD_LIBRARY_AS_DATAFILE )))
770 HRSRC hRsrc = NULL;
771 if (!(flags & FILE_VER_GET_LOCALISED))
773 LANGID english = MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT );
774 hRsrc = FindResourceExW( hModule, MAKEINTRESOURCEW(VS_VERSION_INFO),
775 (LPWSTR)VS_FILE_INFO, english );
777 if (!hRsrc)
778 hRsrc = FindResourceW( hModule, MAKEINTRESOURCEW(VS_VERSION_INFO),
779 (LPWSTR)VS_FILE_INFO );
780 if (hRsrc)
782 HGLOBAL hMem = LoadResource( hModule, hRsrc );
783 magic = IMAGE_NT_SIGNATURE;
784 len = min( SizeofResource(hModule, hRsrc), datasize );
785 memcpy( data, LockResource( hMem ), len );
786 FreeResource( hMem );
788 FreeLibrary( hModule );
791 switch (magic)
793 case IMAGE_OS2_SIGNATURE:
794 /* We have a 16bit resource. */
795 if (TRACE_ON(ver))
796 print_vffi_debug( (VS_FIXEDFILEINFO *)VersionInfo16_Value( (VS_VERSION_INFO_STRUCT16 *)data ));
797 SetLastError(0);
798 return TRUE;
800 case IMAGE_NT_SIGNATURE:
801 /* We have a 32bit resource.
803 * XP/W2K/W2K3 uses a buffer which is 2 times the actual needed space + 4 bytes "FE2X"
804 * This extra buffer is used for Unicode to ANSI conversions in A-Calls
806 len = vvis->wLength + sizeof(signature);
807 if (datasize >= len) memcpy( (char*)data + vvis->wLength, signature, sizeof(signature) );
808 if (TRACE_ON(ver))
809 print_vffi_debug( (VS_FIXEDFILEINFO *)VersionInfo32_Value( vvis ));
810 SetLastError(0);
811 return TRUE;
813 default:
814 SetLastError( lzfd == HFILE_ERROR ? ofs.nErrCode : ERROR_RESOURCE_DATA_NOT_FOUND );
815 return FALSE;
819 /***********************************************************************
820 * GetFileVersionInfoExA [VERSION.@]
822 BOOL WINAPI GetFileVersionInfoExA( DWORD flags, LPCSTR filename, DWORD handle, DWORD datasize, LPVOID data )
824 UNICODE_STRING filenameW;
825 BOOL retval;
827 TRACE("(0x%x,%s,%d,size=%d,data=%p)\n",
828 flags, debugstr_a(filename), handle, datasize, data );
830 if(filename)
831 RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
832 else
833 filenameW.Buffer = NULL;
835 retval = GetFileVersionInfoExW(flags, filenameW.Buffer, handle, datasize, data);
837 RtlFreeUnicodeString(&filenameW);
839 return retval;
842 /***********************************************************************
843 * GetFileVersionInfoW [VERSION.@]
845 BOOL WINAPI GetFileVersionInfoW( LPCWSTR filename, DWORD handle, DWORD datasize, LPVOID data )
847 return GetFileVersionInfoExW(FILE_VER_GET_LOCALISED, filename, handle, datasize, data);
850 /***********************************************************************
851 * GetFileVersionInfoA [VERSION.@]
853 BOOL WINAPI GetFileVersionInfoA( LPCSTR filename, DWORD handle, DWORD datasize, LPVOID data )
855 return GetFileVersionInfoExA(FILE_VER_GET_LOCALISED, filename, handle, datasize, data);
858 /***********************************************************************
859 * VersionInfo16_FindChild [internal]
861 static const VS_VERSION_INFO_STRUCT16 *VersionInfo16_FindChild( const VS_VERSION_INFO_STRUCT16 *info,
862 LPCSTR szKey, UINT cbKey )
864 const VS_VERSION_INFO_STRUCT16 *child = VersionInfo16_Children( info );
866 while ((char *)child < (char *)info + info->wLength )
868 if (!strncasecmp( child->szKey, szKey, cbKey ) && !child->szKey[cbKey])
869 return child;
871 if (!(child->wLength)) return NULL;
872 child = VersionInfo16_Next( child );
875 return NULL;
878 /***********************************************************************
879 * VersionInfo32_FindChild [internal]
881 static const VS_VERSION_INFO_STRUCT32 *VersionInfo32_FindChild( const VS_VERSION_INFO_STRUCT32 *info,
882 LPCWSTR szKey, UINT cbKey )
884 const VS_VERSION_INFO_STRUCT32 *child = VersionInfo32_Children( info );
886 while ((char *)child < (char *)info + info->wLength )
888 if (!strncmpiW( child->szKey, szKey, cbKey ) && !child->szKey[cbKey])
889 return child;
891 if (!(child->wLength)) return NULL;
892 child = VersionInfo32_Next( child );
895 return NULL;
898 /***********************************************************************
899 * VersionInfo16_QueryValue [internal]
901 * Gets a value from a 16-bit NE resource
903 static BOOL VersionInfo16_QueryValue( const VS_VERSION_INFO_STRUCT16 *info, LPCSTR lpSubBlock,
904 LPVOID *lplpBuffer, UINT *puLen )
906 while ( *lpSubBlock )
908 /* Find next path component */
909 LPCSTR lpNextSlash;
910 for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ )
911 if ( *lpNextSlash == '\\' )
912 break;
914 /* Skip empty components */
915 if ( lpNextSlash == lpSubBlock )
917 lpSubBlock++;
918 continue;
921 /* We have a non-empty component: search info for key */
922 info = VersionInfo16_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock );
923 if ( !info )
925 if (puLen) *puLen = 0 ;
926 SetLastError( ERROR_RESOURCE_TYPE_NOT_FOUND );
927 return FALSE;
930 /* Skip path component */
931 lpSubBlock = lpNextSlash;
934 /* Return value */
935 *lplpBuffer = VersionInfo16_Value( info );
936 if (puLen)
937 *puLen = info->wValueLength;
939 return TRUE;
942 /***********************************************************************
943 * VersionInfo32_QueryValue [internal]
945 * Gets a value from a 32-bit PE resource
947 static BOOL VersionInfo32_QueryValue( const VS_VERSION_INFO_STRUCT32 *info, LPCWSTR lpSubBlock,
948 LPVOID *lplpBuffer, UINT *puLen, BOOL *pbText )
950 TRACE("lpSubBlock : (%s)\n", debugstr_w(lpSubBlock));
952 while ( *lpSubBlock )
954 /* Find next path component */
955 LPCWSTR lpNextSlash;
956 for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ )
957 if ( *lpNextSlash == '\\' )
958 break;
960 /* Skip empty components */
961 if ( lpNextSlash == lpSubBlock )
963 lpSubBlock++;
964 continue;
967 /* We have a non-empty component: search info for key */
968 info = VersionInfo32_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock );
969 if ( !info )
971 if (puLen) *puLen = 0 ;
972 SetLastError( ERROR_RESOURCE_TYPE_NOT_FOUND );
973 return FALSE;
976 /* Skip path component */
977 lpSubBlock = lpNextSlash;
980 /* Return value */
981 *lplpBuffer = VersionInfo32_Value( info );
982 if (puLen)
983 *puLen = info->wValueLength;
984 if (pbText)
985 *pbText = info->wType;
987 return TRUE;
990 /***********************************************************************
991 * VerQueryValueA [VERSION.@]
993 BOOL WINAPI VerQueryValueA( LPCVOID pBlock, LPCSTR lpSubBlock,
994 LPVOID *lplpBuffer, PUINT puLen )
996 static const char rootA[] = "\\";
997 const VS_VERSION_INFO_STRUCT16 *info = pBlock;
999 TRACE("(%p,%s,%p,%p)\n",
1000 pBlock, debugstr_a(lpSubBlock), lplpBuffer, puLen );
1002 if (!pBlock)
1003 return FALSE;
1005 if (lpSubBlock == NULL || lpSubBlock[0] == '\0')
1006 lpSubBlock = rootA;
1008 if ( !VersionInfoIs16( info ) )
1010 BOOL ret, isText;
1011 INT len;
1012 LPWSTR lpSubBlockW;
1014 len = MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, NULL, 0);
1015 lpSubBlockW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1017 if (!lpSubBlockW)
1018 return FALSE;
1020 MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, lpSubBlockW, len);
1022 ret = VersionInfo32_QueryValue(pBlock, lpSubBlockW, lplpBuffer, puLen, &isText);
1024 HeapFree(GetProcessHeap(), 0, lpSubBlockW);
1026 if (ret && isText)
1028 /* Set lpBuffer so it points to the 'empty' area where we store
1029 * the converted strings
1031 LPSTR lpBufferA = (LPSTR)pBlock + info->wLength + 4;
1032 DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock;
1034 len = WideCharToMultiByte(CP_ACP, 0, *lplpBuffer, -1,
1035 lpBufferA + pos, info->wLength - pos, NULL, NULL);
1036 *lplpBuffer = lpBufferA + pos;
1037 if (puLen) *puLen = len;
1039 return ret;
1042 return VersionInfo16_QueryValue(info, lpSubBlock, lplpBuffer, puLen);
1045 /***********************************************************************
1046 * VerQueryValueW [VERSION.@]
1048 BOOL WINAPI VerQueryValueW( LPCVOID pBlock, LPCWSTR lpSubBlock,
1049 LPVOID *lplpBuffer, PUINT puLen )
1051 static const WCHAR nullW[] = { 0 };
1052 static const WCHAR rootW[] = { '\\', 0 };
1053 static const WCHAR varfileinfoW[] = { '\\','V','a','r','F','i','l','e','I','n','f','o',
1054 '\\','T','r','a','n','s','l','a','t','i','o','n', 0 };
1056 const VS_VERSION_INFO_STRUCT32 *info = pBlock;
1058 TRACE("(%p,%s,%p,%p)\n",
1059 pBlock, debugstr_w(lpSubBlock), lplpBuffer, puLen );
1061 if (!pBlock)
1062 return FALSE;
1064 if (lpSubBlock == NULL || lpSubBlock[0] == nullW[0])
1065 lpSubBlock = rootW;
1067 if ( VersionInfoIs16( info ) )
1069 BOOL ret;
1070 int len;
1071 LPSTR lpSubBlockA;
1073 len = WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, NULL, 0, NULL, NULL);
1074 lpSubBlockA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
1076 if (!lpSubBlockA)
1077 return FALSE;
1079 WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, lpSubBlockA, len, NULL, NULL);
1081 ret = VersionInfo16_QueryValue(pBlock, lpSubBlockA, lplpBuffer, puLen);
1083 HeapFree(GetProcessHeap(), 0, lpSubBlockA);
1085 if (ret && strcmpiW( lpSubBlock, rootW ) && strcmpiW( lpSubBlock, varfileinfoW ))
1087 /* Set lpBuffer so it points to the 'empty' area where we store
1088 * the converted strings
1090 LPWSTR lpBufferW = (LPWSTR)((LPSTR)pBlock + info->wLength);
1091 DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock;
1092 DWORD max = (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4 - info->wLength;
1094 len = MultiByteToWideChar(CP_ACP, 0, *lplpBuffer, -1,
1095 lpBufferW + pos, max/sizeof(WCHAR) - pos );
1096 *lplpBuffer = lpBufferW + pos;
1097 if (puLen) *puLen = len;
1099 return ret;
1102 return VersionInfo32_QueryValue(info, lpSubBlock, lplpBuffer, puLen, NULL);
1106 /******************************************************************************
1107 * testFileExistenceA
1109 * Tests whether a given path/file combination exists. If the file does
1110 * not exist, the return value is zero. If it does exist, the return
1111 * value is non-zero.
1113 * Revision history
1114 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
1115 * Original implementation
1118 static int testFileExistenceA( char const * path, char const * file, BOOL excl )
1120 char filename[1024];
1121 int filenamelen;
1122 OFSTRUCT fileinfo;
1124 fileinfo.cBytes = sizeof(OFSTRUCT);
1126 strcpy(filename, path);
1127 filenamelen = strlen(filename);
1129 /* Add a trailing \ if necessary */
1130 if(filenamelen) {
1131 if(filename[filenamelen - 1] != '\\')
1132 strcat(filename, "\\");
1134 else /* specify the current directory */
1135 strcpy(filename, ".\\");
1137 /* Create the full pathname */
1138 strcat(filename, file);
1140 return (OpenFile(filename, &fileinfo,
1141 OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR);
1144 /******************************************************************************
1145 * testFileExistenceW
1147 static int testFileExistenceW( const WCHAR *path, const WCHAR *file, BOOL excl )
1149 char *filename;
1150 DWORD pathlen, filelen;
1151 int ret;
1152 OFSTRUCT fileinfo;
1154 fileinfo.cBytes = sizeof(OFSTRUCT);
1156 pathlen = WideCharToMultiByte( CP_ACP, 0, path, -1, NULL, 0, NULL, NULL );
1157 filelen = WideCharToMultiByte( CP_ACP, 0, file, -1, NULL, 0, NULL, NULL );
1158 filename = HeapAlloc( GetProcessHeap(), 0, pathlen+filelen+2 );
1160 WideCharToMultiByte( CP_ACP, 0, path, -1, filename, pathlen, NULL, NULL );
1161 /* Add a trailing \ if necessary */
1162 if (pathlen > 1)
1164 if (filename[pathlen-2] != '\\') strcpy( &filename[pathlen-1], "\\" );
1166 else /* specify the current directory */
1167 strcpy(filename, ".\\");
1169 WideCharToMultiByte( CP_ACP, 0, file, -1, filename+strlen(filename), filelen, NULL, NULL );
1171 ret = (OpenFile(filename, &fileinfo,
1172 OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR);
1173 HeapFree( GetProcessHeap(), 0, filename );
1174 return ret;
1177 /*****************************************************************************
1178 * VerFindFileA [VERSION.@]
1180 * Determines where to install a file based on whether it locates another
1181 * version of the file in the system. The values VerFindFile returns are
1182 * used in a subsequent call to the VerInstallFile function.
1184 * Revision history:
1185 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
1186 * Reimplementation of VerFindFile from original stub.
1188 DWORD WINAPI VerFindFileA(
1189 DWORD flags,
1190 LPCSTR lpszFilename,
1191 LPCSTR lpszWinDir,
1192 LPCSTR lpszAppDir,
1193 LPSTR lpszCurDir,
1194 PUINT lpuCurDirLen,
1195 LPSTR lpszDestDir,
1196 PUINT lpuDestDirLen )
1198 DWORD retval = 0;
1199 const char *curDir;
1200 const char *destDir;
1201 unsigned int curDirSizeReq;
1202 unsigned int destDirSizeReq;
1203 char winDir[MAX_PATH], systemDir[MAX_PATH];
1205 /* Print out debugging information */
1206 TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n",
1207 flags, debugstr_a(lpszFilename), debugstr_a(lpszWinDir), debugstr_a(lpszAppDir),
1208 lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0,
1209 lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 );
1211 /* Figure out where the file should go; shared files default to the
1212 system directory */
1214 GetSystemDirectoryA(systemDir, sizeof(systemDir));
1215 curDir = "";
1217 if(flags & VFFF_ISSHAREDFILE)
1219 destDir = systemDir;
1220 /* Were we given a filename? If so, try to find the file. */
1221 if(lpszFilename)
1223 if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir;
1224 else if(lpszAppDir && testFileExistenceA(lpszAppDir, lpszFilename, FALSE))
1226 curDir = lpszAppDir;
1227 retval |= VFF_CURNEDEST;
1231 else /* not a shared file */
1233 destDir = lpszAppDir ? lpszAppDir : "";
1234 if(lpszFilename)
1236 GetWindowsDirectoryA( winDir, MAX_PATH );
1237 if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir;
1238 else if(testFileExistenceA(winDir, lpszFilename, FALSE))
1240 curDir = winDir;
1241 retval |= VFF_CURNEDEST;
1243 else if(testFileExistenceA(systemDir, lpszFilename, FALSE))
1245 curDir = systemDir;
1246 retval |= VFF_CURNEDEST;
1251 /* Check to see if the file exists and is in use by another application */
1252 if (lpszFilename && testFileExistenceA(curDir, lpszFilename, FALSE)) {
1253 if (lpszFilename && !testFileExistenceA(curDir, lpszFilename, TRUE))
1254 retval |= VFF_FILEINUSE;
1257 curDirSizeReq = strlen(curDir) + 1;
1258 destDirSizeReq = strlen(destDir) + 1;
1260 /* Make sure that the pointers to the size of the buffers are
1261 valid; if not, do NOTHING with that buffer. If that pointer
1262 is valid, then make sure that the buffer pointer is valid, too! */
1264 if(lpuDestDirLen && lpszDestDir)
1266 if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1267 lstrcpynA(lpszDestDir, destDir, *lpuDestDirLen);
1268 *lpuDestDirLen = destDirSizeReq;
1270 if(lpuCurDirLen && lpszCurDir)
1272 if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1273 lstrcpynA(lpszCurDir, curDir, *lpuCurDirLen);
1274 *lpuCurDirLen = curDirSizeReq;
1277 TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval,
1278 (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "",
1279 (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "",
1280 (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "",
1281 debugstr_a(lpszCurDir), debugstr_a(lpszDestDir));
1283 return retval;
1286 /*****************************************************************************
1287 * VerFindFileW [VERSION.@]
1289 DWORD WINAPI VerFindFileW( DWORD flags,LPCWSTR lpszFilename,LPCWSTR lpszWinDir,
1290 LPCWSTR lpszAppDir, LPWSTR lpszCurDir,PUINT lpuCurDirLen,
1291 LPWSTR lpszDestDir,PUINT lpuDestDirLen )
1293 static const WCHAR emptyW;
1294 DWORD retval = 0;
1295 const WCHAR *curDir;
1296 const WCHAR *destDir;
1297 unsigned int curDirSizeReq;
1298 unsigned int destDirSizeReq;
1299 WCHAR winDir[MAX_PATH], systemDir[MAX_PATH];
1301 /* Print out debugging information */
1302 TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n",
1303 flags, debugstr_w(lpszFilename), debugstr_w(lpszWinDir), debugstr_w(lpszAppDir),
1304 lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0,
1305 lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 );
1307 /* Figure out where the file should go; shared files default to the
1308 system directory */
1310 GetSystemDirectoryW(systemDir, sizeof(systemDir)/sizeof(WCHAR));
1311 curDir = &emptyW;
1313 if(flags & VFFF_ISSHAREDFILE)
1315 destDir = systemDir;
1316 /* Were we given a filename? If so, try to find the file. */
1317 if(lpszFilename)
1319 if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir;
1320 else if(lpszAppDir && testFileExistenceW(lpszAppDir, lpszFilename, FALSE))
1322 curDir = lpszAppDir;
1323 retval |= VFF_CURNEDEST;
1327 else /* not a shared file */
1329 destDir = lpszAppDir ? lpszAppDir : &emptyW;
1330 if(lpszFilename)
1332 GetWindowsDirectoryW( winDir, MAX_PATH );
1333 if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir;
1334 else if(testFileExistenceW(winDir, lpszFilename, FALSE))
1336 curDir = winDir;
1337 retval |= VFF_CURNEDEST;
1339 else if(testFileExistenceW(systemDir, lpszFilename, FALSE))
1341 curDir = systemDir;
1342 retval |= VFF_CURNEDEST;
1347 if (lpszFilename && !testFileExistenceW(curDir, lpszFilename, TRUE))
1348 retval |= VFF_FILEINUSE;
1350 curDirSizeReq = strlenW(curDir) + 1;
1351 destDirSizeReq = strlenW(destDir) + 1;
1353 /* Make sure that the pointers to the size of the buffers are
1354 valid; if not, do NOTHING with that buffer. If that pointer
1355 is valid, then make sure that the buffer pointer is valid, too! */
1357 if(lpuDestDirLen && lpszDestDir)
1359 if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1360 lstrcpynW(lpszDestDir, destDir, *lpuDestDirLen);
1361 *lpuDestDirLen = destDirSizeReq;
1363 if(lpuCurDirLen && lpszCurDir)
1365 if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1366 lstrcpynW(lpszCurDir, curDir, *lpuCurDirLen);
1367 *lpuCurDirLen = curDirSizeReq;
1370 TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval,
1371 (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "",
1372 (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "",
1373 (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "",
1374 debugstr_w(lpszCurDir), debugstr_w(lpszDestDir));
1375 return retval;
1378 static LPBYTE
1379 _fetch_versioninfo(LPSTR fn,VS_FIXEDFILEINFO **vffi) {
1380 DWORD alloclen;
1381 LPBYTE buf;
1382 DWORD ret;
1384 alloclen = 1000;
1385 buf=HeapAlloc(GetProcessHeap(), 0, alloclen);
1386 if(buf == NULL) {
1387 WARN("Memory exhausted while fetching version info!\n");
1388 return NULL;
1390 while (1) {
1391 ret = GetFileVersionInfoA(fn,0,alloclen,buf);
1392 if (!ret) {
1393 HeapFree(GetProcessHeap(), 0, buf);
1394 return NULL;
1396 if (alloclen<*(WORD*)buf) {
1397 alloclen = *(WORD*)buf;
1398 HeapFree(GetProcessHeap(), 0, buf);
1399 buf = HeapAlloc(GetProcessHeap(), 0, alloclen);
1400 if(buf == NULL) {
1401 WARN("Memory exhausted while fetching version info!\n");
1402 return NULL;
1404 } else {
1405 *vffi = (VS_FIXEDFILEINFO*)(buf+0x14);
1406 if ((*vffi)->dwSignature == 0x004f0049) /* hack to detect unicode */
1407 *vffi = (VS_FIXEDFILEINFO*)(buf+0x28);
1408 if ((*vffi)->dwSignature != VS_FFI_SIGNATURE)
1409 WARN("Bad VS_FIXEDFILEINFO signature 0x%08x\n",(*vffi)->dwSignature);
1410 return buf;
1415 static DWORD
1416 _error2vif(DWORD error) {
1417 switch (error) {
1418 case ERROR_ACCESS_DENIED:
1419 return VIF_ACCESSVIOLATION;
1420 case ERROR_SHARING_VIOLATION:
1421 return VIF_SHARINGVIOLATION;
1422 default:
1423 return 0;
1428 /******************************************************************************
1429 * VerInstallFileA [VERSION.@]
1431 DWORD WINAPI VerInstallFileA(
1432 DWORD flags,LPCSTR srcfilename,LPCSTR destfilename,LPCSTR srcdir,
1433 LPCSTR destdir,LPCSTR curdir,LPSTR tmpfile,PUINT tmpfilelen )
1435 LPCSTR pdest;
1436 char destfn[260],tmpfn[260],srcfn[260];
1437 HFILE hfsrc,hfdst;
1438 DWORD attr,xret,tmplast;
1439 LONG ret;
1440 LPBYTE buf1,buf2;
1441 OFSTRUCT ofs;
1443 TRACE("(%x,%s,%s,%s,%s,%s,%p,%d)\n",
1444 flags,debugstr_a(srcfilename),debugstr_a(destfilename),
1445 debugstr_a(srcdir),debugstr_a(destdir),debugstr_a(curdir),
1446 tmpfile,*tmpfilelen);
1447 xret = 0;
1448 if (!srcdir || !srcfilename) return VIF_CANNOTREADSRC;
1449 sprintf(srcfn,"%s\\%s",srcdir,srcfilename);
1450 if (!destdir || !*destdir) pdest = srcdir;
1451 else pdest = destdir;
1452 sprintf(destfn,"%s\\%s",pdest,destfilename);
1453 hfsrc=LZOpenFileA(srcfn,&ofs,OF_READ);
1454 if (hfsrc < 0)
1455 return VIF_CANNOTREADSRC;
1456 sprintf(tmpfn,"%s\\%s",pdest,destfilename);
1457 tmplast=strlen(pdest)+1;
1458 attr = GetFileAttributesA(tmpfn);
1459 if (attr != INVALID_FILE_ATTRIBUTES) {
1460 if (attr & FILE_ATTRIBUTE_READONLY) {
1461 LZClose(hfsrc);
1462 return VIF_WRITEPROT;
1464 /* FIXME: check if file currently in use and return VIF_FILEINUSE */
1466 attr = INVALID_FILE_ATTRIBUTES;
1467 if (flags & VIFF_FORCEINSTALL) {
1468 if (tmpfile[0]) {
1469 sprintf(tmpfn,"%s\\%s",pdest,tmpfile);
1470 tmplast = strlen(pdest)+1;
1471 attr = GetFileAttributesA(tmpfn);
1472 /* if it exists, it has been copied by the call before.
1473 * we jump over the copy part...
1477 if (attr == INVALID_FILE_ATTRIBUTES) {
1478 char *s;
1480 GetTempFileNameA(pdest,"ver",0,tmpfn); /* should not fail ... */
1481 s=strrchr(tmpfn,'\\');
1482 if (s)
1483 tmplast = s-tmpfn;
1484 else
1485 tmplast = 0;
1486 hfdst = OpenFile(tmpfn,&ofs,OF_CREATE);
1487 if (hfdst == HFILE_ERROR) {
1488 LZClose(hfsrc);
1489 return VIF_CANNOTCREATE; /* | translated dos error */
1491 ret = LZCopy(hfsrc,hfdst);
1492 _lclose(hfdst);
1493 if (ret < 0) {
1494 /* translate LZ errors into VIF_xxx */
1495 switch (ret) {
1496 case LZERROR_BADINHANDLE:
1497 case LZERROR_READ:
1498 case LZERROR_BADVALUE:
1499 case LZERROR_UNKNOWNALG:
1500 xret = VIF_CANNOTREADSRC;
1501 break;
1502 case LZERROR_BADOUTHANDLE:
1503 case LZERROR_WRITE:
1504 xret = VIF_OUTOFSPACE;
1505 break;
1506 case LZERROR_GLOBALLOC:
1507 case LZERROR_GLOBLOCK:
1508 xret = VIF_OUTOFMEMORY;
1509 break;
1510 default: /* unknown error, should not happen */
1511 FIXME("Unknown LZCopy error %d, ignoring.\n", ret);
1512 xret = 0;
1513 break;
1515 if (xret) {
1516 LZClose(hfsrc);
1517 return xret;
1521 if (!(flags & VIFF_FORCEINSTALL)) {
1522 VS_FIXEDFILEINFO *destvffi,*tmpvffi;
1523 buf1 = _fetch_versioninfo(destfn,&destvffi);
1524 if (buf1) {
1525 buf2 = _fetch_versioninfo(tmpfn,&tmpvffi);
1526 if (buf2) {
1527 char *tbuf1,*tbuf2;
1528 static const CHAR trans_array[] = "\\VarFileInfo\\Translation";
1529 UINT len1,len2;
1531 len1=len2=40;
1533 /* compare file versions */
1534 if ((destvffi->dwFileVersionMS > tmpvffi->dwFileVersionMS)||
1535 ((destvffi->dwFileVersionMS==tmpvffi->dwFileVersionMS)&&
1536 (destvffi->dwFileVersionLS > tmpvffi->dwFileVersionLS)
1539 xret |= VIF_MISMATCH|VIF_SRCOLD;
1540 /* compare filetypes and filesubtypes */
1541 if ((destvffi->dwFileType!=tmpvffi->dwFileType) ||
1542 (destvffi->dwFileSubtype!=tmpvffi->dwFileSubtype)
1544 xret |= VIF_MISMATCH|VIF_DIFFTYPE;
1545 if (VerQueryValueA(buf1,trans_array,(LPVOID*)&tbuf1,&len1) &&
1546 VerQueryValueA(buf2,trans_array,(LPVOID*)&tbuf2,&len2)
1548 /* Do something with tbuf1 and tbuf2
1549 * generates DIFFLANG|MISMATCH
1552 HeapFree(GetProcessHeap(), 0, buf2);
1553 } else
1554 xret=VIF_MISMATCH|VIF_SRCOLD;
1555 HeapFree(GetProcessHeap(), 0, buf1);
1558 if (xret) {
1559 if (*tmpfilelen<strlen(tmpfn+tmplast)) {
1560 xret|=VIF_BUFFTOOSMALL;
1561 DeleteFileA(tmpfn);
1562 } else {
1563 strcpy(tmpfile,tmpfn+tmplast);
1564 *tmpfilelen = strlen(tmpfn+tmplast)+1;
1565 xret|=VIF_TEMPFILE;
1567 } else {
1568 if (INVALID_FILE_ATTRIBUTES!=GetFileAttributesA(destfn))
1569 if (!DeleteFileA(destfn)) {
1570 xret|=_error2vif(GetLastError())|VIF_CANNOTDELETE;
1571 DeleteFileA(tmpfn);
1572 LZClose(hfsrc);
1573 return xret;
1575 if ((!(flags & VIFF_DONTDELETEOLD)) &&
1576 curdir &&
1577 *curdir &&
1578 lstrcmpiA(curdir,pdest)
1580 char curfn[260];
1582 sprintf(curfn,"%s\\%s",curdir,destfilename);
1583 if (INVALID_FILE_ATTRIBUTES != GetFileAttributesA(curfn)) {
1584 /* FIXME: check if in use ... if it is, VIF_CANNOTDELETECUR */
1585 if (!DeleteFileA(curfn))
1586 xret|=_error2vif(GetLastError())|VIF_CANNOTDELETECUR;
1589 if (!MoveFileA(tmpfn,destfn)) {
1590 xret|=_error2vif(GetLastError())|VIF_CANNOTRENAME;
1591 DeleteFileA(tmpfn);
1594 LZClose(hfsrc);
1595 return xret;
1599 /******************************************************************************
1600 * VerInstallFileW [VERSION.@]
1602 DWORD WINAPI VerInstallFileW(
1603 DWORD flags,LPCWSTR srcfilename,LPCWSTR destfilename,LPCWSTR srcdir,
1604 LPCWSTR destdir,LPCWSTR curdir,LPWSTR tmpfile,PUINT tmpfilelen )
1606 LPSTR wsrcf = NULL, wsrcd = NULL, wdestf = NULL, wdestd = NULL, wtmpf = NULL, wcurd = NULL;
1607 DWORD ret = 0;
1608 UINT len;
1610 if (srcfilename)
1612 len = WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, NULL, 0, NULL, NULL );
1613 if ((wsrcf = HeapAlloc( GetProcessHeap(), 0, len )))
1614 WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, wsrcf, len, NULL, NULL );
1615 else
1616 ret = VIF_OUTOFMEMORY;
1618 if (srcdir && !ret)
1620 len = WideCharToMultiByte( CP_ACP, 0, srcdir, -1, NULL, 0, NULL, NULL );
1621 if ((wsrcd = HeapAlloc( GetProcessHeap(), 0, len )))
1622 WideCharToMultiByte( CP_ACP, 0, srcdir, -1, wsrcd, len, NULL, NULL );
1623 else
1624 ret = VIF_OUTOFMEMORY;
1626 if (destfilename && !ret)
1628 len = WideCharToMultiByte( CP_ACP, 0, destfilename, -1, NULL, 0, NULL, NULL );
1629 if ((wdestf = HeapAlloc( GetProcessHeap(), 0, len )))
1630 WideCharToMultiByte( CP_ACP, 0, destfilename, -1, wdestf, len, NULL, NULL );
1631 else
1632 ret = VIF_OUTOFMEMORY;
1634 if (destdir && !ret)
1636 len = WideCharToMultiByte( CP_ACP, 0, destdir, -1, NULL, 0, NULL, NULL );
1637 if ((wdestd = HeapAlloc( GetProcessHeap(), 0, len )))
1638 WideCharToMultiByte( CP_ACP, 0, destdir, -1, wdestd, len, NULL, NULL );
1639 else
1640 ret = VIF_OUTOFMEMORY;
1642 if (curdir && !ret)
1644 len = WideCharToMultiByte( CP_ACP, 0, curdir, -1, NULL, 0, NULL, NULL );
1645 if ((wcurd = HeapAlloc( GetProcessHeap(), 0, len )))
1646 WideCharToMultiByte( CP_ACP, 0, curdir, -1, wcurd, len, NULL, NULL );
1647 else
1648 ret = VIF_OUTOFMEMORY;
1650 if (!ret)
1652 len = *tmpfilelen * sizeof(WCHAR);
1653 wtmpf = HeapAlloc( GetProcessHeap(), 0, len );
1654 if (!wtmpf)
1655 ret = VIF_OUTOFMEMORY;
1657 if (!ret)
1658 ret = VerInstallFileA(flags,wsrcf,wdestf,wsrcd,wdestd,wcurd,wtmpf,&len);
1659 if (!ret)
1660 *tmpfilelen = MultiByteToWideChar( CP_ACP, 0, wtmpf, -1, tmpfile, *tmpfilelen );
1661 else if (ret & VIF_BUFFTOOSMALL)
1662 *tmpfilelen = len; /* FIXME: not correct */
1664 HeapFree( GetProcessHeap(), 0, wsrcf );
1665 HeapFree( GetProcessHeap(), 0, wsrcd );
1666 HeapFree( GetProcessHeap(), 0, wdestf );
1667 HeapFree( GetProcessHeap(), 0, wdestd );
1668 HeapFree( GetProcessHeap(), 0, wtmpf );
1669 HeapFree( GetProcessHeap(), 0, wcurd );
1670 return ret;