xref: /reactos/dll/win32/version/version.c (revision 551c7412)
1 /*
2  * Implementation of VERSION.DLL
3  *
4  * Copyright 1996,1997 Marcus Meissner
5  * Copyright 1997 David Cuthbert
6  * Copyright 1999 Ulrich Weigand
7  * Copyright 2005 Paul Vriens
8  *
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.
13  *
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.
18  *
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
22  *
23  */
24 
25 #include <stdarg.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <stdio.h>
29 
30 #include <sys/types.h>
31 
32 #define NONAMELESSUNION
33 #define NONAMELESSSTRUCT
34 #include "windef.h"
35 #include "winbase.h"
36 #include "winver.h"
37 #include "winuser.h"
38 #include "winnls.h"
39 #include "wine/winternl.h"
40 #include "lzexpand.h"
41 #include "winerror.h"
42 #include "wine/debug.h"
43 
44 
45 WINE_DEFAULT_DEBUG_CHANNEL(ver);
46 
47 typedef struct
48 {
49     WORD offset;
50     WORD length;
51     WORD flags;
52     WORD id;
53     WORD handle;
54     WORD usage;
55 } NE_NAMEINFO;
56 
57 typedef struct
58 {
59     WORD  type_id;
60     WORD  count;
61     DWORD resloader;
62 } NE_TYPEINFO;
63 
64 /**********************************************************************
65  *  find_entry_by_id
66  *
67  * Find an entry by id in a resource directory
68  * Copied from loader/pe_resource.c
69  */
find_entry_by_id(const IMAGE_RESOURCE_DIRECTORY * dir,WORD id,const void * root)70 static const IMAGE_RESOURCE_DIRECTORY *find_entry_by_id( const IMAGE_RESOURCE_DIRECTORY *dir,
71                                                          WORD id, const void *root )
72 {
73     const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
74     int min, max, pos;
75 
76     entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
77     min = dir->NumberOfNamedEntries;
78     max = min + dir->NumberOfIdEntries - 1;
79     while (min <= max)
80     {
81         pos = (min + max) / 2;
82         if (entry[pos].u.Id == id)
83             return (const IMAGE_RESOURCE_DIRECTORY *)((const char *)root + entry[pos].u2.s2.OffsetToDirectory);
84         if (entry[pos].u.Id > id) max = pos - 1;
85         else min = pos + 1;
86     }
87     return NULL;
88 }
89 
90 
91 /**********************************************************************
92  *  find_entry_default
93  *
94  * Find a default entry in a resource directory
95  * Copied from loader/pe_resource.c
96  */
find_entry_default(const IMAGE_RESOURCE_DIRECTORY * dir,const void * root)97 static const IMAGE_RESOURCE_DIRECTORY *find_entry_default( const IMAGE_RESOURCE_DIRECTORY *dir,
98                                                            const void *root )
99 {
100     const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
101 
102     entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
103     return (const IMAGE_RESOURCE_DIRECTORY *)((const char *)root + entry->u2.s2.OffsetToDirectory);
104 }
105 
106 
107 /**********************************************************************
108  *  push_language
109  *
110  * push a language onto the list of languages to try
111  */
push_language(WORD * list,int pos,WORD lang)112 static inline int push_language( WORD *list, int pos, WORD lang )
113 {
114     int i;
115     for (i = 0; i < pos; i++) if (list[i] == lang) return pos;
116     list[pos++] = lang;
117     return pos;
118 }
119 
120 
121 /**********************************************************************
122  *  find_entry_language
123  */
find_entry_language(const IMAGE_RESOURCE_DIRECTORY * dir,const void * root,DWORD flags)124 static const IMAGE_RESOURCE_DIRECTORY *find_entry_language( const IMAGE_RESOURCE_DIRECTORY *dir,
125                                                             const void *root, DWORD flags )
126 {
127     const IMAGE_RESOURCE_DIRECTORY *ret;
128     WORD list[9];
129     int i, pos = 0;
130 
131     if (flags & FILE_VER_GET_LOCALISED)
132     {
133         /* cf. LdrFindResource_U */
134         pos = push_language( list, pos, MAKELANGID( LANG_NEUTRAL, SUBLANG_NEUTRAL ) );
135         pos = push_language( list, pos, LANGIDFROMLCID( NtCurrentTeb()->CurrentLocale ) );
136         pos = push_language( list, pos, GetUserDefaultLangID() );
137         pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(GetUserDefaultLangID()), SUBLANG_NEUTRAL ));
138         pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(GetUserDefaultLangID()), SUBLANG_DEFAULT ));
139         pos = push_language( list, pos, GetSystemDefaultLangID() );
140         pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(GetSystemDefaultLangID()), SUBLANG_NEUTRAL ));
141         pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(GetSystemDefaultLangID()), SUBLANG_DEFAULT ));
142         pos = push_language( list, pos, MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT ) );
143     }
144     else
145     {
146         /* FIXME: resolve LN file here */
147         pos = push_language( list, pos, MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT ) );
148     }
149 
150     for (i = 0; i < pos; i++) if ((ret = find_entry_by_id( dir, list[i], root ))) return ret;
151     return find_entry_default( dir, root );
152 }
153 
154 
155 /***********************************************************************
156  *           read_xx_header         [internal]
157  */
read_xx_header(HFILE lzfd)158 static int read_xx_header( HFILE lzfd )
159 {
160     IMAGE_DOS_HEADER mzh;
161     char magic[3];
162 
163     LZSeek( lzfd, 0, SEEK_SET );
164     if ( sizeof(mzh) != LZRead( lzfd, (LPSTR)&mzh, sizeof(mzh) ) )
165         return 0;
166     if ( mzh.e_magic != IMAGE_DOS_SIGNATURE )
167     {
168         if (!memcmp( &mzh, "\177ELF", 4 )) return 1;  /* ELF */
169         if (*(UINT *)&mzh == 0xfeedface || *(UINT *)&mzh == 0xcefaedfe) return 1;  /* Mach-O */
170         return 0;
171     }
172 
173     LZSeek( lzfd, mzh.e_lfanew, SEEK_SET );
174     if ( 2 != LZRead( lzfd, magic, 2 ) )
175         return 0;
176 
177     LZSeek( lzfd, mzh.e_lfanew, SEEK_SET );
178 
179     if ( magic[0] == 'N' && magic[1] == 'E' )
180         return IMAGE_OS2_SIGNATURE;
181     if ( magic[0] == 'P' && magic[1] == 'E' )
182         return IMAGE_NT_SIGNATURE;
183 
184     magic[2] = '\0';
185     WARN("Can't handle %s files.\n", magic );
186     return 0;
187 }
188 
189 /***********************************************************************
190  *           find_ne_resource         [internal]
191  */
find_ne_resource(HFILE lzfd,DWORD * resLen,DWORD * resOff)192 static BOOL find_ne_resource( HFILE lzfd, DWORD *resLen, DWORD *resOff )
193 {
194     const WORD typeid = VS_FILE_INFO | 0x8000;
195     const WORD resid = VS_VERSION_INFO | 0x8000;
196     IMAGE_OS2_HEADER nehd;
197     NE_TYPEINFO *typeInfo;
198     NE_NAMEINFO *nameInfo;
199     DWORD nehdoffset;
200     LPBYTE resTab;
201     DWORD resTabSize;
202     int count;
203 
204     /* Read in NE header */
205     nehdoffset = LZSeek( lzfd, 0, SEEK_CUR );
206     if ( sizeof(nehd) != LZRead( lzfd, (LPSTR)&nehd, sizeof(nehd) ) ) return FALSE;
207 
208     resTabSize = nehd.ne_restab - nehd.ne_rsrctab;
209     if ( !resTabSize )
210     {
211         TRACE("No resources in NE dll\n" );
212         return FALSE;
213     }
214 
215     /* Read in resource table */
216     resTab = HeapAlloc( GetProcessHeap(), 0, resTabSize );
217     if ( !resTab ) return FALSE;
218 
219     LZSeek( lzfd, nehd.ne_rsrctab + nehdoffset, SEEK_SET );
220     if ( resTabSize != LZRead( lzfd, (char*)resTab, resTabSize ) )
221     {
222         HeapFree( GetProcessHeap(), 0, resTab );
223         return FALSE;
224     }
225 
226     /* Find resource */
227     typeInfo = (NE_TYPEINFO *)(resTab + 2);
228     while (typeInfo->type_id)
229     {
230         if (typeInfo->type_id == typeid) goto found_type;
231         typeInfo = (NE_TYPEINFO *)((char *)(typeInfo + 1) +
232                                    typeInfo->count * sizeof(NE_NAMEINFO));
233     }
234     TRACE("No typeid entry found\n" );
235     HeapFree( GetProcessHeap(), 0, resTab );
236     return FALSE;
237 
238  found_type:
239     nameInfo = (NE_NAMEINFO *)(typeInfo + 1);
240 
241     for (count = typeInfo->count; count > 0; count--, nameInfo++)
242         if (nameInfo->id == resid) goto found_name;
243 
244     TRACE("No resid entry found\n" );
245     HeapFree( GetProcessHeap(), 0, resTab );
246     return FALSE;
247 
248  found_name:
249     /* Return resource data */
250     if ( resLen ) *resLen = nameInfo->length << *(WORD *)resTab;
251     if ( resOff ) *resOff = nameInfo->offset << *(WORD *)resTab;
252 
253     HeapFree( GetProcessHeap(), 0, resTab );
254     return TRUE;
255 }
256 
257 /***********************************************************************
258  *           find_pe_resource         [internal]
259  */
find_pe_resource(HFILE lzfd,DWORD * resLen,DWORD * resOff,DWORD flags)260 static BOOL find_pe_resource( HFILE lzfd, DWORD *resLen, DWORD *resOff, DWORD flags )
261 {
262     union
263     {
264         IMAGE_NT_HEADERS32 nt32;
265         IMAGE_NT_HEADERS64 nt64;
266     } pehd;
267     DWORD pehdoffset;
268     PIMAGE_DATA_DIRECTORY resDataDir;
269     PIMAGE_SECTION_HEADER sections;
270     LPBYTE resSection;
271     DWORD section_size, data_size;
272     const void *resDir;
273     const IMAGE_RESOURCE_DIRECTORY *resPtr;
274     const IMAGE_RESOURCE_DATA_ENTRY *resData;
275     int i, len, nSections;
276     BOOL ret = FALSE;
277 
278     /* Read in PE header */
279     pehdoffset = LZSeek( lzfd, 0, SEEK_CUR );
280     len = LZRead( lzfd, (LPSTR)&pehd, sizeof(pehd) );
281     if (len < sizeof(pehd.nt32.FileHeader)) return FALSE;
282     if (len < sizeof(pehd)) memset( (char *)&pehd + len, 0, sizeof(pehd) - len );
283 
284     switch (pehd.nt32.OptionalHeader.Magic)
285     {
286     case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
287         resDataDir = pehd.nt32.OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_RESOURCE;
288         break;
289     case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
290         resDataDir = pehd.nt64.OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_RESOURCE;
291         break;
292     default:
293         return FALSE;
294     }
295 
296     if ( !resDataDir->Size )
297     {
298         TRACE("No resources in PE dll\n" );
299         return FALSE;
300     }
301 
302     /* Read in section table */
303     nSections = pehd.nt32.FileHeader.NumberOfSections;
304     sections = HeapAlloc( GetProcessHeap(), 0,
305                           nSections * sizeof(IMAGE_SECTION_HEADER) );
306     if ( !sections ) return FALSE;
307 
308     len = FIELD_OFFSET( IMAGE_NT_HEADERS32, OptionalHeader ) + pehd.nt32.FileHeader.SizeOfOptionalHeader;
309     LZSeek( lzfd, pehdoffset + len, SEEK_SET );
310 
311     if ( nSections * sizeof(IMAGE_SECTION_HEADER) !=
312          LZRead( lzfd, (LPSTR)sections, nSections * sizeof(IMAGE_SECTION_HEADER) ) )
313     {
314         HeapFree( GetProcessHeap(), 0, sections );
315         return FALSE;
316     }
317 
318     /* Find resource section */
319     for ( i = 0; i < nSections; i++ )
320         if (    resDataDir->VirtualAddress >= sections[i].VirtualAddress
321              && resDataDir->VirtualAddress <  sections[i].VirtualAddress +
322                                               sections[i].SizeOfRawData )
323             break;
324 
325     if ( i == nSections )
326     {
327         HeapFree( GetProcessHeap(), 0, sections );
328         TRACE("Couldn't find resource section\n" );
329         return FALSE;
330     }
331 
332     /* Read in resource section */
333     data_size = sections[i].SizeOfRawData;
334     section_size = max( data_size, sections[i].Misc.VirtualSize );
335     resSection = HeapAlloc( GetProcessHeap(), 0, section_size );
336     if ( !resSection )
337     {
338         HeapFree( GetProcessHeap(), 0, sections );
339         return FALSE;
340     }
341 
342     LZSeek( lzfd, sections[i].PointerToRawData, SEEK_SET );
343     if (data_size != LZRead( lzfd, (char*)resSection, data_size )) goto done;
344     if (data_size < section_size) memset( (char *)resSection + data_size, 0, section_size - data_size );
345 
346     /* Find resource */
347     resDir = resSection + (resDataDir->VirtualAddress - sections[i].VirtualAddress);
348 
349     resPtr = resDir;
350     resPtr = find_entry_by_id( resPtr, VS_FILE_INFO, resDir );
351     if ( !resPtr )
352     {
353         TRACE("No typeid entry found\n" );
354         goto done;
355     }
356     resPtr = find_entry_by_id( resPtr, VS_VERSION_INFO, resDir );
357     if ( !resPtr )
358     {
359         TRACE("No resid entry found\n" );
360         goto done;
361     }
362     resPtr = find_entry_language( resPtr, resDir, flags );
363     if ( !resPtr )
364     {
365         TRACE("No default language entry found\n" );
366         goto done;
367     }
368 
369     /* Find resource data section */
370     resData = (const IMAGE_RESOURCE_DATA_ENTRY*)resPtr;
371     for ( i = 0; i < nSections; i++ )
372         if (    resData->OffsetToData >= sections[i].VirtualAddress
373              && resData->OffsetToData <  sections[i].VirtualAddress +
374                                          sections[i].SizeOfRawData )
375             break;
376 
377     if ( i == nSections )
378     {
379         TRACE("Couldn't find resource data section\n" );
380         goto done;
381     }
382 
383     /* Return resource data */
384     if ( resLen ) *resLen = resData->Size;
385     if ( resOff ) *resOff = resData->OffsetToData - sections[i].VirtualAddress
386                             + sections[i].PointerToRawData;
387     ret = TRUE;
388 
389  done:
390     HeapFree( GetProcessHeap(), 0, resSection );
391     HeapFree( GetProcessHeap(), 0, sections );
392     return ret;
393 }
394 
395 
396 /***********************************************************************
397  *           find_version_resource         [internal]
398  */
find_version_resource(HFILE lzfd,DWORD * reslen,DWORD * offset,DWORD flags)399 static DWORD find_version_resource( HFILE lzfd, DWORD *reslen, DWORD *offset, DWORD flags )
400 {
401     DWORD magic = read_xx_header( lzfd );
402 
403     switch (magic)
404     {
405     case IMAGE_OS2_SIGNATURE:
406         if (!find_ne_resource( lzfd, reslen, offset )) magic = 0;
407         break;
408     case IMAGE_NT_SIGNATURE:
409         if (!find_pe_resource( lzfd, reslen, offset, flags )) magic = 0;
410         break;
411     }
412     return magic;
413 }
414 
415 /******************************************************************************
416  *
417  *   This function will print via standard TRACE, debug info regarding
418  *   the file info structure vffi.
419  *      15-Feb-1998 Dimitrie Paun (dimi@cs.toronto.edu)
420  *      Added this function to clean up the code.
421  *
422  *****************************************************************************/
print_vffi_debug(const VS_FIXEDFILEINFO * vffi)423 static void print_vffi_debug(const VS_FIXEDFILEINFO *vffi)
424 {
425     BOOL    versioned_printer = FALSE;
426 
427     if((vffi->dwFileType == VFT_DLL) || (vffi->dwFileType == VFT_DRV))
428     {
429         if(vffi->dwFileSubtype == VFT2_DRV_VERSIONED_PRINTER)
430             /* this is documented for newer w2k Drivers and up */
431             versioned_printer = TRUE;
432         else if( (vffi->dwFileSubtype == VFT2_DRV_PRINTER) &&
433                  (vffi->dwFileVersionMS != vffi->dwProductVersionMS) &&
434                  (vffi->dwFileVersionMS > 0) &&
435                  (vffi->dwFileVersionMS <= 3) )
436             /* found this on NT 3.51, NT4.0 and old w2k Drivers */
437             versioned_printer = TRUE;
438     }
439 
440     TRACE("structversion=%u.%u, ",
441             HIWORD(vffi->dwStrucVersion),LOWORD(vffi->dwStrucVersion));
442     if(versioned_printer)
443     {
444         WORD mode = LOWORD(vffi->dwFileVersionMS);
445         WORD ver_rev = HIWORD(vffi->dwFileVersionLS);
446         TRACE("fileversion=%u.%u.%u.%u (%s.major.minor.release), ",
447             (vffi->dwFileVersionMS),
448             HIBYTE(ver_rev), LOBYTE(ver_rev), LOWORD(vffi->dwFileVersionLS),
449             (mode == 3) ? "Usermode" : ((mode <= 2) ? "Kernelmode" : "?") );
450     }
451     else
452     {
453         TRACE("fileversion=%u.%u.%u.%u, ",
454             HIWORD(vffi->dwFileVersionMS),LOWORD(vffi->dwFileVersionMS),
455             HIWORD(vffi->dwFileVersionLS),LOWORD(vffi->dwFileVersionLS));
456     }
457     TRACE("productversion=%u.%u.%u.%u\n",
458           HIWORD(vffi->dwProductVersionMS),LOWORD(vffi->dwProductVersionMS),
459           HIWORD(vffi->dwProductVersionLS),LOWORD(vffi->dwProductVersionLS));
460 
461     TRACE("flagmask=0x%x, flags=0x%x %s%s%s%s%s%s\n",
462           vffi->dwFileFlagsMask, vffi->dwFileFlags,
463           (vffi->dwFileFlags & VS_FF_DEBUG) ? "DEBUG," : "",
464           (vffi->dwFileFlags & VS_FF_PRERELEASE) ? "PRERELEASE," : "",
465           (vffi->dwFileFlags & VS_FF_PATCHED) ? "PATCHED," : "",
466           (vffi->dwFileFlags & VS_FF_PRIVATEBUILD) ? "PRIVATEBUILD," : "",
467           (vffi->dwFileFlags & VS_FF_INFOINFERRED) ? "INFOINFERRED," : "",
468           (vffi->dwFileFlags & VS_FF_SPECIALBUILD) ? "SPECIALBUILD," : "");
469 
470     TRACE("(");
471 
472     TRACE("OS=0x%x.0x%x ", HIWORD(vffi->dwFileOS), LOWORD(vffi->dwFileOS));
473 
474     switch (vffi->dwFileOS&0xFFFF0000)
475     {
476     case VOS_DOS:TRACE("DOS,");break;
477     case VOS_OS216:TRACE("OS/2-16,");break;
478     case VOS_OS232:TRACE("OS/2-32,");break;
479     case VOS_NT:TRACE("NT,");break;
480     case VOS_UNKNOWN:
481     default:
482         TRACE("UNKNOWN(0x%x),",vffi->dwFileOS&0xFFFF0000);break;
483     }
484 
485     switch (LOWORD(vffi->dwFileOS))
486     {
487     case VOS__BASE:TRACE("BASE");break;
488     case VOS__WINDOWS16:TRACE("WIN16");break;
489     case VOS__WINDOWS32:TRACE("WIN32");break;
490     case VOS__PM16:TRACE("PM16");break;
491     case VOS__PM32:TRACE("PM32");break;
492     default:
493         TRACE("UNKNOWN(0x%x)",LOWORD(vffi->dwFileOS));break;
494     }
495 
496     TRACE(")\n");
497 
498     switch (vffi->dwFileType)
499     {
500     case VFT_APP:TRACE("filetype=APP");break;
501     case VFT_DLL:
502         TRACE("filetype=DLL");
503         if(vffi->dwFileSubtype != 0)
504         {
505             if(versioned_printer) /* NT3.x/NT4.0 or old w2k Driver  */
506                 TRACE(",PRINTER");
507             TRACE(" (subtype=0x%x)", vffi->dwFileSubtype);
508         }
509         break;
510     case VFT_DRV:
511         TRACE("filetype=DRV,");
512         switch(vffi->dwFileSubtype)
513         {
514         case VFT2_DRV_PRINTER:TRACE("PRINTER");break;
515         case VFT2_DRV_KEYBOARD:TRACE("KEYBOARD");break;
516         case VFT2_DRV_LANGUAGE:TRACE("LANGUAGE");break;
517         case VFT2_DRV_DISPLAY:TRACE("DISPLAY");break;
518         case VFT2_DRV_MOUSE:TRACE("MOUSE");break;
519         case VFT2_DRV_NETWORK:TRACE("NETWORK");break;
520         case VFT2_DRV_SYSTEM:TRACE("SYSTEM");break;
521         case VFT2_DRV_INSTALLABLE:TRACE("INSTALLABLE");break;
522         case VFT2_DRV_SOUND:TRACE("SOUND");break;
523         case VFT2_DRV_COMM:TRACE("COMM");break;
524         case VFT2_DRV_INPUTMETHOD:TRACE("INPUTMETHOD");break;
525         case VFT2_DRV_VERSIONED_PRINTER:TRACE("VERSIONED_PRINTER");break;
526         case VFT2_UNKNOWN:
527         default:
528             TRACE("UNKNOWN(0x%x)",vffi->dwFileSubtype);break;
529         }
530         break;
531     case VFT_FONT:
532         TRACE("filetype=FONT,");
533         switch (vffi->dwFileSubtype)
534         {
535         case VFT2_FONT_RASTER:TRACE("RASTER");break;
536         case VFT2_FONT_VECTOR:TRACE("VECTOR");break;
537         case VFT2_FONT_TRUETYPE:TRACE("TRUETYPE");break;
538         default:TRACE("UNKNOWN(0x%x)",vffi->dwFileSubtype);break;
539         }
540         break;
541     case VFT_VXD:TRACE("filetype=VXD");break;
542     case VFT_STATIC_LIB:TRACE("filetype=STATIC_LIB");break;
543     case VFT_UNKNOWN:
544     default:
545         TRACE("filetype=Unknown(0x%x)",vffi->dwFileType);break;
546     }
547 
548     TRACE("\n");
549     TRACE("filedate=0x%x.0x%x\n",vffi->dwFileDateMS,vffi->dwFileDateLS);
550 }
551 
552 /***********************************************************************
553  * Version Info Structure
554  */
555 
556 typedef struct
557 {
558     WORD  wLength;
559     WORD  wValueLength;
560     CHAR  szKey[1];
561 #if 0   /* variable length structure */
562     /* DWORD aligned */
563     BYTE  Value[];
564     /* DWORD aligned */
565     VS_VERSION_INFO_STRUCT16 Children[];
566 #endif
567 } VS_VERSION_INFO_STRUCT16;
568 
569 typedef struct
570 {
571     WORD  wLength;
572     WORD  wValueLength;
573     WORD  wType; /* 1:Text, 0:Binary */
574     WCHAR szKey[1];
575 #if 0   /* variable length structure */
576     /* DWORD aligned */
577     BYTE  Value[];
578     /* DWORD aligned */
579     VS_VERSION_INFO_STRUCT32 Children[];
580 #endif
581 } VS_VERSION_INFO_STRUCT32;
582 
583 #define VersionInfoIs16( ver ) \
584     ( ((const VS_VERSION_INFO_STRUCT16 *)ver)->szKey[0] >= ' ' )
585 
586 #define DWORD_ALIGN( base, ptr ) \
587     ( (LPBYTE)(base) + ((((LPBYTE)(ptr) - (LPBYTE)(base)) + 3) & ~3) )
588 
589 #define VersionInfo16_Value( ver )  \
590     DWORD_ALIGN( (ver), (ver)->szKey + strlen((ver)->szKey) + 1 )
591 #define VersionInfo32_Value( ver )  \
592     DWORD_ALIGN( (ver), (ver)->szKey + lstrlenW((ver)->szKey) + 1 )
593 
594 #define VersionInfo16_Children( ver )  \
595     (const VS_VERSION_INFO_STRUCT16 *)( VersionInfo16_Value( ver ) + \
596                            ( ( (ver)->wValueLength + 3 ) & ~3 ) )
597 #define VersionInfo32_Children( ver )  \
598     (const VS_VERSION_INFO_STRUCT32 *)( VersionInfo32_Value( ver ) + \
599                            ( ( (ver)->wValueLength * \
600                                ((ver)->wType? 2 : 1) + 3 ) & ~3 ) )
601 
602 #define VersionInfo16_Next( ver ) \
603     (VS_VERSION_INFO_STRUCT16 *)( (LPBYTE)ver + (((ver)->wLength + 3) & ~3) )
604 #define VersionInfo32_Next( ver ) \
605     (VS_VERSION_INFO_STRUCT32 *)( (LPBYTE)ver + (((ver)->wLength + 3) & ~3) )
606 
607 
608 /***********************************************************************
609  *           GetFileVersionInfoSizeW         [VERSION.@]
610  */
GetFileVersionInfoSizeW(LPCWSTR filename,LPDWORD handle)611 DWORD WINAPI GetFileVersionInfoSizeW( LPCWSTR filename, LPDWORD handle )
612 {
613     return GetFileVersionInfoSizeExW( FILE_VER_GET_LOCALISED, filename, handle );
614 }
615 
616 /***********************************************************************
617  *           GetFileVersionInfoSizeA         [VERSION.@]
618  */
GetFileVersionInfoSizeA(LPCSTR filename,LPDWORD handle)619 DWORD WINAPI GetFileVersionInfoSizeA( LPCSTR filename, LPDWORD handle )
620 {
621     return GetFileVersionInfoSizeExA( FILE_VER_GET_LOCALISED, filename, handle );
622 }
623 
624 /******************************************************************************
625  *           GetFileVersionInfoSizeExW       [VERSION.@]
626  */
GetFileVersionInfoSizeExW(DWORD flags,LPCWSTR filename,LPDWORD handle)627 DWORD WINAPI GetFileVersionInfoSizeExW( DWORD flags, LPCWSTR filename, LPDWORD handle )
628 {
629     DWORD len, offset, magic = 1;
630     HFILE lzfd;
631     HMODULE hModule;
632     OFSTRUCT ofs;
633 
634     TRACE("(0x%x,%s,%p)\n", flags, debugstr_w(filename), handle );
635 
636     if (handle) *handle = 0;
637 
638     if (!filename)
639     {
640         SetLastError(ERROR_INVALID_PARAMETER);
641         return 0;
642     }
643     if (!*filename)
644     {
645         SetLastError(ERROR_BAD_PATHNAME);
646         return 0;
647     }
648     if (flags & ~FILE_VER_GET_LOCALISED)
649         FIXME("flags 0x%x ignored\n", flags & ~FILE_VER_GET_LOCALISED);
650 
651     if ((lzfd = LZOpenFileW( (LPWSTR)filename, &ofs, OF_READ )) != HFILE_ERROR)
652     {
653         magic = find_version_resource( lzfd, &len, &offset, flags );
654         LZClose( lzfd );
655     }
656 
657     if ((magic == 1) && (hModule = LoadLibraryExW( filename, 0, LOAD_LIBRARY_AS_DATAFILE )))
658     {
659         HRSRC hRsrc = NULL;
660         if (!(flags & FILE_VER_GET_LOCALISED))
661         {
662             LANGID english = MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT );
663             hRsrc = FindResourceExW( hModule, MAKEINTRESOURCEW(VS_VERSION_INFO),
664                                      (LPWSTR)VS_FILE_INFO, english );
665         }
666         if (!hRsrc)
667             hRsrc = FindResourceW( hModule, MAKEINTRESOURCEW(VS_VERSION_INFO),
668                                    (LPWSTR)VS_FILE_INFO );
669         if (hRsrc)
670         {
671             magic = IMAGE_NT_SIGNATURE;
672             len = SizeofResource( hModule, hRsrc );
673         }
674         FreeLibrary( hModule );
675     }
676 
677     switch (magic)
678     {
679     case IMAGE_OS2_SIGNATURE:
680         /* We have a 16bit resource.
681          *
682          * XP/W2K/W2K3 uses a buffer which is more than the actual needed space:
683          *
684          * (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4
685          *
686          * This extra buffer is used for ANSI to Unicode conversions in W-Calls.
687          * info->wLength should be the same as len. Currently it isn't but that
688          * doesn't seem to be a problem (len is bigger than info->wLength).
689          */
690         SetLastError(0);
691         return (len - sizeof(VS_FIXEDFILEINFO)) * 4;
692 
693     case IMAGE_NT_SIGNATURE:
694         /* We have a 32bit resource.
695          *
696          * XP/W2K/W2K3 uses a buffer which is 2 times the actual needed space + 4 bytes "FE2X"
697          * This extra buffer is used for Unicode to ANSI conversions in A-Calls
698          */
699         SetLastError(0);
700         return (len * 2) + 4;
701 
702     default:
703         if (lzfd == HFILE_ERROR)
704             SetLastError(ofs.nErrCode);
705         else if (GetVersion() & 0x80000000) /* Windows 95/98 */
706             SetLastError(ERROR_FILE_NOT_FOUND);
707         else
708             SetLastError(ERROR_RESOURCE_DATA_NOT_FOUND);
709         return 0;
710     }
711 }
712 
713 /******************************************************************************
714  *           GetFileVersionInfoSizeExA       [VERSION.@]
715  */
GetFileVersionInfoSizeExA(DWORD flags,LPCSTR filename,LPDWORD handle)716 DWORD WINAPI GetFileVersionInfoSizeExA( DWORD flags, LPCSTR filename, LPDWORD handle )
717 {
718     UNICODE_STRING filenameW;
719     DWORD retval;
720 
721     TRACE("(0x%x,%s,%p)\n", flags, debugstr_a(filename), handle );
722 
723     if(filename)
724         RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
725     else
726         filenameW.Buffer = NULL;
727 
728     retval = GetFileVersionInfoSizeExW(flags, filenameW.Buffer, handle);
729 
730     RtlFreeUnicodeString(&filenameW);
731 
732     return retval;
733 }
734 
735 /***********************************************************************
736  *           GetFileVersionInfoExW           [VERSION.@]
737  */
GetFileVersionInfoExW(DWORD flags,LPCWSTR filename,DWORD handle,DWORD datasize,LPVOID data)738 BOOL WINAPI GetFileVersionInfoExW( DWORD flags, LPCWSTR filename, DWORD handle, DWORD datasize, LPVOID data )
739 {
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;
746 
747     TRACE("(0x%x,%s,%d,size=%d,data=%p)\n",
748           flags, debugstr_w(filename), handle, datasize, data );
749 
750     if (!data)
751     {
752         SetLastError(ERROR_INVALID_DATA);
753         return FALSE;
754     }
755     if (flags & ~FILE_VER_GET_LOCALISED)
756         FIXME("flags 0x%x ignored\n", flags & ~FILE_VER_GET_LOCALISED);
757 
758     if ((lzfd = LZOpenFileW( (LPWSTR)filename, &ofs, OF_READ )) != HFILE_ERROR)
759     {
760         if ((magic = find_version_resource( lzfd, &len, &offset, flags )) > 1)
761         {
762             LZSeek( lzfd, offset, 0 /* SEEK_SET */ );
763             len = LZRead( lzfd, data, min( len, datasize ) );
764         }
765         LZClose( lzfd );
766     }
767 
768     if ((magic == 1) && (hModule = LoadLibraryExW( filename, 0, LOAD_LIBRARY_AS_DATAFILE )))
769     {
770         HRSRC hRsrc = NULL;
771         if (!(flags & FILE_VER_GET_LOCALISED))
772         {
773             LANGID english = MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT );
774             hRsrc = FindResourceExW( hModule, MAKEINTRESOURCEW(VS_VERSION_INFO),
775                                      (LPWSTR)VS_FILE_INFO, english );
776         }
777         if (!hRsrc)
778             hRsrc = FindResourceW( hModule, MAKEINTRESOURCEW(VS_VERSION_INFO),
779                                    (LPWSTR)VS_FILE_INFO );
780         if (hRsrc)
781         {
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 );
787         }
788         FreeLibrary( hModule );
789     }
790 
791     switch (magic)
792     {
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;
799 
800     case IMAGE_NT_SIGNATURE:
801         /* We have a 32bit resource.
802          *
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
805          */
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;
812 
813     default:
814         SetLastError( lzfd == HFILE_ERROR ? ofs.nErrCode : ERROR_RESOURCE_DATA_NOT_FOUND );
815         return FALSE;
816     }
817 }
818 
819 /***********************************************************************
820  *           GetFileVersionInfoExA           [VERSION.@]
821  */
GetFileVersionInfoExA(DWORD flags,LPCSTR filename,DWORD handle,DWORD datasize,LPVOID data)822 BOOL WINAPI GetFileVersionInfoExA( DWORD flags, LPCSTR filename, DWORD handle, DWORD datasize, LPVOID data )
823 {
824     UNICODE_STRING filenameW;
825     BOOL retval;
826 
827     TRACE("(0x%x,%s,%d,size=%d,data=%p)\n",
828           flags, debugstr_a(filename), handle, datasize, data );
829 
830     if(filename)
831         RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
832     else
833         filenameW.Buffer = NULL;
834 
835     retval = GetFileVersionInfoExW(flags, filenameW.Buffer, handle, datasize, data);
836 
837     RtlFreeUnicodeString(&filenameW);
838 
839     return retval;
840 }
841 
842 /***********************************************************************
843  *           GetFileVersionInfoW             [VERSION.@]
844  */
GetFileVersionInfoW(LPCWSTR filename,DWORD handle,DWORD datasize,LPVOID data)845 BOOL WINAPI GetFileVersionInfoW( LPCWSTR filename, DWORD handle, DWORD datasize, LPVOID data )
846 {
847     return GetFileVersionInfoExW(FILE_VER_GET_LOCALISED, filename, handle, datasize, data);
848 }
849 
850 /***********************************************************************
851  *           GetFileVersionInfoA             [VERSION.@]
852  */
GetFileVersionInfoA(LPCSTR filename,DWORD handle,DWORD datasize,LPVOID data)853 BOOL WINAPI GetFileVersionInfoA( LPCSTR filename, DWORD handle, DWORD datasize, LPVOID data )
854 {
855     return GetFileVersionInfoExA(FILE_VER_GET_LOCALISED, filename, handle, datasize, data);
856 }
857 
858 /***********************************************************************
859  *           VersionInfo16_FindChild             [internal]
860  */
VersionInfo16_FindChild(const VS_VERSION_INFO_STRUCT16 * info,LPCSTR szKey,UINT cbKey)861 static const VS_VERSION_INFO_STRUCT16 *VersionInfo16_FindChild( const VS_VERSION_INFO_STRUCT16 *info,
862                                             LPCSTR szKey, UINT cbKey )
863 {
864     const VS_VERSION_INFO_STRUCT16 *child = VersionInfo16_Children( info );
865 
866     while ((char *)child < (char *)info + info->wLength )
867     {
868         if (!_strnicmp( child->szKey, szKey, cbKey ) && !child->szKey[cbKey])
869             return child;
870 
871 	if (!(child->wLength)) return NULL;
872         child = VersionInfo16_Next( child );
873     }
874 
875     return NULL;
876 }
877 
878 /***********************************************************************
879  *           VersionInfo32_FindChild             [internal]
880  */
VersionInfo32_FindChild(const VS_VERSION_INFO_STRUCT32 * info,LPCWSTR szKey,UINT cbKey)881 static const VS_VERSION_INFO_STRUCT32 *VersionInfo32_FindChild( const VS_VERSION_INFO_STRUCT32 *info,
882                                             LPCWSTR szKey, UINT cbKey )
883 {
884     const VS_VERSION_INFO_STRUCT32 *child = VersionInfo32_Children( info );
885 
886     while ((char *)child < (char *)info + info->wLength )
887     {
888         if (!_wcsnicmp( child->szKey, szKey, cbKey ) && !child->szKey[cbKey])
889             return child;
890 
891         if (!(child->wLength)) return NULL;
892         child = VersionInfo32_Next( child );
893     }
894 
895     return NULL;
896 }
897 
898 /***********************************************************************
899  *           VersionInfo16_QueryValue              [internal]
900  *
901  *    Gets a value from a 16-bit NE resource
902  */
VersionInfo16_QueryValue(const VS_VERSION_INFO_STRUCT16 * info,LPCSTR lpSubBlock,LPVOID * lplpBuffer,UINT * puLen)903 static BOOL VersionInfo16_QueryValue( const VS_VERSION_INFO_STRUCT16 *info, LPCSTR lpSubBlock,
904                                LPVOID *lplpBuffer, UINT *puLen )
905 {
906     while ( *lpSubBlock )
907     {
908         /* Find next path component */
909         LPCSTR lpNextSlash;
910         for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ )
911             if ( *lpNextSlash == '\\' )
912                 break;
913 
914         /* Skip empty components */
915         if ( lpNextSlash == lpSubBlock )
916         {
917             lpSubBlock++;
918             continue;
919         }
920 
921         /* We have a non-empty component: search info for key */
922         info = VersionInfo16_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock );
923         if ( !info )
924         {
925             if (puLen) *puLen = 0 ;
926             SetLastError( ERROR_RESOURCE_TYPE_NOT_FOUND );
927             return FALSE;
928         }
929 
930         /* Skip path component */
931         lpSubBlock = lpNextSlash;
932     }
933 
934     /* Return value */
935     *lplpBuffer = VersionInfo16_Value( info );
936     if (puLen)
937         *puLen = info->wValueLength;
938 
939     return TRUE;
940 }
941 
942 /***********************************************************************
943  *           VersionInfo32_QueryValue              [internal]
944  *
945  *    Gets a value from a 32-bit PE resource
946  */
VersionInfo32_QueryValue(const VS_VERSION_INFO_STRUCT32 * info,LPCWSTR lpSubBlock,LPVOID * lplpBuffer,UINT * puLen,BOOL * pbText)947 static BOOL VersionInfo32_QueryValue( const VS_VERSION_INFO_STRUCT32 *info, LPCWSTR lpSubBlock,
948                                       LPVOID *lplpBuffer, UINT *puLen, BOOL *pbText )
949 {
950     TRACE("lpSubBlock : (%s)\n", debugstr_w(lpSubBlock));
951 
952     while ( *lpSubBlock )
953     {
954         /* Find next path component */
955         LPCWSTR lpNextSlash;
956         for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ )
957             if ( *lpNextSlash == '\\' )
958                 break;
959 
960         /* Skip empty components */
961         if ( lpNextSlash == lpSubBlock )
962         {
963             lpSubBlock++;
964             continue;
965         }
966 
967         /* We have a non-empty component: search info for key */
968         info = VersionInfo32_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock );
969         if ( !info )
970         {
971             if (puLen) *puLen = 0 ;
972             SetLastError( ERROR_RESOURCE_TYPE_NOT_FOUND );
973             return FALSE;
974         }
975 
976         /* Skip path component */
977         lpSubBlock = lpNextSlash;
978     }
979 
980     /* Return value */
981     *lplpBuffer = VersionInfo32_Value( info );
982 
983 #ifdef __REACTOS__
984     /* If the wValueLength is zero, then set a UNICODE_NULL only return string.
985      * Use the NULL terminator from the key string for that. This is what Windows does, too. */
986     if (!info->wValueLength)
987       *lplpBuffer = (PVOID)(info->szKey + wcslen(info->szKey));
988 #endif
989 
990     if (puLen)
991         *puLen = info->wValueLength;
992     if (pbText)
993         *pbText = info->wType;
994 
995     return TRUE;
996 }
997 
998 /***********************************************************************
999  *           VerQueryValueA              [VERSION.@]
1000  */
VerQueryValueA(LPCVOID pBlock,LPCSTR lpSubBlock,LPVOID * lplpBuffer,PUINT puLen)1001 BOOL WINAPI VerQueryValueA( LPCVOID pBlock, LPCSTR lpSubBlock,
1002                                LPVOID *lplpBuffer, PUINT puLen )
1003 {
1004     static const char rootA[] = "\\";
1005     const VS_VERSION_INFO_STRUCT16 *info = pBlock;
1006 
1007     TRACE("(%p,%s,%p,%p)\n",
1008                 pBlock, debugstr_a(lpSubBlock), lplpBuffer, puLen );
1009 
1010      if (!pBlock)
1011         return FALSE;
1012 
1013     if (lpSubBlock == NULL || lpSubBlock[0] == '\0')
1014         lpSubBlock = rootA;
1015 
1016     if ( !VersionInfoIs16( info ) )
1017     {
1018         BOOL ret, isText;
1019         INT len;
1020         LPWSTR lpSubBlockW;
1021         UINT value_len;
1022 
1023         len  = MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, NULL, 0);
1024         lpSubBlockW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1025 
1026         if (!lpSubBlockW)
1027             return FALSE;
1028 
1029         MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, lpSubBlockW, len);
1030 
1031         ret = VersionInfo32_QueryValue(pBlock, lpSubBlockW, lplpBuffer, &value_len, &isText);
1032         if (puLen) *puLen = value_len;
1033 
1034         HeapFree(GetProcessHeap(), 0, lpSubBlockW);
1035 
1036         if (ret && isText)
1037         {
1038             /* Set lpBuffer so it points to the 'empty' area where we store
1039              * the converted strings
1040              */
1041             LPSTR lpBufferA = (LPSTR)pBlock + info->wLength + 4;
1042             DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock;
1043             len = WideCharToMultiByte(CP_ACP, 0, *lplpBuffer, value_len,
1044                                       lpBufferA + pos, info->wLength - pos, NULL, NULL);
1045             *lplpBuffer = lpBufferA + pos;
1046             if (puLen) *puLen = len;
1047         }
1048         return ret;
1049     }
1050 
1051     return VersionInfo16_QueryValue(info, lpSubBlock, lplpBuffer, puLen);
1052 }
1053 
1054 /***********************************************************************
1055  *           VerQueryValueW              [VERSION.@]
1056  */
VerQueryValueW(LPCVOID pBlock,LPCWSTR lpSubBlock,LPVOID * lplpBuffer,PUINT puLen)1057 BOOL WINAPI VerQueryValueW( LPCVOID pBlock, LPCWSTR lpSubBlock,
1058                                LPVOID *lplpBuffer, PUINT puLen )
1059 {
1060     static const WCHAR rootW[] = { '\\', 0 };
1061     static const WCHAR varfileinfoW[] = { '\\','V','a','r','F','i','l','e','I','n','f','o',
1062                                           '\\','T','r','a','n','s','l','a','t','i','o','n', 0 };
1063 
1064     const VS_VERSION_INFO_STRUCT32 *info = pBlock;
1065 
1066     TRACE("(%p,%s,%p,%p)\n",
1067                 pBlock, debugstr_w(lpSubBlock), lplpBuffer, puLen );
1068 
1069     if (!pBlock)
1070         return FALSE;
1071 
1072     if (!lpSubBlock || !lpSubBlock[0])
1073         lpSubBlock = rootW;
1074 
1075     if ( VersionInfoIs16( info ) )
1076     {
1077         BOOL ret;
1078         int len;
1079         LPSTR lpSubBlockA;
1080 
1081         len = WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, NULL, 0, NULL, NULL);
1082         lpSubBlockA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
1083 
1084         if (!lpSubBlockA)
1085             return FALSE;
1086 
1087         WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, lpSubBlockA, len, NULL, NULL);
1088 
1089         ret = VersionInfo16_QueryValue(pBlock, lpSubBlockA, lplpBuffer, puLen);
1090 
1091         HeapFree(GetProcessHeap(), 0, lpSubBlockA);
1092 
1093         if (ret && wcsicmp( lpSubBlock, rootW ) && wcsicmp( lpSubBlock, varfileinfoW ))
1094         {
1095             /* Set lpBuffer so it points to the 'empty' area where we store
1096              * the converted strings
1097              */
1098             LPWSTR lpBufferW = (LPWSTR)((LPSTR)pBlock + info->wLength);
1099             DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock;
1100             DWORD max = (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4 - info->wLength;
1101 
1102             len = MultiByteToWideChar(CP_ACP, 0, *lplpBuffer, -1,
1103                                       lpBufferW + pos, max/sizeof(WCHAR) - pos );
1104             *lplpBuffer = lpBufferW + pos;
1105             if (puLen) *puLen = len;
1106         }
1107         return ret;
1108     }
1109 
1110     return VersionInfo32_QueryValue(info, lpSubBlock, lplpBuffer, puLen, NULL);
1111 }
1112 
1113 
1114 /******************************************************************************
1115  *   testFileExistenceA
1116  *
1117  *   Tests whether a given path/file combination exists.  If the file does
1118  *   not exist, the return value is zero.  If it does exist, the return
1119  *   value is non-zero.
1120  *
1121  *   Revision history
1122  *      30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
1123  *         Original implementation
1124  *
1125  */
testFileExistenceA(char const * path,char const * file,BOOL excl)1126 static int testFileExistenceA( char const * path, char const * file, BOOL excl )
1127 {
1128     char  filename[1024];
1129     int  filenamelen;
1130     OFSTRUCT  fileinfo;
1131 
1132     fileinfo.cBytes = sizeof(OFSTRUCT);
1133 
1134     if (path)
1135     {
1136         strcpy(filename, path);
1137         filenamelen = strlen(filename);
1138 
1139         /* Add a trailing \ if necessary */
1140         if(filenamelen)
1141         {
1142             if(filename[filenamelen - 1] != '\\')
1143                 strcat(filename, "\\");
1144         }
1145         else /* specify the current directory */
1146             strcpy(filename, ".\\");
1147     }
1148     else
1149         filename[0] = 0;
1150 
1151     /* Create the full pathname */
1152     strcat(filename, file);
1153 
1154     return (OpenFile(filename, &fileinfo,
1155                      OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR);
1156 }
1157 
1158 /******************************************************************************
1159  *   testFileExistenceW
1160  */
testFileExistenceW(const WCHAR * path,const WCHAR * file,BOOL excl)1161 static int testFileExistenceW( const WCHAR *path, const WCHAR *file, BOOL excl )
1162 {
1163     char *filename;
1164     DWORD pathlen, filelen;
1165     int ret;
1166     OFSTRUCT fileinfo;
1167 
1168     fileinfo.cBytes = sizeof(OFSTRUCT);
1169 
1170     pathlen = WideCharToMultiByte( CP_ACP, 0, path, -1, NULL, 0, NULL, NULL );
1171     filelen = WideCharToMultiByte( CP_ACP, 0, file, -1, NULL, 0, NULL, NULL );
1172     filename = HeapAlloc( GetProcessHeap(), 0, pathlen+filelen+2 );
1173 
1174     WideCharToMultiByte( CP_ACP, 0, path, -1, filename, pathlen, NULL, NULL );
1175     /* Add a trailing \ if necessary */
1176     if (pathlen > 1)
1177     {
1178         if (filename[pathlen-2] != '\\') strcpy( &filename[pathlen-1], "\\" );
1179     }
1180     else /* specify the current directory */
1181         strcpy(filename, ".\\");
1182 
1183     WideCharToMultiByte( CP_ACP, 0, file, -1, filename+strlen(filename), filelen, NULL, NULL );
1184 
1185     ret = (OpenFile(filename, &fileinfo,
1186                     OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR);
1187     HeapFree( GetProcessHeap(), 0, filename );
1188     return ret;
1189 }
1190 
1191 /*****************************************************************************
1192  *   VerFindFileA [VERSION.@]
1193  *
1194  *   Determines where to install a file based on whether it locates another
1195  *   version of the file in the system.  The values VerFindFile returns are
1196  *   used in a subsequent call to the VerInstallFile function.
1197  *
1198  *   Revision history:
1199  *      30-May-1997   Dave Cuthbert (dacut@ece.cmu.edu)
1200  *         Reimplementation of VerFindFile from original stub.
1201  */
VerFindFileA(DWORD flags,LPCSTR lpszFilename,LPCSTR lpszWinDir,LPCSTR lpszAppDir,LPSTR lpszCurDir,PUINT lpuCurDirLen,LPSTR lpszDestDir,PUINT lpuDestDirLen)1202 DWORD WINAPI VerFindFileA(
1203     DWORD flags,
1204     LPCSTR lpszFilename,
1205     LPCSTR lpszWinDir,
1206     LPCSTR lpszAppDir,
1207     LPSTR lpszCurDir,
1208     PUINT lpuCurDirLen,
1209     LPSTR lpszDestDir,
1210     PUINT lpuDestDirLen )
1211 {
1212     DWORD  retval = 0;
1213     const char *curDir;
1214     const char *destDir;
1215     unsigned int  curDirSizeReq;
1216     unsigned int  destDirSizeReq;
1217     char winDir[MAX_PATH], systemDir[MAX_PATH];
1218 
1219     /* Print out debugging information */
1220     TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n",
1221           flags, debugstr_a(lpszFilename), debugstr_a(lpszWinDir), debugstr_a(lpszAppDir),
1222           lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0,
1223           lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 );
1224 
1225     /* Figure out where the file should go; shared files default to the
1226        system directory */
1227 
1228     GetSystemDirectoryA(systemDir, sizeof(systemDir));
1229     curDir = "";
1230 
1231     if(flags & VFFF_ISSHAREDFILE)
1232     {
1233         destDir = systemDir;
1234         /* Were we given a filename?  If so, try to find the file. */
1235         if(lpszFilename)
1236         {
1237             if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir;
1238             else if(lpszAppDir && testFileExistenceA(lpszAppDir, lpszFilename, FALSE))
1239                 curDir = lpszAppDir;
1240 
1241             if(!testFileExistenceA(systemDir, lpszFilename, FALSE))
1242                 retval |= VFF_CURNEDEST;
1243         }
1244     }
1245     else /* not a shared file */
1246     {
1247         destDir = lpszAppDir ? lpszAppDir : "";
1248         if(lpszFilename)
1249         {
1250             GetWindowsDirectoryA( winDir, MAX_PATH );
1251             if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir;
1252             else if(testFileExistenceA(winDir, lpszFilename, FALSE))
1253                 curDir = winDir;
1254             else if(testFileExistenceA(systemDir, lpszFilename, FALSE))
1255                 curDir = systemDir;
1256 
1257             if (lpszAppDir && lpszAppDir[0])
1258             {
1259                 if(!testFileExistenceA(lpszAppDir, lpszFilename, FALSE))
1260                     retval |= VFF_CURNEDEST;
1261             }
1262             else if(testFileExistenceA(NULL, lpszFilename, FALSE))
1263                 retval |= VFF_CURNEDEST;
1264         }
1265     }
1266 
1267     /* Check to see if the file exists and is in use by another application */
1268     if (lpszFilename && testFileExistenceA(curDir, lpszFilename, FALSE)) {
1269         if (lpszFilename && !testFileExistenceA(curDir, lpszFilename, TRUE))
1270            retval |= VFF_FILEINUSE;
1271     }
1272 
1273     curDirSizeReq = strlen(curDir) + 1;
1274     destDirSizeReq = strlen(destDir) + 1;
1275 
1276     /* Make sure that the pointers to the size of the buffers are
1277        valid; if not, do NOTHING with that buffer.  If that pointer
1278        is valid, then make sure that the buffer pointer is valid, too! */
1279 
1280     if(lpuDestDirLen && lpszDestDir)
1281     {
1282         if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1283         lstrcpynA(lpszDestDir, destDir, *lpuDestDirLen);
1284         *lpuDestDirLen = destDirSizeReq;
1285     }
1286     if(lpuCurDirLen && lpszCurDir)
1287     {
1288         if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1289         lstrcpynA(lpszCurDir, curDir, *lpuCurDirLen);
1290         *lpuCurDirLen = curDirSizeReq;
1291     }
1292 
1293     TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval,
1294           (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "",
1295           (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "",
1296           (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "",
1297           debugstr_a(lpszCurDir), debugstr_a(lpszDestDir));
1298 
1299     return retval;
1300 }
1301 
1302 /*****************************************************************************
1303  * VerFindFileW						[VERSION.@]
1304  */
VerFindFileW(DWORD flags,LPCWSTR lpszFilename,LPCWSTR lpszWinDir,LPCWSTR lpszAppDir,LPWSTR lpszCurDir,PUINT lpuCurDirLen,LPWSTR lpszDestDir,PUINT lpuDestDirLen)1305 DWORD WINAPI VerFindFileW( DWORD flags,LPCWSTR lpszFilename,LPCWSTR lpszWinDir,
1306                            LPCWSTR lpszAppDir, LPWSTR lpszCurDir,PUINT lpuCurDirLen,
1307                            LPWSTR lpszDestDir,PUINT lpuDestDirLen )
1308 {
1309     static const WCHAR emptyW;
1310     DWORD retval = 0;
1311     const WCHAR *curDir;
1312     const WCHAR *destDir;
1313     unsigned int curDirSizeReq;
1314     unsigned int destDirSizeReq;
1315     WCHAR winDir[MAX_PATH], systemDir[MAX_PATH];
1316 
1317     /* Print out debugging information */
1318     TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n",
1319           flags, debugstr_w(lpszFilename), debugstr_w(lpszWinDir), debugstr_w(lpszAppDir),
1320           lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0,
1321           lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 );
1322 
1323     /* Figure out where the file should go; shared files default to the
1324        system directory */
1325 
1326     GetSystemDirectoryW(systemDir, ARRAY_SIZE(systemDir));
1327     curDir = &emptyW;
1328 
1329     if(flags & VFFF_ISSHAREDFILE)
1330     {
1331         destDir = systemDir;
1332         /* Were we given a filename?  If so, try to find the file. */
1333         if(lpszFilename)
1334         {
1335             if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir;
1336             else if(lpszAppDir && testFileExistenceW(lpszAppDir, lpszFilename, FALSE))
1337             {
1338                 curDir = lpszAppDir;
1339                 retval |= VFF_CURNEDEST;
1340             }
1341         }
1342     }
1343     else /* not a shared file */
1344     {
1345         destDir = lpszAppDir ? lpszAppDir : &emptyW;
1346         if(lpszFilename)
1347         {
1348             GetWindowsDirectoryW( winDir, MAX_PATH );
1349             if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir;
1350             else if(testFileExistenceW(winDir, lpszFilename, FALSE))
1351             {
1352                 curDir = winDir;
1353                 retval |= VFF_CURNEDEST;
1354             }
1355             else if(testFileExistenceW(systemDir, lpszFilename, FALSE))
1356             {
1357                 curDir = systemDir;
1358                 retval |= VFF_CURNEDEST;
1359             }
1360         }
1361     }
1362 
1363     if (lpszFilename && !testFileExistenceW(curDir, lpszFilename, TRUE))
1364         retval |= VFF_FILEINUSE;
1365 
1366     curDirSizeReq = lstrlenW(curDir) + 1;
1367     destDirSizeReq = lstrlenW(destDir) + 1;
1368 
1369     /* Make sure that the pointers to the size of the buffers are
1370        valid; if not, do NOTHING with that buffer.  If that pointer
1371        is valid, then make sure that the buffer pointer is valid, too! */
1372 
1373     if(lpuDestDirLen && lpszDestDir)
1374     {
1375         if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1376         lstrcpynW(lpszDestDir, destDir, *lpuDestDirLen);
1377         *lpuDestDirLen = destDirSizeReq;
1378     }
1379     if(lpuCurDirLen && lpszCurDir)
1380     {
1381         if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1382         lstrcpynW(lpszCurDir, curDir, *lpuCurDirLen);
1383         *lpuCurDirLen = curDirSizeReq;
1384     }
1385 
1386     TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval,
1387           (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "",
1388           (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "",
1389           (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "",
1390           debugstr_w(lpszCurDir), debugstr_w(lpszDestDir));
1391     return retval;
1392 }
1393 
1394 static LPBYTE
_fetch_versioninfo(LPSTR fn,VS_FIXEDFILEINFO ** vffi)1395 _fetch_versioninfo(LPSTR fn,VS_FIXEDFILEINFO **vffi) {
1396     DWORD	alloclen;
1397     LPBYTE	buf;
1398     DWORD	ret;
1399 
1400     alloclen = 1000;
1401     buf=HeapAlloc(GetProcessHeap(), 0, alloclen);
1402     if(buf == NULL) {
1403         WARN("Memory exhausted while fetching version info!\n");
1404         return NULL;
1405     }
1406     while (1) {
1407 	ret = GetFileVersionInfoA(fn,0,alloclen,buf);
1408 	if (!ret) {
1409 	    HeapFree(GetProcessHeap(), 0, buf);
1410 	    return NULL;
1411 	}
1412 	if (alloclen<*(WORD*)buf) {
1413 	    alloclen = *(WORD*)buf;
1414 	    HeapFree(GetProcessHeap(), 0, buf);
1415 	    buf = HeapAlloc(GetProcessHeap(), 0, alloclen);
1416             if(buf == NULL) {
1417                WARN("Memory exhausted while fetching version info!\n");
1418                return NULL;
1419             }
1420 	} else {
1421 	    *vffi = (VS_FIXEDFILEINFO*)(buf+0x14);
1422 	    if ((*vffi)->dwSignature == 0x004f0049) /* hack to detect unicode */
1423 		*vffi = (VS_FIXEDFILEINFO*)(buf+0x28);
1424 	    if ((*vffi)->dwSignature != VS_FFI_SIGNATURE)
1425                 WARN("Bad VS_FIXEDFILEINFO signature 0x%08x\n",(*vffi)->dwSignature);
1426 	    return buf;
1427 	}
1428     }
1429 }
1430 
1431 static DWORD
_error2vif(DWORD error)1432 _error2vif(DWORD error) {
1433     switch (error) {
1434     case ERROR_ACCESS_DENIED:
1435 	return VIF_ACCESSVIOLATION;
1436     case ERROR_SHARING_VIOLATION:
1437 	return VIF_SHARINGVIOLATION;
1438     default:
1439 	return 0;
1440     }
1441 }
1442 
1443 
1444 /******************************************************************************
1445  * VerInstallFileA [VERSION.@]
1446  */
VerInstallFileA(DWORD flags,LPCSTR srcfilename,LPCSTR destfilename,LPCSTR srcdir,LPCSTR destdir,LPCSTR curdir,LPSTR tmpfile,PUINT tmpfilelen)1447 DWORD WINAPI VerInstallFileA(
1448 	DWORD flags,LPCSTR srcfilename,LPCSTR destfilename,LPCSTR srcdir,
1449 	LPCSTR destdir,LPCSTR curdir,LPSTR tmpfile,PUINT tmpfilelen )
1450 {
1451     LPCSTR pdest;
1452     char	destfn[260],tmpfn[260],srcfn[260];
1453     HFILE	hfsrc,hfdst;
1454     DWORD	attr,xret,tmplast;
1455     LONG	ret;
1456     LPBYTE	buf1,buf2;
1457     OFSTRUCT	ofs;
1458 
1459     TRACE("(%x,%s,%s,%s,%s,%s,%p,%d)\n",
1460           flags,debugstr_a(srcfilename),debugstr_a(destfilename),
1461           debugstr_a(srcdir),debugstr_a(destdir),debugstr_a(curdir),
1462           tmpfile,*tmpfilelen);
1463     xret = 0;
1464     if (!srcdir || !srcfilename) return VIF_CANNOTREADSRC;
1465     sprintf(srcfn,"%s\\%s",srcdir,srcfilename);
1466     if (!destdir || !*destdir) pdest = srcdir;
1467     else pdest = destdir;
1468     sprintf(destfn,"%s\\%s",pdest,destfilename);
1469     hfsrc=LZOpenFileA(srcfn,&ofs,OF_READ);
1470     if (hfsrc < 0)
1471 	return VIF_CANNOTREADSRC;
1472     sprintf(tmpfn,"%s\\%s",pdest,destfilename);
1473     tmplast=strlen(pdest)+1;
1474     attr = GetFileAttributesA(tmpfn);
1475     if (attr != INVALID_FILE_ATTRIBUTES) {
1476 	if (attr & FILE_ATTRIBUTE_READONLY) {
1477 	    LZClose(hfsrc);
1478 	    return VIF_WRITEPROT;
1479 	}
1480 	/* FIXME: check if file currently in use and return VIF_FILEINUSE */
1481     }
1482     attr = INVALID_FILE_ATTRIBUTES;
1483     if (flags & VIFF_FORCEINSTALL) {
1484 	if (tmpfile[0]) {
1485 	    sprintf(tmpfn,"%s\\%s",pdest,tmpfile);
1486 	    tmplast = strlen(pdest)+1;
1487 	    attr = GetFileAttributesA(tmpfn);
1488 	    /* if it exists, it has been copied by the call before.
1489 	     * we jump over the copy part...
1490 	     */
1491 	}
1492     }
1493     if (attr == INVALID_FILE_ATTRIBUTES) {
1494 	char	*s;
1495 
1496 	GetTempFileNameA(pdest,"ver",0,tmpfn); /* should not fail ... */
1497 	s=strrchr(tmpfn,'\\');
1498 	if (s)
1499 	    tmplast = s-tmpfn;
1500 	else
1501 	    tmplast = 0;
1502 	hfdst = OpenFile(tmpfn,&ofs,OF_CREATE);
1503 	if (hfdst == HFILE_ERROR) {
1504 	    LZClose(hfsrc);
1505 	    return VIF_CANNOTCREATE; /* | translated dos error */
1506 	}
1507 	ret = LZCopy(hfsrc,hfdst);
1508 	_lclose(hfdst);
1509 	if (ret < 0) {
1510 	    /* translate LZ errors into VIF_xxx */
1511 	    switch (ret) {
1512 	    case LZERROR_BADINHANDLE:
1513 	    case LZERROR_READ:
1514 	    case LZERROR_BADVALUE:
1515 	    case LZERROR_UNKNOWNALG:
1516 		xret = VIF_CANNOTREADSRC;
1517 		break;
1518 	    case LZERROR_BADOUTHANDLE:
1519 	    case LZERROR_WRITE:
1520 		xret = VIF_OUTOFSPACE;
1521 		break;
1522 	    case LZERROR_GLOBALLOC:
1523 	    case LZERROR_GLOBLOCK:
1524 		xret = VIF_OUTOFMEMORY;
1525 		break;
1526 	    default: /* unknown error, should not happen */
1527 		FIXME("Unknown LZCopy error %d, ignoring.\n", ret);
1528 		xret = 0;
1529 		break;
1530 	    }
1531 	    if (xret) {
1532 		LZClose(hfsrc);
1533 		return xret;
1534 	    }
1535 	}
1536     }
1537     if (!(flags & VIFF_FORCEINSTALL)) {
1538 	VS_FIXEDFILEINFO *destvffi,*tmpvffi;
1539 	buf1 = _fetch_versioninfo(destfn,&destvffi);
1540 	if (buf1) {
1541 	    buf2 = _fetch_versioninfo(tmpfn,&tmpvffi);
1542 	    if (buf2) {
1543 		char	*tbuf1,*tbuf2;
1544 		static const CHAR trans_array[] = "\\VarFileInfo\\Translation";
1545 		UINT	len1,len2;
1546 
1547 		len1=len2=40;
1548 
1549 		/* compare file versions */
1550 		if ((destvffi->dwFileVersionMS > tmpvffi->dwFileVersionMS)||
1551 		    ((destvffi->dwFileVersionMS==tmpvffi->dwFileVersionMS)&&
1552 		     (destvffi->dwFileVersionLS > tmpvffi->dwFileVersionLS)
1553 		    )
1554 		)
1555 		    xret |= VIF_MISMATCH|VIF_SRCOLD;
1556 		/* compare filetypes and filesubtypes */
1557 		if ((destvffi->dwFileType!=tmpvffi->dwFileType) ||
1558 		    (destvffi->dwFileSubtype!=tmpvffi->dwFileSubtype)
1559 		)
1560 		    xret |= VIF_MISMATCH|VIF_DIFFTYPE;
1561 		if (VerQueryValueA(buf1,trans_array,(LPVOID*)&tbuf1,&len1) &&
1562 		    VerQueryValueA(buf2,trans_array,(LPVOID*)&tbuf2,&len2)
1563 		) {
1564                     /* Do something with tbuf1 and tbuf2
1565 		     * generates DIFFLANG|MISMATCH
1566 		     */
1567 		}
1568 		HeapFree(GetProcessHeap(), 0, buf2);
1569 	    } else
1570 		xret=VIF_MISMATCH|VIF_SRCOLD;
1571 	    HeapFree(GetProcessHeap(), 0, buf1);
1572 	}
1573     }
1574     if (xret) {
1575 	if (*tmpfilelen<strlen(tmpfn+tmplast)) {
1576 	    xret|=VIF_BUFFTOOSMALL;
1577 	    DeleteFileA(tmpfn);
1578 	} else {
1579 	    strcpy(tmpfile,tmpfn+tmplast);
1580 	    *tmpfilelen = strlen(tmpfn+tmplast)+1;
1581 	    xret|=VIF_TEMPFILE;
1582 	}
1583     } else {
1584 	if (INVALID_FILE_ATTRIBUTES!=GetFileAttributesA(destfn))
1585 	    if (!DeleteFileA(destfn)) {
1586 		xret|=_error2vif(GetLastError())|VIF_CANNOTDELETE;
1587 		DeleteFileA(tmpfn);
1588 		LZClose(hfsrc);
1589 		return xret;
1590 	    }
1591 	if ((!(flags & VIFF_DONTDELETEOLD))	&&
1592 	    curdir				&&
1593 	    *curdir				&&
1594 	    lstrcmpiA(curdir,pdest)
1595 	) {
1596 	    char curfn[260];
1597 
1598 	    sprintf(curfn,"%s\\%s",curdir,destfilename);
1599 	    if (INVALID_FILE_ATTRIBUTES != GetFileAttributesA(curfn)) {
1600 		/* FIXME: check if in use ... if it is, VIF_CANNOTDELETECUR */
1601 		if (!DeleteFileA(curfn))
1602 		    xret|=_error2vif(GetLastError())|VIF_CANNOTDELETECUR;
1603 	    }
1604 	}
1605 	if (!MoveFileA(tmpfn,destfn)) {
1606 	    xret|=_error2vif(GetLastError())|VIF_CANNOTRENAME;
1607 	    DeleteFileA(tmpfn);
1608 	}
1609     }
1610     LZClose(hfsrc);
1611     return xret;
1612 }
1613 
1614 
1615 /******************************************************************************
1616  * VerInstallFileW				[VERSION.@]
1617  */
VerInstallFileW(DWORD flags,LPCWSTR srcfilename,LPCWSTR destfilename,LPCWSTR srcdir,LPCWSTR destdir,LPCWSTR curdir,LPWSTR tmpfile,PUINT tmpfilelen)1618 DWORD WINAPI VerInstallFileW(
1619 	DWORD flags,LPCWSTR srcfilename,LPCWSTR destfilename,LPCWSTR srcdir,
1620 	LPCWSTR destdir,LPCWSTR curdir,LPWSTR tmpfile,PUINT tmpfilelen )
1621 {
1622     LPSTR wsrcf = NULL, wsrcd = NULL, wdestf = NULL, wdestd = NULL, wtmpf = NULL, wcurd = NULL;
1623     DWORD ret = 0;
1624     UINT len;
1625 
1626     if (srcfilename)
1627     {
1628         len = WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, NULL, 0, NULL, NULL );
1629         if ((wsrcf = HeapAlloc( GetProcessHeap(), 0, len )))
1630             WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, wsrcf, len, NULL, NULL );
1631         else
1632             ret = VIF_OUTOFMEMORY;
1633     }
1634     if (srcdir && !ret)
1635     {
1636         len = WideCharToMultiByte( CP_ACP, 0, srcdir, -1, NULL, 0, NULL, NULL );
1637         if ((wsrcd = HeapAlloc( GetProcessHeap(), 0, len )))
1638             WideCharToMultiByte( CP_ACP, 0, srcdir, -1, wsrcd, len, NULL, NULL );
1639         else
1640             ret = VIF_OUTOFMEMORY;
1641     }
1642     if (destfilename && !ret)
1643     {
1644         len = WideCharToMultiByte( CP_ACP, 0, destfilename, -1, NULL, 0, NULL, NULL );
1645         if ((wdestf = HeapAlloc( GetProcessHeap(), 0, len )))
1646             WideCharToMultiByte( CP_ACP, 0, destfilename, -1, wdestf, len, NULL, NULL );
1647         else
1648             ret = VIF_OUTOFMEMORY;
1649     }
1650     if (destdir && !ret)
1651     {
1652         len = WideCharToMultiByte( CP_ACP, 0, destdir, -1, NULL, 0, NULL, NULL );
1653         if ((wdestd = HeapAlloc( GetProcessHeap(), 0, len )))
1654             WideCharToMultiByte( CP_ACP, 0, destdir, -1, wdestd, len, NULL, NULL );
1655         else
1656             ret = VIF_OUTOFMEMORY;
1657     }
1658     if (curdir && !ret)
1659     {
1660         len = WideCharToMultiByte( CP_ACP, 0, curdir, -1, NULL, 0, NULL, NULL );
1661         if ((wcurd = HeapAlloc( GetProcessHeap(), 0, len )))
1662             WideCharToMultiByte( CP_ACP, 0, curdir, -1, wcurd, len, NULL, NULL );
1663         else
1664             ret = VIF_OUTOFMEMORY;
1665     }
1666     if (!ret)
1667     {
1668         len = *tmpfilelen * sizeof(WCHAR);
1669         wtmpf = HeapAlloc( GetProcessHeap(), 0, len );
1670         if (!wtmpf)
1671             ret = VIF_OUTOFMEMORY;
1672     }
1673     if (!ret)
1674         ret = VerInstallFileA(flags,wsrcf,wdestf,wsrcd,wdestd,wcurd,wtmpf,&len);
1675     if (!ret)
1676         *tmpfilelen = MultiByteToWideChar( CP_ACP, 0, wtmpf, -1, tmpfile, *tmpfilelen );
1677     else if (ret & VIF_BUFFTOOSMALL)
1678         *tmpfilelen = len;  /* FIXME: not correct */
1679 
1680     HeapFree( GetProcessHeap(), 0, wsrcf );
1681     HeapFree( GetProcessHeap(), 0, wsrcd );
1682     HeapFree( GetProcessHeap(), 0, wdestf );
1683     HeapFree( GetProcessHeap(), 0, wdestd );
1684     HeapFree( GetProcessHeap(), 0, wtmpf );
1685     HeapFree( GetProcessHeap(), 0, wcurd );
1686     return ret;
1687 }
1688