If the selection owner doesn't understand TARGETS, try retrieving
[wine] / dlls / version / info.c
1 /*
2  * Implementation of VERSION.DLL - Version Info access
3  *
4  * Copyright 1996,1997 Marcus Meissner
5  * Copyright 1997 David Cuthbert
6  * Copyright 1999 Ulrich Weigand
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  * TODO
23  *   o Verify VerQueryValue()
24  */
25
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include "windef.h"
31 #include "winbase.h"
32 #include "winreg.h"
33 #include "winver.h"
34 #include "winternl.h"
35 #include "wine/winuser16.h"
36 #include "wine/unicode.h"
37 #include "winerror.h"
38 #include "wine/debug.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(ver);
41
42
43 /******************************************************************************
44  *
45  *   This function will print via dprintf[_]ver to stddeb debug info regarding
46  *   the file info structure vffi.
47  *      15-Feb-1998 Dimitrie Paun (dimi@cs.toronto.edu)
48  *      Added this function to clean up the code.
49  *
50  *****************************************************************************/
51 static void print_vffi_debug(VS_FIXEDFILEINFO *vffi)
52 {
53         TRACE(" structversion=%u.%u, fileversion=%u.%u.%u.%u, productversion=%u.%u.%u.%u, flagmask=0x%lx, flags=%s%s%s%s%s%s\n",
54                     HIWORD(vffi->dwStrucVersion),LOWORD(vffi->dwStrucVersion),
55                     HIWORD(vffi->dwFileVersionMS),LOWORD(vffi->dwFileVersionMS),
56                     HIWORD(vffi->dwFileVersionLS),LOWORD(vffi->dwFileVersionLS),
57                     HIWORD(vffi->dwProductVersionMS),LOWORD(vffi->dwProductVersionMS),
58                     HIWORD(vffi->dwProductVersionLS),LOWORD(vffi->dwProductVersionLS),
59                     vffi->dwFileFlagsMask,
60                     (vffi->dwFileFlags & VS_FF_DEBUG) ? "DEBUG," : "",
61                     (vffi->dwFileFlags & VS_FF_PRERELEASE) ? "PRERELEASE," : "",
62                     (vffi->dwFileFlags & VS_FF_PATCHED) ? "PATCHED," : "",
63                     (vffi->dwFileFlags & VS_FF_PRIVATEBUILD) ? "PRIVATEBUILD," : "",
64                     (vffi->dwFileFlags & VS_FF_INFOINFERRED) ? "INFOINFERRED," : "",
65                     (vffi->dwFileFlags & VS_FF_SPECIALBUILD) ? "SPECIALBUILD," : ""
66                     );
67
68         TRACE("(");
69         TRACE(" OS=0x%x.0x%x ",
70                 HIWORD(vffi->dwFileOS),
71                 LOWORD(vffi->dwFileOS)
72         );
73         switch (vffi->dwFileOS&0xFFFF0000) {
74         case VOS_DOS:TRACE("DOS,");break;
75         case VOS_OS216:TRACE("OS/2-16,");break;
76         case VOS_OS232:TRACE("OS/2-32,");break;
77         case VOS_NT:TRACE("NT,");break;
78         case VOS_UNKNOWN:
79         default:
80                 TRACE("UNKNOWN(0x%lx),",vffi->dwFileOS&0xFFFF0000);break;
81         }
82         switch (LOWORD(vffi->dwFileOS)) {
83         case VOS__BASE:TRACE("BASE");break;
84         case VOS__WINDOWS16:TRACE("WIN16");break;
85         case VOS__WINDOWS32:TRACE("WIN32");break;
86         case VOS__PM16:TRACE("PM16");break;
87         case VOS__PM32:TRACE("PM32");break;
88         default:TRACE("UNKNOWN(0x%x)",LOWORD(vffi->dwFileOS));break;
89         }
90         TRACE(")\n");
91
92         switch (vffi->dwFileType) {
93         default:
94         case VFT_UNKNOWN:
95                 TRACE("filetype=Unknown(0x%lx)",vffi->dwFileType);
96                 break;
97         case VFT_APP:TRACE("filetype=APP,");break;
98         case VFT_DLL:TRACE("filetype=DLL,");break;
99         case VFT_DRV:
100                 TRACE("filetype=DRV,");
101                 switch(vffi->dwFileSubtype) {
102                 default:
103                 case VFT2_UNKNOWN:
104                         TRACE("UNKNOWN(0x%lx)",vffi->dwFileSubtype);
105                         break;
106                 case VFT2_DRV_PRINTER:
107                         TRACE("PRINTER");
108                         break;
109                 case VFT2_DRV_KEYBOARD:
110                         TRACE("KEYBOARD");
111                         break;
112                 case VFT2_DRV_LANGUAGE:
113                         TRACE("LANGUAGE");
114                         break;
115                 case VFT2_DRV_DISPLAY:
116                         TRACE("DISPLAY");
117                         break;
118                 case VFT2_DRV_MOUSE:
119                         TRACE("MOUSE");
120                         break;
121                 case VFT2_DRV_NETWORK:
122                         TRACE("NETWORK");
123                         break;
124                 case VFT2_DRV_SYSTEM:
125                         TRACE("SYSTEM");
126                         break;
127                 case VFT2_DRV_INSTALLABLE:
128                         TRACE("INSTALLABLE");
129                         break;
130                 case VFT2_DRV_SOUND:
131                         TRACE("SOUND");
132                         break;
133                 case VFT2_DRV_COMM:
134                         TRACE("COMM");
135                         break;
136                 case VFT2_DRV_INPUTMETHOD:
137                         TRACE("INPUTMETHOD");
138                         break;
139                 }
140                 break;
141         case VFT_FONT:
142                 TRACE("filetype=FONT.");
143                 switch (vffi->dwFileSubtype) {
144                 default:
145                         TRACE("UNKNOWN(0x%lx)",vffi->dwFileSubtype);
146                         break;
147                 case VFT2_FONT_RASTER:TRACE("RASTER");break;
148                 case VFT2_FONT_VECTOR:TRACE("VECTOR");break;
149                 case VFT2_FONT_TRUETYPE:TRACE("TRUETYPE");break;
150                 }
151                 break;
152         case VFT_VXD:TRACE("filetype=VXD");break;
153         case VFT_STATIC_LIB:TRACE("filetype=STATIC_LIB");break;
154         }
155         TRACE("\n");
156         TRACE("  filedata=0x%lx.0x%lx\n",
157                     vffi->dwFileDateMS,vffi->dwFileDateLS);
158 }
159
160
161 /***********************************************************************
162  * Version Info Structure
163  */
164
165 typedef struct
166 {
167     WORD  wLength;
168     WORD  wValueLength;
169     CHAR  szKey[1];
170 #if 0   /* variable length structure */
171     /* DWORD aligned */
172     BYTE  Value[];
173     /* DWORD aligned */
174     VS_VERSION_INFO_STRUCT16 Children[];
175 #endif
176 } VS_VERSION_INFO_STRUCT16;
177
178 typedef struct
179 {
180     WORD  wLength;
181     WORD  wValueLength;
182     WORD  bText;
183     WCHAR szKey[1];
184 #if 0   /* variable length structure */
185     /* DWORD aligned */
186     BYTE  Value[];
187     /* DWORD aligned */
188     VS_VERSION_INFO_STRUCT32 Children[];
189 #endif
190 } VS_VERSION_INFO_STRUCT32;
191
192 #define VersionInfoIs16( ver ) \
193     ( ((VS_VERSION_INFO_STRUCT16 *)ver)->szKey[0] >= ' ' )
194
195 #define DWORD_ALIGN( base, ptr ) \
196     ( (LPBYTE)(base) + ((((LPBYTE)(ptr) - (LPBYTE)(base)) + 3) & ~3) )
197
198 #define VersionInfo16_Value( ver )  \
199     DWORD_ALIGN( (ver), (ver)->szKey + strlen((ver)->szKey) + 1 )
200 #define VersionInfo32_Value( ver )  \
201     DWORD_ALIGN( (ver), (ver)->szKey + strlenW((ver)->szKey) + 1 )
202
203 #define VersionInfo16_Children( ver )  \
204     (VS_VERSION_INFO_STRUCT16 *)( VersionInfo16_Value( ver ) + \
205                            ( ( (ver)->wValueLength + 3 ) & ~3 ) )
206 #define VersionInfo32_Children( ver )  \
207     (VS_VERSION_INFO_STRUCT32 *)( VersionInfo32_Value( ver ) + \
208                            ( ( (ver)->wValueLength * \
209                                ((ver)->bText? 2 : 1) + 3 ) & ~3 ) )
210
211 #define VersionInfo16_Next( ver ) \
212     (VS_VERSION_INFO_STRUCT16 *)( (LPBYTE)ver + (((ver)->wLength + 3) & ~3) )
213 #define VersionInfo32_Next( ver ) \
214     (VS_VERSION_INFO_STRUCT32 *)( (LPBYTE)ver + (((ver)->wLength + 3) & ~3) )
215
216 /***********************************************************************
217  *           ConvertVersionInfo32To16        [internal]
218  */
219 void ConvertVersionInfo32To16( VS_VERSION_INFO_STRUCT32 *info32,
220                                VS_VERSION_INFO_STRUCT16 *info16 )
221 {
222     /* Copy data onto local stack to prevent overwrites */
223     WORD wLength = info32->wLength;
224     WORD wValueLength = info32->wValueLength;
225     WORD bText = info32->bText;
226     LPBYTE lpValue = VersionInfo32_Value( info32 );
227     VS_VERSION_INFO_STRUCT32 *child32 = VersionInfo32_Children( info32 );
228     VS_VERSION_INFO_STRUCT16 *child16;
229
230     TRACE("Converting %p to %p\n", info32, info16 );
231     TRACE("wLength %d, wValueLength %d, bText %d, value %p, child %p\n",
232                 wLength, wValueLength, bText, lpValue, child32 );
233
234     /* Convert key */
235     WideCharToMultiByte( CP_ACP, 0, info32->szKey, -1, info16->szKey, 0x7fffffff, NULL, NULL );
236
237     TRACE("Copied key from %p to %p: %s\n", info32->szKey, info16->szKey,
238                 debugstr_a(info16->szKey) );
239
240     /* Convert value */
241     if ( wValueLength == 0 )
242     {
243         info16->wValueLength = 0;
244         TRACE("No value present\n" );
245     }
246     else if ( bText )
247     {
248         info16->wValueLength = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)lpValue, -1, NULL, 0, NULL, NULL );
249         WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)lpValue, -1,
250                              VersionInfo16_Value( info16 ), info16->wValueLength, NULL, NULL );
251
252         TRACE("Copied value from %p to %p: %s\n", lpValue,
253                     VersionInfo16_Value( info16 ),
254                     debugstr_a(VersionInfo16_Value( info16 )) );
255     }
256     else
257     {
258         info16->wValueLength = wValueLength;
259         memmove( VersionInfo16_Value( info16 ), lpValue, wValueLength );
260
261         TRACE("Copied value from %p to %p: %d bytes\n", lpValue,
262                      VersionInfo16_Value( info16 ), wValueLength );
263     }
264
265     /* Convert children */
266     child16 = VersionInfo16_Children( info16 );
267     while ( (DWORD)child32 < (DWORD)info32 + wLength && child32->wLength != 0 )
268     {
269         VS_VERSION_INFO_STRUCT32 *nextChild = VersionInfo32_Next( child32 );
270
271         ConvertVersionInfo32To16( child32, child16 );
272
273         child16 = VersionInfo16_Next( child16 );
274         child32 = nextChild;
275     }
276
277     /* Fixup length */
278     info16->wLength = (DWORD)child16 - (DWORD)info16;
279
280     TRACE("Finished, length is %d (%p - %p)\n",
281                 info16->wLength, info16, child16 );
282 }
283
284 /***********************************************************************
285  *           VERSION_GetFileVersionInfo_PE             [internal]
286  *
287  *    NOTE: returns size of the PE VERSION resource or 0xFFFFFFFF
288  *    in the case if file exists, but VERSION_INFO not found.
289  *    FIXME: handle is not used.
290  */
291 static DWORD VERSION_GetFileVersionInfo_PE( LPCWSTR filename, LPDWORD handle,
292                                     DWORD datasize, LPVOID data )
293 {
294     VS_FIXEDFILEINFO *vffi;
295     DWORD len;
296     BYTE *buf;
297     HMODULE hModule;
298     HRSRC hRsrc;
299     HGLOBAL hMem;
300
301     TRACE("(%s,%p)\n", debugstr_w(filename), handle );
302
303     hModule = GetModuleHandleW(filename);
304     if(!hModule)
305         hModule = LoadLibraryExW(filename, 0, LOAD_LIBRARY_AS_DATAFILE);
306     else
307         hModule = LoadLibraryExW(filename, 0, 0);
308     if(!hModule)
309     {
310         WARN("Could not load %s\n", debugstr_w(filename));
311         return 0;
312     }
313     hRsrc = FindResourceW(hModule,
314                           MAKEINTRESOURCEW(VS_VERSION_INFO),
315                           MAKEINTRESOURCEW(VS_FILE_INFO));
316     if(!hRsrc)
317     {
318         WARN("Could not find VS_VERSION_INFO in %s\n", debugstr_w(filename));
319         FreeLibrary(hModule);
320         return 0xFFFFFFFF;
321     }
322     len = SizeofResource(hModule, hRsrc);
323     hMem = LoadResource(hModule, hRsrc);
324     if(!hMem)
325     {
326         WARN("Could not load VS_VERSION_INFO from %s\n", debugstr_w(filename));
327         FreeLibrary(hModule);
328         return 0xFFFFFFFF;
329     }
330     buf = LockResource(hMem);
331
332     vffi = (VS_FIXEDFILEINFO *)VersionInfo32_Value( (VS_VERSION_INFO_STRUCT32 *)buf );
333
334     if ( vffi->dwSignature != VS_FFI_SIGNATURE )
335     {
336         WARN("vffi->dwSignature is 0x%08lx, but not 0x%08lx!\n",
337                    vffi->dwSignature, VS_FFI_SIGNATURE );
338         len = 0xFFFFFFFF;
339         goto END;
340     }
341
342     if ( TRACE_ON(ver) )
343         print_vffi_debug( vffi );
344
345     if(data)
346     {
347         if(datasize < len)
348             len = datasize; /* truncate data */
349         if(len)
350             memcpy(data, buf, len);
351         else
352             len = 0xFFFFFFFF;
353     }
354     SetLastError(0);
355 END:
356     FreeResource(hMem);
357     FreeLibrary(hModule);
358
359     return len;
360 }
361
362 /***********************************************************************
363  *           VERSION_GetFileVersionInfo_16             [internal]
364  *
365  *    NOTE: returns size of the 16-bit VERSION resource or 0xFFFFFFFF
366  *    in the case if file exists, but VERSION_INFO not found.
367  *    FIXME: handle is not used.
368  */
369 static DWORD VERSION_GetFileVersionInfo_16( LPCSTR filename, LPDWORD handle,
370                                     DWORD datasize, LPVOID data )
371 {
372     VS_FIXEDFILEINFO *vffi;
373     DWORD len;
374     BYTE *buf;
375     HMODULE16 hModule;
376     HRSRC16 hRsrc;
377     HGLOBAL16 hMem;
378
379     TRACE("(%s,%p)\n", debugstr_a(filename), handle );
380
381     hModule = LoadLibrary16(filename);
382     if(hModule < 32)
383     {
384         WARN("Could not load %s\n", debugstr_a(filename));
385         return 0;
386     }
387     hRsrc = FindResource16(hModule,
388                           MAKEINTRESOURCEA(VS_VERSION_INFO),
389                           MAKEINTRESOURCEA(VS_FILE_INFO));
390     if(!hRsrc)
391     {
392         WARN("Could not find VS_VERSION_INFO in %s\n", debugstr_a(filename));
393         FreeLibrary16(hModule);
394         return 0xFFFFFFFF;
395     }
396     len = SizeofResource16(hModule, hRsrc);
397     hMem = LoadResource16(hModule, hRsrc);
398     if(!hMem)
399     {
400         WARN("Could not load VS_VERSION_INFO from %s\n", debugstr_a(filename));
401         FreeLibrary16(hModule);
402         return 0xFFFFFFFF;
403     }
404     buf = LockResource16(hMem);
405
406     if(!VersionInfoIs16(buf))
407         goto END;
408
409     vffi = (VS_FIXEDFILEINFO *)VersionInfo16_Value( (VS_VERSION_INFO_STRUCT16 *)buf );
410
411     if ( vffi->dwSignature != VS_FFI_SIGNATURE )
412     {
413         WARN("vffi->dwSignature is 0x%08lx, but not 0x%08lx!\n",
414                    vffi->dwSignature, VS_FFI_SIGNATURE );
415         len = 0xFFFFFFFF;
416         goto END;
417     }
418
419     if ( TRACE_ON(ver) )
420         print_vffi_debug( vffi );
421
422     if(data)
423     {
424         if(datasize < len)
425             len = datasize; /* truncate data */
426         if(len)
427             memcpy(data, buf, len);
428         else
429             len = 0xFFFFFFFF;
430     }
431 END:
432     FreeResource16(hMem);
433     FreeLibrary16(hModule);
434
435     return len;
436 }
437
438 /***********************************************************************
439  *           GetFileVersionInfoSizeW         [VERSION.@]
440  */
441 DWORD WINAPI GetFileVersionInfoSizeW( LPCWSTR filename, LPDWORD handle )
442 {
443     DWORD ret, offset, len = (filename && strlenW(filename)) ? strlenW(filename) + 1: MAX_PATH;
444     LPSTR filenameA = NULL;
445     LPWSTR filenameW;
446     VS_FIXEDFILEINFO *vffi;
447     BYTE buf[144];
448
449     TRACE("(%s,%p)\n", debugstr_w(filename), handle );
450
451     filenameW = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR) * len );
452     if (filename && strlenW(filename))
453         strcpyW(filenameW, filename);
454     else {
455         DWORD nSize = GetModuleFileNameW(NULL, filenameW, len);
456         if (!nSize || nSize >= len)
457         {
458             len = 0;
459             goto End;
460         }
461     }
462
463     len = VERSION_GetFileVersionInfo_PE(filenameW, handle, 0, NULL);
464     /* 0xFFFFFFFF means: file exists, but VERSION_INFO not found */
465     if(len == 0xFFFFFFFF)
466     {
467         if ( handle ) *handle = 0L;
468         SetLastError(ERROR_RESOURCE_DATA_NOT_FOUND);
469         len = 0;
470         goto End;
471     }
472     if(len) {
473         if ( handle ) *handle = 0L;
474         goto End;
475     }
476
477     /* FIXME: handle not set correctly after this point
478      */
479
480     len = WideCharToMultiByte( CP_ACP, 0, filename, -1, NULL, 0, NULL, NULL );
481     filenameA = HeapAlloc( GetProcessHeap(), 0, len );
482     WideCharToMultiByte( CP_ACP, 0, filename, -1, filenameA, len, NULL, NULL );
483
484     len = VERSION_GetFileVersionInfo_16(filenameA, handle, 0, NULL);
485     /* 0xFFFFFFFF means: file exists, but VERSION_INFO not found */
486     if(len == 0xFFFFFFFF)
487     {
488         SetLastError(ERROR_RESOURCE_DATA_NOT_FOUND);
489         len = 0;
490         goto End;
491     }
492     if(len) goto End;
493
494     /* FIXME: last error not handled after this point
495      */
496     len = GetFileResourceSize16( filenameA,
497                                  MAKEINTRESOURCEA(VS_FILE_INFO),
498                                  MAKEINTRESOURCEA(VS_VERSION_INFO),
499                                  &offset );
500     if (!len) goto End;
501
502     ret = GetFileResource16( filenameA,
503                              MAKEINTRESOURCEA(VS_FILE_INFO),
504                              MAKEINTRESOURCEA(VS_VERSION_INFO),
505                              offset, sizeof( buf ), buf );
506     if (!ret) goto End;
507
508     if ( handle ) *handle = offset;
509
510     if ( VersionInfoIs16( buf ) )
511         vffi = (VS_FIXEDFILEINFO *)VersionInfo16_Value( (VS_VERSION_INFO_STRUCT16 *)buf );
512     else
513         vffi = (VS_FIXEDFILEINFO *)VersionInfo32_Value( (VS_VERSION_INFO_STRUCT32 *)buf );
514
515     if ( vffi->dwSignature != VS_FFI_SIGNATURE )
516     {
517         WARN("vffi->dwSignature is 0x%08lx, but not 0x%08lx!\n",
518                    vffi->dwSignature, VS_FFI_SIGNATURE );
519         len = 0;
520         goto End;
521     }
522
523     if ( ((VS_VERSION_INFO_STRUCT16 *)buf)->wLength < len )
524         len = ((VS_VERSION_INFO_STRUCT16 *)buf)->wLength;
525
526     if ( TRACE_ON(ver) )
527         print_vffi_debug( vffi );
528 End:
529     HeapFree( GetProcessHeap(), 0, filenameW);
530     if (filenameA)
531         HeapFree( GetProcessHeap(), 0, filenameA);
532     return len;
533 }
534
535 /***********************************************************************
536  *           GetFileVersionInfoSizeA         [VERSION.@]
537  */
538 DWORD WINAPI GetFileVersionInfoSizeA( LPCSTR filename, LPDWORD handle )
539 {   UNICODE_STRING filenameW;
540     DWORD retval;
541     if(filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
542     else filenameW.Buffer = NULL;
543     retval = GetFileVersionInfoSizeW(filenameW.Buffer, handle);
544     RtlFreeUnicodeString(&filenameW);
545     return retval;
546 }
547
548 /***********************************************************************
549  *           GetFileVersionInfoA             [VERSION.@]
550  */
551 BOOL WINAPI GetFileVersionInfoA( LPCSTR filename, DWORD handle,
552                                     DWORD datasize, LPVOID data )
553 {   UNICODE_STRING filenameW;
554     DWORD len;
555
556     TRACE("(%s,%ld,size=%ld,data=%p)\n",
557                 debugstr_a(filename), handle, datasize, data );
558
559     if(filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
560     else filenameW.Buffer = NULL;
561     len = VERSION_GetFileVersionInfo_PE(filenameW.Buffer, &handle, datasize, data);
562     /* 0xFFFFFFFF means: file exists, but VERSION_INFO not found */
563     RtlFreeUnicodeString(&filenameW);
564     if(len == 0xFFFFFFFF) return FALSE;
565     if(len)
566         goto DO_CONVERT;
567     len = VERSION_GetFileVersionInfo_16(filename, &handle, datasize, data);
568     /* 0xFFFFFFFF means: file exists, but VERSION_INFO not found */
569     if(len == 0xFFFFFFFF) return FALSE;
570     if(len)
571         goto DO_CONVERT;
572
573     if ( !GetFileResource16( filename, MAKEINTRESOURCEA(VS_FILE_INFO),
574                                        MAKEINTRESOURCEA(VS_VERSION_INFO),
575                                        handle, datasize, data ) )
576         return FALSE;
577 DO_CONVERT:
578     if (    datasize >= sizeof(VS_VERSION_INFO_STRUCT16)
579          && datasize >= ((VS_VERSION_INFO_STRUCT16 *)data)->wLength
580          && !VersionInfoIs16( data ) )
581     {
582         /* convert resource from PE format to NE format */
583         ConvertVersionInfo32To16( (VS_VERSION_INFO_STRUCT32 *)data,
584                                   (VS_VERSION_INFO_STRUCT16 *)data );
585     }
586
587     return TRUE;
588 }
589
590 /***********************************************************************
591  *           GetFileVersionInfoW             [VERSION.@]
592  */
593 BOOL WINAPI GetFileVersionInfoW( LPCWSTR filename, DWORD handle,
594                                     DWORD datasize, LPVOID data )
595 {
596     DWORD len = WideCharToMultiByte( CP_ACP, 0, filename, -1, NULL, 0, NULL, NULL );
597     LPSTR fn = HeapAlloc( GetProcessHeap(), 0, len );
598     DWORD retv = TRUE;
599
600     WideCharToMultiByte( CP_ACP, 0, filename, -1, fn, len, NULL, NULL );
601
602     TRACE("(%s,%ld,size=%ld,data=%p)\n",
603                 debugstr_w(filename), handle, datasize, data );
604
605     if(VERSION_GetFileVersionInfo_PE(filename, &handle, datasize, data))
606         goto END;
607     if(VERSION_GetFileVersionInfo_16(fn, &handle, datasize, data))
608         goto END;
609
610     if ( !GetFileResource16( fn, MAKEINTRESOURCEA(VS_FILE_INFO),
611                                  MAKEINTRESOURCEA(VS_VERSION_INFO),
612                                  handle, datasize, data ) )
613         retv = FALSE;
614
615     else if (    datasize >= sizeof(VS_VERSION_INFO_STRUCT16)
616               && datasize >= ((VS_VERSION_INFO_STRUCT16 *)data)->wLength
617               && VersionInfoIs16( data ) )
618     {
619         ERR("Cannot access NE resource in %s\n", debugstr_a(fn) );
620         retv =  FALSE;
621     }
622 END:
623     HeapFree( GetProcessHeap(), 0, fn );
624     return retv;
625 }
626
627
628 /***********************************************************************
629  *           VersionInfo16_FindChild             [internal]
630  */
631 static VS_VERSION_INFO_STRUCT16 *VersionInfo16_FindChild( VS_VERSION_INFO_STRUCT16 *info,
632                                             LPCSTR szKey, UINT cbKey )
633 {
634     VS_VERSION_INFO_STRUCT16 *child = VersionInfo16_Children( info );
635
636     while ( (DWORD)child < (DWORD)info + info->wLength )
637     {
638         if ( !strncasecmp( child->szKey, szKey, cbKey ) )
639             return child;
640
641         if (!(child->wLength)) return NULL;
642         child = VersionInfo16_Next( child );
643     }
644
645     return NULL;
646 }
647
648 /***********************************************************************
649  *           VersionInfo32_FindChild             [internal]
650  */
651 static VS_VERSION_INFO_STRUCT32 *VersionInfo32_FindChild( VS_VERSION_INFO_STRUCT32 *info,
652                                             LPCWSTR szKey, UINT cbKey )
653 {
654     VS_VERSION_INFO_STRUCT32 *child = VersionInfo32_Children( info );
655
656     while ( (DWORD)child < (DWORD)info + info->wLength )
657     {
658         if ( !strncmpiW( child->szKey, szKey, cbKey ) )
659             return child;
660
661         child = VersionInfo32_Next( child );
662     }
663
664     return NULL;
665 }
666
667 /***********************************************************************
668  *           VersionInfo16_QueryValue              [internal]
669  *
670  *    Gets a value from a 16-bit NE resource
671  */
672 DWORD WINAPI VersionInfo16_QueryValue( VS_VERSION_INFO_STRUCT16 *info, LPCSTR lpSubBlock,
673                                LPVOID *lplpBuffer, UINT *puLen )
674 {
675     while ( *lpSubBlock )
676     {
677         /* Find next path component */
678         LPCSTR lpNextSlash;
679         for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ )
680             if ( *lpNextSlash == '\\' )
681                 break;
682
683         /* Skip empty components */
684         if ( lpNextSlash == lpSubBlock )
685         {
686             lpSubBlock++;
687             continue;
688         }
689
690         /* We have a non-empty component: search info for key */
691         info = VersionInfo16_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock );
692         if ( !info ) return FALSE;
693
694         /* Skip path component */
695         lpSubBlock = lpNextSlash;
696     }
697
698     /* Return value */
699     *lplpBuffer = VersionInfo16_Value( info );
700     *puLen = info->wValueLength;
701
702     return TRUE;
703 }
704
705 /***********************************************************************
706  *           VerQueryValueA              [VERSION.@]
707  */
708 DWORD WINAPI VerQueryValueA( LPVOID pBlock, LPCSTR lpSubBlock,
709                                LPVOID *lplpBuffer, UINT *puLen )
710 {
711     VS_VERSION_INFO_STRUCT16 *info = (VS_VERSION_INFO_STRUCT16 *)pBlock;
712
713     TRACE("(%p,%s,%p,%p)\n",
714                 pBlock, debugstr_a(lpSubBlock), lplpBuffer, puLen );
715
716     if ( !VersionInfoIs16( info ) )
717     {
718         INT len;
719         LPWSTR wide_str;
720         DWORD give;
721
722         /* <lawson_whitney@juno.com> Feb 2001 */
723         /* AOL 5.0 does this, expecting to get this: */
724         len = MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, NULL, 0);
725         wide_str = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
726         MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, wide_str, len);
727
728         give = VerQueryValueW(pBlock, wide_str, lplpBuffer, puLen);
729         HeapFree(GetProcessHeap(), 0, wide_str);
730         return give;
731     }
732
733     return VersionInfo16_QueryValue(info, lpSubBlock, lplpBuffer, puLen);
734 }
735
736 /***********************************************************************
737  *           VerQueryValueW              [VERSION.@]
738  */
739 DWORD WINAPI VerQueryValueW( LPVOID pBlock, LPCWSTR lpSubBlock,
740                                LPVOID *lplpBuffer, UINT *puLen )
741 {
742     VS_VERSION_INFO_STRUCT32 *info = (VS_VERSION_INFO_STRUCT32 *)pBlock;
743
744     TRACE("(%p,%s,%p,%p)\n",
745                 pBlock, debugstr_w(lpSubBlock), lplpBuffer, puLen );
746
747     if ( VersionInfoIs16( info ) )
748     {
749         DWORD ret;
750         int len = WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, NULL, 0, NULL, NULL);
751         LPSTR lpSubBlockA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
752         if (!lpSubBlockA)
753             return FALSE;
754         WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, lpSubBlockA, len, NULL, NULL);
755         ret = VersionInfo16_QueryValue(pBlock, lpSubBlockA, lplpBuffer, puLen);
756         HeapFree(GetProcessHeap(), 0, lpSubBlockA);
757         return ret;
758     }
759
760     while ( *lpSubBlock )
761     {
762         /* Find next path component */
763         LPCWSTR lpNextSlash;
764         for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ )
765             if ( *lpNextSlash == '\\' )
766                 break;
767
768         /* Skip empty components */
769         if ( lpNextSlash == lpSubBlock )
770         {
771             lpSubBlock++;
772             continue;
773         }
774
775         /* We have a non-empty component: search info for key */
776         info = VersionInfo32_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock );
777         if ( !info ) return FALSE;
778
779         /* Skip path component */
780         lpSubBlock = lpNextSlash;
781     }
782
783     /* Return value */
784     *lplpBuffer = VersionInfo32_Value( info );
785     *puLen = info->wValueLength;
786
787     return TRUE;
788 }