msi: Fix an off-by-one error in STREAMS_find_matching_rows.
[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 "winuser.h"
33 #include "winternl.h"
34 #include "lzexpand.h"
35 #include "wine/unicode.h"
36 #include "winerror.h"
37 #include "wine/debug.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(ver);
40
41 extern DWORD find_version_resource( HFILE lzfd, DWORD *reslen, DWORD *offset );
42
43 /******************************************************************************
44  *
45  *   This function will print via standard TRACE, 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(const VS_FIXEDFILEINFO *vffi)
52 {
53     BOOL    versioned_printer = FALSE;
54
55     if((vffi->dwFileType == VFT_DLL) || (vffi->dwFileType == VFT_DRV))
56     {
57         if(vffi->dwFileSubtype == VFT2_DRV_VERSIONED_PRINTER)
58             /* this is documented for newer w2k Drivers and up */
59             versioned_printer = TRUE;
60         else if( (vffi->dwFileSubtype == VFT2_DRV_PRINTER) &&
61                  (vffi->dwFileVersionMS != vffi->dwProductVersionMS) &&
62                  (vffi->dwFileVersionMS > 0) &&
63                  (vffi->dwFileVersionMS <= 3) )
64             /* found this on NT 3.51, NT4.0 and old w2k Drivers */
65             versioned_printer = TRUE;
66     }
67
68     TRACE("structversion=%u.%u, ",
69             HIWORD(vffi->dwStrucVersion),LOWORD(vffi->dwStrucVersion));
70     if(versioned_printer)
71     {
72         WORD mode = LOWORD(vffi->dwFileVersionMS);
73         WORD ver_rev = HIWORD(vffi->dwFileVersionLS);
74         TRACE("fileversion=%u.%u.%u.%u (%s.major.minor.release), ",
75             (vffi->dwFileVersionMS),
76             HIBYTE(ver_rev), LOBYTE(ver_rev), LOWORD(vffi->dwFileVersionLS),
77             (mode == 3) ? "Usermode" : ((mode <= 2) ? "Kernelmode" : "?") );
78     }
79     else
80     {
81         TRACE("fileversion=%u.%u.%u.%u, ",
82             HIWORD(vffi->dwFileVersionMS),LOWORD(vffi->dwFileVersionMS),
83             HIWORD(vffi->dwFileVersionLS),LOWORD(vffi->dwFileVersionLS));
84     }
85     TRACE("productversion=%u.%u.%u.%u\n",
86           HIWORD(vffi->dwProductVersionMS),LOWORD(vffi->dwProductVersionMS),
87           HIWORD(vffi->dwProductVersionLS),LOWORD(vffi->dwProductVersionLS));
88
89     TRACE("flagmask=0x%x, flags=0x%x %s%s%s%s%s%s\n",
90           vffi->dwFileFlagsMask, vffi->dwFileFlags,
91           (vffi->dwFileFlags & VS_FF_DEBUG) ? "DEBUG," : "",
92           (vffi->dwFileFlags & VS_FF_PRERELEASE) ? "PRERELEASE," : "",
93           (vffi->dwFileFlags & VS_FF_PATCHED) ? "PATCHED," : "",
94           (vffi->dwFileFlags & VS_FF_PRIVATEBUILD) ? "PRIVATEBUILD," : "",
95           (vffi->dwFileFlags & VS_FF_INFOINFERRED) ? "INFOINFERRED," : "",
96           (vffi->dwFileFlags & VS_FF_SPECIALBUILD) ? "SPECIALBUILD," : "");
97
98     TRACE("(");
99
100     TRACE("OS=0x%x.0x%x ", HIWORD(vffi->dwFileOS), LOWORD(vffi->dwFileOS));
101
102     switch (vffi->dwFileOS&0xFFFF0000)
103     {
104     case VOS_DOS:TRACE("DOS,");break;
105     case VOS_OS216:TRACE("OS/2-16,");break;
106     case VOS_OS232:TRACE("OS/2-32,");break;
107     case VOS_NT:TRACE("NT,");break;
108     case VOS_UNKNOWN:
109     default:
110         TRACE("UNKNOWN(0x%x),",vffi->dwFileOS&0xFFFF0000);break;
111     }
112
113     switch (LOWORD(vffi->dwFileOS))
114     {
115     case VOS__BASE:TRACE("BASE");break;
116     case VOS__WINDOWS16:TRACE("WIN16");break;
117     case VOS__WINDOWS32:TRACE("WIN32");break;
118     case VOS__PM16:TRACE("PM16");break;
119     case VOS__PM32:TRACE("PM32");break;
120     default:
121         TRACE("UNKNOWN(0x%x)",LOWORD(vffi->dwFileOS));break;
122     }
123
124     TRACE(")\n");
125
126     switch (vffi->dwFileType)
127     {
128     case VFT_APP:TRACE("filetype=APP");break;
129     case VFT_DLL:
130         TRACE("filetype=DLL");
131         if(vffi->dwFileSubtype != 0)
132         {
133             if(versioned_printer) /* NT3.x/NT4.0 or old w2k Driver  */
134                 TRACE(",PRINTER");
135             TRACE(" (subtype=0x%x)", vffi->dwFileSubtype);
136         }
137         break;
138     case VFT_DRV:
139         TRACE("filetype=DRV,");
140         switch(vffi->dwFileSubtype)
141         {
142         case VFT2_DRV_PRINTER:TRACE("PRINTER");break;
143         case VFT2_DRV_KEYBOARD:TRACE("KEYBOARD");break;
144         case VFT2_DRV_LANGUAGE:TRACE("LANGUAGE");break;
145         case VFT2_DRV_DISPLAY:TRACE("DISPLAY");break;
146         case VFT2_DRV_MOUSE:TRACE("MOUSE");break;
147         case VFT2_DRV_NETWORK:TRACE("NETWORK");break;
148         case VFT2_DRV_SYSTEM:TRACE("SYSTEM");break;
149         case VFT2_DRV_INSTALLABLE:TRACE("INSTALLABLE");break;
150         case VFT2_DRV_SOUND:TRACE("SOUND");break;
151         case VFT2_DRV_COMM:TRACE("COMM");break;
152         case VFT2_DRV_INPUTMETHOD:TRACE("INPUTMETHOD");break;
153         case VFT2_DRV_VERSIONED_PRINTER:TRACE("VERSIONED_PRINTER");break;
154         case VFT2_UNKNOWN:
155         default:
156             TRACE("UNKNOWN(0x%x)",vffi->dwFileSubtype);break;
157         }
158         break;
159     case VFT_FONT:
160         TRACE("filetype=FONT,");
161         switch (vffi->dwFileSubtype)
162         {
163         case VFT2_FONT_RASTER:TRACE("RASTER");break;
164         case VFT2_FONT_VECTOR:TRACE("VECTOR");break;
165         case VFT2_FONT_TRUETYPE:TRACE("TRUETYPE");break;
166         default:TRACE("UNKNOWN(0x%x)",vffi->dwFileSubtype);break;
167         }
168         break;
169     case VFT_VXD:TRACE("filetype=VXD");break;
170     case VFT_STATIC_LIB:TRACE("filetype=STATIC_LIB");break;
171     case VFT_UNKNOWN:
172     default:
173         TRACE("filetype=Unknown(0x%x)",vffi->dwFileType);break;
174     }
175
176     TRACE("\n");
177     TRACE("filedate=0x%x.0x%x\n",vffi->dwFileDateMS,vffi->dwFileDateLS);
178 }
179
180 /***********************************************************************
181  * Version Info Structure
182  */
183
184 typedef struct
185 {
186     WORD  wLength;
187     WORD  wValueLength;
188     CHAR  szKey[1];
189 #if 0   /* variable length structure */
190     /* DWORD aligned */
191     BYTE  Value[];
192     /* DWORD aligned */
193     VS_VERSION_INFO_STRUCT16 Children[];
194 #endif
195 } VS_VERSION_INFO_STRUCT16;
196
197 typedef struct
198 {
199     WORD  wLength;
200     WORD  wValueLength;
201     WORD  wType;
202     WCHAR szKey[1];
203 #if 0   /* variable length structure */
204     /* DWORD aligned */
205     BYTE  Value[];
206     /* DWORD aligned */
207     VS_VERSION_INFO_STRUCT32 Children[];
208 #endif
209 } VS_VERSION_INFO_STRUCT32;
210
211 #define VersionInfoIs16( ver ) \
212     ( ((const VS_VERSION_INFO_STRUCT16 *)ver)->szKey[0] >= ' ' )
213
214 #define DWORD_ALIGN( base, ptr ) \
215     ( (LPBYTE)(base) + ((((LPBYTE)(ptr) - (LPBYTE)(base)) + 3) & ~3) )
216
217 #define VersionInfo16_Value( ver )  \
218     DWORD_ALIGN( (ver), (ver)->szKey + strlen((ver)->szKey) + 1 )
219 #define VersionInfo32_Value( ver )  \
220     DWORD_ALIGN( (ver), (ver)->szKey + strlenW((ver)->szKey) + 1 )
221
222 #define VersionInfo16_Children( ver )  \
223     (const VS_VERSION_INFO_STRUCT16 *)( VersionInfo16_Value( ver ) + \
224                            ( ( (ver)->wValueLength + 3 ) & ~3 ) )
225 #define VersionInfo32_Children( ver )  \
226     (const VS_VERSION_INFO_STRUCT32 *)( VersionInfo32_Value( ver ) + \
227                            ( ( (ver)->wValueLength * \
228                                ((ver)->wType? 2 : 1) + 3 ) & ~3 ) )
229
230 #define VersionInfo16_Next( ver ) \
231     (VS_VERSION_INFO_STRUCT16 *)( (LPBYTE)ver + (((ver)->wLength + 3) & ~3) )
232 #define VersionInfo32_Next( ver ) \
233     (VS_VERSION_INFO_STRUCT32 *)( (LPBYTE)ver + (((ver)->wLength + 3) & ~3) )
234
235
236 /***********************************************************************
237  *           GetFileVersionInfoSizeW         [VERSION.@]
238  */
239 DWORD WINAPI GetFileVersionInfoSizeW( LPCWSTR filename, LPDWORD handle )
240 {
241     DWORD len, offset, magic = 0;
242     HFILE lzfd;
243     HMODULE hModule;
244     OFSTRUCT ofs;
245
246     TRACE("(%s,%p)\n", debugstr_w(filename), handle );
247
248     if (handle) *handle = 0;
249
250     if (!filename)
251     {
252         SetLastError(ERROR_INVALID_PARAMETER);
253         return 0;
254     }
255     if (!*filename)
256     {
257         SetLastError(ERROR_BAD_PATHNAME);
258         return 0;
259     }
260
261     if ((lzfd = LZOpenFileW( (LPWSTR)filename, &ofs, OF_READ )) != HFILE_ERROR)
262     {
263         magic = find_version_resource( lzfd, &len, &offset );
264         LZClose( lzfd );
265     }
266     else if ((hModule = LoadLibraryExW( filename, 0, LOAD_LIBRARY_AS_DATAFILE )))
267     {
268         HRSRC hRsrc = FindResourceW( hModule, MAKEINTRESOURCEW(VS_VERSION_INFO),
269                                      MAKEINTRESOURCEW(VS_FILE_INFO) );
270         if (hRsrc)
271         {
272             magic = IMAGE_NT_SIGNATURE;
273             len = SizeofResource( hModule, hRsrc );
274         }
275         FreeLibrary( hModule );
276     }
277     else
278     {
279         SetLastError( ofs.nErrCode );
280         return 0;
281     }
282
283     switch (magic)
284     {
285     case IMAGE_OS2_SIGNATURE:
286         /* We have a 16bit resource.
287          *
288          * XP/W2K/W2K3 uses a buffer which is more than the actual needed space:
289          *
290          * (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4
291          *
292          * This extra buffer is used for ANSI to Unicode conversions in W-Calls.
293          * info->wLength should be the same as len. Currently it isn't but that
294          * doesn't seem to be a problem (len is bigger than info->wLength).
295          */
296         SetLastError(0);
297         return (len - sizeof(VS_FIXEDFILEINFO)) * 4;
298
299     case IMAGE_NT_SIGNATURE:
300         /* We have a 32bit resource.
301          *
302          * XP/W2K/W2K3 uses a buffer which is 2 times the actual needed space + 4 bytes "FE2X"
303          * This extra buffer is used for Unicode to ANSI conversions in A-Calls
304          */
305         SetLastError(0);
306         return (len * 2) + 4;
307
308     default:
309         SetLastError(ERROR_RESOURCE_DATA_NOT_FOUND);
310         return 0;
311     }
312 }
313
314 /***********************************************************************
315  *           GetFileVersionInfoSizeA         [VERSION.@]
316  */
317 DWORD WINAPI GetFileVersionInfoSizeA( LPCSTR filename, LPDWORD handle )
318 {
319     UNICODE_STRING filenameW;
320     DWORD retval;
321
322     TRACE("(%s,%p)\n", debugstr_a(filename), handle );
323
324     if(filename)
325         RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
326     else
327         filenameW.Buffer = NULL;
328
329     retval = GetFileVersionInfoSizeW(filenameW.Buffer, handle);
330
331     RtlFreeUnicodeString(&filenameW);
332
333     return retval;
334 }
335
336 /***********************************************************************
337  *           GetFileVersionInfoW             [VERSION.@]
338  */
339 BOOL WINAPI GetFileVersionInfoW( LPCWSTR filename, DWORD handle,
340                                     DWORD datasize, LPVOID data )
341 {
342     static const char signature[4] = "FE2X";
343     DWORD len, offset, magic = 0;
344     HFILE lzfd;
345     OFSTRUCT ofs;
346     HMODULE hModule;
347     VS_VERSION_INFO_STRUCT32* vvis = data;
348
349     TRACE("(%s,%d,size=%d,data=%p)\n",
350                 debugstr_w(filename), handle, datasize, data );
351
352     if (!data)
353     {
354         SetLastError(ERROR_INVALID_DATA);
355         return FALSE;
356     }
357
358     if ((lzfd = LZOpenFileW( (LPWSTR)filename, &ofs, OF_READ )) != HFILE_ERROR)
359     {
360         if ((magic = find_version_resource( lzfd, &len, &offset )))
361         {
362             LZSeek( lzfd, offset, 0 /* SEEK_SET */ );
363             len = LZRead( lzfd, data, min( len, datasize ) );
364         }
365         LZClose( lzfd );
366     }
367     else if ((hModule = LoadLibraryExW( filename, 0, LOAD_LIBRARY_AS_DATAFILE )))
368     {
369         HRSRC hRsrc = FindResourceW( hModule, MAKEINTRESOURCEW(VS_VERSION_INFO),
370                                      MAKEINTRESOURCEW(VS_FILE_INFO) );
371         if (hRsrc)
372         {
373             HGLOBAL hMem = LoadResource( hModule, hRsrc );
374             magic = IMAGE_NT_SIGNATURE;
375             len = min( SizeofResource(hModule, hRsrc), datasize );
376             memcpy( data, LockResource( hMem ), len );
377             FreeResource( hMem );
378         }
379         FreeLibrary( hModule );
380     }
381     else
382     {
383         SetLastError( ofs.nErrCode );
384         return FALSE;
385     }
386
387     switch (magic)
388     {
389     case IMAGE_OS2_SIGNATURE:
390         /* We have a 16bit resource. */
391         if (TRACE_ON(ver))
392             print_vffi_debug( (VS_FIXEDFILEINFO *)VersionInfo16_Value( (VS_VERSION_INFO_STRUCT16 *)data ));
393         SetLastError(0);
394         return TRUE;
395
396     case IMAGE_NT_SIGNATURE:
397         /* We have a 32bit resource.
398          *
399          * XP/W2K/W2K3 uses a buffer which is 2 times the actual needed space + 4 bytes "FE2X"
400          * This extra buffer is used for Unicode to ANSI conversions in A-Calls
401          */
402         len = vvis->wLength + sizeof(signature);
403         if (datasize >= len) memcpy( (char*)data + vvis->wLength, signature, sizeof(signature) );
404         if (TRACE_ON(ver))
405             print_vffi_debug( (VS_FIXEDFILEINFO *)VersionInfo32_Value( vvis ));
406         SetLastError(0);
407         return TRUE;
408
409     default:
410         SetLastError(ERROR_RESOURCE_DATA_NOT_FOUND);
411         return FALSE;
412     }
413 }
414
415 /***********************************************************************
416  *           GetFileVersionInfoA             [VERSION.@]
417  */
418 BOOL WINAPI GetFileVersionInfoA( LPCSTR filename, DWORD handle,
419                                     DWORD datasize, LPVOID data )
420 {
421     UNICODE_STRING filenameW;
422     BOOL retval;
423
424     TRACE("(%s,%d,size=%d,data=%p)\n",
425                 debugstr_a(filename), handle, datasize, data );
426
427     if(filename)
428         RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
429     else
430         filenameW.Buffer = NULL;
431
432     retval = GetFileVersionInfoW(filenameW.Buffer, handle, datasize, data);
433
434     RtlFreeUnicodeString(&filenameW);
435
436     return retval;
437 }
438
439 /***********************************************************************
440  *           VersionInfo16_FindChild             [internal]
441  */
442 static const VS_VERSION_INFO_STRUCT16 *VersionInfo16_FindChild( const VS_VERSION_INFO_STRUCT16 *info,
443                                             LPCSTR szKey, UINT cbKey )
444 {
445     const VS_VERSION_INFO_STRUCT16 *child = VersionInfo16_Children( info );
446
447     while ((char *)child < (char *)info + info->wLength )
448     {
449         if (!strncasecmp( child->szKey, szKey, cbKey ) && !child->szKey[cbKey])
450             return child;
451
452         if (!(child->wLength)) return NULL;
453         child = VersionInfo16_Next( child );
454     }
455
456     return NULL;
457 }
458
459 /***********************************************************************
460  *           VersionInfo32_FindChild             [internal]
461  */
462 static const VS_VERSION_INFO_STRUCT32 *VersionInfo32_FindChild( const VS_VERSION_INFO_STRUCT32 *info,
463                                             LPCWSTR szKey, UINT cbKey )
464 {
465     const VS_VERSION_INFO_STRUCT32 *child = VersionInfo32_Children( info );
466
467     while ((char *)child < (char *)info + info->wLength )
468     {
469         if (!strncmpiW( child->szKey, szKey, cbKey ) && !child->szKey[cbKey])
470             return child;
471
472         if (!(child->wLength)) return NULL;
473         child = VersionInfo32_Next( child );
474     }
475
476     return NULL;
477 }
478
479 /***********************************************************************
480  *           VersionInfo16_QueryValue              [internal]
481  *
482  *    Gets a value from a 16-bit NE resource
483  */
484 static BOOL VersionInfo16_QueryValue( const VS_VERSION_INFO_STRUCT16 *info, LPCSTR lpSubBlock,
485                                LPVOID *lplpBuffer, UINT *puLen )
486 {
487     while ( *lpSubBlock )
488     {
489         /* Find next path component */
490         LPCSTR lpNextSlash;
491         for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ )
492             if ( *lpNextSlash == '\\' )
493                 break;
494
495         /* Skip empty components */
496         if ( lpNextSlash == lpSubBlock )
497         {
498             lpSubBlock++;
499             continue;
500         }
501
502         /* We have a non-empty component: search info for key */
503         info = VersionInfo16_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock );
504         if ( !info )
505         {
506             if (puLen) *puLen = 0 ;
507             SetLastError( ERROR_RESOURCE_TYPE_NOT_FOUND );
508             return FALSE;
509         }
510
511         /* Skip path component */
512         lpSubBlock = lpNextSlash;
513     }
514
515     /* Return value */
516     *lplpBuffer = VersionInfo16_Value( info );
517     if (puLen)
518         *puLen = info->wValueLength;
519
520     return TRUE;
521 }
522
523 /***********************************************************************
524  *           VersionInfo32_QueryValue              [internal]
525  *
526  *    Gets a value from a 32-bit PE resource
527  */
528 static BOOL VersionInfo32_QueryValue( const VS_VERSION_INFO_STRUCT32 *info, LPCWSTR lpSubBlock,
529                                LPVOID *lplpBuffer, UINT *puLen )
530 {
531     TRACE("lpSubBlock : (%s)\n", debugstr_w(lpSubBlock));
532
533     while ( *lpSubBlock )
534     {
535         /* Find next path component */
536         LPCWSTR lpNextSlash;
537         for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ )
538             if ( *lpNextSlash == '\\' )
539                 break;
540
541         /* Skip empty components */
542         if ( lpNextSlash == lpSubBlock )
543         {
544             lpSubBlock++;
545             continue;
546         }
547
548         /* We have a non-empty component: search info for key */
549         info = VersionInfo32_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock );
550         if ( !info )
551         {
552             if (puLen) *puLen = 0 ;
553             SetLastError( ERROR_RESOURCE_TYPE_NOT_FOUND );
554             return FALSE;
555         }
556
557         /* Skip path component */
558         lpSubBlock = lpNextSlash;
559     }
560
561     /* Return value */
562     *lplpBuffer = VersionInfo32_Value( info );
563     if (puLen)
564         *puLen = info->wValueLength;
565
566     return TRUE;
567 }
568
569 /***********************************************************************
570  *           VerQueryValueA              [VERSION.@]
571  */
572 BOOL WINAPI VerQueryValueA( LPCVOID pBlock, LPCSTR lpSubBlock,
573                                LPVOID *lplpBuffer, PUINT puLen )
574 {
575     static const char rootA[] = "\\";
576     static const char varfileinfoA[] = "\\VarFileInfo\\Translation";
577     const VS_VERSION_INFO_STRUCT16 *info = pBlock;
578
579     TRACE("(%p,%s,%p,%p)\n",
580                 pBlock, debugstr_a(lpSubBlock), lplpBuffer, puLen );
581
582      if (!pBlock)
583         return FALSE;
584
585     if (lpSubBlock == NULL || lpSubBlock[0] == '\0')
586         lpSubBlock = rootA;
587
588     if ( !VersionInfoIs16( info ) )
589     {
590         BOOL ret;
591         INT len;
592         LPWSTR lpSubBlockW;
593
594         len  = MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, NULL, 0);
595         lpSubBlockW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
596
597         if (!lpSubBlockW)
598             return FALSE;
599
600         MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, lpSubBlockW, len);
601
602         ret = VersionInfo32_QueryValue(pBlock, lpSubBlockW, lplpBuffer, puLen);
603
604         HeapFree(GetProcessHeap(), 0, lpSubBlockW);
605
606         if (ret && strcasecmp( lpSubBlock, rootA ) && strcasecmp( lpSubBlock, varfileinfoA ))
607         {
608             /* Set lpBuffer so it points to the 'empty' area where we store
609              * the converted strings
610              */
611             LPSTR lpBufferA = (LPSTR)pBlock + info->wLength + 4;
612             DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock;
613
614             len = WideCharToMultiByte(CP_ACP, 0, *lplpBuffer, -1,
615                                       lpBufferA + pos, info->wLength - pos, NULL, NULL);
616             *lplpBuffer = lpBufferA + pos;
617             *puLen = len;
618         }
619         return ret;
620     }
621
622     return VersionInfo16_QueryValue(info, lpSubBlock, lplpBuffer, puLen);
623 }
624
625 /***********************************************************************
626  *           VerQueryValueW              [VERSION.@]
627  */
628 BOOL WINAPI VerQueryValueW( LPCVOID pBlock, LPCWSTR lpSubBlock,
629                                LPVOID *lplpBuffer, PUINT puLen )
630 {
631     static const WCHAR nullW[] = { 0 };
632     static const WCHAR rootW[] = { '\\', 0 };
633     static const WCHAR varfileinfoW[] = { '\\','V','a','r','F','i','l','e','I','n','f','o',
634                                           '\\','T','r','a','n','s','l','a','t','i','o','n', 0 };
635
636     const VS_VERSION_INFO_STRUCT32 *info = pBlock;
637
638     TRACE("(%p,%s,%p,%p)\n",
639                 pBlock, debugstr_w(lpSubBlock), lplpBuffer, puLen );
640
641     if (!pBlock)
642         return FALSE;
643
644     if (lpSubBlock == NULL || lpSubBlock[0] == nullW[0])
645         lpSubBlock = rootW;
646
647     if ( VersionInfoIs16( info ) )
648     {
649         BOOL ret;
650         int len;
651         LPSTR lpSubBlockA;
652
653         len = WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, NULL, 0, NULL, NULL);
654         lpSubBlockA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
655
656         if (!lpSubBlockA)
657             return FALSE;
658
659         WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, lpSubBlockA, len, NULL, NULL);
660
661         ret = VersionInfo16_QueryValue(pBlock, lpSubBlockA, lplpBuffer, puLen);
662
663         HeapFree(GetProcessHeap(), 0, lpSubBlockA);
664
665         if (ret && strcmpiW( lpSubBlock, rootW ) && strcmpiW( lpSubBlock, varfileinfoW ))
666         {
667             /* Set lpBuffer so it points to the 'empty' area where we store
668              * the converted strings
669              */
670             LPWSTR lpBufferW = (LPWSTR)((LPSTR)pBlock + info->wLength);
671             DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock;
672             DWORD max = (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4 - info->wLength;
673
674             len = MultiByteToWideChar(CP_ACP, 0, *lplpBuffer, -1,
675                                       lpBufferW + pos, max/sizeof(WCHAR) - pos );
676             *lplpBuffer = lpBufferW + pos;
677             *puLen = len;
678         }
679         return ret;
680     }
681
682     return VersionInfo32_QueryValue(info, lpSubBlock, lplpBuffer, puLen);
683 }