ntdll/tests: Wait for async I/O to complete when the result is STATUS_PENDING.
[wine.git] / dlls / version / version.c
blob1e581ee7d2a473dccddd9a1728c1566ee53690da
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 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 if(lpszAppDir)
1137 destDir = lpszAppDir;
1138 if(lpszFilename)
1140 if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir;
1141 else if(testFileExistenceA(systemDir, lpszFilename, FALSE))
1143 curDir = systemDir;
1144 retval |= VFF_CURNEDEST;
1150 /* Check to see if the file exists and is in use by another application */
1151 if (lpszFilename && testFileExistenceA(curDir, lpszFilename, FALSE)) {
1152 if (lpszFilename && !testFileExistenceA(curDir, lpszFilename, TRUE))
1153 retval |= VFF_FILEINUSE;
1156 curDirSizeReq = strlen(curDir) + 1;
1157 destDirSizeReq = strlen(destDir) + 1;
1159 /* Make sure that the pointers to the size of the buffers are
1160 valid; if not, do NOTHING with that buffer. If that pointer
1161 is valid, then make sure that the buffer pointer is valid, too! */
1163 if(lpuDestDirLen && lpszDestDir)
1165 if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1166 lstrcpynA(lpszDestDir, destDir, *lpuDestDirLen);
1167 *lpuDestDirLen = destDirSizeReq;
1169 if(lpuCurDirLen && lpszCurDir)
1171 if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1172 lstrcpynA(lpszCurDir, curDir, *lpuCurDirLen);
1173 *lpuCurDirLen = curDirSizeReq;
1176 TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval,
1177 (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "",
1178 (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "",
1179 (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "",
1180 debugstr_a(lpszCurDir), debugstr_a(lpszDestDir));
1182 return retval;
1185 /*****************************************************************************
1186 * VerFindFileW [VERSION.@]
1188 DWORD WINAPI VerFindFileW( DWORD flags,LPCWSTR lpszFilename,LPCWSTR lpszWinDir,
1189 LPCWSTR lpszAppDir, LPWSTR lpszCurDir,PUINT lpuCurDirLen,
1190 LPWSTR lpszDestDir,PUINT lpuDestDirLen )
1192 static const WCHAR emptyW;
1193 DWORD retval = 0;
1194 const WCHAR *curDir;
1195 const WCHAR *destDir;
1196 unsigned int curDirSizeReq;
1197 unsigned int destDirSizeReq;
1198 WCHAR systemDir[MAX_PATH];
1200 /* Print out debugging information */
1201 TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n",
1202 flags, debugstr_w(lpszFilename), debugstr_w(lpszWinDir), debugstr_w(lpszAppDir),
1203 lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0,
1204 lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 );
1206 /* Figure out where the file should go; shared files default to the
1207 system directory */
1209 GetSystemDirectoryW(systemDir, sizeof(systemDir)/sizeof(WCHAR));
1210 curDir = &emptyW;
1211 destDir = &emptyW;
1213 if(flags & VFFF_ISSHAREDFILE)
1215 destDir = systemDir;
1216 /* Were we given a filename? If so, try to find the file. */
1217 if(lpszFilename)
1219 if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir;
1220 else if(lpszAppDir && testFileExistenceW(lpszAppDir, lpszFilename, FALSE))
1222 curDir = lpszAppDir;
1223 retval |= VFF_CURNEDEST;
1227 else /* not a shared file */
1229 if(lpszAppDir)
1231 destDir = lpszAppDir;
1232 if(lpszFilename)
1234 if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir;
1235 else if(testFileExistenceW(systemDir, lpszFilename, FALSE))
1237 curDir = systemDir;
1238 retval |= VFF_CURNEDEST;
1244 if (lpszFilename && !testFileExistenceW(curDir, lpszFilename, TRUE))
1245 retval |= VFF_FILEINUSE;
1247 curDirSizeReq = strlenW(curDir) + 1;
1248 destDirSizeReq = strlenW(destDir) + 1;
1250 /* Make sure that the pointers to the size of the buffers are
1251 valid; if not, do NOTHING with that buffer. If that pointer
1252 is valid, then make sure that the buffer pointer is valid, too! */
1254 if(lpuDestDirLen && lpszDestDir)
1256 if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1257 lstrcpynW(lpszDestDir, destDir, *lpuDestDirLen);
1258 *lpuDestDirLen = destDirSizeReq;
1260 if(lpuCurDirLen && lpszCurDir)
1262 if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1263 lstrcpynW(lpszCurDir, curDir, *lpuCurDirLen);
1264 *lpuCurDirLen = curDirSizeReq;
1267 TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval,
1268 (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "",
1269 (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "",
1270 (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "",
1271 debugstr_w(lpszCurDir), debugstr_w(lpszDestDir));
1272 return retval;
1275 static LPBYTE
1276 _fetch_versioninfo(LPSTR fn,VS_FIXEDFILEINFO **vffi) {
1277 DWORD alloclen;
1278 LPBYTE buf;
1279 DWORD ret;
1281 alloclen = 1000;
1282 buf=HeapAlloc(GetProcessHeap(), 0, alloclen);
1283 if(buf == NULL) {
1284 WARN("Memory exausted while fetching version info!\n");
1285 return NULL;
1287 while (1) {
1288 ret = GetFileVersionInfoA(fn,0,alloclen,buf);
1289 if (!ret) {
1290 HeapFree(GetProcessHeap(), 0, buf);
1291 return NULL;
1293 if (alloclen<*(WORD*)buf) {
1294 alloclen = *(WORD*)buf;
1295 HeapFree(GetProcessHeap(), 0, buf);
1296 buf = HeapAlloc(GetProcessHeap(), 0, alloclen);
1297 if(buf == NULL) {
1298 WARN("Memory exausted while fetching version info!\n");
1299 return NULL;
1301 } else {
1302 *vffi = (VS_FIXEDFILEINFO*)(buf+0x14);
1303 if ((*vffi)->dwSignature == 0x004f0049) /* hack to detect unicode */
1304 *vffi = (VS_FIXEDFILEINFO*)(buf+0x28);
1305 if ((*vffi)->dwSignature != VS_FFI_SIGNATURE)
1306 WARN("Bad VS_FIXEDFILEINFO signature 0x%08x\n",(*vffi)->dwSignature);
1307 return buf;
1312 static DWORD
1313 _error2vif(DWORD error) {
1314 switch (error) {
1315 case ERROR_ACCESS_DENIED:
1316 return VIF_ACCESSVIOLATION;
1317 case ERROR_SHARING_VIOLATION:
1318 return VIF_SHARINGVIOLATION;
1319 default:
1320 return 0;
1325 /******************************************************************************
1326 * VerInstallFileA [VERSION.@]
1328 DWORD WINAPI VerInstallFileA(
1329 DWORD flags,LPCSTR srcfilename,LPCSTR destfilename,LPCSTR srcdir,
1330 LPCSTR destdir,LPCSTR curdir,LPSTR tmpfile,PUINT tmpfilelen )
1332 LPCSTR pdest;
1333 char destfn[260],tmpfn[260],srcfn[260];
1334 HFILE hfsrc,hfdst;
1335 DWORD attr,xret,tmplast;
1336 LONG ret;
1337 LPBYTE buf1,buf2;
1338 OFSTRUCT ofs;
1340 TRACE("(%x,%s,%s,%s,%s,%s,%p,%d)\n",
1341 flags,srcfilename,destfilename,srcdir,destdir,curdir,tmpfile,*tmpfilelen
1343 xret = 0;
1344 sprintf(srcfn,"%s\\%s",srcdir,srcfilename);
1345 if (!destdir || !*destdir) pdest = srcdir;
1346 else pdest = destdir;
1347 sprintf(destfn,"%s\\%s",pdest,destfilename);
1348 hfsrc=LZOpenFileA(srcfn,&ofs,OF_READ);
1349 if (hfsrc < 0)
1350 return VIF_CANNOTREADSRC;
1351 sprintf(tmpfn,"%s\\%s",pdest,destfilename);
1352 tmplast=strlen(pdest)+1;
1353 attr = GetFileAttributesA(tmpfn);
1354 if (attr != INVALID_FILE_ATTRIBUTES) {
1355 if (attr & FILE_ATTRIBUTE_READONLY) {
1356 LZClose(hfsrc);
1357 return VIF_WRITEPROT;
1359 /* FIXME: check if file currently in use and return VIF_FILEINUSE */
1361 attr = INVALID_FILE_ATTRIBUTES;
1362 if (flags & VIFF_FORCEINSTALL) {
1363 if (tmpfile[0]) {
1364 sprintf(tmpfn,"%s\\%s",pdest,tmpfile);
1365 tmplast = strlen(pdest)+1;
1366 attr = GetFileAttributesA(tmpfn);
1367 /* if it exists, it has been copied by the call before.
1368 * we jump over the copy part...
1372 if (attr == INVALID_FILE_ATTRIBUTES) {
1373 char *s;
1375 GetTempFileNameA(pdest,"ver",0,tmpfn); /* should not fail ... */
1376 s=strrchr(tmpfn,'\\');
1377 if (s)
1378 tmplast = s-tmpfn;
1379 else
1380 tmplast = 0;
1381 hfdst = OpenFile(tmpfn,&ofs,OF_CREATE);
1382 if (hfdst == HFILE_ERROR) {
1383 LZClose(hfsrc);
1384 return VIF_CANNOTCREATE; /* | translated dos error */
1386 ret = LZCopy(hfsrc,hfdst);
1387 _lclose(hfdst);
1388 if (ret < 0) {
1389 /* translate LZ errors into VIF_xxx */
1390 switch (ret) {
1391 case LZERROR_BADINHANDLE:
1392 case LZERROR_READ:
1393 case LZERROR_BADVALUE:
1394 case LZERROR_UNKNOWNALG:
1395 xret = VIF_CANNOTREADSRC;
1396 break;
1397 case LZERROR_BADOUTHANDLE:
1398 case LZERROR_WRITE:
1399 xret = VIF_OUTOFSPACE;
1400 break;
1401 case LZERROR_GLOBALLOC:
1402 case LZERROR_GLOBLOCK:
1403 xret = VIF_OUTOFMEMORY;
1404 break;
1405 default: /* unknown error, should not happen */
1406 FIXME("Unknown LZCopy error %d, ignoring.\n", ret);
1407 xret = 0;
1408 break;
1410 if (xret) {
1411 LZClose(hfsrc);
1412 return xret;
1416 if (!(flags & VIFF_FORCEINSTALL)) {
1417 VS_FIXEDFILEINFO *destvffi,*tmpvffi;
1418 buf1 = _fetch_versioninfo(destfn,&destvffi);
1419 if (buf1) {
1420 buf2 = _fetch_versioninfo(tmpfn,&tmpvffi);
1421 if (buf2) {
1422 char *tbuf1,*tbuf2;
1423 static const CHAR trans_array[] = "\\VarFileInfo\\Translation";
1424 UINT len1,len2;
1426 len1=len2=40;
1428 /* compare file versions */
1429 if ((destvffi->dwFileVersionMS > tmpvffi->dwFileVersionMS)||
1430 ((destvffi->dwFileVersionMS==tmpvffi->dwFileVersionMS)&&
1431 (destvffi->dwFileVersionLS > tmpvffi->dwFileVersionLS)
1434 xret |= VIF_MISMATCH|VIF_SRCOLD;
1435 /* compare filetypes and filesubtypes */
1436 if ((destvffi->dwFileType!=tmpvffi->dwFileType) ||
1437 (destvffi->dwFileSubtype!=tmpvffi->dwFileSubtype)
1439 xret |= VIF_MISMATCH|VIF_DIFFTYPE;
1440 if (VerQueryValueA(buf1,trans_array,(LPVOID*)&tbuf1,&len1) &&
1441 VerQueryValueA(buf2,trans_array,(LPVOID*)&tbuf2,&len2)
1443 /* Do something with tbuf1 and tbuf2
1444 * generates DIFFLANG|MISMATCH
1447 HeapFree(GetProcessHeap(), 0, buf2);
1448 } else
1449 xret=VIF_MISMATCH|VIF_SRCOLD;
1450 HeapFree(GetProcessHeap(), 0, buf1);
1453 if (xret) {
1454 if (*tmpfilelen<strlen(tmpfn+tmplast)) {
1455 xret|=VIF_BUFFTOOSMALL;
1456 DeleteFileA(tmpfn);
1457 } else {
1458 strcpy(tmpfile,tmpfn+tmplast);
1459 *tmpfilelen = strlen(tmpfn+tmplast)+1;
1460 xret|=VIF_TEMPFILE;
1462 } else {
1463 if (INVALID_FILE_ATTRIBUTES!=GetFileAttributesA(destfn))
1464 if (!DeleteFileA(destfn)) {
1465 xret|=_error2vif(GetLastError())|VIF_CANNOTDELETE;
1466 DeleteFileA(tmpfn);
1467 LZClose(hfsrc);
1468 return xret;
1470 if ((!(flags & VIFF_DONTDELETEOLD)) &&
1471 curdir &&
1472 *curdir &&
1473 lstrcmpiA(curdir,pdest)
1475 char curfn[260];
1477 sprintf(curfn,"%s\\%s",curdir,destfilename);
1478 if (INVALID_FILE_ATTRIBUTES != GetFileAttributesA(curfn)) {
1479 /* FIXME: check if in use ... if it is, VIF_CANNOTDELETECUR */
1480 if (!DeleteFileA(curfn))
1481 xret|=_error2vif(GetLastError())|VIF_CANNOTDELETECUR;
1484 if (!MoveFileA(tmpfn,destfn)) {
1485 xret|=_error2vif(GetLastError())|VIF_CANNOTRENAME;
1486 DeleteFileA(tmpfn);
1489 LZClose(hfsrc);
1490 return xret;
1494 /******************************************************************************
1495 * VerInstallFileW [VERSION.@]
1497 DWORD WINAPI VerInstallFileW(
1498 DWORD flags,LPCWSTR srcfilename,LPCWSTR destfilename,LPCWSTR srcdir,
1499 LPCWSTR destdir,LPCWSTR curdir,LPWSTR tmpfile,PUINT tmpfilelen )
1501 LPSTR wsrcf = NULL, wsrcd = NULL, wdestf = NULL, wdestd = NULL, wtmpf = NULL, wcurd = NULL;
1502 DWORD ret = 0;
1503 UINT len;
1505 if (srcfilename)
1507 len = WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, NULL, 0, NULL, NULL );
1508 if ((wsrcf = HeapAlloc( GetProcessHeap(), 0, len )))
1509 WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, wsrcf, len, NULL, NULL );
1510 else
1511 ret = VIF_OUTOFMEMORY;
1513 if (srcdir && !ret)
1515 len = WideCharToMultiByte( CP_ACP, 0, srcdir, -1, NULL, 0, NULL, NULL );
1516 if ((wsrcd = HeapAlloc( GetProcessHeap(), 0, len )))
1517 WideCharToMultiByte( CP_ACP, 0, srcdir, -1, wsrcd, len, NULL, NULL );
1518 else
1519 ret = VIF_OUTOFMEMORY;
1521 if (destfilename && !ret)
1523 len = WideCharToMultiByte( CP_ACP, 0, destfilename, -1, NULL, 0, NULL, NULL );
1524 if ((wdestf = HeapAlloc( GetProcessHeap(), 0, len )))
1525 WideCharToMultiByte( CP_ACP, 0, destfilename, -1, wdestf, len, NULL, NULL );
1526 else
1527 ret = VIF_OUTOFMEMORY;
1529 if (destdir && !ret)
1531 len = WideCharToMultiByte( CP_ACP, 0, destdir, -1, NULL, 0, NULL, NULL );
1532 if ((wdestd = HeapAlloc( GetProcessHeap(), 0, len )))
1533 WideCharToMultiByte( CP_ACP, 0, destdir, -1, wdestd, len, NULL, NULL );
1534 else
1535 ret = VIF_OUTOFMEMORY;
1537 if (curdir && !ret)
1539 len = WideCharToMultiByte( CP_ACP, 0, curdir, -1, NULL, 0, NULL, NULL );
1540 if ((wcurd = HeapAlloc( GetProcessHeap(), 0, len )))
1541 WideCharToMultiByte( CP_ACP, 0, curdir, -1, wcurd, len, NULL, NULL );
1542 else
1543 ret = VIF_OUTOFMEMORY;
1545 if (!ret)
1547 len = *tmpfilelen * sizeof(WCHAR);
1548 wtmpf = HeapAlloc( GetProcessHeap(), 0, len );
1549 if (!wtmpf)
1550 ret = VIF_OUTOFMEMORY;
1552 if (!ret)
1553 ret = VerInstallFileA(flags,wsrcf,wdestf,wsrcd,wdestd,wcurd,wtmpf,&len);
1554 if (!ret)
1555 *tmpfilelen = MultiByteToWideChar( CP_ACP, 0, wtmpf, -1, tmpfile, *tmpfilelen );
1556 else if (ret & VIF_BUFFTOOSMALL)
1557 *tmpfilelen = len; /* FIXME: not correct */
1559 HeapFree( GetProcessHeap(), 0, wsrcf );
1560 HeapFree( GetProcessHeap(), 0, wsrcd );
1561 HeapFree( GetProcessHeap(), 0, wdestf );
1562 HeapFree( GetProcessHeap(), 0, wdestd );
1563 HeapFree( GetProcessHeap(), 0, wtmpf );
1564 HeapFree( GetProcessHeap(), 0, wcurd );
1565 return ret;