mssign32: Add a trailing '\n' to a few FIXME() messages.
[wine/multimedia.git] / dlls / version / version.c
blobbc90dccfd9cf141d427e2a73f3a3fdf0aeb39fc3
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].u1.s2.Id == id)
88 return (const IMAGE_RESOURCE_DIRECTORY *)((const char *)root + entry[pos].u2.s3.OffsetToDirectory);
89 if (entry[pos].u1.s2.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.s3.OffsetToDirectory);
112 /***********************************************************************
113 * read_xx_header [internal]
115 static int read_xx_header( HFILE lzfd )
117 IMAGE_DOS_HEADER mzh;
118 char magic[3];
120 LZSeek( lzfd, 0, SEEK_SET );
121 if ( sizeof(mzh) != LZRead( lzfd, (LPSTR)&mzh, sizeof(mzh) ) )
122 return 0;
123 if ( mzh.e_magic != IMAGE_DOS_SIGNATURE )
125 if (!memcmp( &mzh, "\177ELF", 4 )) return 1; /* ELF */
126 if (*(UINT *)&mzh == 0xfeedface || *(UINT *)&mzh == 0xcefaedfe) return 1; /* Mach-O */
127 return 0;
130 LZSeek( lzfd, mzh.e_lfanew, SEEK_SET );
131 if ( 2 != LZRead( lzfd, magic, 2 ) )
132 return 0;
134 LZSeek( lzfd, mzh.e_lfanew, SEEK_SET );
136 if ( magic[0] == 'N' && magic[1] == 'E' )
137 return IMAGE_OS2_SIGNATURE;
138 if ( magic[0] == 'P' && magic[1] == 'E' )
139 return IMAGE_NT_SIGNATURE;
141 magic[2] = '\0';
142 WARN("Can't handle %s files.\n", magic );
143 return 0;
146 /***********************************************************************
147 * find_ne_resource [internal]
149 static BOOL find_ne_resource( HFILE lzfd, DWORD *resLen, DWORD *resOff )
151 const WORD typeid = VS_FILE_INFO | 0x8000;
152 const WORD resid = VS_VERSION_INFO | 0x8000;
153 IMAGE_OS2_HEADER nehd;
154 NE_TYPEINFO *typeInfo;
155 NE_NAMEINFO *nameInfo;
156 DWORD nehdoffset;
157 LPBYTE resTab;
158 DWORD resTabSize;
159 int count;
161 /* Read in NE header */
162 nehdoffset = LZSeek( lzfd, 0, SEEK_CUR );
163 if ( sizeof(nehd) != LZRead( lzfd, (LPSTR)&nehd, sizeof(nehd) ) ) return 0;
165 resTabSize = nehd.ne_restab - nehd.ne_rsrctab;
166 if ( !resTabSize )
168 TRACE("No resources in NE dll\n" );
169 return FALSE;
172 /* Read in resource table */
173 resTab = HeapAlloc( GetProcessHeap(), 0, resTabSize );
174 if ( !resTab ) return FALSE;
176 LZSeek( lzfd, nehd.ne_rsrctab + nehdoffset, SEEK_SET );
177 if ( resTabSize != LZRead( lzfd, (char*)resTab, resTabSize ) )
179 HeapFree( GetProcessHeap(), 0, resTab );
180 return FALSE;
183 /* Find resource */
184 typeInfo = (NE_TYPEINFO *)(resTab + 2);
185 while (typeInfo->type_id)
187 if (typeInfo->type_id == typeid) goto found_type;
188 typeInfo = (NE_TYPEINFO *)((char *)(typeInfo + 1) +
189 typeInfo->count * sizeof(NE_NAMEINFO));
191 TRACE("No typeid entry found\n" );
192 HeapFree( GetProcessHeap(), 0, resTab );
193 return FALSE;
195 found_type:
196 nameInfo = (NE_NAMEINFO *)(typeInfo + 1);
198 for (count = typeInfo->count; count > 0; count--, nameInfo++)
199 if (nameInfo->id == resid) goto found_name;
201 TRACE("No resid entry found\n" );
202 HeapFree( GetProcessHeap(), 0, resTab );
203 return FALSE;
205 found_name:
206 /* Return resource data */
207 if ( resLen ) *resLen = nameInfo->length << *(WORD *)resTab;
208 if ( resOff ) *resOff = nameInfo->offset << *(WORD *)resTab;
210 HeapFree( GetProcessHeap(), 0, resTab );
211 return TRUE;
214 /***********************************************************************
215 * find_pe_resource [internal]
217 static BOOL find_pe_resource( HFILE lzfd, DWORD *resLen, DWORD *resOff )
219 union
221 IMAGE_NT_HEADERS32 nt32;
222 IMAGE_NT_HEADERS64 nt64;
223 } pehd;
224 DWORD pehdoffset;
225 PIMAGE_DATA_DIRECTORY resDataDir;
226 PIMAGE_SECTION_HEADER sections;
227 LPBYTE resSection;
228 DWORD section_size, data_size;
229 const void *resDir;
230 const IMAGE_RESOURCE_DIRECTORY *resPtr;
231 const IMAGE_RESOURCE_DATA_ENTRY *resData;
232 int i, len, nSections;
233 BOOL ret = FALSE;
235 /* Read in PE header */
236 pehdoffset = LZSeek( lzfd, 0, SEEK_CUR );
237 len = LZRead( lzfd, (LPSTR)&pehd, sizeof(pehd) );
238 if (len < sizeof(pehd.nt32.FileHeader)) return 0;
239 if (len < sizeof(pehd)) memset( (char *)&pehd + len, 0, sizeof(pehd) - len );
241 switch (pehd.nt32.OptionalHeader.Magic)
243 case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
244 resDataDir = pehd.nt32.OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_RESOURCE;
245 break;
246 case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
247 resDataDir = pehd.nt64.OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_RESOURCE;
248 break;
249 default:
250 return 0;
253 if ( !resDataDir->Size )
255 TRACE("No resources in PE dll\n" );
256 return FALSE;
259 /* Read in section table */
260 nSections = pehd.nt32.FileHeader.NumberOfSections;
261 sections = HeapAlloc( GetProcessHeap(), 0,
262 nSections * sizeof(IMAGE_SECTION_HEADER) );
263 if ( !sections ) return FALSE;
265 len = FIELD_OFFSET( IMAGE_NT_HEADERS32, OptionalHeader ) + pehd.nt32.FileHeader.SizeOfOptionalHeader;
266 LZSeek( lzfd, pehdoffset + len, SEEK_SET );
268 if ( nSections * sizeof(IMAGE_SECTION_HEADER) !=
269 LZRead( lzfd, (LPSTR)sections, nSections * sizeof(IMAGE_SECTION_HEADER) ) )
271 HeapFree( GetProcessHeap(), 0, sections );
272 return FALSE;
275 /* Find resource section */
276 for ( i = 0; i < nSections; i++ )
277 if ( resDataDir->VirtualAddress >= sections[i].VirtualAddress
278 && resDataDir->VirtualAddress < sections[i].VirtualAddress +
279 sections[i].SizeOfRawData )
280 break;
282 if ( i == nSections )
284 HeapFree( GetProcessHeap(), 0, sections );
285 TRACE("Couldn't find resource section\n" );
286 return FALSE;
289 /* Read in resource section */
290 data_size = sections[i].SizeOfRawData;
291 section_size = max( data_size, sections[i].Misc.VirtualSize );
292 resSection = HeapAlloc( GetProcessHeap(), 0, section_size );
293 if ( !resSection )
295 HeapFree( GetProcessHeap(), 0, sections );
296 return FALSE;
299 LZSeek( lzfd, sections[i].PointerToRawData, SEEK_SET );
300 if (data_size != LZRead( lzfd, (char*)resSection, data_size )) goto done;
301 if (data_size < section_size) memset( (char *)resSection + data_size, 0, section_size - data_size );
303 /* Find resource */
304 resDir = resSection + (resDataDir->VirtualAddress - sections[i].VirtualAddress);
306 resPtr = resDir;
307 resPtr = find_entry_by_id( resPtr, VS_FILE_INFO, resDir );
308 if ( !resPtr )
310 TRACE("No typeid entry found\n" );
311 goto done;
313 resPtr = find_entry_by_id( resPtr, VS_VERSION_INFO, resDir );
314 if ( !resPtr )
316 TRACE("No resid entry found\n" );
317 goto done;
319 resPtr = find_entry_default( resPtr, resDir );
320 if ( !resPtr )
322 TRACE("No default language entry found\n" );
323 goto done;
326 /* Find resource data section */
327 resData = (const IMAGE_RESOURCE_DATA_ENTRY*)resPtr;
328 for ( i = 0; i < nSections; i++ )
329 if ( resData->OffsetToData >= sections[i].VirtualAddress
330 && resData->OffsetToData < sections[i].VirtualAddress +
331 sections[i].SizeOfRawData )
332 break;
334 if ( i == nSections )
336 TRACE("Couldn't find resource data section\n" );
337 goto done;
340 /* Return resource data */
341 if ( resLen ) *resLen = resData->Size;
342 if ( resOff ) *resOff = resData->OffsetToData - sections[i].VirtualAddress
343 + sections[i].PointerToRawData;
344 ret = TRUE;
346 done:
347 HeapFree( GetProcessHeap(), 0, resSection );
348 HeapFree( GetProcessHeap(), 0, sections );
349 return ret;
353 /***********************************************************************
354 * find_version_resource [internal]
356 static DWORD find_version_resource( HFILE lzfd, DWORD *reslen, DWORD *offset )
358 DWORD magic = read_xx_header( lzfd );
360 switch (magic)
362 case IMAGE_OS2_SIGNATURE:
363 if (!find_ne_resource( lzfd, reslen, offset )) magic = 0;
364 break;
365 case IMAGE_NT_SIGNATURE:
366 if (!find_pe_resource( lzfd, reslen, offset )) magic = 0;
367 break;
369 return magic;
372 /******************************************************************************
374 * This function will print via standard TRACE, debug info regarding
375 * the file info structure vffi.
376 * 15-Feb-1998 Dimitrie Paun (dimi@cs.toronto.edu)
377 * Added this function to clean up the code.
379 *****************************************************************************/
380 static void print_vffi_debug(const VS_FIXEDFILEINFO *vffi)
382 BOOL versioned_printer = FALSE;
384 if((vffi->dwFileType == VFT_DLL) || (vffi->dwFileType == VFT_DRV))
386 if(vffi->dwFileSubtype == VFT2_DRV_VERSIONED_PRINTER)
387 /* this is documented for newer w2k Drivers and up */
388 versioned_printer = TRUE;
389 else if( (vffi->dwFileSubtype == VFT2_DRV_PRINTER) &&
390 (vffi->dwFileVersionMS != vffi->dwProductVersionMS) &&
391 (vffi->dwFileVersionMS > 0) &&
392 (vffi->dwFileVersionMS <= 3) )
393 /* found this on NT 3.51, NT4.0 and old w2k Drivers */
394 versioned_printer = TRUE;
397 TRACE("structversion=%u.%u, ",
398 HIWORD(vffi->dwStrucVersion),LOWORD(vffi->dwStrucVersion));
399 if(versioned_printer)
401 WORD mode = LOWORD(vffi->dwFileVersionMS);
402 WORD ver_rev = HIWORD(vffi->dwFileVersionLS);
403 TRACE("fileversion=%u.%u.%u.%u (%s.major.minor.release), ",
404 (vffi->dwFileVersionMS),
405 HIBYTE(ver_rev), LOBYTE(ver_rev), LOWORD(vffi->dwFileVersionLS),
406 (mode == 3) ? "Usermode" : ((mode <= 2) ? "Kernelmode" : "?") );
408 else
410 TRACE("fileversion=%u.%u.%u.%u, ",
411 HIWORD(vffi->dwFileVersionMS),LOWORD(vffi->dwFileVersionMS),
412 HIWORD(vffi->dwFileVersionLS),LOWORD(vffi->dwFileVersionLS));
414 TRACE("productversion=%u.%u.%u.%u\n",
415 HIWORD(vffi->dwProductVersionMS),LOWORD(vffi->dwProductVersionMS),
416 HIWORD(vffi->dwProductVersionLS),LOWORD(vffi->dwProductVersionLS));
418 TRACE("flagmask=0x%x, flags=0x%x %s%s%s%s%s%s\n",
419 vffi->dwFileFlagsMask, vffi->dwFileFlags,
420 (vffi->dwFileFlags & VS_FF_DEBUG) ? "DEBUG," : "",
421 (vffi->dwFileFlags & VS_FF_PRERELEASE) ? "PRERELEASE," : "",
422 (vffi->dwFileFlags & VS_FF_PATCHED) ? "PATCHED," : "",
423 (vffi->dwFileFlags & VS_FF_PRIVATEBUILD) ? "PRIVATEBUILD," : "",
424 (vffi->dwFileFlags & VS_FF_INFOINFERRED) ? "INFOINFERRED," : "",
425 (vffi->dwFileFlags & VS_FF_SPECIALBUILD) ? "SPECIALBUILD," : "");
427 TRACE("(");
429 TRACE("OS=0x%x.0x%x ", HIWORD(vffi->dwFileOS), LOWORD(vffi->dwFileOS));
431 switch (vffi->dwFileOS&0xFFFF0000)
433 case VOS_DOS:TRACE("DOS,");break;
434 case VOS_OS216:TRACE("OS/2-16,");break;
435 case VOS_OS232:TRACE("OS/2-32,");break;
436 case VOS_NT:TRACE("NT,");break;
437 case VOS_UNKNOWN:
438 default:
439 TRACE("UNKNOWN(0x%x),",vffi->dwFileOS&0xFFFF0000);break;
442 switch (LOWORD(vffi->dwFileOS))
444 case VOS__BASE:TRACE("BASE");break;
445 case VOS__WINDOWS16:TRACE("WIN16");break;
446 case VOS__WINDOWS32:TRACE("WIN32");break;
447 case VOS__PM16:TRACE("PM16");break;
448 case VOS__PM32:TRACE("PM32");break;
449 default:
450 TRACE("UNKNOWN(0x%x)",LOWORD(vffi->dwFileOS));break;
453 TRACE(")\n");
455 switch (vffi->dwFileType)
457 case VFT_APP:TRACE("filetype=APP");break;
458 case VFT_DLL:
459 TRACE("filetype=DLL");
460 if(vffi->dwFileSubtype != 0)
462 if(versioned_printer) /* NT3.x/NT4.0 or old w2k Driver */
463 TRACE(",PRINTER");
464 TRACE(" (subtype=0x%x)", vffi->dwFileSubtype);
466 break;
467 case VFT_DRV:
468 TRACE("filetype=DRV,");
469 switch(vffi->dwFileSubtype)
471 case VFT2_DRV_PRINTER:TRACE("PRINTER");break;
472 case VFT2_DRV_KEYBOARD:TRACE("KEYBOARD");break;
473 case VFT2_DRV_LANGUAGE:TRACE("LANGUAGE");break;
474 case VFT2_DRV_DISPLAY:TRACE("DISPLAY");break;
475 case VFT2_DRV_MOUSE:TRACE("MOUSE");break;
476 case VFT2_DRV_NETWORK:TRACE("NETWORK");break;
477 case VFT2_DRV_SYSTEM:TRACE("SYSTEM");break;
478 case VFT2_DRV_INSTALLABLE:TRACE("INSTALLABLE");break;
479 case VFT2_DRV_SOUND:TRACE("SOUND");break;
480 case VFT2_DRV_COMM:TRACE("COMM");break;
481 case VFT2_DRV_INPUTMETHOD:TRACE("INPUTMETHOD");break;
482 case VFT2_DRV_VERSIONED_PRINTER:TRACE("VERSIONED_PRINTER");break;
483 case VFT2_UNKNOWN:
484 default:
485 TRACE("UNKNOWN(0x%x)",vffi->dwFileSubtype);break;
487 break;
488 case VFT_FONT:
489 TRACE("filetype=FONT,");
490 switch (vffi->dwFileSubtype)
492 case VFT2_FONT_RASTER:TRACE("RASTER");break;
493 case VFT2_FONT_VECTOR:TRACE("VECTOR");break;
494 case VFT2_FONT_TRUETYPE:TRACE("TRUETYPE");break;
495 default:TRACE("UNKNOWN(0x%x)",vffi->dwFileSubtype);break;
497 break;
498 case VFT_VXD:TRACE("filetype=VXD");break;
499 case VFT_STATIC_LIB:TRACE("filetype=STATIC_LIB");break;
500 case VFT_UNKNOWN:
501 default:
502 TRACE("filetype=Unknown(0x%x)",vffi->dwFileType);break;
505 TRACE("\n");
506 TRACE("filedate=0x%x.0x%x\n",vffi->dwFileDateMS,vffi->dwFileDateLS);
509 /***********************************************************************
510 * Version Info Structure
513 typedef struct
515 WORD wLength;
516 WORD wValueLength;
517 CHAR szKey[1];
518 #if 0 /* variable length structure */
519 /* DWORD aligned */
520 BYTE Value[];
521 /* DWORD aligned */
522 VS_VERSION_INFO_STRUCT16 Children[];
523 #endif
524 } VS_VERSION_INFO_STRUCT16;
526 typedef struct
528 WORD wLength;
529 WORD wValueLength;
530 WORD wType;
531 WCHAR szKey[1];
532 #if 0 /* variable length structure */
533 /* DWORD aligned */
534 BYTE Value[];
535 /* DWORD aligned */
536 VS_VERSION_INFO_STRUCT32 Children[];
537 #endif
538 } VS_VERSION_INFO_STRUCT32;
540 #define VersionInfoIs16( ver ) \
541 ( ((const VS_VERSION_INFO_STRUCT16 *)ver)->szKey[0] >= ' ' )
543 #define DWORD_ALIGN( base, ptr ) \
544 ( (LPBYTE)(base) + ((((LPBYTE)(ptr) - (LPBYTE)(base)) + 3) & ~3) )
546 #define VersionInfo16_Value( ver ) \
547 DWORD_ALIGN( (ver), (ver)->szKey + strlen((ver)->szKey) + 1 )
548 #define VersionInfo32_Value( ver ) \
549 DWORD_ALIGN( (ver), (ver)->szKey + strlenW((ver)->szKey) + 1 )
551 #define VersionInfo16_Children( ver ) \
552 (const VS_VERSION_INFO_STRUCT16 *)( VersionInfo16_Value( ver ) + \
553 ( ( (ver)->wValueLength + 3 ) & ~3 ) )
554 #define VersionInfo32_Children( ver ) \
555 (const VS_VERSION_INFO_STRUCT32 *)( VersionInfo32_Value( ver ) + \
556 ( ( (ver)->wValueLength * \
557 ((ver)->wType? 2 : 1) + 3 ) & ~3 ) )
559 #define VersionInfo16_Next( ver ) \
560 (VS_VERSION_INFO_STRUCT16 *)( (LPBYTE)ver + (((ver)->wLength + 3) & ~3) )
561 #define VersionInfo32_Next( ver ) \
562 (VS_VERSION_INFO_STRUCT32 *)( (LPBYTE)ver + (((ver)->wLength + 3) & ~3) )
565 /***********************************************************************
566 * GetFileVersionInfoSizeW [VERSION.@]
568 DWORD WINAPI GetFileVersionInfoSizeW( LPCWSTR filename, LPDWORD handle )
570 DWORD len, offset, magic = 1;
571 HFILE lzfd;
572 HMODULE hModule;
573 OFSTRUCT ofs;
575 TRACE("(%s,%p)\n", debugstr_w(filename), handle );
577 if (handle) *handle = 0;
579 if (!filename)
581 SetLastError(ERROR_INVALID_PARAMETER);
582 return 0;
584 if (!*filename)
586 SetLastError(ERROR_BAD_PATHNAME);
587 return 0;
590 if ((lzfd = LZOpenFileW( (LPWSTR)filename, &ofs, OF_READ )) != HFILE_ERROR)
592 magic = find_version_resource( lzfd, &len, &offset );
593 LZClose( lzfd );
596 if ((magic == 1) && (hModule = LoadLibraryExW( filename, 0, LOAD_LIBRARY_AS_DATAFILE )))
598 HRSRC hRsrc = FindResourceW( hModule, MAKEINTRESOURCEW(VS_VERSION_INFO),
599 MAKEINTRESOURCEW(VS_FILE_INFO) );
600 if (hRsrc)
602 magic = IMAGE_NT_SIGNATURE;
603 len = SizeofResource( hModule, hRsrc );
605 FreeLibrary( hModule );
608 switch (magic)
610 case IMAGE_OS2_SIGNATURE:
611 /* We have a 16bit resource.
613 * XP/W2K/W2K3 uses a buffer which is more than the actual needed space:
615 * (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4
617 * This extra buffer is used for ANSI to Unicode conversions in W-Calls.
618 * info->wLength should be the same as len. Currently it isn't but that
619 * doesn't seem to be a problem (len is bigger than info->wLength).
621 SetLastError(0);
622 return (len - sizeof(VS_FIXEDFILEINFO)) * 4;
624 case IMAGE_NT_SIGNATURE:
625 /* We have a 32bit resource.
627 * XP/W2K/W2K3 uses a buffer which is 2 times the actual needed space + 4 bytes "FE2X"
628 * This extra buffer is used for Unicode to ANSI conversions in A-Calls
630 SetLastError(0);
631 return (len * 2) + 4;
633 default:
634 SetLastError( lzfd == HFILE_ERROR ? ofs.nErrCode : ERROR_RESOURCE_DATA_NOT_FOUND );
635 return 0;
639 /***********************************************************************
640 * GetFileVersionInfoSizeA [VERSION.@]
642 DWORD WINAPI GetFileVersionInfoSizeA( LPCSTR filename, LPDWORD handle )
644 UNICODE_STRING filenameW;
645 DWORD retval;
647 TRACE("(%s,%p)\n", debugstr_a(filename), handle );
649 if(filename)
650 RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
651 else
652 filenameW.Buffer = NULL;
654 retval = GetFileVersionInfoSizeW(filenameW.Buffer, handle);
656 RtlFreeUnicodeString(&filenameW);
658 return retval;
661 /***********************************************************************
662 * GetFileVersionInfoW [VERSION.@]
664 BOOL WINAPI GetFileVersionInfoW( LPCWSTR filename, DWORD handle,
665 DWORD datasize, LPVOID data )
667 static const char signature[4] = "FE2X";
668 DWORD len, offset, magic = 1;
669 HFILE lzfd;
670 OFSTRUCT ofs;
671 HMODULE hModule;
672 VS_VERSION_INFO_STRUCT32* vvis = data;
674 TRACE("(%s,%d,size=%d,data=%p)\n",
675 debugstr_w(filename), handle, datasize, data );
677 if (!data)
679 SetLastError(ERROR_INVALID_DATA);
680 return FALSE;
683 if ((lzfd = LZOpenFileW( (LPWSTR)filename, &ofs, OF_READ )) != HFILE_ERROR)
685 if ((magic = find_version_resource( lzfd, &len, &offset )) > 1)
687 LZSeek( lzfd, offset, 0 /* SEEK_SET */ );
688 len = LZRead( lzfd, data, min( len, datasize ) );
690 LZClose( lzfd );
693 if ((magic == 1) && (hModule = LoadLibraryExW( filename, 0, LOAD_LIBRARY_AS_DATAFILE )))
695 HRSRC hRsrc = FindResourceW( hModule, MAKEINTRESOURCEW(VS_VERSION_INFO),
696 MAKEINTRESOURCEW(VS_FILE_INFO) );
697 if (hRsrc)
699 HGLOBAL hMem = LoadResource( hModule, hRsrc );
700 magic = IMAGE_NT_SIGNATURE;
701 len = min( SizeofResource(hModule, hRsrc), datasize );
702 memcpy( data, LockResource( hMem ), len );
703 FreeResource( hMem );
705 FreeLibrary( hModule );
708 switch (magic)
710 case IMAGE_OS2_SIGNATURE:
711 /* We have a 16bit resource. */
712 if (TRACE_ON(ver))
713 print_vffi_debug( (VS_FIXEDFILEINFO *)VersionInfo16_Value( (VS_VERSION_INFO_STRUCT16 *)data ));
714 SetLastError(0);
715 return TRUE;
717 case IMAGE_NT_SIGNATURE:
718 /* We have a 32bit resource.
720 * XP/W2K/W2K3 uses a buffer which is 2 times the actual needed space + 4 bytes "FE2X"
721 * This extra buffer is used for Unicode to ANSI conversions in A-Calls
723 len = vvis->wLength + sizeof(signature);
724 if (datasize >= len) memcpy( (char*)data + vvis->wLength, signature, sizeof(signature) );
725 if (TRACE_ON(ver))
726 print_vffi_debug( (VS_FIXEDFILEINFO *)VersionInfo32_Value( vvis ));
727 SetLastError(0);
728 return TRUE;
730 default:
731 SetLastError( lzfd == HFILE_ERROR ? ofs.nErrCode : ERROR_RESOURCE_DATA_NOT_FOUND );
732 return FALSE;
736 /***********************************************************************
737 * GetFileVersionInfoA [VERSION.@]
739 BOOL WINAPI GetFileVersionInfoA( LPCSTR filename, DWORD handle,
740 DWORD datasize, LPVOID data )
742 UNICODE_STRING filenameW;
743 BOOL retval;
745 TRACE("(%s,%d,size=%d,data=%p)\n",
746 debugstr_a(filename), handle, datasize, data );
748 if(filename)
749 RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
750 else
751 filenameW.Buffer = NULL;
753 retval = GetFileVersionInfoW(filenameW.Buffer, handle, datasize, data);
755 RtlFreeUnicodeString(&filenameW);
757 return retval;
760 /***********************************************************************
761 * VersionInfo16_FindChild [internal]
763 static const VS_VERSION_INFO_STRUCT16 *VersionInfo16_FindChild( const VS_VERSION_INFO_STRUCT16 *info,
764 LPCSTR szKey, UINT cbKey )
766 const VS_VERSION_INFO_STRUCT16 *child = VersionInfo16_Children( info );
768 while ((char *)child < (char *)info + info->wLength )
770 if (!strncasecmp( child->szKey, szKey, cbKey ) && !child->szKey[cbKey])
771 return child;
773 if (!(child->wLength)) return NULL;
774 child = VersionInfo16_Next( child );
777 return NULL;
780 /***********************************************************************
781 * VersionInfo32_FindChild [internal]
783 static const VS_VERSION_INFO_STRUCT32 *VersionInfo32_FindChild( const VS_VERSION_INFO_STRUCT32 *info,
784 LPCWSTR szKey, UINT cbKey )
786 const VS_VERSION_INFO_STRUCT32 *child = VersionInfo32_Children( info );
788 while ((char *)child < (char *)info + info->wLength )
790 if (!strncmpiW( child->szKey, szKey, cbKey ) && !child->szKey[cbKey])
791 return child;
793 if (!(child->wLength)) return NULL;
794 child = VersionInfo32_Next( child );
797 return NULL;
800 /***********************************************************************
801 * VersionInfo16_QueryValue [internal]
803 * Gets a value from a 16-bit NE resource
805 static BOOL VersionInfo16_QueryValue( const VS_VERSION_INFO_STRUCT16 *info, LPCSTR lpSubBlock,
806 LPVOID *lplpBuffer, UINT *puLen )
808 while ( *lpSubBlock )
810 /* Find next path component */
811 LPCSTR lpNextSlash;
812 for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ )
813 if ( *lpNextSlash == '\\' )
814 break;
816 /* Skip empty components */
817 if ( lpNextSlash == lpSubBlock )
819 lpSubBlock++;
820 continue;
823 /* We have a non-empty component: search info for key */
824 info = VersionInfo16_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock );
825 if ( !info )
827 if (puLen) *puLen = 0 ;
828 SetLastError( ERROR_RESOURCE_TYPE_NOT_FOUND );
829 return FALSE;
832 /* Skip path component */
833 lpSubBlock = lpNextSlash;
836 /* Return value */
837 *lplpBuffer = VersionInfo16_Value( info );
838 if (puLen)
839 *puLen = info->wValueLength;
841 return TRUE;
844 /***********************************************************************
845 * VersionInfo32_QueryValue [internal]
847 * Gets a value from a 32-bit PE resource
849 static BOOL VersionInfo32_QueryValue( const VS_VERSION_INFO_STRUCT32 *info, LPCWSTR lpSubBlock,
850 LPVOID *lplpBuffer, UINT *puLen )
852 TRACE("lpSubBlock : (%s)\n", debugstr_w(lpSubBlock));
854 while ( *lpSubBlock )
856 /* Find next path component */
857 LPCWSTR lpNextSlash;
858 for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ )
859 if ( *lpNextSlash == '\\' )
860 break;
862 /* Skip empty components */
863 if ( lpNextSlash == lpSubBlock )
865 lpSubBlock++;
866 continue;
869 /* We have a non-empty component: search info for key */
870 info = VersionInfo32_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock );
871 if ( !info )
873 if (puLen) *puLen = 0 ;
874 SetLastError( ERROR_RESOURCE_TYPE_NOT_FOUND );
875 return FALSE;
878 /* Skip path component */
879 lpSubBlock = lpNextSlash;
882 /* Return value */
883 *lplpBuffer = VersionInfo32_Value( info );
884 if (puLen)
885 *puLen = info->wValueLength;
887 return TRUE;
890 /***********************************************************************
891 * VerQueryValueA [VERSION.@]
893 BOOL WINAPI VerQueryValueA( LPCVOID pBlock, LPCSTR lpSubBlock,
894 LPVOID *lplpBuffer, PUINT puLen )
896 static const char rootA[] = "\\";
897 static const char varfileinfoA[] = "\\VarFileInfo\\Translation";
898 const VS_VERSION_INFO_STRUCT16 *info = pBlock;
900 TRACE("(%p,%s,%p,%p)\n",
901 pBlock, debugstr_a(lpSubBlock), lplpBuffer, puLen );
903 if (!pBlock)
904 return FALSE;
906 if (lpSubBlock == NULL || lpSubBlock[0] == '\0')
907 lpSubBlock = rootA;
909 if ( !VersionInfoIs16( info ) )
911 BOOL ret;
912 INT len;
913 LPWSTR lpSubBlockW;
915 len = MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, NULL, 0);
916 lpSubBlockW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
918 if (!lpSubBlockW)
919 return FALSE;
921 MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, lpSubBlockW, len);
923 ret = VersionInfo32_QueryValue(pBlock, lpSubBlockW, lplpBuffer, puLen);
925 HeapFree(GetProcessHeap(), 0, lpSubBlockW);
927 if (ret && strcasecmp( lpSubBlock, rootA ) && strcasecmp( lpSubBlock, varfileinfoA ))
929 /* Set lpBuffer so it points to the 'empty' area where we store
930 * the converted strings
932 LPSTR lpBufferA = (LPSTR)pBlock + info->wLength + 4;
933 DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock;
935 len = WideCharToMultiByte(CP_ACP, 0, *lplpBuffer, -1,
936 lpBufferA + pos, info->wLength - pos, NULL, NULL);
937 *lplpBuffer = lpBufferA + pos;
938 *puLen = len;
940 return ret;
943 return VersionInfo16_QueryValue(info, lpSubBlock, lplpBuffer, puLen);
946 /***********************************************************************
947 * VerQueryValueW [VERSION.@]
949 BOOL WINAPI VerQueryValueW( LPCVOID pBlock, LPCWSTR lpSubBlock,
950 LPVOID *lplpBuffer, PUINT puLen )
952 static const WCHAR nullW[] = { 0 };
953 static const WCHAR rootW[] = { '\\', 0 };
954 static const WCHAR varfileinfoW[] = { '\\','V','a','r','F','i','l','e','I','n','f','o',
955 '\\','T','r','a','n','s','l','a','t','i','o','n', 0 };
957 const VS_VERSION_INFO_STRUCT32 *info = pBlock;
959 TRACE("(%p,%s,%p,%p)\n",
960 pBlock, debugstr_w(lpSubBlock), lplpBuffer, puLen );
962 if (!pBlock)
963 return FALSE;
965 if (lpSubBlock == NULL || lpSubBlock[0] == nullW[0])
966 lpSubBlock = rootW;
968 if ( VersionInfoIs16( info ) )
970 BOOL ret;
971 int len;
972 LPSTR lpSubBlockA;
974 len = WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, NULL, 0, NULL, NULL);
975 lpSubBlockA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
977 if (!lpSubBlockA)
978 return FALSE;
980 WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, lpSubBlockA, len, NULL, NULL);
982 ret = VersionInfo16_QueryValue(pBlock, lpSubBlockA, lplpBuffer, puLen);
984 HeapFree(GetProcessHeap(), 0, lpSubBlockA);
986 if (ret && strcmpiW( lpSubBlock, rootW ) && strcmpiW( lpSubBlock, varfileinfoW ))
988 /* Set lpBuffer so it points to the 'empty' area where we store
989 * the converted strings
991 LPWSTR lpBufferW = (LPWSTR)((LPSTR)pBlock + info->wLength);
992 DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock;
993 DWORD max = (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4 - info->wLength;
995 len = MultiByteToWideChar(CP_ACP, 0, *lplpBuffer, -1,
996 lpBufferW + pos, max/sizeof(WCHAR) - pos );
997 *lplpBuffer = lpBufferW + pos;
998 *puLen = len;
1000 return ret;
1003 return VersionInfo32_QueryValue(info, lpSubBlock, lplpBuffer, puLen);
1007 /******************************************************************************
1008 * testFileExistenceA
1010 * Tests whether a given path/file combination exists. If the file does
1011 * not exist, the return value is zero. If it does exist, the return
1012 * value is non-zero.
1014 * Revision history
1015 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
1016 * Original implementation
1019 static int testFileExistenceA( char const * path, char const * file, BOOL excl )
1021 char filename[1024];
1022 int filenamelen;
1023 OFSTRUCT fileinfo;
1025 fileinfo.cBytes = sizeof(OFSTRUCT);
1027 strcpy(filename, path);
1028 filenamelen = strlen(filename);
1030 /* Add a trailing \ if necessary */
1031 if(filenamelen) {
1032 if(filename[filenamelen - 1] != '\\')
1033 strcat(filename, "\\");
1035 else /* specify the current directory */
1036 strcpy(filename, ".\\");
1038 /* Create the full pathname */
1039 strcat(filename, file);
1041 return (OpenFile(filename, &fileinfo,
1042 OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR);
1045 /******************************************************************************
1046 * testFileExistenceW
1048 static int testFileExistenceW( const WCHAR *path, const WCHAR *file, BOOL excl )
1050 char *filename;
1051 DWORD pathlen, filelen;
1052 int ret;
1053 OFSTRUCT fileinfo;
1055 fileinfo.cBytes = sizeof(OFSTRUCT);
1057 pathlen = WideCharToMultiByte( CP_ACP, 0, path, -1, NULL, 0, NULL, NULL );
1058 filelen = WideCharToMultiByte( CP_ACP, 0, file, -1, NULL, 0, NULL, NULL );
1059 filename = HeapAlloc( GetProcessHeap(), 0, pathlen+filelen+2 );
1061 WideCharToMultiByte( CP_ACP, 0, path, -1, filename, pathlen, NULL, NULL );
1062 /* Add a trailing \ if necessary */
1063 if (pathlen > 1)
1065 if (filename[pathlen-2] != '\\') strcpy( &filename[pathlen-1], "\\" );
1067 else /* specify the current directory */
1068 strcpy(filename, ".\\");
1070 WideCharToMultiByte( CP_ACP, 0, file, -1, filename+strlen(filename), filelen, NULL, NULL );
1072 ret = (OpenFile(filename, &fileinfo,
1073 OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR);
1074 HeapFree( GetProcessHeap(), 0, filename );
1075 return ret;
1078 /*****************************************************************************
1079 * VerFindFileA [VERSION.@]
1081 * Determines where to install a file based on whether it locates another
1082 * version of the file in the system. The values VerFindFile returns are
1083 * used in a subsequent call to the VerInstallFile function.
1085 * Revision history:
1086 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
1087 * Reimplementation of VerFindFile from original stub.
1089 DWORD WINAPI VerFindFileA(
1090 DWORD flags,
1091 LPCSTR lpszFilename,
1092 LPCSTR lpszWinDir,
1093 LPCSTR lpszAppDir,
1094 LPSTR lpszCurDir,
1095 PUINT lpuCurDirLen,
1096 LPSTR lpszDestDir,
1097 PUINT lpuDestDirLen )
1099 DWORD retval = 0;
1100 const char *curDir;
1101 const char *destDir;
1102 unsigned int curDirSizeReq;
1103 unsigned int destDirSizeReq;
1104 char winDir[MAX_PATH], systemDir[MAX_PATH];
1106 /* Print out debugging information */
1107 TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n",
1108 flags, debugstr_a(lpszFilename), debugstr_a(lpszWinDir), debugstr_a(lpszAppDir),
1109 lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0,
1110 lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 );
1112 /* Figure out where the file should go; shared files default to the
1113 system directory */
1115 GetSystemDirectoryA(systemDir, sizeof(systemDir));
1116 curDir = "";
1117 destDir = "";
1119 if(flags & VFFF_ISSHAREDFILE)
1121 destDir = systemDir;
1122 /* Were we given a filename? If so, try to find the file. */
1123 if(lpszFilename)
1125 if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir;
1126 else if(lpszAppDir && testFileExistenceA(lpszAppDir, lpszFilename, FALSE))
1128 curDir = lpszAppDir;
1129 retval |= VFF_CURNEDEST;
1133 else /* not a shared file */
1135 destDir = lpszAppDir ? lpszAppDir : "";
1136 if(lpszFilename)
1138 GetWindowsDirectoryA( winDir, MAX_PATH );
1139 if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir;
1140 else if(testFileExistenceA(winDir, lpszFilename, FALSE))
1142 curDir = winDir;
1143 retval |= VFF_CURNEDEST;
1145 else if(testFileExistenceA(systemDir, lpszFilename, FALSE))
1147 curDir = systemDir;
1148 retval |= VFF_CURNEDEST;
1153 /* Check to see if the file exists and is in use by another application */
1154 if (lpszFilename && testFileExistenceA(curDir, lpszFilename, FALSE)) {
1155 if (lpszFilename && !testFileExistenceA(curDir, lpszFilename, TRUE))
1156 retval |= VFF_FILEINUSE;
1159 curDirSizeReq = strlen(curDir) + 1;
1160 destDirSizeReq = strlen(destDir) + 1;
1162 /* Make sure that the pointers to the size of the buffers are
1163 valid; if not, do NOTHING with that buffer. If that pointer
1164 is valid, then make sure that the buffer pointer is valid, too! */
1166 if(lpuDestDirLen && lpszDestDir)
1168 if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1169 lstrcpynA(lpszDestDir, destDir, *lpuDestDirLen);
1170 *lpuDestDirLen = destDirSizeReq;
1172 if(lpuCurDirLen && lpszCurDir)
1174 if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1175 lstrcpynA(lpszCurDir, curDir, *lpuCurDirLen);
1176 *lpuCurDirLen = curDirSizeReq;
1179 TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval,
1180 (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "",
1181 (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "",
1182 (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "",
1183 debugstr_a(lpszCurDir), debugstr_a(lpszDestDir));
1185 return retval;
1188 /*****************************************************************************
1189 * VerFindFileW [VERSION.@]
1191 DWORD WINAPI VerFindFileW( DWORD flags,LPCWSTR lpszFilename,LPCWSTR lpszWinDir,
1192 LPCWSTR lpszAppDir, LPWSTR lpszCurDir,PUINT lpuCurDirLen,
1193 LPWSTR lpszDestDir,PUINT lpuDestDirLen )
1195 static const WCHAR emptyW;
1196 DWORD retval = 0;
1197 const WCHAR *curDir;
1198 const WCHAR *destDir;
1199 unsigned int curDirSizeReq;
1200 unsigned int destDirSizeReq;
1201 WCHAR winDir[MAX_PATH], systemDir[MAX_PATH];
1203 /* Print out debugging information */
1204 TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n",
1205 flags, debugstr_w(lpszFilename), debugstr_w(lpszWinDir), debugstr_w(lpszAppDir),
1206 lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0,
1207 lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 );
1209 /* Figure out where the file should go; shared files default to the
1210 system directory */
1212 GetSystemDirectoryW(systemDir, sizeof(systemDir)/sizeof(WCHAR));
1213 curDir = &emptyW;
1214 destDir = &emptyW;
1216 if(flags & VFFF_ISSHAREDFILE)
1218 destDir = systemDir;
1219 /* Were we given a filename? If so, try to find the file. */
1220 if(lpszFilename)
1222 if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir;
1223 else if(lpszAppDir && testFileExistenceW(lpszAppDir, lpszFilename, FALSE))
1225 curDir = lpszAppDir;
1226 retval |= VFF_CURNEDEST;
1230 else /* not a shared file */
1232 destDir = lpszAppDir ? lpszAppDir : &emptyW;
1233 if(lpszFilename)
1235 GetWindowsDirectoryW( winDir, MAX_PATH );
1236 if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir;
1237 else if(testFileExistenceW(winDir, lpszFilename, FALSE))
1239 curDir = winDir;
1240 retval |= VFF_CURNEDEST;
1242 else if(testFileExistenceW(systemDir, lpszFilename, FALSE))
1244 curDir = systemDir;
1245 retval |= VFF_CURNEDEST;
1250 if (lpszFilename && !testFileExistenceW(curDir, lpszFilename, TRUE))
1251 retval |= VFF_FILEINUSE;
1253 curDirSizeReq = strlenW(curDir) + 1;
1254 destDirSizeReq = strlenW(destDir) + 1;
1256 /* Make sure that the pointers to the size of the buffers are
1257 valid; if not, do NOTHING with that buffer. If that pointer
1258 is valid, then make sure that the buffer pointer is valid, too! */
1260 if(lpuDestDirLen && lpszDestDir)
1262 if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1263 lstrcpynW(lpszDestDir, destDir, *lpuDestDirLen);
1264 *lpuDestDirLen = destDirSizeReq;
1266 if(lpuCurDirLen && lpszCurDir)
1268 if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1269 lstrcpynW(lpszCurDir, curDir, *lpuCurDirLen);
1270 *lpuCurDirLen = curDirSizeReq;
1273 TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval,
1274 (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "",
1275 (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "",
1276 (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "",
1277 debugstr_w(lpszCurDir), debugstr_w(lpszDestDir));
1278 return retval;
1281 static LPBYTE
1282 _fetch_versioninfo(LPSTR fn,VS_FIXEDFILEINFO **vffi) {
1283 DWORD alloclen;
1284 LPBYTE buf;
1285 DWORD ret;
1287 alloclen = 1000;
1288 buf=HeapAlloc(GetProcessHeap(), 0, alloclen);
1289 if(buf == NULL) {
1290 WARN("Memory exausted while fetching version info!\n");
1291 return NULL;
1293 while (1) {
1294 ret = GetFileVersionInfoA(fn,0,alloclen,buf);
1295 if (!ret) {
1296 HeapFree(GetProcessHeap(), 0, buf);
1297 return NULL;
1299 if (alloclen<*(WORD*)buf) {
1300 alloclen = *(WORD*)buf;
1301 HeapFree(GetProcessHeap(), 0, buf);
1302 buf = HeapAlloc(GetProcessHeap(), 0, alloclen);
1303 if(buf == NULL) {
1304 WARN("Memory exausted while fetching version info!\n");
1305 return NULL;
1307 } else {
1308 *vffi = (VS_FIXEDFILEINFO*)(buf+0x14);
1309 if ((*vffi)->dwSignature == 0x004f0049) /* hack to detect unicode */
1310 *vffi = (VS_FIXEDFILEINFO*)(buf+0x28);
1311 if ((*vffi)->dwSignature != VS_FFI_SIGNATURE)
1312 WARN("Bad VS_FIXEDFILEINFO signature 0x%08x\n",(*vffi)->dwSignature);
1313 return buf;
1318 static DWORD
1319 _error2vif(DWORD error) {
1320 switch (error) {
1321 case ERROR_ACCESS_DENIED:
1322 return VIF_ACCESSVIOLATION;
1323 case ERROR_SHARING_VIOLATION:
1324 return VIF_SHARINGVIOLATION;
1325 default:
1326 return 0;
1331 /******************************************************************************
1332 * VerInstallFileA [VERSION.@]
1334 DWORD WINAPI VerInstallFileA(
1335 DWORD flags,LPCSTR srcfilename,LPCSTR destfilename,LPCSTR srcdir,
1336 LPCSTR destdir,LPCSTR curdir,LPSTR tmpfile,PUINT tmpfilelen )
1338 LPCSTR pdest;
1339 char destfn[260],tmpfn[260],srcfn[260];
1340 HFILE hfsrc,hfdst;
1341 DWORD attr,xret,tmplast;
1342 LONG ret;
1343 LPBYTE buf1,buf2;
1344 OFSTRUCT ofs;
1346 TRACE("(%x,%s,%s,%s,%s,%s,%p,%d)\n",
1347 flags,debugstr_a(srcfilename),debugstr_a(destfilename),
1348 debugstr_a(srcdir),debugstr_a(destdir),debugstr_a(curdir),
1349 tmpfile,*tmpfilelen);
1350 xret = 0;
1351 if (!srcdir || !srcfilename) return VIF_CANNOTREADSRC;
1352 sprintf(srcfn,"%s\\%s",srcdir,srcfilename);
1353 if (!destdir || !*destdir) pdest = srcdir;
1354 else pdest = destdir;
1355 sprintf(destfn,"%s\\%s",pdest,destfilename);
1356 hfsrc=LZOpenFileA(srcfn,&ofs,OF_READ);
1357 if (hfsrc < 0)
1358 return VIF_CANNOTREADSRC;
1359 sprintf(tmpfn,"%s\\%s",pdest,destfilename);
1360 tmplast=strlen(pdest)+1;
1361 attr = GetFileAttributesA(tmpfn);
1362 if (attr != INVALID_FILE_ATTRIBUTES) {
1363 if (attr & FILE_ATTRIBUTE_READONLY) {
1364 LZClose(hfsrc);
1365 return VIF_WRITEPROT;
1367 /* FIXME: check if file currently in use and return VIF_FILEINUSE */
1369 attr = INVALID_FILE_ATTRIBUTES;
1370 if (flags & VIFF_FORCEINSTALL) {
1371 if (tmpfile[0]) {
1372 sprintf(tmpfn,"%s\\%s",pdest,tmpfile);
1373 tmplast = strlen(pdest)+1;
1374 attr = GetFileAttributesA(tmpfn);
1375 /* if it exists, it has been copied by the call before.
1376 * we jump over the copy part...
1380 if (attr == INVALID_FILE_ATTRIBUTES) {
1381 char *s;
1383 GetTempFileNameA(pdest,"ver",0,tmpfn); /* should not fail ... */
1384 s=strrchr(tmpfn,'\\');
1385 if (s)
1386 tmplast = s-tmpfn;
1387 else
1388 tmplast = 0;
1389 hfdst = OpenFile(tmpfn,&ofs,OF_CREATE);
1390 if (hfdst == HFILE_ERROR) {
1391 LZClose(hfsrc);
1392 return VIF_CANNOTCREATE; /* | translated dos error */
1394 ret = LZCopy(hfsrc,hfdst);
1395 _lclose(hfdst);
1396 if (ret < 0) {
1397 /* translate LZ errors into VIF_xxx */
1398 switch (ret) {
1399 case LZERROR_BADINHANDLE:
1400 case LZERROR_READ:
1401 case LZERROR_BADVALUE:
1402 case LZERROR_UNKNOWNALG:
1403 xret = VIF_CANNOTREADSRC;
1404 break;
1405 case LZERROR_BADOUTHANDLE:
1406 case LZERROR_WRITE:
1407 xret = VIF_OUTOFSPACE;
1408 break;
1409 case LZERROR_GLOBALLOC:
1410 case LZERROR_GLOBLOCK:
1411 xret = VIF_OUTOFMEMORY;
1412 break;
1413 default: /* unknown error, should not happen */
1414 FIXME("Unknown LZCopy error %d, ignoring.\n", ret);
1415 xret = 0;
1416 break;
1418 if (xret) {
1419 LZClose(hfsrc);
1420 return xret;
1424 if (!(flags & VIFF_FORCEINSTALL)) {
1425 VS_FIXEDFILEINFO *destvffi,*tmpvffi;
1426 buf1 = _fetch_versioninfo(destfn,&destvffi);
1427 if (buf1) {
1428 buf2 = _fetch_versioninfo(tmpfn,&tmpvffi);
1429 if (buf2) {
1430 char *tbuf1,*tbuf2;
1431 static const CHAR trans_array[] = "\\VarFileInfo\\Translation";
1432 UINT len1,len2;
1434 len1=len2=40;
1436 /* compare file versions */
1437 if ((destvffi->dwFileVersionMS > tmpvffi->dwFileVersionMS)||
1438 ((destvffi->dwFileVersionMS==tmpvffi->dwFileVersionMS)&&
1439 (destvffi->dwFileVersionLS > tmpvffi->dwFileVersionLS)
1442 xret |= VIF_MISMATCH|VIF_SRCOLD;
1443 /* compare filetypes and filesubtypes */
1444 if ((destvffi->dwFileType!=tmpvffi->dwFileType) ||
1445 (destvffi->dwFileSubtype!=tmpvffi->dwFileSubtype)
1447 xret |= VIF_MISMATCH|VIF_DIFFTYPE;
1448 if (VerQueryValueA(buf1,trans_array,(LPVOID*)&tbuf1,&len1) &&
1449 VerQueryValueA(buf2,trans_array,(LPVOID*)&tbuf2,&len2)
1451 /* Do something with tbuf1 and tbuf2
1452 * generates DIFFLANG|MISMATCH
1455 HeapFree(GetProcessHeap(), 0, buf2);
1456 } else
1457 xret=VIF_MISMATCH|VIF_SRCOLD;
1458 HeapFree(GetProcessHeap(), 0, buf1);
1461 if (xret) {
1462 if (*tmpfilelen<strlen(tmpfn+tmplast)) {
1463 xret|=VIF_BUFFTOOSMALL;
1464 DeleteFileA(tmpfn);
1465 } else {
1466 strcpy(tmpfile,tmpfn+tmplast);
1467 *tmpfilelen = strlen(tmpfn+tmplast)+1;
1468 xret|=VIF_TEMPFILE;
1470 } else {
1471 if (INVALID_FILE_ATTRIBUTES!=GetFileAttributesA(destfn))
1472 if (!DeleteFileA(destfn)) {
1473 xret|=_error2vif(GetLastError())|VIF_CANNOTDELETE;
1474 DeleteFileA(tmpfn);
1475 LZClose(hfsrc);
1476 return xret;
1478 if ((!(flags & VIFF_DONTDELETEOLD)) &&
1479 curdir &&
1480 *curdir &&
1481 lstrcmpiA(curdir,pdest)
1483 char curfn[260];
1485 sprintf(curfn,"%s\\%s",curdir,destfilename);
1486 if (INVALID_FILE_ATTRIBUTES != GetFileAttributesA(curfn)) {
1487 /* FIXME: check if in use ... if it is, VIF_CANNOTDELETECUR */
1488 if (!DeleteFileA(curfn))
1489 xret|=_error2vif(GetLastError())|VIF_CANNOTDELETECUR;
1492 if (!MoveFileA(tmpfn,destfn)) {
1493 xret|=_error2vif(GetLastError())|VIF_CANNOTRENAME;
1494 DeleteFileA(tmpfn);
1497 LZClose(hfsrc);
1498 return xret;
1502 /******************************************************************************
1503 * VerInstallFileW [VERSION.@]
1505 DWORD WINAPI VerInstallFileW(
1506 DWORD flags,LPCWSTR srcfilename,LPCWSTR destfilename,LPCWSTR srcdir,
1507 LPCWSTR destdir,LPCWSTR curdir,LPWSTR tmpfile,PUINT tmpfilelen )
1509 LPSTR wsrcf = NULL, wsrcd = NULL, wdestf = NULL, wdestd = NULL, wtmpf = NULL, wcurd = NULL;
1510 DWORD ret = 0;
1511 UINT len;
1513 if (srcfilename)
1515 len = WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, NULL, 0, NULL, NULL );
1516 if ((wsrcf = HeapAlloc( GetProcessHeap(), 0, len )))
1517 WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, wsrcf, len, NULL, NULL );
1518 else
1519 ret = VIF_OUTOFMEMORY;
1521 if (srcdir && !ret)
1523 len = WideCharToMultiByte( CP_ACP, 0, srcdir, -1, NULL, 0, NULL, NULL );
1524 if ((wsrcd = HeapAlloc( GetProcessHeap(), 0, len )))
1525 WideCharToMultiByte( CP_ACP, 0, srcdir, -1, wsrcd, len, NULL, NULL );
1526 else
1527 ret = VIF_OUTOFMEMORY;
1529 if (destfilename && !ret)
1531 len = WideCharToMultiByte( CP_ACP, 0, destfilename, -1, NULL, 0, NULL, NULL );
1532 if ((wdestf = HeapAlloc( GetProcessHeap(), 0, len )))
1533 WideCharToMultiByte( CP_ACP, 0, destfilename, -1, wdestf, len, NULL, NULL );
1534 else
1535 ret = VIF_OUTOFMEMORY;
1537 if (destdir && !ret)
1539 len = WideCharToMultiByte( CP_ACP, 0, destdir, -1, NULL, 0, NULL, NULL );
1540 if ((wdestd = HeapAlloc( GetProcessHeap(), 0, len )))
1541 WideCharToMultiByte( CP_ACP, 0, destdir, -1, wdestd, len, NULL, NULL );
1542 else
1543 ret = VIF_OUTOFMEMORY;
1545 if (curdir && !ret)
1547 len = WideCharToMultiByte( CP_ACP, 0, curdir, -1, NULL, 0, NULL, NULL );
1548 if ((wcurd = HeapAlloc( GetProcessHeap(), 0, len )))
1549 WideCharToMultiByte( CP_ACP, 0, curdir, -1, wcurd, len, NULL, NULL );
1550 else
1551 ret = VIF_OUTOFMEMORY;
1553 if (!ret)
1555 len = *tmpfilelen * sizeof(WCHAR);
1556 wtmpf = HeapAlloc( GetProcessHeap(), 0, len );
1557 if (!wtmpf)
1558 ret = VIF_OUTOFMEMORY;
1560 if (!ret)
1561 ret = VerInstallFileA(flags,wsrcf,wdestf,wsrcd,wdestd,wcurd,wtmpf,&len);
1562 if (!ret)
1563 *tmpfilelen = MultiByteToWideChar( CP_ACP, 0, wtmpf, -1, tmpfile, *tmpfilelen );
1564 else if (ret & VIF_BUFFTOOSMALL)
1565 *tmpfilelen = len; /* FIXME: not correct */
1567 HeapFree( GetProcessHeap(), 0, wsrcf );
1568 HeapFree( GetProcessHeap(), 0, wsrcd );
1569 HeapFree( GetProcessHeap(), 0, wdestf );
1570 HeapFree( GetProcessHeap(), 0, wdestd );
1571 HeapFree( GetProcessHeap(), 0, wtmpf );
1572 HeapFree( GetProcessHeap(), 0, wcurd );
1573 return ret;