schedsvc: Print an error if the service couldn't start monitoring the tasks directory.
[wine.git] / dlls / version / version.c
blob13436cad16ed59da6bcb6b68d63eb980a1591f3a
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;
1013 UINT value_len;
1015 len = MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, NULL, 0);
1016 lpSubBlockW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1018 if (!lpSubBlockW)
1019 return FALSE;
1021 MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, lpSubBlockW, len);
1023 ret = VersionInfo32_QueryValue(pBlock, lpSubBlockW, lplpBuffer, &value_len, &isText);
1024 if (puLen) *puLen = value_len;
1026 HeapFree(GetProcessHeap(), 0, lpSubBlockW);
1028 if (ret && isText)
1030 /* Set lpBuffer so it points to the 'empty' area where we store
1031 * the converted strings
1033 LPSTR lpBufferA = (LPSTR)pBlock + info->wLength + 4;
1034 DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock;
1035 len = WideCharToMultiByte(CP_ACP, 0, *lplpBuffer, value_len,
1036 lpBufferA + pos, info->wLength - pos, NULL, NULL);
1037 *lplpBuffer = lpBufferA + pos;
1038 if (puLen) *puLen = len;
1040 return ret;
1043 return VersionInfo16_QueryValue(info, lpSubBlock, lplpBuffer, puLen);
1046 /***********************************************************************
1047 * VerQueryValueW [VERSION.@]
1049 BOOL WINAPI VerQueryValueW( LPCVOID pBlock, LPCWSTR lpSubBlock,
1050 LPVOID *lplpBuffer, PUINT puLen )
1052 static const WCHAR nullW[] = { 0 };
1053 static const WCHAR rootW[] = { '\\', 0 };
1054 static const WCHAR varfileinfoW[] = { '\\','V','a','r','F','i','l','e','I','n','f','o',
1055 '\\','T','r','a','n','s','l','a','t','i','o','n', 0 };
1057 const VS_VERSION_INFO_STRUCT32 *info = pBlock;
1059 TRACE("(%p,%s,%p,%p)\n",
1060 pBlock, debugstr_w(lpSubBlock), lplpBuffer, puLen );
1062 if (!pBlock)
1063 return FALSE;
1065 if (lpSubBlock == NULL || lpSubBlock[0] == nullW[0])
1066 lpSubBlock = rootW;
1068 if ( VersionInfoIs16( info ) )
1070 BOOL ret;
1071 int len;
1072 LPSTR lpSubBlockA;
1074 len = WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, NULL, 0, NULL, NULL);
1075 lpSubBlockA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
1077 if (!lpSubBlockA)
1078 return FALSE;
1080 WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, lpSubBlockA, len, NULL, NULL);
1082 ret = VersionInfo16_QueryValue(pBlock, lpSubBlockA, lplpBuffer, puLen);
1084 HeapFree(GetProcessHeap(), 0, lpSubBlockA);
1086 if (ret && strcmpiW( lpSubBlock, rootW ) && strcmpiW( lpSubBlock, varfileinfoW ))
1088 /* Set lpBuffer so it points to the 'empty' area where we store
1089 * the converted strings
1091 LPWSTR lpBufferW = (LPWSTR)((LPSTR)pBlock + info->wLength);
1092 DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock;
1093 DWORD max = (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4 - info->wLength;
1095 len = MultiByteToWideChar(CP_ACP, 0, *lplpBuffer, -1,
1096 lpBufferW + pos, max/sizeof(WCHAR) - pos );
1097 *lplpBuffer = lpBufferW + pos;
1098 if (puLen) *puLen = len;
1100 return ret;
1103 return VersionInfo32_QueryValue(info, lpSubBlock, lplpBuffer, puLen, NULL);
1107 /******************************************************************************
1108 * testFileExistenceA
1110 * Tests whether a given path/file combination exists. If the file does
1111 * not exist, the return value is zero. If it does exist, the return
1112 * value is non-zero.
1114 * Revision history
1115 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
1116 * Original implementation
1119 static int testFileExistenceA( char const * path, char const * file, BOOL excl )
1121 char filename[1024];
1122 int filenamelen;
1123 OFSTRUCT fileinfo;
1125 fileinfo.cBytes = sizeof(OFSTRUCT);
1127 strcpy(filename, path);
1128 filenamelen = strlen(filename);
1130 /* Add a trailing \ if necessary */
1131 if(filenamelen) {
1132 if(filename[filenamelen - 1] != '\\')
1133 strcat(filename, "\\");
1135 else /* specify the current directory */
1136 strcpy(filename, ".\\");
1138 /* Create the full pathname */
1139 strcat(filename, file);
1141 return (OpenFile(filename, &fileinfo,
1142 OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR);
1145 /******************************************************************************
1146 * testFileExistenceW
1148 static int testFileExistenceW( const WCHAR *path, const WCHAR *file, BOOL excl )
1150 char *filename;
1151 DWORD pathlen, filelen;
1152 int ret;
1153 OFSTRUCT fileinfo;
1155 fileinfo.cBytes = sizeof(OFSTRUCT);
1157 pathlen = WideCharToMultiByte( CP_ACP, 0, path, -1, NULL, 0, NULL, NULL );
1158 filelen = WideCharToMultiByte( CP_ACP, 0, file, -1, NULL, 0, NULL, NULL );
1159 filename = HeapAlloc( GetProcessHeap(), 0, pathlen+filelen+2 );
1161 WideCharToMultiByte( CP_ACP, 0, path, -1, filename, pathlen, NULL, NULL );
1162 /* Add a trailing \ if necessary */
1163 if (pathlen > 1)
1165 if (filename[pathlen-2] != '\\') strcpy( &filename[pathlen-1], "\\" );
1167 else /* specify the current directory */
1168 strcpy(filename, ".\\");
1170 WideCharToMultiByte( CP_ACP, 0, file, -1, filename+strlen(filename), filelen, NULL, NULL );
1172 ret = (OpenFile(filename, &fileinfo,
1173 OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR);
1174 HeapFree( GetProcessHeap(), 0, filename );
1175 return ret;
1178 /*****************************************************************************
1179 * VerFindFileA [VERSION.@]
1181 * Determines where to install a file based on whether it locates another
1182 * version of the file in the system. The values VerFindFile returns are
1183 * used in a subsequent call to the VerInstallFile function.
1185 * Revision history:
1186 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
1187 * Reimplementation of VerFindFile from original stub.
1189 DWORD WINAPI VerFindFileA(
1190 DWORD flags,
1191 LPCSTR lpszFilename,
1192 LPCSTR lpszWinDir,
1193 LPCSTR lpszAppDir,
1194 LPSTR lpszCurDir,
1195 PUINT lpuCurDirLen,
1196 LPSTR lpszDestDir,
1197 PUINT lpuDestDirLen )
1199 DWORD retval = 0;
1200 const char *curDir;
1201 const char *destDir;
1202 unsigned int curDirSizeReq;
1203 unsigned int destDirSizeReq;
1204 char winDir[MAX_PATH], systemDir[MAX_PATH];
1206 /* Print out debugging information */
1207 TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n",
1208 flags, debugstr_a(lpszFilename), debugstr_a(lpszWinDir), debugstr_a(lpszAppDir),
1209 lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0,
1210 lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 );
1212 /* Figure out where the file should go; shared files default to the
1213 system directory */
1215 GetSystemDirectoryA(systemDir, sizeof(systemDir));
1216 curDir = "";
1218 if(flags & VFFF_ISSHAREDFILE)
1220 destDir = systemDir;
1221 /* Were we given a filename? If so, try to find the file. */
1222 if(lpszFilename)
1224 if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir;
1225 else if(lpszAppDir && testFileExistenceA(lpszAppDir, lpszFilename, FALSE))
1227 curDir = lpszAppDir;
1228 retval |= VFF_CURNEDEST;
1232 else /* not a shared file */
1234 destDir = lpszAppDir ? lpszAppDir : "";
1235 if(lpszFilename)
1237 GetWindowsDirectoryA( winDir, MAX_PATH );
1238 if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir;
1239 else if(testFileExistenceA(winDir, lpszFilename, FALSE))
1241 curDir = winDir;
1242 retval |= VFF_CURNEDEST;
1244 else if(testFileExistenceA(systemDir, lpszFilename, FALSE))
1246 curDir = systemDir;
1247 retval |= VFF_CURNEDEST;
1252 /* Check to see if the file exists and is in use by another application */
1253 if (lpszFilename && testFileExistenceA(curDir, lpszFilename, FALSE)) {
1254 if (lpszFilename && !testFileExistenceA(curDir, lpszFilename, TRUE))
1255 retval |= VFF_FILEINUSE;
1258 curDirSizeReq = strlen(curDir) + 1;
1259 destDirSizeReq = strlen(destDir) + 1;
1261 /* Make sure that the pointers to the size of the buffers are
1262 valid; if not, do NOTHING with that buffer. If that pointer
1263 is valid, then make sure that the buffer pointer is valid, too! */
1265 if(lpuDestDirLen && lpszDestDir)
1267 if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1268 lstrcpynA(lpszDestDir, destDir, *lpuDestDirLen);
1269 *lpuDestDirLen = destDirSizeReq;
1271 if(lpuCurDirLen && lpszCurDir)
1273 if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1274 lstrcpynA(lpszCurDir, curDir, *lpuCurDirLen);
1275 *lpuCurDirLen = curDirSizeReq;
1278 TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval,
1279 (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "",
1280 (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "",
1281 (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "",
1282 debugstr_a(lpszCurDir), debugstr_a(lpszDestDir));
1284 return retval;
1287 /*****************************************************************************
1288 * VerFindFileW [VERSION.@]
1290 DWORD WINAPI VerFindFileW( DWORD flags,LPCWSTR lpszFilename,LPCWSTR lpszWinDir,
1291 LPCWSTR lpszAppDir, LPWSTR lpszCurDir,PUINT lpuCurDirLen,
1292 LPWSTR lpszDestDir,PUINT lpuDestDirLen )
1294 static const WCHAR emptyW;
1295 DWORD retval = 0;
1296 const WCHAR *curDir;
1297 const WCHAR *destDir;
1298 unsigned int curDirSizeReq;
1299 unsigned int destDirSizeReq;
1300 WCHAR winDir[MAX_PATH], systemDir[MAX_PATH];
1302 /* Print out debugging information */
1303 TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n",
1304 flags, debugstr_w(lpszFilename), debugstr_w(lpszWinDir), debugstr_w(lpszAppDir),
1305 lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0,
1306 lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 );
1308 /* Figure out where the file should go; shared files default to the
1309 system directory */
1311 GetSystemDirectoryW(systemDir, sizeof(systemDir)/sizeof(WCHAR));
1312 curDir = &emptyW;
1314 if(flags & VFFF_ISSHAREDFILE)
1316 destDir = systemDir;
1317 /* Were we given a filename? If so, try to find the file. */
1318 if(lpszFilename)
1320 if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir;
1321 else if(lpszAppDir && testFileExistenceW(lpszAppDir, lpszFilename, FALSE))
1323 curDir = lpszAppDir;
1324 retval |= VFF_CURNEDEST;
1328 else /* not a shared file */
1330 destDir = lpszAppDir ? lpszAppDir : &emptyW;
1331 if(lpszFilename)
1333 GetWindowsDirectoryW( winDir, MAX_PATH );
1334 if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir;
1335 else if(testFileExistenceW(winDir, lpszFilename, FALSE))
1337 curDir = winDir;
1338 retval |= VFF_CURNEDEST;
1340 else if(testFileExistenceW(systemDir, lpszFilename, FALSE))
1342 curDir = systemDir;
1343 retval |= VFF_CURNEDEST;
1348 if (lpszFilename && !testFileExistenceW(curDir, lpszFilename, TRUE))
1349 retval |= VFF_FILEINUSE;
1351 curDirSizeReq = strlenW(curDir) + 1;
1352 destDirSizeReq = strlenW(destDir) + 1;
1354 /* Make sure that the pointers to the size of the buffers are
1355 valid; if not, do NOTHING with that buffer. If that pointer
1356 is valid, then make sure that the buffer pointer is valid, too! */
1358 if(lpuDestDirLen && lpszDestDir)
1360 if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1361 lstrcpynW(lpszDestDir, destDir, *lpuDestDirLen);
1362 *lpuDestDirLen = destDirSizeReq;
1364 if(lpuCurDirLen && lpszCurDir)
1366 if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1367 lstrcpynW(lpszCurDir, curDir, *lpuCurDirLen);
1368 *lpuCurDirLen = curDirSizeReq;
1371 TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval,
1372 (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "",
1373 (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "",
1374 (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "",
1375 debugstr_w(lpszCurDir), debugstr_w(lpszDestDir));
1376 return retval;
1379 static LPBYTE
1380 _fetch_versioninfo(LPSTR fn,VS_FIXEDFILEINFO **vffi) {
1381 DWORD alloclen;
1382 LPBYTE buf;
1383 DWORD ret;
1385 alloclen = 1000;
1386 buf=HeapAlloc(GetProcessHeap(), 0, alloclen);
1387 if(buf == NULL) {
1388 WARN("Memory exhausted while fetching version info!\n");
1389 return NULL;
1391 while (1) {
1392 ret = GetFileVersionInfoA(fn,0,alloclen,buf);
1393 if (!ret) {
1394 HeapFree(GetProcessHeap(), 0, buf);
1395 return NULL;
1397 if (alloclen<*(WORD*)buf) {
1398 alloclen = *(WORD*)buf;
1399 HeapFree(GetProcessHeap(), 0, buf);
1400 buf = HeapAlloc(GetProcessHeap(), 0, alloclen);
1401 if(buf == NULL) {
1402 WARN("Memory exhausted while fetching version info!\n");
1403 return NULL;
1405 } else {
1406 *vffi = (VS_FIXEDFILEINFO*)(buf+0x14);
1407 if ((*vffi)->dwSignature == 0x004f0049) /* hack to detect unicode */
1408 *vffi = (VS_FIXEDFILEINFO*)(buf+0x28);
1409 if ((*vffi)->dwSignature != VS_FFI_SIGNATURE)
1410 WARN("Bad VS_FIXEDFILEINFO signature 0x%08x\n",(*vffi)->dwSignature);
1411 return buf;
1416 static DWORD
1417 _error2vif(DWORD error) {
1418 switch (error) {
1419 case ERROR_ACCESS_DENIED:
1420 return VIF_ACCESSVIOLATION;
1421 case ERROR_SHARING_VIOLATION:
1422 return VIF_SHARINGVIOLATION;
1423 default:
1424 return 0;
1429 /******************************************************************************
1430 * VerInstallFileA [VERSION.@]
1432 DWORD WINAPI VerInstallFileA(
1433 DWORD flags,LPCSTR srcfilename,LPCSTR destfilename,LPCSTR srcdir,
1434 LPCSTR destdir,LPCSTR curdir,LPSTR tmpfile,PUINT tmpfilelen )
1436 LPCSTR pdest;
1437 char destfn[260],tmpfn[260],srcfn[260];
1438 HFILE hfsrc,hfdst;
1439 DWORD attr,xret,tmplast;
1440 LONG ret;
1441 LPBYTE buf1,buf2;
1442 OFSTRUCT ofs;
1444 TRACE("(%x,%s,%s,%s,%s,%s,%p,%d)\n",
1445 flags,debugstr_a(srcfilename),debugstr_a(destfilename),
1446 debugstr_a(srcdir),debugstr_a(destdir),debugstr_a(curdir),
1447 tmpfile,*tmpfilelen);
1448 xret = 0;
1449 if (!srcdir || !srcfilename) return VIF_CANNOTREADSRC;
1450 sprintf(srcfn,"%s\\%s",srcdir,srcfilename);
1451 if (!destdir || !*destdir) pdest = srcdir;
1452 else pdest = destdir;
1453 sprintf(destfn,"%s\\%s",pdest,destfilename);
1454 hfsrc=LZOpenFileA(srcfn,&ofs,OF_READ);
1455 if (hfsrc < 0)
1456 return VIF_CANNOTREADSRC;
1457 sprintf(tmpfn,"%s\\%s",pdest,destfilename);
1458 tmplast=strlen(pdest)+1;
1459 attr = GetFileAttributesA(tmpfn);
1460 if (attr != INVALID_FILE_ATTRIBUTES) {
1461 if (attr & FILE_ATTRIBUTE_READONLY) {
1462 LZClose(hfsrc);
1463 return VIF_WRITEPROT;
1465 /* FIXME: check if file currently in use and return VIF_FILEINUSE */
1467 attr = INVALID_FILE_ATTRIBUTES;
1468 if (flags & VIFF_FORCEINSTALL) {
1469 if (tmpfile[0]) {
1470 sprintf(tmpfn,"%s\\%s",pdest,tmpfile);
1471 tmplast = strlen(pdest)+1;
1472 attr = GetFileAttributesA(tmpfn);
1473 /* if it exists, it has been copied by the call before.
1474 * we jump over the copy part...
1478 if (attr == INVALID_FILE_ATTRIBUTES) {
1479 char *s;
1481 GetTempFileNameA(pdest,"ver",0,tmpfn); /* should not fail ... */
1482 s=strrchr(tmpfn,'\\');
1483 if (s)
1484 tmplast = s-tmpfn;
1485 else
1486 tmplast = 0;
1487 hfdst = OpenFile(tmpfn,&ofs,OF_CREATE);
1488 if (hfdst == HFILE_ERROR) {
1489 LZClose(hfsrc);
1490 return VIF_CANNOTCREATE; /* | translated dos error */
1492 ret = LZCopy(hfsrc,hfdst);
1493 _lclose(hfdst);
1494 if (ret < 0) {
1495 /* translate LZ errors into VIF_xxx */
1496 switch (ret) {
1497 case LZERROR_BADINHANDLE:
1498 case LZERROR_READ:
1499 case LZERROR_BADVALUE:
1500 case LZERROR_UNKNOWNALG:
1501 xret = VIF_CANNOTREADSRC;
1502 break;
1503 case LZERROR_BADOUTHANDLE:
1504 case LZERROR_WRITE:
1505 xret = VIF_OUTOFSPACE;
1506 break;
1507 case LZERROR_GLOBALLOC:
1508 case LZERROR_GLOBLOCK:
1509 xret = VIF_OUTOFMEMORY;
1510 break;
1511 default: /* unknown error, should not happen */
1512 FIXME("Unknown LZCopy error %d, ignoring.\n", ret);
1513 xret = 0;
1514 break;
1516 if (xret) {
1517 LZClose(hfsrc);
1518 return xret;
1522 if (!(flags & VIFF_FORCEINSTALL)) {
1523 VS_FIXEDFILEINFO *destvffi,*tmpvffi;
1524 buf1 = _fetch_versioninfo(destfn,&destvffi);
1525 if (buf1) {
1526 buf2 = _fetch_versioninfo(tmpfn,&tmpvffi);
1527 if (buf2) {
1528 char *tbuf1,*tbuf2;
1529 static const CHAR trans_array[] = "\\VarFileInfo\\Translation";
1530 UINT len1,len2;
1532 len1=len2=40;
1534 /* compare file versions */
1535 if ((destvffi->dwFileVersionMS > tmpvffi->dwFileVersionMS)||
1536 ((destvffi->dwFileVersionMS==tmpvffi->dwFileVersionMS)&&
1537 (destvffi->dwFileVersionLS > tmpvffi->dwFileVersionLS)
1540 xret |= VIF_MISMATCH|VIF_SRCOLD;
1541 /* compare filetypes and filesubtypes */
1542 if ((destvffi->dwFileType!=tmpvffi->dwFileType) ||
1543 (destvffi->dwFileSubtype!=tmpvffi->dwFileSubtype)
1545 xret |= VIF_MISMATCH|VIF_DIFFTYPE;
1546 if (VerQueryValueA(buf1,trans_array,(LPVOID*)&tbuf1,&len1) &&
1547 VerQueryValueA(buf2,trans_array,(LPVOID*)&tbuf2,&len2)
1549 /* Do something with tbuf1 and tbuf2
1550 * generates DIFFLANG|MISMATCH
1553 HeapFree(GetProcessHeap(), 0, buf2);
1554 } else
1555 xret=VIF_MISMATCH|VIF_SRCOLD;
1556 HeapFree(GetProcessHeap(), 0, buf1);
1559 if (xret) {
1560 if (*tmpfilelen<strlen(tmpfn+tmplast)) {
1561 xret|=VIF_BUFFTOOSMALL;
1562 DeleteFileA(tmpfn);
1563 } else {
1564 strcpy(tmpfile,tmpfn+tmplast);
1565 *tmpfilelen = strlen(tmpfn+tmplast)+1;
1566 xret|=VIF_TEMPFILE;
1568 } else {
1569 if (INVALID_FILE_ATTRIBUTES!=GetFileAttributesA(destfn))
1570 if (!DeleteFileA(destfn)) {
1571 xret|=_error2vif(GetLastError())|VIF_CANNOTDELETE;
1572 DeleteFileA(tmpfn);
1573 LZClose(hfsrc);
1574 return xret;
1576 if ((!(flags & VIFF_DONTDELETEOLD)) &&
1577 curdir &&
1578 *curdir &&
1579 lstrcmpiA(curdir,pdest)
1581 char curfn[260];
1583 sprintf(curfn,"%s\\%s",curdir,destfilename);
1584 if (INVALID_FILE_ATTRIBUTES != GetFileAttributesA(curfn)) {
1585 /* FIXME: check if in use ... if it is, VIF_CANNOTDELETECUR */
1586 if (!DeleteFileA(curfn))
1587 xret|=_error2vif(GetLastError())|VIF_CANNOTDELETECUR;
1590 if (!MoveFileA(tmpfn,destfn)) {
1591 xret|=_error2vif(GetLastError())|VIF_CANNOTRENAME;
1592 DeleteFileA(tmpfn);
1595 LZClose(hfsrc);
1596 return xret;
1600 /******************************************************************************
1601 * VerInstallFileW [VERSION.@]
1603 DWORD WINAPI VerInstallFileW(
1604 DWORD flags,LPCWSTR srcfilename,LPCWSTR destfilename,LPCWSTR srcdir,
1605 LPCWSTR destdir,LPCWSTR curdir,LPWSTR tmpfile,PUINT tmpfilelen )
1607 LPSTR wsrcf = NULL, wsrcd = NULL, wdestf = NULL, wdestd = NULL, wtmpf = NULL, wcurd = NULL;
1608 DWORD ret = 0;
1609 UINT len;
1611 if (srcfilename)
1613 len = WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, NULL, 0, NULL, NULL );
1614 if ((wsrcf = HeapAlloc( GetProcessHeap(), 0, len )))
1615 WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, wsrcf, len, NULL, NULL );
1616 else
1617 ret = VIF_OUTOFMEMORY;
1619 if (srcdir && !ret)
1621 len = WideCharToMultiByte( CP_ACP, 0, srcdir, -1, NULL, 0, NULL, NULL );
1622 if ((wsrcd = HeapAlloc( GetProcessHeap(), 0, len )))
1623 WideCharToMultiByte( CP_ACP, 0, srcdir, -1, wsrcd, len, NULL, NULL );
1624 else
1625 ret = VIF_OUTOFMEMORY;
1627 if (destfilename && !ret)
1629 len = WideCharToMultiByte( CP_ACP, 0, destfilename, -1, NULL, 0, NULL, NULL );
1630 if ((wdestf = HeapAlloc( GetProcessHeap(), 0, len )))
1631 WideCharToMultiByte( CP_ACP, 0, destfilename, -1, wdestf, len, NULL, NULL );
1632 else
1633 ret = VIF_OUTOFMEMORY;
1635 if (destdir && !ret)
1637 len = WideCharToMultiByte( CP_ACP, 0, destdir, -1, NULL, 0, NULL, NULL );
1638 if ((wdestd = HeapAlloc( GetProcessHeap(), 0, len )))
1639 WideCharToMultiByte( CP_ACP, 0, destdir, -1, wdestd, len, NULL, NULL );
1640 else
1641 ret = VIF_OUTOFMEMORY;
1643 if (curdir && !ret)
1645 len = WideCharToMultiByte( CP_ACP, 0, curdir, -1, NULL, 0, NULL, NULL );
1646 if ((wcurd = HeapAlloc( GetProcessHeap(), 0, len )))
1647 WideCharToMultiByte( CP_ACP, 0, curdir, -1, wcurd, len, NULL, NULL );
1648 else
1649 ret = VIF_OUTOFMEMORY;
1651 if (!ret)
1653 len = *tmpfilelen * sizeof(WCHAR);
1654 wtmpf = HeapAlloc( GetProcessHeap(), 0, len );
1655 if (!wtmpf)
1656 ret = VIF_OUTOFMEMORY;
1658 if (!ret)
1659 ret = VerInstallFileA(flags,wsrcf,wdestf,wsrcd,wdestd,wcurd,wtmpf,&len);
1660 if (!ret)
1661 *tmpfilelen = MultiByteToWideChar( CP_ACP, 0, wtmpf, -1, tmpfile, *tmpfilelen );
1662 else if (ret & VIF_BUFFTOOSMALL)
1663 *tmpfilelen = len; /* FIXME: not correct */
1665 HeapFree( GetProcessHeap(), 0, wsrcf );
1666 HeapFree( GetProcessHeap(), 0, wsrcd );
1667 HeapFree( GetProcessHeap(), 0, wdestf );
1668 HeapFree( GetProcessHeap(), 0, wdestd );
1669 HeapFree( GetProcessHeap(), 0, wtmpf );
1670 HeapFree( GetProcessHeap(), 0, wcurd );
1671 return ret;