shlwapi: Add a slash to the URL if there is no path component.
[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  * 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
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winver.h"
32 #include "winternl.h"
33 #include "wine/winuser16.h"
34 #include "wine/unicode.h"
35 #include "winerror.h"
36 #include "wine/debug.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(ver);
39
40 /******************************************************************************
41  *
42  *   This function will print via standard TRACE, debug info regarding
43  *   the file info structure vffi.
44  *      15-Feb-1998 Dimitrie Paun (dimi@cs.toronto.edu)
45  *      Added this function to clean up the code.
46  *
47  *****************************************************************************/
48 static void print_vffi_debug(const VS_FIXEDFILEINFO *vffi)
49 {
50     BOOL    versioned_printer = FALSE;
51
52     if((vffi->dwFileType == VFT_DLL) || (vffi->dwFileType == VFT_DRV))
53     {
54         if(vffi->dwFileSubtype == VFT2_DRV_VERSIONED_PRINTER)
55             /* this is documented for newer w2k Drivers and up */
56             versioned_printer = TRUE;
57         else if( (vffi->dwFileSubtype == VFT2_DRV_PRINTER) &&
58                  (vffi->dwFileVersionMS != vffi->dwProductVersionMS) &&
59                  (vffi->dwFileVersionMS > 0) &&
60                  (vffi->dwFileVersionMS <= 3) )
61             /* found this on NT 3.51, NT4.0 and old w2k Drivers */
62             versioned_printer = TRUE;
63     }
64
65     TRACE("structversion=%u.%u, ",
66             HIWORD(vffi->dwStrucVersion),LOWORD(vffi->dwStrucVersion));
67     if(versioned_printer)
68     {
69         WORD mode = LOWORD(vffi->dwFileVersionMS);
70         WORD ver_rev = HIWORD(vffi->dwFileVersionLS);
71         TRACE("fileversion=%u.%u.%u.%u (%s.major.minor.release), ",
72             (vffi->dwFileVersionMS),
73             HIBYTE(ver_rev), LOBYTE(ver_rev), LOWORD(vffi->dwFileVersionLS),
74             (mode == 3) ? "Usermode" : ((mode <= 2) ? "Kernelmode" : "?") );
75     }
76     else
77     {
78         TRACE("fileversion=%u.%u.%u.%u, ",
79             HIWORD(vffi->dwFileVersionMS),LOWORD(vffi->dwFileVersionMS),
80             HIWORD(vffi->dwFileVersionLS),LOWORD(vffi->dwFileVersionLS));
81     }
82     TRACE("productversion=%u.%u.%u.%u\n",
83           HIWORD(vffi->dwProductVersionMS),LOWORD(vffi->dwProductVersionMS),
84           HIWORD(vffi->dwProductVersionLS),LOWORD(vffi->dwProductVersionLS));
85
86     TRACE("flagmask=0x%x, flags=0x%x %s%s%s%s%s%s\n",
87           vffi->dwFileFlagsMask, vffi->dwFileFlags,
88           (vffi->dwFileFlags & VS_FF_DEBUG) ? "DEBUG," : "",
89           (vffi->dwFileFlags & VS_FF_PRERELEASE) ? "PRERELEASE," : "",
90           (vffi->dwFileFlags & VS_FF_PATCHED) ? "PATCHED," : "",
91           (vffi->dwFileFlags & VS_FF_PRIVATEBUILD) ? "PRIVATEBUILD," : "",
92           (vffi->dwFileFlags & VS_FF_INFOINFERRED) ? "INFOINFERRED," : "",
93           (vffi->dwFileFlags & VS_FF_SPECIALBUILD) ? "SPECIALBUILD," : "");
94
95     TRACE("(");
96
97     TRACE("OS=0x%x.0x%x ", HIWORD(vffi->dwFileOS), LOWORD(vffi->dwFileOS));
98
99     switch (vffi->dwFileOS&0xFFFF0000)
100     {
101     case VOS_DOS:TRACE("DOS,");break;
102     case VOS_OS216:TRACE("OS/2-16,");break;
103     case VOS_OS232:TRACE("OS/2-32,");break;
104     case VOS_NT:TRACE("NT,");break;
105     case VOS_UNKNOWN:
106     default:
107         TRACE("UNKNOWN(0x%x),",vffi->dwFileOS&0xFFFF0000);break;
108     }
109
110     switch (LOWORD(vffi->dwFileOS))
111     {
112     case VOS__BASE:TRACE("BASE");break;
113     case VOS__WINDOWS16:TRACE("WIN16");break;
114     case VOS__WINDOWS32:TRACE("WIN32");break;
115     case VOS__PM16:TRACE("PM16");break;
116     case VOS__PM32:TRACE("PM32");break;
117     default:
118         TRACE("UNKNOWN(0x%x)",LOWORD(vffi->dwFileOS));break;
119     }
120
121     TRACE(")\n");
122
123     switch (vffi->dwFileType)
124     {
125     case VFT_APP:TRACE("filetype=APP");break;
126     case VFT_DLL:
127         TRACE("filetype=DLL");
128         if(vffi->dwFileSubtype != 0)
129         {
130             if(versioned_printer) /* NT3.x/NT4.0 or old w2k Driver  */
131                 TRACE(",PRINTER");
132             TRACE(" (subtype=0x%x)", vffi->dwFileSubtype);
133         }
134         break;
135     case VFT_DRV:
136         TRACE("filetype=DRV,");
137         switch(vffi->dwFileSubtype)
138         {
139         case VFT2_DRV_PRINTER:TRACE("PRINTER");break;
140         case VFT2_DRV_KEYBOARD:TRACE("KEYBOARD");break;
141         case VFT2_DRV_LANGUAGE:TRACE("LANGUAGE");break;
142         case VFT2_DRV_DISPLAY:TRACE("DISPLAY");break;
143         case VFT2_DRV_MOUSE:TRACE("MOUSE");break;
144         case VFT2_DRV_NETWORK:TRACE("NETWORK");break;
145         case VFT2_DRV_SYSTEM:TRACE("SYSTEM");break;
146         case VFT2_DRV_INSTALLABLE:TRACE("INSTALLABLE");break;
147         case VFT2_DRV_SOUND:TRACE("SOUND");break;
148         case VFT2_DRV_COMM:TRACE("COMM");break;
149         case VFT2_DRV_INPUTMETHOD:TRACE("INPUTMETHOD");break;
150         case VFT2_DRV_VERSIONED_PRINTER:TRACE("VERSIONED_PRINTER");break;
151         case VFT2_UNKNOWN:
152         default:
153             TRACE("UNKNOWN(0x%x)",vffi->dwFileSubtype);break;
154         }
155         break;
156     case VFT_FONT:
157         TRACE("filetype=FONT,");
158         switch (vffi->dwFileSubtype)
159         {
160         case VFT2_FONT_RASTER:TRACE("RASTER");break;
161         case VFT2_FONT_VECTOR:TRACE("VECTOR");break;
162         case VFT2_FONT_TRUETYPE:TRACE("TRUETYPE");break;
163         default:TRACE("UNKNOWN(0x%x)",vffi->dwFileSubtype);break;
164         }
165         break;
166     case VFT_VXD:TRACE("filetype=VXD");break;
167     case VFT_STATIC_LIB:TRACE("filetype=STATIC_LIB");break;
168     case VFT_UNKNOWN:
169     default:
170         TRACE("filetype=Unknown(0x%x)",vffi->dwFileType);break;
171     }
172
173     TRACE("\n");
174     TRACE("filedate=0x%x.0x%x\n",vffi->dwFileDateMS,vffi->dwFileDateLS);
175 }
176
177 /***********************************************************************
178  * Version Info Structure
179  */
180
181 typedef struct
182 {
183     WORD  wLength;
184     WORD  wValueLength;
185     CHAR  szKey[1];
186 #if 0   /* variable length structure */
187     /* DWORD aligned */
188     BYTE  Value[];
189     /* DWORD aligned */
190     VS_VERSION_INFO_STRUCT16 Children[];
191 #endif
192 } VS_VERSION_INFO_STRUCT16;
193
194 typedef struct
195 {
196     WORD  wLength;
197     WORD  wValueLength;
198     WORD  wType;
199     WCHAR szKey[1];
200 #if 0   /* variable length structure */
201     /* DWORD aligned */
202     BYTE  Value[];
203     /* DWORD aligned */
204     VS_VERSION_INFO_STRUCT32 Children[];
205 #endif
206 } VS_VERSION_INFO_STRUCT32;
207
208 #define VersionInfoIs16( ver ) \
209     ( ((VS_VERSION_INFO_STRUCT16 *)ver)->szKey[0] >= ' ' )
210
211 #define DWORD_ALIGN( base, ptr ) \
212     ( (LPBYTE)(base) + ((((LPBYTE)(ptr) - (LPBYTE)(base)) + 3) & ~3) )
213
214 #define VersionInfo16_Value( ver )  \
215     DWORD_ALIGN( (ver), (ver)->szKey + strlen((ver)->szKey) + 1 )
216 #define VersionInfo32_Value( ver )  \
217     DWORD_ALIGN( (ver), (ver)->szKey + strlenW((ver)->szKey) + 1 )
218
219 #define VersionInfo16_Children( ver )  \
220     (VS_VERSION_INFO_STRUCT16 *)( VersionInfo16_Value( ver ) + \
221                            ( ( (ver)->wValueLength + 3 ) & ~3 ) )
222 #define VersionInfo32_Children( ver )  \
223     (VS_VERSION_INFO_STRUCT32 *)( VersionInfo32_Value( ver ) + \
224                            ( ( (ver)->wValueLength * \
225                                ((ver)->wType? 2 : 1) + 3 ) & ~3 ) )
226
227 #define VersionInfo16_Next( ver ) \
228     (VS_VERSION_INFO_STRUCT16 *)( (LPBYTE)ver + (((ver)->wLength + 3) & ~3) )
229 #define VersionInfo32_Next( ver ) \
230     (VS_VERSION_INFO_STRUCT32 *)( (LPBYTE)ver + (((ver)->wLength + 3) & ~3) )
231
232 /***********************************************************************
233  *           VERSION_GetFileVersionInfo_PE             [internal]
234  *
235  *    NOTE: returns size of the PE VERSION resource or 0xFFFFFFFF
236  *    in the case the file is a PE module, but VERSION_INFO not found.
237  */
238 static DWORD VERSION_GetFileVersionInfo_PE( LPCWSTR filename, DWORD datasize, LPVOID data )
239 {
240     const VS_FIXEDFILEINFO *vffi;
241     DWORD len;
242     BYTE *buf;
243     HMODULE hModule;
244     HRSRC hRsrc;
245     HGLOBAL hMem;
246
247     TRACE("%s\n", debugstr_w(filename));
248
249     hModule = GetModuleHandleW(filename);
250     if(!hModule)
251         hModule = LoadLibraryExW(filename, 0, LOAD_LIBRARY_AS_DATAFILE);
252     else
253         hModule = LoadLibraryExW(filename, 0, 0);
254     if(!hModule)
255     {
256         WARN("Could not load %s\n", debugstr_w(filename));
257         return 0;
258     }
259     hRsrc = FindResourceW(hModule,
260                           MAKEINTRESOURCEW(VS_VERSION_INFO),
261                           MAKEINTRESOURCEW(VS_FILE_INFO));
262     if(!hRsrc)
263     {
264         WARN("Could not find VS_VERSION_INFO in %s\n", debugstr_w(filename));
265         FreeLibrary(hModule);
266         return 0xFFFFFFFF;
267     }
268     len = SizeofResource(hModule, hRsrc);
269     hMem = LoadResource(hModule, hRsrc);
270     if(!hMem)
271     {
272         WARN("Could not load VS_VERSION_INFO from %s\n", debugstr_w(filename));
273         FreeLibrary(hModule);
274         return 0xFFFFFFFF;
275     }
276     buf = LockResource(hMem);
277
278     vffi = (VS_FIXEDFILEINFO *)VersionInfo32_Value( (VS_VERSION_INFO_STRUCT32 *)buf );
279
280     if ( vffi->dwSignature != VS_FFI_SIGNATURE )
281     {
282         WARN("vffi->dwSignature is 0x%08x, but not 0x%08lx!\n",
283                    vffi->dwSignature, VS_FFI_SIGNATURE );
284         len = 0xFFFFFFFF;
285         goto END;
286     }
287
288     if ( TRACE_ON(ver) )
289         print_vffi_debug( vffi );
290
291     if(data)
292     {
293         if(datasize < len)
294             len = datasize; /* truncate data */
295         if(len)
296             memcpy(data, buf, len);
297         else
298             len = 0xFFFFFFFF;
299     }
300 END:
301     FreeResource(hMem);
302     FreeLibrary(hModule);
303
304     return len;
305 }
306
307 /***********************************************************************
308  *           VERSION_GetFileVersionInfo_16             [internal]
309  *
310  *    NOTE: returns size of the 16-bit VERSION resource or 0xFFFFFFFF
311  *    in the case the file exists, but VERSION_INFO not found.
312  */
313 static DWORD VERSION_GetFileVersionInfo_16( LPCSTR filename, DWORD datasize, LPVOID data )
314 {
315     const VS_FIXEDFILEINFO *vffi;
316     DWORD len, offset;
317     BYTE *buf;
318     HMODULE16 hModule;
319     HRSRC16 hRsrc;
320     HGLOBAL16 hMem;
321     char dllname[20], owner[20], *p;
322     const char *basename;
323     BOOL is_builtin = FALSE;
324
325     TRACE("%s\n", debugstr_a(filename));
326
327     /* strip path information */
328
329     basename = filename;
330     if (basename[0] && basename[1] == ':') basename += 2;  /* strip drive specification */
331     if ((p = strrchr( basename, '\\' ))) basename = p + 1;
332     if ((p = strrchr( basename, '/' ))) basename = p + 1;
333
334     if (strlen(basename) < sizeof(dllname)-4)
335     {
336         int file_exists;
337
338         strcpy( dllname, basename );
339         p = strrchr( dllname, '.' );
340         if (!p) strcat( dllname, ".dll" );
341         for (p = dllname; *p; p++) if (*p >= 'A' && *p <= 'Z') *p += 32;
342
343         if (wine_dll_get_owner( dllname, owner, sizeof(owner), &file_exists ) == 0)
344             is_builtin = TRUE;
345     }
346
347     /* first try without loading a 16-bit module */
348     if (is_builtin)
349         len = 0;
350     else
351         len = GetFileResourceSize16( filename,
352                                      MAKEINTRESOURCEA(VS_FILE_INFO),
353                                      MAKEINTRESOURCEA(VS_VERSION_INFO),
354                                      &offset );
355     if (len)
356     {
357         if (!data) return len;
358
359         len = GetFileResource16( filename,
360                                  MAKEINTRESOURCEA(VS_FILE_INFO),
361                                  MAKEINTRESOURCEA(VS_VERSION_INFO),
362                                  offset, datasize, data );
363         if (len)
364         {
365             vffi = (VS_FIXEDFILEINFO *)VersionInfo16_Value( (VS_VERSION_INFO_STRUCT16 *)data );
366
367             if ( vffi->dwSignature == VS_FFI_SIGNATURE )
368             {
369                 if ( ((VS_VERSION_INFO_STRUCT16 *)data)->wLength < len )
370                     len = ((VS_VERSION_INFO_STRUCT16 *)data)->wLength;
371
372                 if ( TRACE_ON(ver) )
373                     print_vffi_debug( vffi );
374
375                 return len;
376             }
377         }
378     }
379
380     /* this might be a builtin 16-bit module */
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     {
408         len = 0xFFFFFFFF;
409         goto END;
410     }
411
412     vffi = (VS_FIXEDFILEINFO *)VersionInfo16_Value( (VS_VERSION_INFO_STRUCT16 *)buf );
413
414     if ( vffi->dwSignature != VS_FFI_SIGNATURE )
415     {
416         WARN("vffi->dwSignature is 0x%08x, but not 0x%08lx!\n",
417                    vffi->dwSignature, VS_FFI_SIGNATURE );
418         len = 0xFFFFFFFF;
419         goto END;
420     }
421
422     if ( TRACE_ON(ver) )
423         print_vffi_debug( vffi );
424
425     if(data)
426     {
427         if(datasize < len)
428             len = datasize; /* truncate data */
429         if(len)
430             memcpy(data, buf, len);
431         else
432             len = 0xFFFFFFFF;
433     }
434 END:
435     FreeResource16(hMem);
436     FreeLibrary16(hModule);
437
438     return len;
439 }
440
441 /***********************************************************************
442  *           GetFileVersionInfoSizeW         [VERSION.@]
443  */
444 DWORD WINAPI GetFileVersionInfoSizeW( LPCWSTR filename, LPDWORD handle )
445 {
446     DWORD len;
447
448     TRACE("(%s,%p)\n", debugstr_w(filename), handle );
449
450     if (handle) *handle = 0;
451
452     if (!filename)
453     {
454         SetLastError(ERROR_INVALID_PARAMETER);
455         return 0;
456     }
457     if (!*filename)
458     {
459         SetLastError(ERROR_BAD_PATHNAME);
460         return 0;
461     }
462
463     len = VERSION_GetFileVersionInfo_PE(filename, 0, NULL);
464     /* 0xFFFFFFFF means: file is a PE module, but VERSION_INFO not found */
465     if(len == 0xFFFFFFFF)
466     {
467         SetLastError(ERROR_RESOURCE_DATA_NOT_FOUND);
468         return 0;
469     }
470
471     if (!len)
472     {
473         LPSTR filenameA;
474
475         len = WideCharToMultiByte( CP_ACP, 0, filename, -1, NULL, 0, NULL, NULL );
476         filenameA = HeapAlloc( GetProcessHeap(), 0, len );
477         WideCharToMultiByte( CP_ACP, 0, filename, -1, filenameA, len, NULL, NULL );
478
479         len = VERSION_GetFileVersionInfo_16(filenameA, 0, NULL);
480         HeapFree( GetProcessHeap(), 0, filenameA );
481         /* 0xFFFFFFFF means: file exists, but VERSION_INFO not found */
482         if (!len)
483         {
484             SetLastError(ERROR_FILE_NOT_FOUND);
485             return 0;
486         }
487         if (len == 0xFFFFFFFF)
488         {
489             SetLastError(ERROR_RESOURCE_DATA_NOT_FOUND);
490             return 0;
491         }
492
493         /* We have a 16bit resource.
494          *
495          * XP/W2K/W2K3 uses a buffer which is more than the actual needed space:
496          *
497          * (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4
498          *
499          * This extra buffer is used for ANSI to Unicode conversions in W-Calls.
500          * info->wLength should be the same as len. Currently it isn't but that
501          * doesn't seem to be a problem (len is bigger then info->wLength).
502          */
503          len = (len - sizeof(VS_FIXEDFILEINFO)) * 4;
504     }
505     else
506     {
507         /* We have a 32bit resource.
508          *
509          * XP/W2K/W2K3 uses a buffer which is 2 times the actual needed space + 4 bytes "FE2X"
510          * This extra buffer is used for Unicode to ANSI conversions in A-Calls
511          */
512          len = (len * 2) + 4;
513     }
514
515     SetLastError(0);
516     return len;
517 }
518
519 /***********************************************************************
520  *           GetFileVersionInfoSizeA         [VERSION.@]
521  */
522 DWORD WINAPI GetFileVersionInfoSizeA( LPCSTR filename, LPDWORD handle )
523 {
524     UNICODE_STRING filenameW;
525     DWORD retval;
526
527     TRACE("(%s,%p)\n", debugstr_a(filename), handle );
528
529     if(filename)
530         RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
531     else
532         filenameW.Buffer = NULL;
533
534     retval = GetFileVersionInfoSizeW(filenameW.Buffer, handle);
535
536     RtlFreeUnicodeString(&filenameW);
537
538     return retval;
539 }
540
541 /***********************************************************************
542  *           GetFileVersionInfoW             [VERSION.@]
543  */
544 BOOL WINAPI GetFileVersionInfoW( LPCWSTR filename, DWORD handle,
545                                     DWORD datasize, LPVOID data )
546 {
547     DWORD len;
548     VS_VERSION_INFO_STRUCT32* vvis = (VS_VERSION_INFO_STRUCT32*)data;
549
550     TRACE("(%s,%d,size=%d,data=%p)\n",
551                 debugstr_w(filename), handle, datasize, data );
552
553     if (!data)
554     {
555         SetLastError(ERROR_INVALID_DATA);
556         return FALSE;
557     }
558     len = VERSION_GetFileVersionInfo_PE(filename, datasize, data);
559     /* 0xFFFFFFFF means: file is a PE module, but VERSION_INFO not found */
560     if (len == 0xFFFFFFFF)
561     {
562         SetLastError(ERROR_RESOURCE_DATA_NOT_FOUND);
563         return FALSE;
564     }
565
566     if (!len)
567     {
568         LPSTR filenameA;
569
570         len = WideCharToMultiByte( CP_ACP, 0, filename, -1, NULL, 0, NULL, NULL );
571         filenameA = HeapAlloc( GetProcessHeap(), 0, len );
572         WideCharToMultiByte( CP_ACP, 0, filename, -1, filenameA, len, NULL, NULL );
573
574         len = VERSION_GetFileVersionInfo_16(filenameA, datasize, data);
575         HeapFree( GetProcessHeap(), 0, filenameA );
576         /* 0xFFFFFFFF means: file exists, but VERSION_INFO not found */
577         if (!len || len == 0xFFFFFFFF)
578         {
579             SetLastError(ERROR_RESOURCE_DATA_NOT_FOUND);
580             return FALSE;
581         }
582         /* We have a 16bit resource. */
583     }
584     else 
585     {
586         static const char signature[] = "FE2X";
587         DWORD bufsize = vvis->wLength + strlen(signature);
588         DWORD convbuf;
589  
590         /* We have a 32bit resource.
591          *
592          * XP/W2K/W2K3 uses a buffer which is 2 times the actual needed space + 4 bytes "FE2X"
593          * This extra buffer is used for Unicode to ANSI conversions in A-Calls
594          */
595
596         /* information is truncated to datasize bytes */
597         if (datasize >= bufsize)
598         {
599             convbuf = datasize - vvis->wLength;
600             memcpy( ((char*)(data))+vvis->wLength, signature, convbuf > 4 ? 4 : convbuf );
601         }
602     }
603
604     SetLastError(0);
605     return TRUE;
606 }
607
608 /***********************************************************************
609  *           GetFileVersionInfoA             [VERSION.@]
610  */
611 BOOL WINAPI GetFileVersionInfoA( LPCSTR filename, DWORD handle,
612                                     DWORD datasize, LPVOID data )
613 {
614     UNICODE_STRING filenameW;
615     BOOL retval;
616
617     TRACE("(%s,%d,size=%d,data=%p)\n",
618                 debugstr_a(filename), handle, datasize, data );
619
620     if(filename)
621         RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
622     else
623         filenameW.Buffer = NULL;
624
625     retval = GetFileVersionInfoW(filenameW.Buffer, handle, datasize, data);
626
627     RtlFreeUnicodeString(&filenameW);
628
629     return retval;
630 }
631
632 /***********************************************************************
633  *           VersionInfo16_FindChild             [internal]
634  */
635 static VS_VERSION_INFO_STRUCT16 *VersionInfo16_FindChild( VS_VERSION_INFO_STRUCT16 *info,
636                                             LPCSTR szKey, UINT cbKey )
637 {
638     VS_VERSION_INFO_STRUCT16 *child = VersionInfo16_Children( info );
639
640     while ((char *)child < (char *)info + info->wLength )
641     {
642         if (!strncasecmp( child->szKey, szKey, cbKey ) && !child->szKey[cbKey])
643             return child;
644
645         if (!(child->wLength)) return NULL;
646         child = VersionInfo16_Next( child );
647     }
648
649     return NULL;
650 }
651
652 /***********************************************************************
653  *           VersionInfo32_FindChild             [internal]
654  */
655 static VS_VERSION_INFO_STRUCT32 *VersionInfo32_FindChild( VS_VERSION_INFO_STRUCT32 *info,
656                                             LPCWSTR szKey, UINT cbKey )
657 {
658     VS_VERSION_INFO_STRUCT32 *child = VersionInfo32_Children( info );
659
660     while ((char *)child < (char *)info + info->wLength )
661     {
662         if (!strncmpiW( child->szKey, szKey, cbKey ) && !child->szKey[cbKey])
663             return child;
664
665         child = VersionInfo32_Next( child );
666     }
667
668     return NULL;
669 }
670
671 /***********************************************************************
672  *           VersionInfo16_QueryValue              [internal]
673  *
674  *    Gets a value from a 16-bit NE resource
675  */
676 static BOOL WINAPI VersionInfo16_QueryValue( VS_VERSION_INFO_STRUCT16 *info, LPCSTR lpSubBlock,
677                                LPVOID *lplpBuffer, UINT *puLen )
678 {
679     while ( *lpSubBlock )
680     {
681         /* Find next path component */
682         LPCSTR lpNextSlash;
683         for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ )
684             if ( *lpNextSlash == '\\' )
685                 break;
686
687         /* Skip empty components */
688         if ( lpNextSlash == lpSubBlock )
689         {
690             lpSubBlock++;
691             continue;
692         }
693
694         /* We have a non-empty component: search info for key */
695         info = VersionInfo16_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock );
696         if ( !info )
697         {
698             if (puLen) *puLen = 0 ;
699             SetLastError( ERROR_RESOURCE_TYPE_NOT_FOUND );
700             return FALSE;
701         }
702
703         /* Skip path component */
704         lpSubBlock = lpNextSlash;
705     }
706
707     /* Return value */
708     *lplpBuffer = VersionInfo16_Value( info );
709     if (puLen)
710         *puLen = info->wValueLength;
711
712     return TRUE;
713 }
714
715 /***********************************************************************
716  *           VersionInfo32_QueryValue              [internal]
717  *
718  *    Gets a value from a 32-bit PE resource
719  */
720 static BOOL WINAPI VersionInfo32_QueryValue( VS_VERSION_INFO_STRUCT32 *info, LPCWSTR lpSubBlock,
721                                LPVOID *lplpBuffer, UINT *puLen )
722 {
723     TRACE("lpSubBlock : (%s)\n", debugstr_w(lpSubBlock));
724
725     while ( *lpSubBlock )
726     {
727         /* Find next path component */
728         LPCWSTR lpNextSlash;
729         for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ )
730             if ( *lpNextSlash == '\\' )
731                 break;
732
733         /* Skip empty components */
734         if ( lpNextSlash == lpSubBlock )
735         {
736             lpSubBlock++;
737             continue;
738         }
739
740         /* We have a non-empty component: search info for key */
741         info = VersionInfo32_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock );
742         if ( !info )
743         {
744             if (puLen) *puLen = 0 ;
745             SetLastError( ERROR_RESOURCE_TYPE_NOT_FOUND );
746             return FALSE;
747         }
748
749         /* Skip path component */
750         lpSubBlock = lpNextSlash;
751     }
752
753     /* Return value */
754     *lplpBuffer = VersionInfo32_Value( info );
755     if (puLen)
756         *puLen = info->wValueLength;
757
758     return TRUE;
759 }
760
761 /***********************************************************************
762  *           VerQueryValueA              [VERSION.@]
763  */
764 BOOL WINAPI VerQueryValueA( LPVOID pBlock, LPCSTR lpSubBlock,
765                                LPVOID *lplpBuffer, UINT *puLen )
766 {
767     static const char rootA[] = "\\";
768     static const char varfileinfoA[] = "\\VarFileInfo\\Translation";
769     VS_VERSION_INFO_STRUCT16 *info = (VS_VERSION_INFO_STRUCT16 *)pBlock;
770
771     TRACE("(%p,%s,%p,%p)\n",
772                 pBlock, debugstr_a(lpSubBlock), lplpBuffer, puLen );
773
774     if ( !VersionInfoIs16( info ) )
775     {
776         BOOL ret;
777         INT len;
778         LPWSTR lpSubBlockW;
779
780         len  = MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, NULL, 0);
781         lpSubBlockW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
782
783         if (!lpSubBlockW)
784             return FALSE;
785
786         MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, lpSubBlockW, len);
787
788         ret = VersionInfo32_QueryValue(pBlock, lpSubBlockW, lplpBuffer, puLen);
789
790         HeapFree(GetProcessHeap(), 0, lpSubBlockW);
791
792         if (ret && strcasecmp( lpSubBlock, rootA ) && strcasecmp( lpSubBlock, varfileinfoA ))
793         {
794             LPSTR lpBufferA = (LPSTR)pBlock + info->wLength + 4;
795             DWORD pos = (LPSTR)*lplpBuffer - (LPSTR)pBlock;
796
797             len = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)*lplpBuffer, -1,
798                                       lpBufferA + pos, info->wLength - pos, NULL, NULL);
799             *lplpBuffer = lpBufferA + pos;
800             *puLen = len;
801         }
802         return ret;
803     }
804
805     return VersionInfo16_QueryValue(info, lpSubBlock, lplpBuffer, puLen);
806 }
807
808 /***********************************************************************
809  *           VerQueryValueW              [VERSION.@]
810  */
811 BOOL WINAPI VerQueryValueW( LPVOID pBlock, LPCWSTR lpSubBlock,
812                                LPVOID *lplpBuffer, UINT *puLen )
813 {
814     static const WCHAR rootW[] = { '\\', 0 };
815     static const WCHAR varfileinfoW[] = { '\\','V','a','r','F','i','l','e','I','n','f','o',
816                                           '\\','T','r','a','n','s','l','a','t','i','o','n', 0 };
817
818     VS_VERSION_INFO_STRUCT32 *info = (VS_VERSION_INFO_STRUCT32 *)pBlock;
819
820     TRACE("(%p,%s,%p,%p)\n",
821                 pBlock, debugstr_w(lpSubBlock), lplpBuffer, puLen );
822
823     if ( VersionInfoIs16( info ) )
824     {
825         BOOL ret;
826         int len;
827         LPSTR lpSubBlockA;
828
829         len = WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, NULL, 0, NULL, NULL);
830         lpSubBlockA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
831
832         if (!lpSubBlockA)
833             return FALSE;
834
835         WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, lpSubBlockA, len, NULL, NULL);
836
837         ret = VersionInfo16_QueryValue(pBlock, lpSubBlockA, lplpBuffer, puLen);
838
839         HeapFree(GetProcessHeap(), 0, lpSubBlockA);
840
841         if (ret && strcmpiW( lpSubBlock, rootW ) && strcmpiW( lpSubBlock, varfileinfoW ))
842         {
843             LPWSTR lpBufferW = (LPWSTR)((LPSTR)pBlock + info->wLength);
844             DWORD pos = (LPSTR)*lplpBuffer - (LPSTR)pBlock;
845             DWORD max = (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4 - info->wLength;
846
847             len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)*lplpBuffer, -1,
848                                       lpBufferW + pos, max/sizeof(WCHAR) - pos );
849             *lplpBuffer = lpBufferW + pos;
850             *puLen = len;
851         }
852         return ret;
853     }
854
855     return VersionInfo32_QueryValue(info, lpSubBlock, lplpBuffer, puLen);
856 }