services: Check for services without lpBinaryPathName in get_winedevice_process.
[wine.git] / dlls / version / version.c
blob43f5c18c209cd297c0a48129465114bdbdebee12
1 /*
2 * Implementation of VERSION.DLL
4 * Copyright 1996,1997 Marcus Meissner
5 * Copyright 1997 David Cuthbert
6 * Copyright 1999 Ulrich Weigand
7 * Copyright 2005 Paul Vriens
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #include "config.h"
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdio.h>
31 #include <sys/types.h>
32 #ifdef HAVE_UNISTD_H
33 # include <unistd.h>
34 #endif
36 #define NONAMELESSUNION
37 #define NONAMELESSSTRUCT
38 #include "windef.h"
39 #include "winbase.h"
40 #include "winver.h"
41 #include "winuser.h"
42 #include "winnls.h"
43 #include "winternl.h"
44 #include "lzexpand.h"
45 #include "wine/unicode.h"
46 #include "winerror.h"
47 #include "wine/debug.h"
50 WINE_DEFAULT_DEBUG_CHANNEL(ver);
52 typedef struct
54 WORD offset;
55 WORD length;
56 WORD flags;
57 WORD id;
58 WORD handle;
59 WORD usage;
60 } NE_NAMEINFO;
62 typedef struct
64 WORD type_id;
65 WORD count;
66 DWORD resloader;
67 } NE_TYPEINFO;
69 /**********************************************************************
70 * find_entry_by_id
72 * Find an entry by id in a resource directory
73 * Copied from loader/pe_resource.c
75 static const IMAGE_RESOURCE_DIRECTORY *find_entry_by_id( const IMAGE_RESOURCE_DIRECTORY *dir,
76 WORD id, const void *root )
78 const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
79 int min, max, pos;
81 entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
82 min = dir->NumberOfNamedEntries;
83 max = min + dir->NumberOfIdEntries - 1;
84 while (min <= max)
86 pos = (min + max) / 2;
87 if (entry[pos].u.Id == id)
88 return (const IMAGE_RESOURCE_DIRECTORY *)((const char *)root + entry[pos].u2.s2.OffsetToDirectory);
89 if (entry[pos].u.Id > id) max = pos - 1;
90 else min = pos + 1;
92 return NULL;
96 /**********************************************************************
97 * find_entry_default
99 * Find a default entry in a resource directory
100 * Copied from loader/pe_resource.c
102 static const IMAGE_RESOURCE_DIRECTORY *find_entry_default( const IMAGE_RESOURCE_DIRECTORY *dir,
103 const void *root )
105 const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
107 entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
108 return (const IMAGE_RESOURCE_DIRECTORY *)((const char *)root + entry->u2.s2.OffsetToDirectory);
112 /**********************************************************************
113 * push_language
115 * push a language onto the list of languages to try
117 static inline int push_language( WORD *list, int pos, WORD lang )
119 int i;
120 for (i = 0; i < pos; i++) if (list[i] == lang) return pos;
121 list[pos++] = lang;
122 return pos;
126 /**********************************************************************
127 * find_entry_language
129 static const IMAGE_RESOURCE_DIRECTORY *find_entry_language( const IMAGE_RESOURCE_DIRECTORY *dir,
130 const void *root )
132 const IMAGE_RESOURCE_DIRECTORY *ret;
133 WORD list[9];
134 int i, pos = 0;
136 /* cf. LdrFindResource_U */
137 pos = push_language( list, pos, MAKELANGID( LANG_NEUTRAL, SUBLANG_NEUTRAL ) );
138 pos = push_language( list, pos, LANGIDFROMLCID( NtCurrentTeb()->CurrentLocale ) );
139 pos = push_language( list, pos, GetUserDefaultLangID() );
140 pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(GetUserDefaultLangID()), SUBLANG_NEUTRAL ));
141 pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(GetUserDefaultLangID()), SUBLANG_DEFAULT ));
142 pos = push_language( list, pos, GetSystemDefaultLangID() );
143 pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(GetSystemDefaultLangID()), SUBLANG_NEUTRAL ));
144 pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(GetSystemDefaultLangID()), SUBLANG_DEFAULT ));
145 pos = push_language( list, pos, MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT ) );
147 for (i = 0; i < pos; i++) if ((ret = find_entry_by_id( dir, list[i], root ))) return ret;
148 return find_entry_default( dir, root );
152 /***********************************************************************
153 * read_xx_header [internal]
155 static int read_xx_header( HFILE lzfd )
157 IMAGE_DOS_HEADER mzh;
158 char magic[3];
160 LZSeek( lzfd, 0, SEEK_SET );
161 if ( sizeof(mzh) != LZRead( lzfd, (LPSTR)&mzh, sizeof(mzh) ) )
162 return 0;
163 if ( mzh.e_magic != IMAGE_DOS_SIGNATURE )
165 if (!memcmp( &mzh, "\177ELF", 4 )) return 1; /* ELF */
166 if (*(UINT *)&mzh == 0xfeedface || *(UINT *)&mzh == 0xcefaedfe) return 1; /* Mach-O */
167 return 0;
170 LZSeek( lzfd, mzh.e_lfanew, SEEK_SET );
171 if ( 2 != LZRead( lzfd, magic, 2 ) )
172 return 0;
174 LZSeek( lzfd, mzh.e_lfanew, SEEK_SET );
176 if ( magic[0] == 'N' && magic[1] == 'E' )
177 return IMAGE_OS2_SIGNATURE;
178 if ( magic[0] == 'P' && magic[1] == 'E' )
179 return IMAGE_NT_SIGNATURE;
181 magic[2] = '\0';
182 WARN("Can't handle %s files.\n", magic );
183 return 0;
186 /***********************************************************************
187 * find_ne_resource [internal]
189 static BOOL find_ne_resource( HFILE lzfd, DWORD *resLen, DWORD *resOff )
191 const WORD typeid = VS_FILE_INFO | 0x8000;
192 const WORD resid = VS_VERSION_INFO | 0x8000;
193 IMAGE_OS2_HEADER nehd;
194 NE_TYPEINFO *typeInfo;
195 NE_NAMEINFO *nameInfo;
196 DWORD nehdoffset;
197 LPBYTE resTab;
198 DWORD resTabSize;
199 int count;
201 /* Read in NE header */
202 nehdoffset = LZSeek( lzfd, 0, SEEK_CUR );
203 if ( sizeof(nehd) != LZRead( lzfd, (LPSTR)&nehd, sizeof(nehd) ) ) return FALSE;
205 resTabSize = nehd.ne_restab - nehd.ne_rsrctab;
206 if ( !resTabSize )
208 TRACE("No resources in NE dll\n" );
209 return FALSE;
212 /* Read in resource table */
213 resTab = HeapAlloc( GetProcessHeap(), 0, resTabSize );
214 if ( !resTab ) return FALSE;
216 LZSeek( lzfd, nehd.ne_rsrctab + nehdoffset, SEEK_SET );
217 if ( resTabSize != LZRead( lzfd, (char*)resTab, resTabSize ) )
219 HeapFree( GetProcessHeap(), 0, resTab );
220 return FALSE;
223 /* Find resource */
224 typeInfo = (NE_TYPEINFO *)(resTab + 2);
225 while (typeInfo->type_id)
227 if (typeInfo->type_id == typeid) goto found_type;
228 typeInfo = (NE_TYPEINFO *)((char *)(typeInfo + 1) +
229 typeInfo->count * sizeof(NE_NAMEINFO));
231 TRACE("No typeid entry found\n" );
232 HeapFree( GetProcessHeap(), 0, resTab );
233 return FALSE;
235 found_type:
236 nameInfo = (NE_NAMEINFO *)(typeInfo + 1);
238 for (count = typeInfo->count; count > 0; count--, nameInfo++)
239 if (nameInfo->id == resid) goto found_name;
241 TRACE("No resid entry found\n" );
242 HeapFree( GetProcessHeap(), 0, resTab );
243 return FALSE;
245 found_name:
246 /* Return resource data */
247 if ( resLen ) *resLen = nameInfo->length << *(WORD *)resTab;
248 if ( resOff ) *resOff = nameInfo->offset << *(WORD *)resTab;
250 HeapFree( GetProcessHeap(), 0, resTab );
251 return TRUE;
254 /***********************************************************************
255 * find_pe_resource [internal]
257 static BOOL find_pe_resource( HFILE lzfd, DWORD *resLen, DWORD *resOff )
259 union
261 IMAGE_NT_HEADERS32 nt32;
262 IMAGE_NT_HEADERS64 nt64;
263 } pehd;
264 DWORD pehdoffset;
265 PIMAGE_DATA_DIRECTORY resDataDir;
266 PIMAGE_SECTION_HEADER sections;
267 LPBYTE resSection;
268 DWORD section_size, data_size;
269 const void *resDir;
270 const IMAGE_RESOURCE_DIRECTORY *resPtr;
271 const IMAGE_RESOURCE_DATA_ENTRY *resData;
272 int i, len, nSections;
273 BOOL ret = FALSE;
275 /* Read in PE header */
276 pehdoffset = LZSeek( lzfd, 0, SEEK_CUR );
277 len = LZRead( lzfd, (LPSTR)&pehd, sizeof(pehd) );
278 if (len < sizeof(pehd.nt32.FileHeader)) return FALSE;
279 if (len < sizeof(pehd)) memset( (char *)&pehd + len, 0, sizeof(pehd) - len );
281 switch (pehd.nt32.OptionalHeader.Magic)
283 case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
284 resDataDir = pehd.nt32.OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_RESOURCE;
285 break;
286 case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
287 resDataDir = pehd.nt64.OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_RESOURCE;
288 break;
289 default:
290 return FALSE;
293 if ( !resDataDir->Size )
295 TRACE("No resources in PE dll\n" );
296 return FALSE;
299 /* Read in section table */
300 nSections = pehd.nt32.FileHeader.NumberOfSections;
301 sections = HeapAlloc( GetProcessHeap(), 0,
302 nSections * sizeof(IMAGE_SECTION_HEADER) );
303 if ( !sections ) return FALSE;
305 len = FIELD_OFFSET( IMAGE_NT_HEADERS32, OptionalHeader ) + pehd.nt32.FileHeader.SizeOfOptionalHeader;
306 LZSeek( lzfd, pehdoffset + len, SEEK_SET );
308 if ( nSections * sizeof(IMAGE_SECTION_HEADER) !=
309 LZRead( lzfd, (LPSTR)sections, nSections * sizeof(IMAGE_SECTION_HEADER) ) )
311 HeapFree( GetProcessHeap(), 0, sections );
312 return FALSE;
315 /* Find resource section */
316 for ( i = 0; i < nSections; i++ )
317 if ( resDataDir->VirtualAddress >= sections[i].VirtualAddress
318 && resDataDir->VirtualAddress < sections[i].VirtualAddress +
319 sections[i].SizeOfRawData )
320 break;
322 if ( i == nSections )
324 HeapFree( GetProcessHeap(), 0, sections );
325 TRACE("Couldn't find resource section\n" );
326 return FALSE;
329 /* Read in resource section */
330 data_size = sections[i].SizeOfRawData;
331 section_size = max( data_size, sections[i].Misc.VirtualSize );
332 resSection = HeapAlloc( GetProcessHeap(), 0, section_size );
333 if ( !resSection )
335 HeapFree( GetProcessHeap(), 0, sections );
336 return FALSE;
339 LZSeek( lzfd, sections[i].PointerToRawData, SEEK_SET );
340 if (data_size != LZRead( lzfd, (char*)resSection, data_size )) goto done;
341 if (data_size < section_size) memset( (char *)resSection + data_size, 0, section_size - data_size );
343 /* Find resource */
344 resDir = resSection + (resDataDir->VirtualAddress - sections[i].VirtualAddress);
346 resPtr = resDir;
347 resPtr = find_entry_by_id( resPtr, VS_FILE_INFO, resDir );
348 if ( !resPtr )
350 TRACE("No typeid entry found\n" );
351 goto done;
353 resPtr = find_entry_by_id( resPtr, VS_VERSION_INFO, resDir );
354 if ( !resPtr )
356 TRACE("No resid entry found\n" );
357 goto done;
359 resPtr = find_entry_language( resPtr, resDir );
360 if ( !resPtr )
362 TRACE("No default language entry found\n" );
363 goto done;
366 /* Find resource data section */
367 resData = (const IMAGE_RESOURCE_DATA_ENTRY*)resPtr;
368 for ( i = 0; i < nSections; i++ )
369 if ( resData->OffsetToData >= sections[i].VirtualAddress
370 && resData->OffsetToData < sections[i].VirtualAddress +
371 sections[i].SizeOfRawData )
372 break;
374 if ( i == nSections )
376 TRACE("Couldn't find resource data section\n" );
377 goto done;
380 /* Return resource data */
381 if ( resLen ) *resLen = resData->Size;
382 if ( resOff ) *resOff = resData->OffsetToData - sections[i].VirtualAddress
383 + sections[i].PointerToRawData;
384 ret = TRUE;
386 done:
387 HeapFree( GetProcessHeap(), 0, resSection );
388 HeapFree( GetProcessHeap(), 0, sections );
389 return ret;
393 /***********************************************************************
394 * find_version_resource [internal]
396 static DWORD find_version_resource( HFILE lzfd, DWORD *reslen, DWORD *offset )
398 DWORD magic = read_xx_header( lzfd );
400 switch (magic)
402 case IMAGE_OS2_SIGNATURE:
403 if (!find_ne_resource( lzfd, reslen, offset )) magic = 0;
404 break;
405 case IMAGE_NT_SIGNATURE:
406 if (!find_pe_resource( lzfd, reslen, offset )) magic = 0;
407 break;
409 return magic;
412 /******************************************************************************
414 * This function will print via standard TRACE, debug info regarding
415 * the file info structure vffi.
416 * 15-Feb-1998 Dimitrie Paun (dimi@cs.toronto.edu)
417 * Added this function to clean up the code.
419 *****************************************************************************/
420 static void print_vffi_debug(const VS_FIXEDFILEINFO *vffi)
422 BOOL versioned_printer = FALSE;
424 if((vffi->dwFileType == VFT_DLL) || (vffi->dwFileType == VFT_DRV))
426 if(vffi->dwFileSubtype == VFT2_DRV_VERSIONED_PRINTER)
427 /* this is documented for newer w2k Drivers and up */
428 versioned_printer = TRUE;
429 else if( (vffi->dwFileSubtype == VFT2_DRV_PRINTER) &&
430 (vffi->dwFileVersionMS != vffi->dwProductVersionMS) &&
431 (vffi->dwFileVersionMS > 0) &&
432 (vffi->dwFileVersionMS <= 3) )
433 /* found this on NT 3.51, NT4.0 and old w2k Drivers */
434 versioned_printer = TRUE;
437 TRACE("structversion=%u.%u, ",
438 HIWORD(vffi->dwStrucVersion),LOWORD(vffi->dwStrucVersion));
439 if(versioned_printer)
441 WORD mode = LOWORD(vffi->dwFileVersionMS);
442 WORD ver_rev = HIWORD(vffi->dwFileVersionLS);
443 TRACE("fileversion=%u.%u.%u.%u (%s.major.minor.release), ",
444 (vffi->dwFileVersionMS),
445 HIBYTE(ver_rev), LOBYTE(ver_rev), LOWORD(vffi->dwFileVersionLS),
446 (mode == 3) ? "Usermode" : ((mode <= 2) ? "Kernelmode" : "?") );
448 else
450 TRACE("fileversion=%u.%u.%u.%u, ",
451 HIWORD(vffi->dwFileVersionMS),LOWORD(vffi->dwFileVersionMS),
452 HIWORD(vffi->dwFileVersionLS),LOWORD(vffi->dwFileVersionLS));
454 TRACE("productversion=%u.%u.%u.%u\n",
455 HIWORD(vffi->dwProductVersionMS),LOWORD(vffi->dwProductVersionMS),
456 HIWORD(vffi->dwProductVersionLS),LOWORD(vffi->dwProductVersionLS));
458 TRACE("flagmask=0x%x, flags=0x%x %s%s%s%s%s%s\n",
459 vffi->dwFileFlagsMask, vffi->dwFileFlags,
460 (vffi->dwFileFlags & VS_FF_DEBUG) ? "DEBUG," : "",
461 (vffi->dwFileFlags & VS_FF_PRERELEASE) ? "PRERELEASE," : "",
462 (vffi->dwFileFlags & VS_FF_PATCHED) ? "PATCHED," : "",
463 (vffi->dwFileFlags & VS_FF_PRIVATEBUILD) ? "PRIVATEBUILD," : "",
464 (vffi->dwFileFlags & VS_FF_INFOINFERRED) ? "INFOINFERRED," : "",
465 (vffi->dwFileFlags & VS_FF_SPECIALBUILD) ? "SPECIALBUILD," : "");
467 TRACE("(");
469 TRACE("OS=0x%x.0x%x ", HIWORD(vffi->dwFileOS), LOWORD(vffi->dwFileOS));
471 switch (vffi->dwFileOS&0xFFFF0000)
473 case VOS_DOS:TRACE("DOS,");break;
474 case VOS_OS216:TRACE("OS/2-16,");break;
475 case VOS_OS232:TRACE("OS/2-32,");break;
476 case VOS_NT:TRACE("NT,");break;
477 case VOS_UNKNOWN:
478 default:
479 TRACE("UNKNOWN(0x%x),",vffi->dwFileOS&0xFFFF0000);break;
482 switch (LOWORD(vffi->dwFileOS))
484 case VOS__BASE:TRACE("BASE");break;
485 case VOS__WINDOWS16:TRACE("WIN16");break;
486 case VOS__WINDOWS32:TRACE("WIN32");break;
487 case VOS__PM16:TRACE("PM16");break;
488 case VOS__PM32:TRACE("PM32");break;
489 default:
490 TRACE("UNKNOWN(0x%x)",LOWORD(vffi->dwFileOS));break;
493 TRACE(")\n");
495 switch (vffi->dwFileType)
497 case VFT_APP:TRACE("filetype=APP");break;
498 case VFT_DLL:
499 TRACE("filetype=DLL");
500 if(vffi->dwFileSubtype != 0)
502 if(versioned_printer) /* NT3.x/NT4.0 or old w2k Driver */
503 TRACE(",PRINTER");
504 TRACE(" (subtype=0x%x)", vffi->dwFileSubtype);
506 break;
507 case VFT_DRV:
508 TRACE("filetype=DRV,");
509 switch(vffi->dwFileSubtype)
511 case VFT2_DRV_PRINTER:TRACE("PRINTER");break;
512 case VFT2_DRV_KEYBOARD:TRACE("KEYBOARD");break;
513 case VFT2_DRV_LANGUAGE:TRACE("LANGUAGE");break;
514 case VFT2_DRV_DISPLAY:TRACE("DISPLAY");break;
515 case VFT2_DRV_MOUSE:TRACE("MOUSE");break;
516 case VFT2_DRV_NETWORK:TRACE("NETWORK");break;
517 case VFT2_DRV_SYSTEM:TRACE("SYSTEM");break;
518 case VFT2_DRV_INSTALLABLE:TRACE("INSTALLABLE");break;
519 case VFT2_DRV_SOUND:TRACE("SOUND");break;
520 case VFT2_DRV_COMM:TRACE("COMM");break;
521 case VFT2_DRV_INPUTMETHOD:TRACE("INPUTMETHOD");break;
522 case VFT2_DRV_VERSIONED_PRINTER:TRACE("VERSIONED_PRINTER");break;
523 case VFT2_UNKNOWN:
524 default:
525 TRACE("UNKNOWN(0x%x)",vffi->dwFileSubtype);break;
527 break;
528 case VFT_FONT:
529 TRACE("filetype=FONT,");
530 switch (vffi->dwFileSubtype)
532 case VFT2_FONT_RASTER:TRACE("RASTER");break;
533 case VFT2_FONT_VECTOR:TRACE("VECTOR");break;
534 case VFT2_FONT_TRUETYPE:TRACE("TRUETYPE");break;
535 default:TRACE("UNKNOWN(0x%x)",vffi->dwFileSubtype);break;
537 break;
538 case VFT_VXD:TRACE("filetype=VXD");break;
539 case VFT_STATIC_LIB:TRACE("filetype=STATIC_LIB");break;
540 case VFT_UNKNOWN:
541 default:
542 TRACE("filetype=Unknown(0x%x)",vffi->dwFileType);break;
545 TRACE("\n");
546 TRACE("filedate=0x%x.0x%x\n",vffi->dwFileDateMS,vffi->dwFileDateLS);
549 /***********************************************************************
550 * Version Info Structure
553 typedef struct
555 WORD wLength;
556 WORD wValueLength;
557 CHAR szKey[1];
558 #if 0 /* variable length structure */
559 /* DWORD aligned */
560 BYTE Value[];
561 /* DWORD aligned */
562 VS_VERSION_INFO_STRUCT16 Children[];
563 #endif
564 } VS_VERSION_INFO_STRUCT16;
566 typedef struct
568 WORD wLength;
569 WORD wValueLength;
570 WORD wType; /* 1:Text, 0:Binary */
571 WCHAR szKey[1];
572 #if 0 /* variable length structure */
573 /* DWORD aligned */
574 BYTE Value[];
575 /* DWORD aligned */
576 VS_VERSION_INFO_STRUCT32 Children[];
577 #endif
578 } VS_VERSION_INFO_STRUCT32;
580 #define VersionInfoIs16( ver ) \
581 ( ((const VS_VERSION_INFO_STRUCT16 *)ver)->szKey[0] >= ' ' )
583 #define DWORD_ALIGN( base, ptr ) \
584 ( (LPBYTE)(base) + ((((LPBYTE)(ptr) - (LPBYTE)(base)) + 3) & ~3) )
586 #define VersionInfo16_Value( ver ) \
587 DWORD_ALIGN( (ver), (ver)->szKey + strlen((ver)->szKey) + 1 )
588 #define VersionInfo32_Value( ver ) \
589 DWORD_ALIGN( (ver), (ver)->szKey + strlenW((ver)->szKey) + 1 )
591 #define VersionInfo16_Children( ver ) \
592 (const VS_VERSION_INFO_STRUCT16 *)( VersionInfo16_Value( ver ) + \
593 ( ( (ver)->wValueLength + 3 ) & ~3 ) )
594 #define VersionInfo32_Children( ver ) \
595 (const VS_VERSION_INFO_STRUCT32 *)( VersionInfo32_Value( ver ) + \
596 ( ( (ver)->wValueLength * \
597 ((ver)->wType? 2 : 1) + 3 ) & ~3 ) )
599 #define VersionInfo16_Next( ver ) \
600 (VS_VERSION_INFO_STRUCT16 *)( (LPBYTE)ver + (((ver)->wLength + 3) & ~3) )
601 #define VersionInfo32_Next( ver ) \
602 (VS_VERSION_INFO_STRUCT32 *)( (LPBYTE)ver + (((ver)->wLength + 3) & ~3) )
605 /***********************************************************************
606 * GetFileVersionInfoSizeW [VERSION.@]
608 DWORD WINAPI GetFileVersionInfoSizeW( LPCWSTR filename, LPDWORD handle )
610 return GetFileVersionInfoSizeExW( 0, filename, handle );
613 /***********************************************************************
614 * GetFileVersionInfoSizeA [VERSION.@]
616 DWORD WINAPI GetFileVersionInfoSizeA( LPCSTR filename, LPDWORD handle )
618 return GetFileVersionInfoSizeExA( 0, filename, handle );
621 /******************************************************************************
622 * GetFileVersionInfoSizeExW [VERSION.@]
624 DWORD WINAPI GetFileVersionInfoSizeExW( DWORD flags, LPCWSTR filename, LPDWORD handle )
626 DWORD len, offset, magic = 1;
627 HFILE lzfd;
628 HMODULE hModule;
629 OFSTRUCT ofs;
631 if (flags)
633 FIXME("stub: %x %s %p\n", flags, wine_dbgstr_w(filename), handle);
634 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
635 return 0;
638 TRACE("(%s,%p)\n", debugstr_w(filename), handle );
640 if (handle) *handle = 0;
642 if (!filename)
644 SetLastError(ERROR_INVALID_PARAMETER);
645 return 0;
647 if (!*filename)
649 SetLastError(ERROR_BAD_PATHNAME);
650 return 0;
653 if ((lzfd = LZOpenFileW( (LPWSTR)filename, &ofs, OF_READ )) != HFILE_ERROR)
655 magic = find_version_resource( lzfd, &len, &offset );
656 LZClose( lzfd );
659 if ((magic == 1) && (hModule = LoadLibraryExW( filename, 0, LOAD_LIBRARY_AS_DATAFILE )))
661 HRSRC hRsrc = FindResourceW( hModule, MAKEINTRESOURCEW(VS_VERSION_INFO),
662 (LPWSTR)VS_FILE_INFO );
663 if (hRsrc)
665 magic = IMAGE_NT_SIGNATURE;
666 len = SizeofResource( hModule, hRsrc );
668 FreeLibrary( hModule );
671 switch (magic)
673 case IMAGE_OS2_SIGNATURE:
674 /* We have a 16bit resource.
676 * XP/W2K/W2K3 uses a buffer which is more than the actual needed space:
678 * (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4
680 * This extra buffer is used for ANSI to Unicode conversions in W-Calls.
681 * info->wLength should be the same as len. Currently it isn't but that
682 * doesn't seem to be a problem (len is bigger than info->wLength).
684 SetLastError(0);
685 return (len - sizeof(VS_FIXEDFILEINFO)) * 4;
687 case IMAGE_NT_SIGNATURE:
688 /* We have a 32bit resource.
690 * XP/W2K/W2K3 uses a buffer which is 2 times the actual needed space + 4 bytes "FE2X"
691 * This extra buffer is used for Unicode to ANSI conversions in A-Calls
693 SetLastError(0);
694 return (len * 2) + 4;
696 default:
697 SetLastError( lzfd == HFILE_ERROR ? ofs.nErrCode : ERROR_RESOURCE_DATA_NOT_FOUND );
698 return 0;
702 /******************************************************************************
703 * GetFileVersionInfoSizeExA [VERSION.@]
705 DWORD WINAPI GetFileVersionInfoSizeExA( DWORD flags, LPCSTR filename, LPDWORD handle )
707 UNICODE_STRING filenameW;
708 DWORD retval;
710 TRACE("(%s,%p)\n", debugstr_a(filename), handle );
712 if(filename)
713 RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
714 else
715 filenameW.Buffer = NULL;
717 retval = GetFileVersionInfoSizeExW(0, filenameW.Buffer, handle);
719 RtlFreeUnicodeString(&filenameW);
721 return retval;
724 /***********************************************************************
725 * GetFileVersionInfoExW [VERSION.@]
727 BOOL WINAPI GetFileVersionInfoExW( DWORD flags, LPCWSTR filename, DWORD handle, DWORD datasize, LPVOID data )
729 static const char signature[4] = "FE2X";
730 DWORD len, offset, magic = 1;
731 HFILE lzfd;
732 OFSTRUCT ofs;
733 HMODULE hModule;
734 VS_VERSION_INFO_STRUCT32* vvis = data;
736 if (flags)
738 FIXME("stub: %x %s %u %u %p\n", flags, wine_dbgstr_w(filename), handle, datasize, data);
739 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
740 return 0;
743 TRACE("(%s,%d,size=%d,data=%p)\n",
744 debugstr_w(filename), handle, datasize, data );
746 if (!data)
748 SetLastError(ERROR_INVALID_DATA);
749 return FALSE;
752 if ((lzfd = LZOpenFileW( (LPWSTR)filename, &ofs, OF_READ )) != HFILE_ERROR)
754 if ((magic = find_version_resource( lzfd, &len, &offset )) > 1)
756 LZSeek( lzfd, offset, 0 /* SEEK_SET */ );
757 len = LZRead( lzfd, data, min( len, datasize ) );
759 LZClose( lzfd );
762 if ((magic == 1) && (hModule = LoadLibraryExW( filename, 0, LOAD_LIBRARY_AS_DATAFILE )))
764 HRSRC hRsrc = FindResourceW( hModule, MAKEINTRESOURCEW(VS_VERSION_INFO),
765 (LPWSTR)VS_FILE_INFO );
766 if (hRsrc)
768 HGLOBAL hMem = LoadResource( hModule, hRsrc );
769 magic = IMAGE_NT_SIGNATURE;
770 len = min( SizeofResource(hModule, hRsrc), datasize );
771 memcpy( data, LockResource( hMem ), len );
772 FreeResource( hMem );
774 FreeLibrary( hModule );
777 switch (magic)
779 case IMAGE_OS2_SIGNATURE:
780 /* We have a 16bit resource. */
781 if (TRACE_ON(ver))
782 print_vffi_debug( (VS_FIXEDFILEINFO *)VersionInfo16_Value( (VS_VERSION_INFO_STRUCT16 *)data ));
783 SetLastError(0);
784 return TRUE;
786 case IMAGE_NT_SIGNATURE:
787 /* We have a 32bit resource.
789 * XP/W2K/W2K3 uses a buffer which is 2 times the actual needed space + 4 bytes "FE2X"
790 * This extra buffer is used for Unicode to ANSI conversions in A-Calls
792 len = vvis->wLength + sizeof(signature);
793 if (datasize >= len) memcpy( (char*)data + vvis->wLength, signature, sizeof(signature) );
794 if (TRACE_ON(ver))
795 print_vffi_debug( (VS_FIXEDFILEINFO *)VersionInfo32_Value( vvis ));
796 SetLastError(0);
797 return TRUE;
799 default:
800 SetLastError( lzfd == HFILE_ERROR ? ofs.nErrCode : ERROR_RESOURCE_DATA_NOT_FOUND );
801 return FALSE;
805 /***********************************************************************
806 * GetFileVersionInfoExA [VERSION.@]
808 BOOL WINAPI GetFileVersionInfoExA( DWORD flags, LPCSTR filename, DWORD handle, DWORD datasize, LPVOID data )
810 UNICODE_STRING filenameW;
811 BOOL retval;
813 TRACE("(%s,%d,size=%d,data=%p)\n",
814 debugstr_a(filename), handle, datasize, data );
816 if(filename)
817 RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
818 else
819 filenameW.Buffer = NULL;
821 retval = GetFileVersionInfoExW(flags, filenameW.Buffer, handle, datasize, data);
823 RtlFreeUnicodeString(&filenameW);
825 return retval;
828 /***********************************************************************
829 * GetFileVersionInfoW [VERSION.@]
831 BOOL WINAPI GetFileVersionInfoW( LPCWSTR filename, DWORD handle, DWORD datasize, LPVOID data )
833 return GetFileVersionInfoExW(0, filename, handle, datasize, data);
836 /***********************************************************************
837 * GetFileVersionInfoA [VERSION.@]
839 BOOL WINAPI GetFileVersionInfoA( LPCSTR filename, DWORD handle, DWORD datasize, LPVOID data )
841 return GetFileVersionInfoExA(0, filename, handle, datasize, data);
844 /***********************************************************************
845 * VersionInfo16_FindChild [internal]
847 static const VS_VERSION_INFO_STRUCT16 *VersionInfo16_FindChild( const VS_VERSION_INFO_STRUCT16 *info,
848 LPCSTR szKey, UINT cbKey )
850 const VS_VERSION_INFO_STRUCT16 *child = VersionInfo16_Children( info );
852 while ((char *)child < (char *)info + info->wLength )
854 if (!strncasecmp( child->szKey, szKey, cbKey ) && !child->szKey[cbKey])
855 return child;
857 if (!(child->wLength)) return NULL;
858 child = VersionInfo16_Next( child );
861 return NULL;
864 /***********************************************************************
865 * VersionInfo32_FindChild [internal]
867 static const VS_VERSION_INFO_STRUCT32 *VersionInfo32_FindChild( const VS_VERSION_INFO_STRUCT32 *info,
868 LPCWSTR szKey, UINT cbKey )
870 const VS_VERSION_INFO_STRUCT32 *child = VersionInfo32_Children( info );
872 while ((char *)child < (char *)info + info->wLength )
874 if (!strncmpiW( child->szKey, szKey, cbKey ) && !child->szKey[cbKey])
875 return child;
877 if (!(child->wLength)) return NULL;
878 child = VersionInfo32_Next( child );
881 return NULL;
884 /***********************************************************************
885 * VersionInfo16_QueryValue [internal]
887 * Gets a value from a 16-bit NE resource
889 static BOOL VersionInfo16_QueryValue( const VS_VERSION_INFO_STRUCT16 *info, LPCSTR lpSubBlock,
890 LPVOID *lplpBuffer, UINT *puLen )
892 while ( *lpSubBlock )
894 /* Find next path component */
895 LPCSTR lpNextSlash;
896 for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ )
897 if ( *lpNextSlash == '\\' )
898 break;
900 /* Skip empty components */
901 if ( lpNextSlash == lpSubBlock )
903 lpSubBlock++;
904 continue;
907 /* We have a non-empty component: search info for key */
908 info = VersionInfo16_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock );
909 if ( !info )
911 if (puLen) *puLen = 0 ;
912 SetLastError( ERROR_RESOURCE_TYPE_NOT_FOUND );
913 return FALSE;
916 /* Skip path component */
917 lpSubBlock = lpNextSlash;
920 /* Return value */
921 *lplpBuffer = VersionInfo16_Value( info );
922 if (puLen)
923 *puLen = info->wValueLength;
925 return TRUE;
928 /***********************************************************************
929 * VersionInfo32_QueryValue [internal]
931 * Gets a value from a 32-bit PE resource
933 static BOOL VersionInfo32_QueryValue( const VS_VERSION_INFO_STRUCT32 *info, LPCWSTR lpSubBlock,
934 LPVOID *lplpBuffer, UINT *puLen, BOOL *pbText )
936 TRACE("lpSubBlock : (%s)\n", debugstr_w(lpSubBlock));
938 while ( *lpSubBlock )
940 /* Find next path component */
941 LPCWSTR lpNextSlash;
942 for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ )
943 if ( *lpNextSlash == '\\' )
944 break;
946 /* Skip empty components */
947 if ( lpNextSlash == lpSubBlock )
949 lpSubBlock++;
950 continue;
953 /* We have a non-empty component: search info for key */
954 info = VersionInfo32_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock );
955 if ( !info )
957 if (puLen) *puLen = 0 ;
958 SetLastError( ERROR_RESOURCE_TYPE_NOT_FOUND );
959 return FALSE;
962 /* Skip path component */
963 lpSubBlock = lpNextSlash;
966 /* Return value */
967 *lplpBuffer = VersionInfo32_Value( info );
968 if (puLen)
969 *puLen = info->wValueLength;
970 if (pbText)
971 *pbText = info->wType;
973 return TRUE;
976 /***********************************************************************
977 * VerQueryValueA [VERSION.@]
979 BOOL WINAPI VerQueryValueA( LPCVOID pBlock, LPCSTR lpSubBlock,
980 LPVOID *lplpBuffer, PUINT puLen )
982 static const char rootA[] = "\\";
983 const VS_VERSION_INFO_STRUCT16 *info = pBlock;
985 TRACE("(%p,%s,%p,%p)\n",
986 pBlock, debugstr_a(lpSubBlock), lplpBuffer, puLen );
988 if (!pBlock)
989 return FALSE;
991 if (lpSubBlock == NULL || lpSubBlock[0] == '\0')
992 lpSubBlock = rootA;
994 if ( !VersionInfoIs16( info ) )
996 BOOL ret, isText;
997 INT len;
998 LPWSTR lpSubBlockW;
1000 len = MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, NULL, 0);
1001 lpSubBlockW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1003 if (!lpSubBlockW)
1004 return FALSE;
1006 MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, lpSubBlockW, len);
1008 ret = VersionInfo32_QueryValue(pBlock, lpSubBlockW, lplpBuffer, puLen, &isText);
1010 HeapFree(GetProcessHeap(), 0, lpSubBlockW);
1012 if (ret && isText)
1014 /* Set lpBuffer so it points to the 'empty' area where we store
1015 * the converted strings
1017 LPSTR lpBufferA = (LPSTR)pBlock + info->wLength + 4;
1018 DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock;
1020 len = WideCharToMultiByte(CP_ACP, 0, *lplpBuffer, -1,
1021 lpBufferA + pos, info->wLength - pos, NULL, NULL);
1022 *lplpBuffer = lpBufferA + pos;
1023 if (puLen) *puLen = len;
1025 return ret;
1028 return VersionInfo16_QueryValue(info, lpSubBlock, lplpBuffer, puLen);
1031 /***********************************************************************
1032 * VerQueryValueW [VERSION.@]
1034 BOOL WINAPI VerQueryValueW( LPCVOID pBlock, LPCWSTR lpSubBlock,
1035 LPVOID *lplpBuffer, PUINT puLen )
1037 static const WCHAR nullW[] = { 0 };
1038 static const WCHAR rootW[] = { '\\', 0 };
1039 static const WCHAR varfileinfoW[] = { '\\','V','a','r','F','i','l','e','I','n','f','o',
1040 '\\','T','r','a','n','s','l','a','t','i','o','n', 0 };
1042 const VS_VERSION_INFO_STRUCT32 *info = pBlock;
1044 TRACE("(%p,%s,%p,%p)\n",
1045 pBlock, debugstr_w(lpSubBlock), lplpBuffer, puLen );
1047 if (!pBlock)
1048 return FALSE;
1050 if (lpSubBlock == NULL || lpSubBlock[0] == nullW[0])
1051 lpSubBlock = rootW;
1053 if ( VersionInfoIs16( info ) )
1055 BOOL ret;
1056 int len;
1057 LPSTR lpSubBlockA;
1059 len = WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, NULL, 0, NULL, NULL);
1060 lpSubBlockA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
1062 if (!lpSubBlockA)
1063 return FALSE;
1065 WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, lpSubBlockA, len, NULL, NULL);
1067 ret = VersionInfo16_QueryValue(pBlock, lpSubBlockA, lplpBuffer, puLen);
1069 HeapFree(GetProcessHeap(), 0, lpSubBlockA);
1071 if (ret && strcmpiW( lpSubBlock, rootW ) && strcmpiW( lpSubBlock, varfileinfoW ))
1073 /* Set lpBuffer so it points to the 'empty' area where we store
1074 * the converted strings
1076 LPWSTR lpBufferW = (LPWSTR)((LPSTR)pBlock + info->wLength);
1077 DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock;
1078 DWORD max = (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4 - info->wLength;
1080 len = MultiByteToWideChar(CP_ACP, 0, *lplpBuffer, -1,
1081 lpBufferW + pos, max/sizeof(WCHAR) - pos );
1082 *lplpBuffer = lpBufferW + pos;
1083 if (puLen) *puLen = len;
1085 return ret;
1088 return VersionInfo32_QueryValue(info, lpSubBlock, lplpBuffer, puLen, NULL);
1092 /******************************************************************************
1093 * testFileExistenceA
1095 * Tests whether a given path/file combination exists. If the file does
1096 * not exist, the return value is zero. If it does exist, the return
1097 * value is non-zero.
1099 * Revision history
1100 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
1101 * Original implementation
1104 static int testFileExistenceA( char const * path, char const * file, BOOL excl )
1106 char filename[1024];
1107 int filenamelen;
1108 OFSTRUCT fileinfo;
1110 fileinfo.cBytes = sizeof(OFSTRUCT);
1112 strcpy(filename, path);
1113 filenamelen = strlen(filename);
1115 /* Add a trailing \ if necessary */
1116 if(filenamelen) {
1117 if(filename[filenamelen - 1] != '\\')
1118 strcat(filename, "\\");
1120 else /* specify the current directory */
1121 strcpy(filename, ".\\");
1123 /* Create the full pathname */
1124 strcat(filename, file);
1126 return (OpenFile(filename, &fileinfo,
1127 OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR);
1130 /******************************************************************************
1131 * testFileExistenceW
1133 static int testFileExistenceW( const WCHAR *path, const WCHAR *file, BOOL excl )
1135 char *filename;
1136 DWORD pathlen, filelen;
1137 int ret;
1138 OFSTRUCT fileinfo;
1140 fileinfo.cBytes = sizeof(OFSTRUCT);
1142 pathlen = WideCharToMultiByte( CP_ACP, 0, path, -1, NULL, 0, NULL, NULL );
1143 filelen = WideCharToMultiByte( CP_ACP, 0, file, -1, NULL, 0, NULL, NULL );
1144 filename = HeapAlloc( GetProcessHeap(), 0, pathlen+filelen+2 );
1146 WideCharToMultiByte( CP_ACP, 0, path, -1, filename, pathlen, NULL, NULL );
1147 /* Add a trailing \ if necessary */
1148 if (pathlen > 1)
1150 if (filename[pathlen-2] != '\\') strcpy( &filename[pathlen-1], "\\" );
1152 else /* specify the current directory */
1153 strcpy(filename, ".\\");
1155 WideCharToMultiByte( CP_ACP, 0, file, -1, filename+strlen(filename), filelen, NULL, NULL );
1157 ret = (OpenFile(filename, &fileinfo,
1158 OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR);
1159 HeapFree( GetProcessHeap(), 0, filename );
1160 return ret;
1163 /*****************************************************************************
1164 * VerFindFileA [VERSION.@]
1166 * Determines where to install a file based on whether it locates another
1167 * version of the file in the system. The values VerFindFile returns are
1168 * used in a subsequent call to the VerInstallFile function.
1170 * Revision history:
1171 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
1172 * Reimplementation of VerFindFile from original stub.
1174 DWORD WINAPI VerFindFileA(
1175 DWORD flags,
1176 LPCSTR lpszFilename,
1177 LPCSTR lpszWinDir,
1178 LPCSTR lpszAppDir,
1179 LPSTR lpszCurDir,
1180 PUINT lpuCurDirLen,
1181 LPSTR lpszDestDir,
1182 PUINT lpuDestDirLen )
1184 DWORD retval = 0;
1185 const char *curDir;
1186 const char *destDir;
1187 unsigned int curDirSizeReq;
1188 unsigned int destDirSizeReq;
1189 char winDir[MAX_PATH], systemDir[MAX_PATH];
1191 /* Print out debugging information */
1192 TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n",
1193 flags, debugstr_a(lpszFilename), debugstr_a(lpszWinDir), debugstr_a(lpszAppDir),
1194 lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0,
1195 lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 );
1197 /* Figure out where the file should go; shared files default to the
1198 system directory */
1200 GetSystemDirectoryA(systemDir, sizeof(systemDir));
1201 curDir = "";
1203 if(flags & VFFF_ISSHAREDFILE)
1205 destDir = systemDir;
1206 /* Were we given a filename? If so, try to find the file. */
1207 if(lpszFilename)
1209 if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir;
1210 else if(lpszAppDir && testFileExistenceA(lpszAppDir, lpszFilename, FALSE))
1212 curDir = lpszAppDir;
1213 retval |= VFF_CURNEDEST;
1217 else /* not a shared file */
1219 destDir = lpszAppDir ? lpszAppDir : "";
1220 if(lpszFilename)
1222 GetWindowsDirectoryA( winDir, MAX_PATH );
1223 if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir;
1224 else if(testFileExistenceA(winDir, lpszFilename, FALSE))
1226 curDir = winDir;
1227 retval |= VFF_CURNEDEST;
1229 else if(testFileExistenceA(systemDir, lpszFilename, FALSE))
1231 curDir = systemDir;
1232 retval |= VFF_CURNEDEST;
1237 /* Check to see if the file exists and is in use by another application */
1238 if (lpszFilename && testFileExistenceA(curDir, lpszFilename, FALSE)) {
1239 if (lpszFilename && !testFileExistenceA(curDir, lpszFilename, TRUE))
1240 retval |= VFF_FILEINUSE;
1243 curDirSizeReq = strlen(curDir) + 1;
1244 destDirSizeReq = strlen(destDir) + 1;
1246 /* Make sure that the pointers to the size of the buffers are
1247 valid; if not, do NOTHING with that buffer. If that pointer
1248 is valid, then make sure that the buffer pointer is valid, too! */
1250 if(lpuDestDirLen && lpszDestDir)
1252 if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1253 lstrcpynA(lpszDestDir, destDir, *lpuDestDirLen);
1254 *lpuDestDirLen = destDirSizeReq;
1256 if(lpuCurDirLen && lpszCurDir)
1258 if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1259 lstrcpynA(lpszCurDir, curDir, *lpuCurDirLen);
1260 *lpuCurDirLen = curDirSizeReq;
1263 TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval,
1264 (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "",
1265 (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "",
1266 (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "",
1267 debugstr_a(lpszCurDir), debugstr_a(lpszDestDir));
1269 return retval;
1272 /*****************************************************************************
1273 * VerFindFileW [VERSION.@]
1275 DWORD WINAPI VerFindFileW( DWORD flags,LPCWSTR lpszFilename,LPCWSTR lpszWinDir,
1276 LPCWSTR lpszAppDir, LPWSTR lpszCurDir,PUINT lpuCurDirLen,
1277 LPWSTR lpszDestDir,PUINT lpuDestDirLen )
1279 static const WCHAR emptyW;
1280 DWORD retval = 0;
1281 const WCHAR *curDir;
1282 const WCHAR *destDir;
1283 unsigned int curDirSizeReq;
1284 unsigned int destDirSizeReq;
1285 WCHAR winDir[MAX_PATH], systemDir[MAX_PATH];
1287 /* Print out debugging information */
1288 TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n",
1289 flags, debugstr_w(lpszFilename), debugstr_w(lpszWinDir), debugstr_w(lpszAppDir),
1290 lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0,
1291 lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 );
1293 /* Figure out where the file should go; shared files default to the
1294 system directory */
1296 GetSystemDirectoryW(systemDir, sizeof(systemDir)/sizeof(WCHAR));
1297 curDir = &emptyW;
1299 if(flags & VFFF_ISSHAREDFILE)
1301 destDir = systemDir;
1302 /* Were we given a filename? If so, try to find the file. */
1303 if(lpszFilename)
1305 if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir;
1306 else if(lpszAppDir && testFileExistenceW(lpszAppDir, lpszFilename, FALSE))
1308 curDir = lpszAppDir;
1309 retval |= VFF_CURNEDEST;
1313 else /* not a shared file */
1315 destDir = lpszAppDir ? lpszAppDir : &emptyW;
1316 if(lpszFilename)
1318 GetWindowsDirectoryW( winDir, MAX_PATH );
1319 if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir;
1320 else if(testFileExistenceW(winDir, lpszFilename, FALSE))
1322 curDir = winDir;
1323 retval |= VFF_CURNEDEST;
1325 else if(testFileExistenceW(systemDir, lpszFilename, FALSE))
1327 curDir = systemDir;
1328 retval |= VFF_CURNEDEST;
1333 if (lpszFilename && !testFileExistenceW(curDir, lpszFilename, TRUE))
1334 retval |= VFF_FILEINUSE;
1336 curDirSizeReq = strlenW(curDir) + 1;
1337 destDirSizeReq = strlenW(destDir) + 1;
1339 /* Make sure that the pointers to the size of the buffers are
1340 valid; if not, do NOTHING with that buffer. If that pointer
1341 is valid, then make sure that the buffer pointer is valid, too! */
1343 if(lpuDestDirLen && lpszDestDir)
1345 if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1346 lstrcpynW(lpszDestDir, destDir, *lpuDestDirLen);
1347 *lpuDestDirLen = destDirSizeReq;
1349 if(lpuCurDirLen && lpszCurDir)
1351 if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1352 lstrcpynW(lpszCurDir, curDir, *lpuCurDirLen);
1353 *lpuCurDirLen = curDirSizeReq;
1356 TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval,
1357 (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "",
1358 (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "",
1359 (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "",
1360 debugstr_w(lpszCurDir), debugstr_w(lpszDestDir));
1361 return retval;
1364 static LPBYTE
1365 _fetch_versioninfo(LPSTR fn,VS_FIXEDFILEINFO **vffi) {
1366 DWORD alloclen;
1367 LPBYTE buf;
1368 DWORD ret;
1370 alloclen = 1000;
1371 buf=HeapAlloc(GetProcessHeap(), 0, alloclen);
1372 if(buf == NULL) {
1373 WARN("Memory exhausted while fetching version info!\n");
1374 return NULL;
1376 while (1) {
1377 ret = GetFileVersionInfoA(fn,0,alloclen,buf);
1378 if (!ret) {
1379 HeapFree(GetProcessHeap(), 0, buf);
1380 return NULL;
1382 if (alloclen<*(WORD*)buf) {
1383 alloclen = *(WORD*)buf;
1384 HeapFree(GetProcessHeap(), 0, buf);
1385 buf = HeapAlloc(GetProcessHeap(), 0, alloclen);
1386 if(buf == NULL) {
1387 WARN("Memory exhausted while fetching version info!\n");
1388 return NULL;
1390 } else {
1391 *vffi = (VS_FIXEDFILEINFO*)(buf+0x14);
1392 if ((*vffi)->dwSignature == 0x004f0049) /* hack to detect unicode */
1393 *vffi = (VS_FIXEDFILEINFO*)(buf+0x28);
1394 if ((*vffi)->dwSignature != VS_FFI_SIGNATURE)
1395 WARN("Bad VS_FIXEDFILEINFO signature 0x%08x\n",(*vffi)->dwSignature);
1396 return buf;
1401 static DWORD
1402 _error2vif(DWORD error) {
1403 switch (error) {
1404 case ERROR_ACCESS_DENIED:
1405 return VIF_ACCESSVIOLATION;
1406 case ERROR_SHARING_VIOLATION:
1407 return VIF_SHARINGVIOLATION;
1408 default:
1409 return 0;
1414 /******************************************************************************
1415 * VerInstallFileA [VERSION.@]
1417 DWORD WINAPI VerInstallFileA(
1418 DWORD flags,LPCSTR srcfilename,LPCSTR destfilename,LPCSTR srcdir,
1419 LPCSTR destdir,LPCSTR curdir,LPSTR tmpfile,PUINT tmpfilelen )
1421 LPCSTR pdest;
1422 char destfn[260],tmpfn[260],srcfn[260];
1423 HFILE hfsrc,hfdst;
1424 DWORD attr,xret,tmplast;
1425 LONG ret;
1426 LPBYTE buf1,buf2;
1427 OFSTRUCT ofs;
1429 TRACE("(%x,%s,%s,%s,%s,%s,%p,%d)\n",
1430 flags,debugstr_a(srcfilename),debugstr_a(destfilename),
1431 debugstr_a(srcdir),debugstr_a(destdir),debugstr_a(curdir),
1432 tmpfile,*tmpfilelen);
1433 xret = 0;
1434 if (!srcdir || !srcfilename) return VIF_CANNOTREADSRC;
1435 sprintf(srcfn,"%s\\%s",srcdir,srcfilename);
1436 if (!destdir || !*destdir) pdest = srcdir;
1437 else pdest = destdir;
1438 sprintf(destfn,"%s\\%s",pdest,destfilename);
1439 hfsrc=LZOpenFileA(srcfn,&ofs,OF_READ);
1440 if (hfsrc < 0)
1441 return VIF_CANNOTREADSRC;
1442 sprintf(tmpfn,"%s\\%s",pdest,destfilename);
1443 tmplast=strlen(pdest)+1;
1444 attr = GetFileAttributesA(tmpfn);
1445 if (attr != INVALID_FILE_ATTRIBUTES) {
1446 if (attr & FILE_ATTRIBUTE_READONLY) {
1447 LZClose(hfsrc);
1448 return VIF_WRITEPROT;
1450 /* FIXME: check if file currently in use and return VIF_FILEINUSE */
1452 attr = INVALID_FILE_ATTRIBUTES;
1453 if (flags & VIFF_FORCEINSTALL) {
1454 if (tmpfile[0]) {
1455 sprintf(tmpfn,"%s\\%s",pdest,tmpfile);
1456 tmplast = strlen(pdest)+1;
1457 attr = GetFileAttributesA(tmpfn);
1458 /* if it exists, it has been copied by the call before.
1459 * we jump over the copy part...
1463 if (attr == INVALID_FILE_ATTRIBUTES) {
1464 char *s;
1466 GetTempFileNameA(pdest,"ver",0,tmpfn); /* should not fail ... */
1467 s=strrchr(tmpfn,'\\');
1468 if (s)
1469 tmplast = s-tmpfn;
1470 else
1471 tmplast = 0;
1472 hfdst = OpenFile(tmpfn,&ofs,OF_CREATE);
1473 if (hfdst == HFILE_ERROR) {
1474 LZClose(hfsrc);
1475 return VIF_CANNOTCREATE; /* | translated dos error */
1477 ret = LZCopy(hfsrc,hfdst);
1478 _lclose(hfdst);
1479 if (ret < 0) {
1480 /* translate LZ errors into VIF_xxx */
1481 switch (ret) {
1482 case LZERROR_BADINHANDLE:
1483 case LZERROR_READ:
1484 case LZERROR_BADVALUE:
1485 case LZERROR_UNKNOWNALG:
1486 xret = VIF_CANNOTREADSRC;
1487 break;
1488 case LZERROR_BADOUTHANDLE:
1489 case LZERROR_WRITE:
1490 xret = VIF_OUTOFSPACE;
1491 break;
1492 case LZERROR_GLOBALLOC:
1493 case LZERROR_GLOBLOCK:
1494 xret = VIF_OUTOFMEMORY;
1495 break;
1496 default: /* unknown error, should not happen */
1497 FIXME("Unknown LZCopy error %d, ignoring.\n", ret);
1498 xret = 0;
1499 break;
1501 if (xret) {
1502 LZClose(hfsrc);
1503 return xret;
1507 if (!(flags & VIFF_FORCEINSTALL)) {
1508 VS_FIXEDFILEINFO *destvffi,*tmpvffi;
1509 buf1 = _fetch_versioninfo(destfn,&destvffi);
1510 if (buf1) {
1511 buf2 = _fetch_versioninfo(tmpfn,&tmpvffi);
1512 if (buf2) {
1513 char *tbuf1,*tbuf2;
1514 static const CHAR trans_array[] = "\\VarFileInfo\\Translation";
1515 UINT len1,len2;
1517 len1=len2=40;
1519 /* compare file versions */
1520 if ((destvffi->dwFileVersionMS > tmpvffi->dwFileVersionMS)||
1521 ((destvffi->dwFileVersionMS==tmpvffi->dwFileVersionMS)&&
1522 (destvffi->dwFileVersionLS > tmpvffi->dwFileVersionLS)
1525 xret |= VIF_MISMATCH|VIF_SRCOLD;
1526 /* compare filetypes and filesubtypes */
1527 if ((destvffi->dwFileType!=tmpvffi->dwFileType) ||
1528 (destvffi->dwFileSubtype!=tmpvffi->dwFileSubtype)
1530 xret |= VIF_MISMATCH|VIF_DIFFTYPE;
1531 if (VerQueryValueA(buf1,trans_array,(LPVOID*)&tbuf1,&len1) &&
1532 VerQueryValueA(buf2,trans_array,(LPVOID*)&tbuf2,&len2)
1534 /* Do something with tbuf1 and tbuf2
1535 * generates DIFFLANG|MISMATCH
1538 HeapFree(GetProcessHeap(), 0, buf2);
1539 } else
1540 xret=VIF_MISMATCH|VIF_SRCOLD;
1541 HeapFree(GetProcessHeap(), 0, buf1);
1544 if (xret) {
1545 if (*tmpfilelen<strlen(tmpfn+tmplast)) {
1546 xret|=VIF_BUFFTOOSMALL;
1547 DeleteFileA(tmpfn);
1548 } else {
1549 strcpy(tmpfile,tmpfn+tmplast);
1550 *tmpfilelen = strlen(tmpfn+tmplast)+1;
1551 xret|=VIF_TEMPFILE;
1553 } else {
1554 if (INVALID_FILE_ATTRIBUTES!=GetFileAttributesA(destfn))
1555 if (!DeleteFileA(destfn)) {
1556 xret|=_error2vif(GetLastError())|VIF_CANNOTDELETE;
1557 DeleteFileA(tmpfn);
1558 LZClose(hfsrc);
1559 return xret;
1561 if ((!(flags & VIFF_DONTDELETEOLD)) &&
1562 curdir &&
1563 *curdir &&
1564 lstrcmpiA(curdir,pdest)
1566 char curfn[260];
1568 sprintf(curfn,"%s\\%s",curdir,destfilename);
1569 if (INVALID_FILE_ATTRIBUTES != GetFileAttributesA(curfn)) {
1570 /* FIXME: check if in use ... if it is, VIF_CANNOTDELETECUR */
1571 if (!DeleteFileA(curfn))
1572 xret|=_error2vif(GetLastError())|VIF_CANNOTDELETECUR;
1575 if (!MoveFileA(tmpfn,destfn)) {
1576 xret|=_error2vif(GetLastError())|VIF_CANNOTRENAME;
1577 DeleteFileA(tmpfn);
1580 LZClose(hfsrc);
1581 return xret;
1585 /******************************************************************************
1586 * VerInstallFileW [VERSION.@]
1588 DWORD WINAPI VerInstallFileW(
1589 DWORD flags,LPCWSTR srcfilename,LPCWSTR destfilename,LPCWSTR srcdir,
1590 LPCWSTR destdir,LPCWSTR curdir,LPWSTR tmpfile,PUINT tmpfilelen )
1592 LPSTR wsrcf = NULL, wsrcd = NULL, wdestf = NULL, wdestd = NULL, wtmpf = NULL, wcurd = NULL;
1593 DWORD ret = 0;
1594 UINT len;
1596 if (srcfilename)
1598 len = WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, NULL, 0, NULL, NULL );
1599 if ((wsrcf = HeapAlloc( GetProcessHeap(), 0, len )))
1600 WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, wsrcf, len, NULL, NULL );
1601 else
1602 ret = VIF_OUTOFMEMORY;
1604 if (srcdir && !ret)
1606 len = WideCharToMultiByte( CP_ACP, 0, srcdir, -1, NULL, 0, NULL, NULL );
1607 if ((wsrcd = HeapAlloc( GetProcessHeap(), 0, len )))
1608 WideCharToMultiByte( CP_ACP, 0, srcdir, -1, wsrcd, len, NULL, NULL );
1609 else
1610 ret = VIF_OUTOFMEMORY;
1612 if (destfilename && !ret)
1614 len = WideCharToMultiByte( CP_ACP, 0, destfilename, -1, NULL, 0, NULL, NULL );
1615 if ((wdestf = HeapAlloc( GetProcessHeap(), 0, len )))
1616 WideCharToMultiByte( CP_ACP, 0, destfilename, -1, wdestf, len, NULL, NULL );
1617 else
1618 ret = VIF_OUTOFMEMORY;
1620 if (destdir && !ret)
1622 len = WideCharToMultiByte( CP_ACP, 0, destdir, -1, NULL, 0, NULL, NULL );
1623 if ((wdestd = HeapAlloc( GetProcessHeap(), 0, len )))
1624 WideCharToMultiByte( CP_ACP, 0, destdir, -1, wdestd, len, NULL, NULL );
1625 else
1626 ret = VIF_OUTOFMEMORY;
1628 if (curdir && !ret)
1630 len = WideCharToMultiByte( CP_ACP, 0, curdir, -1, NULL, 0, NULL, NULL );
1631 if ((wcurd = HeapAlloc( GetProcessHeap(), 0, len )))
1632 WideCharToMultiByte( CP_ACP, 0, curdir, -1, wcurd, len, NULL, NULL );
1633 else
1634 ret = VIF_OUTOFMEMORY;
1636 if (!ret)
1638 len = *tmpfilelen * sizeof(WCHAR);
1639 wtmpf = HeapAlloc( GetProcessHeap(), 0, len );
1640 if (!wtmpf)
1641 ret = VIF_OUTOFMEMORY;
1643 if (!ret)
1644 ret = VerInstallFileA(flags,wsrcf,wdestf,wsrcd,wdestd,wcurd,wtmpf,&len);
1645 if (!ret)
1646 *tmpfilelen = MultiByteToWideChar( CP_ACP, 0, wtmpf, -1, tmpfile, *tmpfilelen );
1647 else if (ret & VIF_BUFFTOOSMALL)
1648 *tmpfilelen = len; /* FIXME: not correct */
1650 HeapFree( GetProcessHeap(), 0, wsrcf );
1651 HeapFree( GetProcessHeap(), 0, wsrcd );
1652 HeapFree( GetProcessHeap(), 0, wdestf );
1653 HeapFree( GetProcessHeap(), 0, wdestd );
1654 HeapFree( GetProcessHeap(), 0, wtmpf );
1655 HeapFree( GetProcessHeap(), 0, wcurd );
1656 return ret;