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