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