Commit | Line | Data |
---|---|---|
9a624916 | 1 | /* |
ce821773 | 2 | * Implementation of VERSION.DLL - Version Info access |
9a624916 | 3 | * |
ce821773 UW |
4 | * Copyright 1996,1997 Marcus Meissner |
5 | * Copyright 1997 David Cuthbert | |
6 | * Copyright 1999 Ulrich Weigand | |
5b2810bb | 7 | * Copyright 2005 Paul Vriens |
0799c1a7 AJ |
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 | |
360a3f91 | 21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
b99ab2dd | 22 | * |
ce821773 UW |
23 | */ |
24 | ||
e37c6e18 | 25 | #include <stdarg.h> |
ce821773 UW |
26 | #include <stdlib.h> |
27 | #include <string.h> | |
28 | ||
e37c6e18 AJ |
29 | #include "windef.h" |
30 | #include "winbase.h" | |
317af320 | 31 | #include "winver.h" |
1ce19853 | 32 | #include "winternl.h" |
b85a6adc | 33 | #include "wine/winuser16.h" |
5ea22474 | 34 | #include "wine/unicode.h" |
ce821773 | 35 | #include "winerror.h" |
0799c1a7 | 36 | #include "wine/debug.h" |
ce821773 | 37 | |
0799c1a7 | 38 | WINE_DEFAULT_DEBUG_CHANNEL(ver); |
b4b9fae6 | 39 | |
ce821773 UW |
40 | /****************************************************************************** |
41 | * | |
c232ce43 | 42 | * This function will print via standard TRACE, debug info regarding |
ce821773 UW |
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 | *****************************************************************************/ | |
b940df14 | 48 | static void print_vffi_debug(const VS_FIXEDFILEINFO *vffi) |
ce821773 | 49 | { |
2ee26f21 DR |
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); | |
54b1c5e0 | 71 | TRACE("fileversion=%u.%u.%u.%u (%s.major.minor.release), ", |
2ee26f21 DR |
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", | |
c232ce43 | 83 | HIWORD(vffi->dwProductVersionMS),LOWORD(vffi->dwProductVersionMS), |
2ee26f21 DR |
84 | HIWORD(vffi->dwProductVersionLS),LOWORD(vffi->dwProductVersionLS)); |
85 | ||
54b1c5e0 | 86 | TRACE("flagmask=0x%x, flags=0x%x %s%s%s%s%s%s\n", |
2ee26f21 | 87 | vffi->dwFileFlagsMask, vffi->dwFileFlags, |
c232ce43 PV |
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," : ""); | |
ce821773 | 94 | |
c232ce43 | 95 | TRACE("("); |
ce821773 | 96 | |
c232ce43 PV |
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: | |
54b1c5e0 | 107 | TRACE("UNKNOWN(0x%x),",vffi->dwFileOS&0xFFFF0000);break; |
c232ce43 PV |
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 | } | |
ce821773 | 120 | |
c232ce43 PV |
121 | TRACE(")\n"); |
122 | ||
123 | switch (vffi->dwFileType) | |
124 | { | |
125 | case VFT_APP:TRACE("filetype=APP");break; | |
2ee26f21 DR |
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"); | |
54b1c5e0 | 132 | TRACE(" (subtype=0x%x)", vffi->dwFileSubtype); |
2ee26f21 DR |
133 | } |
134 | break; | |
c232ce43 PV |
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; | |
2ee26f21 | 150 | case VFT2_DRV_VERSIONED_PRINTER:TRACE("VERSIONED_PRINTER");break; |
c232ce43 PV |
151 | case VFT2_UNKNOWN: |
152 | default: | |
54b1c5e0 | 153 | TRACE("UNKNOWN(0x%x)",vffi->dwFileSubtype);break; |
c232ce43 PV |
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; | |
54b1c5e0 | 163 | default:TRACE("UNKNOWN(0x%x)",vffi->dwFileSubtype);break; |
c232ce43 PV |
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: | |
54b1c5e0 | 170 | TRACE("filetype=Unknown(0x%x)",vffi->dwFileType);break; |
c232ce43 PV |
171 | } |
172 | ||
173 | TRACE("\n"); | |
54b1c5e0 | 174 | TRACE("filedate=0x%x.0x%x\n",vffi->dwFileDateMS,vffi->dwFileDateLS); |
c232ce43 | 175 | } |
ce821773 UW |
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 */ | |
4ca9d755 | 190 | VS_VERSION_INFO_STRUCT16 Children[]; |
ce821773 | 191 | #endif |
4ca9d755 | 192 | } VS_VERSION_INFO_STRUCT16; |
ce821773 UW |
193 | |
194 | typedef struct | |
195 | { | |
196 | WORD wLength; | |
197 | WORD wValueLength; | |
98e33334 | 198 | WORD wType; |
ce821773 UW |
199 | WCHAR szKey[1]; |
200 | #if 0 /* variable length structure */ | |
201 | /* DWORD aligned */ | |
202 | BYTE Value[]; | |
203 | /* DWORD aligned */ | |
4ca9d755 | 204 | VS_VERSION_INFO_STRUCT32 Children[]; |
ce821773 | 205 | #endif |
4ca9d755 | 206 | } VS_VERSION_INFO_STRUCT32; |
ce821773 UW |
207 | |
208 | #define VersionInfoIs16( ver ) \ | |
ab91c216 | 209 | ( ((const VS_VERSION_INFO_STRUCT16 *)ver)->szKey[0] >= ' ' ) |
ce821773 | 210 | |
f030d752 UW |
211 | #define DWORD_ALIGN( base, ptr ) \ |
212 | ( (LPBYTE)(base) + ((((LPBYTE)(ptr) - (LPBYTE)(base)) + 3) & ~3) ) | |
c9df14d8 | 213 | |
ce821773 | 214 | #define VersionInfo16_Value( ver ) \ |
c7e7df8b | 215 | DWORD_ALIGN( (ver), (ver)->szKey + strlen((ver)->szKey) + 1 ) |
ce821773 | 216 | #define VersionInfo32_Value( ver ) \ |
c7e7df8b | 217 | DWORD_ALIGN( (ver), (ver)->szKey + strlenW((ver)->szKey) + 1 ) |
ce821773 UW |
218 | |
219 | #define VersionInfo16_Children( ver ) \ | |
ab91c216 | 220 | (const VS_VERSION_INFO_STRUCT16 *)( VersionInfo16_Value( ver ) + \ |
ce821773 UW |
221 | ( ( (ver)->wValueLength + 3 ) & ~3 ) ) |
222 | #define VersionInfo32_Children( ver ) \ | |
ab91c216 | 223 | (const VS_VERSION_INFO_STRUCT32 *)( VersionInfo32_Value( ver ) + \ |
ce821773 | 224 | ( ( (ver)->wValueLength * \ |
98e33334 | 225 | ((ver)->wType? 2 : 1) + 3 ) & ~3 ) ) |
ce821773 UW |
226 | |
227 | #define VersionInfo16_Next( ver ) \ | |
4ca9d755 | 228 | (VS_VERSION_INFO_STRUCT16 *)( (LPBYTE)ver + (((ver)->wLength + 3) & ~3) ) |
ce821773 | 229 | #define VersionInfo32_Next( ver ) \ |
4ca9d755 | 230 | (VS_VERSION_INFO_STRUCT32 *)( (LPBYTE)ver + (((ver)->wLength + 3) & ~3) ) |
ce821773 | 231 | |
8f73cd14 DT |
232 | /*********************************************************************** |
233 | * VERSION_GetFileVersionInfo_PE [internal] | |
234 | * | |
4868e715 | 235 | * NOTE: returns size of the PE VERSION resource or 0xFFFFFFFF |
97551bce | 236 | * in the case the file is a PE module, but VERSION_INFO not found. |
8f73cd14 | 237 | */ |
97551bce | 238 | static DWORD VERSION_GetFileVersionInfo_PE( LPCWSTR filename, DWORD datasize, LPVOID data ) |
8f73cd14 | 239 | { |
96c84497 | 240 | const VS_FIXEDFILEINFO *vffi; |
8f73cd14 DT |
241 | DWORD len; |
242 | BYTE *buf; | |
243 | HMODULE hModule; | |
244 | HRSRC hRsrc; | |
245 | HGLOBAL hMem; | |
8f73cd14 | 246 | |
97551bce | 247 | TRACE("%s\n", debugstr_w(filename)); |
8f73cd14 | 248 | |
09c3d32f | 249 | if (!GetModuleHandleExW(0, filename, &hModule)) |
1ce19853 | 250 | hModule = LoadLibraryExW(filename, 0, LOAD_LIBRARY_AS_DATAFILE); |
09c3d32f | 251 | |
8f73cd14 DT |
252 | if(!hModule) |
253 | { | |
1ce19853 | 254 | WARN("Could not load %s\n", debugstr_w(filename)); |
972c61d3 | 255 | |
8f73cd14 DT |
256 | return 0; |
257 | } | |
258 | hRsrc = FindResourceW(hModule, | |
259 | MAKEINTRESOURCEW(VS_VERSION_INFO), | |
260 | MAKEINTRESOURCEW(VS_FILE_INFO)); | |
261 | if(!hRsrc) | |
262 | { | |
1ce19853 | 263 | WARN("Could not find VS_VERSION_INFO in %s\n", debugstr_w(filename)); |
0ca9bba6 | 264 | FreeLibrary(hModule); |
4868e715 | 265 | return 0xFFFFFFFF; |
8f73cd14 DT |
266 | } |
267 | len = SizeofResource(hModule, hRsrc); | |
268 | hMem = LoadResource(hModule, hRsrc); | |
269 | if(!hMem) | |
270 | { | |
1ce19853 | 271 | WARN("Could not load VS_VERSION_INFO from %s\n", debugstr_w(filename)); |
0ca9bba6 | 272 | FreeLibrary(hModule); |
4868e715 | 273 | return 0xFFFFFFFF; |
8f73cd14 DT |
274 | } |
275 | buf = LockResource(hMem); | |
276 | ||
277 | vffi = (VS_FIXEDFILEINFO *)VersionInfo32_Value( (VS_VERSION_INFO_STRUCT32 *)buf ); | |
278 | ||
279 | if ( vffi->dwSignature != VS_FFI_SIGNATURE ) | |
280 | { | |
54b1c5e0 | 281 | WARN("vffi->dwSignature is 0x%08x, but not 0x%08lx!\n", |
8f73cd14 | 282 | vffi->dwSignature, VS_FFI_SIGNATURE ); |
4868e715 | 283 | len = 0xFFFFFFFF; |
8f73cd14 DT |
284 | goto END; |
285 | } | |
286 | ||
287 | if ( TRACE_ON(ver) ) | |
288 | print_vffi_debug( vffi ); | |
289 | ||
290 | if(data) | |
291 | { | |
f7e7cd4a DT |
292 | if(datasize < len) |
293 | len = datasize; /* truncate data */ | |
294 | if(len) | |
8f73cd14 DT |
295 | memcpy(data, buf, len); |
296 | else | |
4868e715 | 297 | len = 0xFFFFFFFF; |
8f73cd14 DT |
298 | } |
299 | END: | |
300 | FreeResource(hMem); | |
0ca9bba6 | 301 | FreeLibrary(hModule); |
8f73cd14 DT |
302 | |
303 | return len; | |
304 | } | |
305 | ||
306 | /*********************************************************************** | |
307 | * VERSION_GetFileVersionInfo_16 [internal] | |
308 | * | |
4868e715 | 309 | * NOTE: returns size of the 16-bit VERSION resource or 0xFFFFFFFF |
97551bce | 310 | * in the case the file exists, but VERSION_INFO not found. |
8f73cd14 | 311 | */ |
97551bce | 312 | static DWORD VERSION_GetFileVersionInfo_16( LPCSTR filename, DWORD datasize, LPVOID data ) |
8f73cd14 | 313 | { |
96c84497 | 314 | const VS_FIXEDFILEINFO *vffi; |
97551bce | 315 | DWORD len, offset; |
8f73cd14 DT |
316 | BYTE *buf; |
317 | HMODULE16 hModule; | |
318 | HRSRC16 hRsrc; | |
319 | HGLOBAL16 hMem; | |
97551bce DT |
320 | char dllname[20], owner[20], *p; |
321 | const char *basename; | |
322 | BOOL is_builtin = FALSE; | |
323 | ||
324 | TRACE("%s\n", debugstr_a(filename)); | |
8f73cd14 | 325 | |
97551bce | 326 | /* strip path information */ |
8f73cd14 | 327 | |
97551bce DT |
328 | basename = filename; |
329 | if (basename[0] && basename[1] == ':') basename += 2; /* strip drive specification */ | |
330 | if ((p = strrchr( basename, '\\' ))) basename = p + 1; | |
331 | if ((p = strrchr( basename, '/' ))) basename = p + 1; | |
332 | ||
333 | if (strlen(basename) < sizeof(dllname)-4) | |
334 | { | |
335 | int file_exists; | |
336 | ||
337 | strcpy( dllname, basename ); | |
338 | p = strrchr( dllname, '.' ); | |
339 | if (!p) strcat( dllname, ".dll" ); | |
340 | for (p = dllname; *p; p++) if (*p >= 'A' && *p <= 'Z') *p += 32; | |
341 | ||
342 | if (wine_dll_get_owner( dllname, owner, sizeof(owner), &file_exists ) == 0) | |
343 | is_builtin = TRUE; | |
344 | } | |
345 | ||
346 | /* first try without loading a 16-bit module */ | |
347 | if (is_builtin) | |
348 | len = 0; | |
349 | else | |
350 | len = GetFileResourceSize16( filename, | |
351 | MAKEINTRESOURCEA(VS_FILE_INFO), | |
352 | MAKEINTRESOURCEA(VS_VERSION_INFO), | |
353 | &offset ); | |
354 | if (len) | |
355 | { | |
356 | if (!data) return len; | |
357 | ||
358 | len = GetFileResource16( filename, | |
359 | MAKEINTRESOURCEA(VS_FILE_INFO), | |
360 | MAKEINTRESOURCEA(VS_VERSION_INFO), | |
361 | offset, datasize, data ); | |
362 | if (len) | |
363 | { | |
e5503f96 | 364 | vffi = (VS_FIXEDFILEINFO *)VersionInfo16_Value( (VS_VERSION_INFO_STRUCT16 *)data ); |
97551bce DT |
365 | |
366 | if ( vffi->dwSignature == VS_FFI_SIGNATURE ) | |
367 | { | |
368 | if ( ((VS_VERSION_INFO_STRUCT16 *)data)->wLength < len ) | |
369 | len = ((VS_VERSION_INFO_STRUCT16 *)data)->wLength; | |
370 | ||
371 | if ( TRACE_ON(ver) ) | |
372 | print_vffi_debug( vffi ); | |
373 | ||
97551bce DT |
374 | return len; |
375 | } | |
376 | } | |
377 | } | |
378 | ||
379 | /* this might be a builtin 16-bit module */ | |
0ca9bba6 | 380 | hModule = LoadLibrary16(filename); |
8f73cd14 DT |
381 | if(hModule < 32) |
382 | { | |
383 | WARN("Could not load %s\n", debugstr_a(filename)); | |
1a328d91 AS |
384 | if (hModule == ERROR_BAD_FORMAT) |
385 | return 0xFFFFFFFF; | |
386 | else | |
387 | return 0x0; | |
8f73cd14 DT |
388 | } |
389 | hRsrc = FindResource16(hModule, | |
390 | MAKEINTRESOURCEA(VS_VERSION_INFO), | |
391 | MAKEINTRESOURCEA(VS_FILE_INFO)); | |
392 | if(!hRsrc) | |
393 | { | |
394 | WARN("Could not find VS_VERSION_INFO in %s\n", debugstr_a(filename)); | |
0ca9bba6 | 395 | FreeLibrary16(hModule); |
4868e715 | 396 | return 0xFFFFFFFF; |
8f73cd14 DT |
397 | } |
398 | len = SizeofResource16(hModule, hRsrc); | |
399 | hMem = LoadResource16(hModule, hRsrc); | |
400 | if(!hMem) | |
401 | { | |
402 | WARN("Could not load VS_VERSION_INFO from %s\n", debugstr_a(filename)); | |
0ca9bba6 | 403 | FreeLibrary16(hModule); |
4868e715 | 404 | return 0xFFFFFFFF; |
8f73cd14 DT |
405 | } |
406 | buf = LockResource16(hMem); | |
407 | ||
408 | if(!VersionInfoIs16(buf)) | |
97551bce DT |
409 | { |
410 | len = 0xFFFFFFFF; | |
8f73cd14 | 411 | goto END; |
97551bce | 412 | } |
8f73cd14 DT |
413 | |
414 | vffi = (VS_FIXEDFILEINFO *)VersionInfo16_Value( (VS_VERSION_INFO_STRUCT16 *)buf ); | |
415 | ||
416 | if ( vffi->dwSignature != VS_FFI_SIGNATURE ) | |
417 | { | |
54b1c5e0 | 418 | WARN("vffi->dwSignature is 0x%08x, but not 0x%08lx!\n", |
8f73cd14 | 419 | vffi->dwSignature, VS_FFI_SIGNATURE ); |
4868e715 | 420 | len = 0xFFFFFFFF; |
8f73cd14 DT |
421 | goto END; |
422 | } | |
423 | ||
424 | if ( TRACE_ON(ver) ) | |
425 | print_vffi_debug( vffi ); | |
426 | ||
427 | if(data) | |
428 | { | |
f7e7cd4a DT |
429 | if(datasize < len) |
430 | len = datasize; /* truncate data */ | |
431 | if(len) | |
8f73cd14 DT |
432 | memcpy(data, buf, len); |
433 | else | |
4868e715 | 434 | len = 0xFFFFFFFF; |
8f73cd14 DT |
435 | } |
436 | END: | |
437 | FreeResource16(hMem); | |
0ca9bba6 | 438 | FreeLibrary16(hModule); |
8f73cd14 DT |
439 | |
440 | return len; | |
441 | } | |
ce821773 UW |
442 | |
443 | /*********************************************************************** | |
1ce19853 | 444 | * GetFileVersionInfoSizeW [VERSION.@] |
ce821773 | 445 | */ |
1ce19853 | 446 | DWORD WINAPI GetFileVersionInfoSizeW( LPCWSTR filename, LPDWORD handle ) |
ce821773 | 447 | { |
97551bce | 448 | DWORD len; |
ce821773 | 449 | |
1ce19853 SL |
450 | TRACE("(%s,%p)\n", debugstr_w(filename), handle ); |
451 | ||
97551bce | 452 | if (handle) *handle = 0; |
ce821773 | 453 | |
55cc226d DT |
454 | if (!filename) |
455 | { | |
456 | SetLastError(ERROR_INVALID_PARAMETER); | |
457 | return 0; | |
458 | } | |
459 | if (!*filename) | |
460 | { | |
461 | SetLastError(ERROR_BAD_PATHNAME); | |
462 | return 0; | |
463 | } | |
464 | ||
97551bce DT |
465 | len = VERSION_GetFileVersionInfo_PE(filename, 0, NULL); |
466 | /* 0xFFFFFFFF means: file is a PE module, but VERSION_INFO not found */ | |
1dc62b7e FG |
467 | if(len == 0xFFFFFFFF) |
468 | { | |
469 | SetLastError(ERROR_RESOURCE_DATA_NOT_FOUND); | |
97551bce | 470 | return 0; |
1ce19853 | 471 | } |
1ce19853 | 472 | |
97551bce | 473 | if (!len) |
1dc62b7e | 474 | { |
97551bce | 475 | LPSTR filenameA; |
ce821773 | 476 | |
97551bce DT |
477 | len = WideCharToMultiByte( CP_ACP, 0, filename, -1, NULL, 0, NULL, NULL ); |
478 | filenameA = HeapAlloc( GetProcessHeap(), 0, len ); | |
479 | WideCharToMultiByte( CP_ACP, 0, filename, -1, filenameA, len, NULL, NULL ); | |
ce821773 | 480 | |
97551bce DT |
481 | len = VERSION_GetFileVersionInfo_16(filenameA, 0, NULL); |
482 | HeapFree( GetProcessHeap(), 0, filenameA ); | |
483 | /* 0xFFFFFFFF means: file exists, but VERSION_INFO not found */ | |
55cc226d DT |
484 | if (!len) |
485 | { | |
486 | SetLastError(ERROR_FILE_NOT_FOUND); | |
487 | return 0; | |
488 | } | |
489 | if (len == 0xFFFFFFFF) | |
97551bce DT |
490 | { |
491 | SetLastError(ERROR_RESOURCE_DATA_NOT_FOUND); | |
492 | return 0; | |
493 | } | |
31d0e742 PV |
494 | |
495 | /* We have a 16bit resource. | |
496 | * | |
497 | * XP/W2K/W2K3 uses a buffer which is more than the actual needed space: | |
498 | * | |
499 | * (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4 | |
500 | * | |
501 | * This extra buffer is used for ANSI to Unicode conversions in W-Calls. | |
502 | * info->wLength should be the same as len. Currently it isn't but that | |
96c337f0 | 503 | * doesn't seem to be a problem (len is bigger than info->wLength). |
31d0e742 PV |
504 | */ |
505 | len = (len - sizeof(VS_FIXEDFILEINFO)) * 4; | |
506 | } | |
507 | else | |
508 | { | |
509 | /* We have a 32bit resource. | |
510 | * | |
511 | * XP/W2K/W2K3 uses a buffer which is 2 times the actual needed space + 4 bytes "FE2X" | |
512 | * This extra buffer is used for Unicode to ANSI conversions in A-Calls | |
513 | */ | |
514 | len = (len * 2) + 4; | |
ce821773 UW |
515 | } |
516 | ||
97551bce | 517 | SetLastError(0); |
ce821773 UW |
518 | return len; |
519 | } | |
520 | ||
521 | /*********************************************************************** | |
1ce19853 | 522 | * GetFileVersionInfoSizeA [VERSION.@] |
ce821773 | 523 | */ |
1ce19853 | 524 | DWORD WINAPI GetFileVersionInfoSizeA( LPCSTR filename, LPDWORD handle ) |
b296b042 PV |
525 | { |
526 | UNICODE_STRING filenameW; | |
1ce19853 | 527 | DWORD retval; |
8e541c8a PV |
528 | |
529 | TRACE("(%s,%p)\n", debugstr_a(filename), handle ); | |
530 | ||
b296b042 PV |
531 | if(filename) |
532 | RtlCreateUnicodeStringFromAsciiz(&filenameW, filename); | |
533 | else | |
534 | filenameW.Buffer = NULL; | |
4868e715 | 535 | |
b296b042 | 536 | retval = GetFileVersionInfoSizeW(filenameW.Buffer, handle); |
ce821773 | 537 | |
1ce19853 | 538 | RtlFreeUnicodeString(&filenameW); |
97551bce | 539 | |
b296b042 | 540 | return retval; |
ce821773 UW |
541 | } |
542 | ||
543 | /*********************************************************************** | |
8b216b3d | 544 | * GetFileVersionInfoW [VERSION.@] |
ce821773 | 545 | */ |
07b6e6e6 | 546 | BOOL WINAPI GetFileVersionInfoW( LPCWSTR filename, DWORD handle, |
ce821773 UW |
547 | DWORD datasize, LPVOID data ) |
548 | { | |
97551bce | 549 | DWORD len; |
31d0e742 | 550 | VS_VERSION_INFO_STRUCT32* vvis = (VS_VERSION_INFO_STRUCT32*)data; |
0e44f63c | 551 | |
54b1c5e0 | 552 | TRACE("(%s,%d,size=%d,data=%p)\n", |
0e44f63c | 553 | debugstr_w(filename), handle, datasize, data ); |
ce821773 | 554 | |
18a1a6e1 RR |
555 | if (!data) |
556 | { | |
557 | SetLastError(ERROR_INVALID_DATA); | |
558 | return FALSE; | |
559 | } | |
97551bce DT |
560 | len = VERSION_GetFileVersionInfo_PE(filename, datasize, data); |
561 | /* 0xFFFFFFFF means: file is a PE module, but VERSION_INFO not found */ | |
562 | if (len == 0xFFFFFFFF) | |
563 | { | |
564 | SetLastError(ERROR_RESOURCE_DATA_NOT_FOUND); | |
565 | return FALSE; | |
566 | } | |
8f73cd14 | 567 | |
97551bce DT |
568 | if (!len) |
569 | { | |
570 | LPSTR filenameA; | |
ce821773 | 571 | |
97551bce DT |
572 | len = WideCharToMultiByte( CP_ACP, 0, filename, -1, NULL, 0, NULL, NULL ); |
573 | filenameA = HeapAlloc( GetProcessHeap(), 0, len ); | |
574 | WideCharToMultiByte( CP_ACP, 0, filename, -1, filenameA, len, NULL, NULL ); | |
575 | ||
576 | len = VERSION_GetFileVersionInfo_16(filenameA, datasize, data); | |
577 | HeapFree( GetProcessHeap(), 0, filenameA ); | |
578 | /* 0xFFFFFFFF means: file exists, but VERSION_INFO not found */ | |
579 | if (!len || len == 0xFFFFFFFF) | |
580 | { | |
581 | SetLastError(ERROR_RESOURCE_DATA_NOT_FOUND); | |
582 | return FALSE; | |
583 | } | |
31d0e742 PV |
584 | /* We have a 16bit resource. */ |
585 | } | |
586 | else | |
587 | { | |
0eddf434 JH |
588 | static const char signature[] = "FE2X"; |
589 | DWORD bufsize = vvis->wLength + strlen(signature); | |
31d0e742 PV |
590 | DWORD convbuf; |
591 | ||
592 | /* We have a 32bit resource. | |
593 | * | |
594 | * XP/W2K/W2K3 uses a buffer which is 2 times the actual needed space + 4 bytes "FE2X" | |
595 | * This extra buffer is used for Unicode to ANSI conversions in A-Calls | |
596 | */ | |
597 | ||
0eddf434 JH |
598 | /* information is truncated to datasize bytes */ |
599 | if (datasize >= bufsize) | |
600 | { | |
601 | convbuf = datasize - vvis->wLength; | |
602 | memcpy( ((char*)(data))+vvis->wLength, signature, convbuf > 4 ? 4 : convbuf ); | |
603 | } | |
97551bce | 604 | } |
97551bce DT |
605 | |
606 | SetLastError(0); | |
607 | return TRUE; | |
ce821773 UW |
608 | } |
609 | ||
b296b042 PV |
610 | /*********************************************************************** |
611 | * GetFileVersionInfoA [VERSION.@] | |
612 | */ | |
613 | BOOL WINAPI GetFileVersionInfoA( LPCSTR filename, DWORD handle, | |
614 | DWORD datasize, LPVOID data ) | |
615 | { | |
616 | UNICODE_STRING filenameW; | |
617 | BOOL retval; | |
111a913d | 618 | |
54b1c5e0 | 619 | TRACE("(%s,%d,size=%d,data=%p)\n", |
b296b042 | 620 | debugstr_a(filename), handle, datasize, data ); |
111a913d | 621 | |
b296b042 PV |
622 | if(filename) |
623 | RtlCreateUnicodeStringFromAsciiz(&filenameW, filename); | |
624 | else | |
625 | filenameW.Buffer = NULL; | |
111a913d | 626 | |
b296b042 | 627 | retval = GetFileVersionInfoW(filenameW.Buffer, handle, datasize, data); |
111a913d | 628 | |
5b2810bb PV |
629 | RtlFreeUnicodeString(&filenameW); |
630 | ||
b296b042 PV |
631 | return retval; |
632 | } | |
ce821773 UW |
633 | |
634 | /*********************************************************************** | |
635 | * VersionInfo16_FindChild [internal] | |
636 | */ | |
ab91c216 | 637 | static const VS_VERSION_INFO_STRUCT16 *VersionInfo16_FindChild( const VS_VERSION_INFO_STRUCT16 *info, |
a3960292 | 638 | LPCSTR szKey, UINT cbKey ) |
ce821773 | 639 | { |
ab91c216 | 640 | const VS_VERSION_INFO_STRUCT16 *child = VersionInfo16_Children( info ); |
ce821773 | 641 | |
261e3764 | 642 | while ((char *)child < (char *)info + info->wLength ) |
ce821773 | 643 | { |
31368dec | 644 | if (!strncasecmp( child->szKey, szKey, cbKey ) && !child->szKey[cbKey]) |
ce821773 UW |
645 | return child; |
646 | ||
17d0d4e8 | 647 | if (!(child->wLength)) return NULL; |
ce821773 UW |
648 | child = VersionInfo16_Next( child ); |
649 | } | |
650 | ||
651 | return NULL; | |
652 | } | |
653 | ||
654 | /*********************************************************************** | |
655 | * VersionInfo32_FindChild [internal] | |
656 | */ | |
ab91c216 | 657 | static const VS_VERSION_INFO_STRUCT32 *VersionInfo32_FindChild( const VS_VERSION_INFO_STRUCT32 *info, |
a3960292 | 658 | LPCWSTR szKey, UINT cbKey ) |
ce821773 | 659 | { |
ab91c216 | 660 | const VS_VERSION_INFO_STRUCT32 *child = VersionInfo32_Children( info ); |
ce821773 | 661 | |
261e3764 | 662 | while ((char *)child < (char *)info + info->wLength ) |
ce821773 | 663 | { |
31368dec | 664 | if (!strncmpiW( child->szKey, szKey, cbKey ) && !child->szKey[cbKey]) |
ce821773 UW |
665 | return child; |
666 | ||
667 | child = VersionInfo32_Next( child ); | |
668 | } | |
669 | ||
670 | return NULL; | |
671 | } | |
672 | ||
673 | /*********************************************************************** | |
bcc80032 RS |
674 | * VersionInfo16_QueryValue [internal] |
675 | * | |
676 | * Gets a value from a 16-bit NE resource | |
ce821773 | 677 | */ |
ab91c216 | 678 | static BOOL WINAPI VersionInfo16_QueryValue( const VS_VERSION_INFO_STRUCT16 *info, LPCSTR lpSubBlock, |
a3960292 | 679 | LPVOID *lplpBuffer, UINT *puLen ) |
ce821773 | 680 | { |
ce821773 UW |
681 | while ( *lpSubBlock ) |
682 | { | |
683 | /* Find next path component */ | |
684 | LPCSTR lpNextSlash; | |
685 | for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ ) | |
686 | if ( *lpNextSlash == '\\' ) | |
687 | break; | |
688 | ||
689 | /* Skip empty components */ | |
690 | if ( lpNextSlash == lpSubBlock ) | |
691 | { | |
692 | lpSubBlock++; | |
693 | continue; | |
694 | } | |
695 | ||
696 | /* We have a non-empty component: search info for key */ | |
697 | info = VersionInfo16_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock ); | |
31368dec DT |
698 | if ( !info ) |
699 | { | |
700 | if (puLen) *puLen = 0 ; | |
701 | SetLastError( ERROR_RESOURCE_TYPE_NOT_FOUND ); | |
702 | return FALSE; | |
703 | } | |
ce821773 UW |
704 | |
705 | /* Skip path component */ | |
706 | lpSubBlock = lpNextSlash; | |
707 | } | |
708 | ||
709 | /* Return value */ | |
710 | *lplpBuffer = VersionInfo16_Value( info ); | |
18a1a6e1 RR |
711 | if (puLen) |
712 | *puLen = info->wValueLength; | |
ce821773 UW |
713 | |
714 | return TRUE; | |
715 | } | |
716 | ||
00d81121 PV |
717 | /*********************************************************************** |
718 | * VersionInfo32_QueryValue [internal] | |
719 | * | |
720 | * Gets a value from a 32-bit PE resource | |
721 | */ | |
ab91c216 | 722 | static BOOL WINAPI VersionInfo32_QueryValue( const VS_VERSION_INFO_STRUCT32 *info, LPCWSTR lpSubBlock, |
00d81121 PV |
723 | LPVOID *lplpBuffer, UINT *puLen ) |
724 | { | |
725 | TRACE("lpSubBlock : (%s)\n", debugstr_w(lpSubBlock)); | |
726 | ||
727 | while ( *lpSubBlock ) | |
728 | { | |
729 | /* Find next path component */ | |
730 | LPCWSTR lpNextSlash; | |
731 | for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ ) | |
732 | if ( *lpNextSlash == '\\' ) | |
733 | break; | |
734 | ||
735 | /* Skip empty components */ | |
736 | if ( lpNextSlash == lpSubBlock ) | |
737 | { | |
738 | lpSubBlock++; | |
739 | continue; | |
740 | } | |
741 | ||
742 | /* We have a non-empty component: search info for key */ | |
743 | info = VersionInfo32_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock ); | |
31368dec DT |
744 | if ( !info ) |
745 | { | |
746 | if (puLen) *puLen = 0 ; | |
747 | SetLastError( ERROR_RESOURCE_TYPE_NOT_FOUND ); | |
748 | return FALSE; | |
749 | } | |
00d81121 PV |
750 | |
751 | /* Skip path component */ | |
752 | lpSubBlock = lpNextSlash; | |
753 | } | |
754 | ||
755 | /* Return value */ | |
756 | *lplpBuffer = VersionInfo32_Value( info ); | |
757 | if (puLen) | |
758 | *puLen = info->wValueLength; | |
759 | ||
760 | return TRUE; | |
761 | } | |
762 | ||
bcc80032 RS |
763 | /*********************************************************************** |
764 | * VerQueryValueA [VERSION.@] | |
765 | */ | |
ab91c216 | 766 | BOOL WINAPI VerQueryValueA( LPCVOID pBlock, LPCSTR lpSubBlock, |
015dc79c | 767 | LPVOID *lplpBuffer, PUINT puLen ) |
bcc80032 | 768 | { |
111a913d PV |
769 | static const char rootA[] = "\\"; |
770 | static const char varfileinfoA[] = "\\VarFileInfo\\Translation"; | |
ab91c216 | 771 | const VS_VERSION_INFO_STRUCT16 *info = (const VS_VERSION_INFO_STRUCT16 *)pBlock; |
bcc80032 RS |
772 | |
773 | TRACE("(%p,%s,%p,%p)\n", | |
774 | pBlock, debugstr_a(lpSubBlock), lplpBuffer, puLen ); | |
775 | ||
e1347ecd SE |
776 | if (!pBlock) |
777 | return FALSE; | |
778 | ||
bcc80032 RS |
779 | if ( !VersionInfoIs16( info ) ) |
780 | { | |
111a913d PV |
781 | BOOL ret; |
782 | INT len; | |
783 | LPWSTR lpSubBlockW; | |
784 | ||
785 | len = MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, NULL, 0); | |
786 | lpSubBlockW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); | |
787 | ||
788 | if (!lpSubBlockW) | |
789 | return FALSE; | |
790 | ||
791 | MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, lpSubBlockW, len); | |
792 | ||
793 | ret = VersionInfo32_QueryValue(pBlock, lpSubBlockW, lplpBuffer, puLen); | |
794 | ||
795 | HeapFree(GetProcessHeap(), 0, lpSubBlockW); | |
796 | ||
2bacc463 | 797 | if (ret && strcasecmp( lpSubBlock, rootA ) && strcasecmp( lpSubBlock, varfileinfoA )) |
111a913d | 798 | { |
ab91c216 FG |
799 | /* Set lpBuffer so it points to the 'empty' area where we store |
800 | * the converted strings | |
801 | */ | |
37871ce3 | 802 | LPSTR lpBufferA = (LPSTR)pBlock + info->wLength + 4; |
ab91c216 | 803 | DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock; |
111a913d | 804 | |
37871ce3 AJ |
805 | len = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)*lplpBuffer, -1, |
806 | lpBufferA + pos, info->wLength - pos, NULL, NULL); | |
807 | *lplpBuffer = lpBufferA + pos; | |
111a913d | 808 | *puLen = len; |
111a913d | 809 | } |
2bacc463 | 810 | return ret; |
bcc80032 RS |
811 | } |
812 | ||
813 | return VersionInfo16_QueryValue(info, lpSubBlock, lplpBuffer, puLen); | |
814 | } | |
815 | ||
ce821773 | 816 | /*********************************************************************** |
8b216b3d | 817 | * VerQueryValueW [VERSION.@] |
ce821773 | 818 | */ |
ab91c216 | 819 | BOOL WINAPI VerQueryValueW( LPCVOID pBlock, LPCWSTR lpSubBlock, |
015dc79c | 820 | LPVOID *lplpBuffer, PUINT puLen ) |
ce821773 | 821 | { |
111a913d PV |
822 | static const WCHAR rootW[] = { '\\', 0 }; |
823 | static const WCHAR varfileinfoW[] = { '\\','V','a','r','F','i','l','e','I','n','f','o', | |
824 | '\\','T','r','a','n','s','l','a','t','i','o','n', 0 }; | |
825 | ||
ab91c216 | 826 | const VS_VERSION_INFO_STRUCT32 *info = (const VS_VERSION_INFO_STRUCT32 *)pBlock; |
ce821773 | 827 | |
9fe7a254 | 828 | TRACE("(%p,%s,%p,%p)\n", |
ce821773 UW |
829 | pBlock, debugstr_w(lpSubBlock), lplpBuffer, puLen ); |
830 | ||
e1347ecd SE |
831 | if (!pBlock) |
832 | return FALSE; | |
833 | ||
bcc80032 RS |
834 | if ( VersionInfoIs16( info ) ) |
835 | { | |
72fba73a | 836 | BOOL ret; |
111a913d PV |
837 | int len; |
838 | LPSTR lpSubBlockA; | |
839 | ||
840 | len = WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, NULL, 0, NULL, NULL); | |
841 | lpSubBlockA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char)); | |
842 | ||
bcc80032 RS |
843 | if (!lpSubBlockA) |
844 | return FALSE; | |
111a913d | 845 | |
bcc80032 | 846 | WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, lpSubBlockA, len, NULL, NULL); |
111a913d | 847 | |
bcc80032 | 848 | ret = VersionInfo16_QueryValue(pBlock, lpSubBlockA, lplpBuffer, puLen); |
111a913d | 849 | |
bcc80032 | 850 | HeapFree(GetProcessHeap(), 0, lpSubBlockA); |
111a913d | 851 | |
2bacc463 | 852 | if (ret && strcmpiW( lpSubBlock, rootW ) && strcmpiW( lpSubBlock, varfileinfoW )) |
111a913d | 853 | { |
ab91c216 FG |
854 | /* Set lpBuffer so it points to the 'empty' area where we store |
855 | * the converted strings | |
856 | */ | |
37871ce3 | 857 | LPWSTR lpBufferW = (LPWSTR)((LPSTR)pBlock + info->wLength); |
ab91c216 | 858 | DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock; |
37871ce3 | 859 | DWORD max = (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4 - info->wLength; |
111a913d | 860 | |
37871ce3 AJ |
861 | len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)*lplpBuffer, -1, |
862 | lpBufferW + pos, max/sizeof(WCHAR) - pos ); | |
863 | *lplpBuffer = lpBufferW + pos; | |
111a913d | 864 | *puLen = len; |
111a913d | 865 | } |
2bacc463 | 866 | return ret; |
bcc80032 RS |
867 | } |
868 | ||
00d81121 | 869 | return VersionInfo32_QueryValue(info, lpSubBlock, lplpBuffer, puLen); |
ce821773 | 870 | } |