| /* |
| * Implementation of VERSION.DLL - Version Info access |
| * |
| * Copyright 1996,1997 Marcus Meissner |
| * Copyright 1997 David Cuthbert |
| * Copyright 1999 Ulrich Weigand |
| * Copyright 2005 Paul Vriens |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| * |
| */ |
| |
| #include <stdarg.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "winver.h" |
| #include "winternl.h" |
| #include "wine/winuser16.h" |
| #include "wine/unicode.h" |
| #include "winerror.h" |
| #include "wine/debug.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(ver); |
| |
| /****************************************************************************** |
| * |
| * This function will print via standard TRACE, debug info regarding |
| * the file info structure vffi. |
| * 15-Feb-1998 Dimitrie Paun (dimi@cs.toronto.edu) |
| * Added this function to clean up the code. |
| * |
| *****************************************************************************/ |
| static void print_vffi_debug(const VS_FIXEDFILEINFO *vffi) |
| { |
| BOOL versioned_printer = FALSE; |
| |
| if((vffi->dwFileType == VFT_DLL) || (vffi->dwFileType == VFT_DRV)) |
| { |
| if(vffi->dwFileSubtype == VFT2_DRV_VERSIONED_PRINTER) |
| /* this is documented for newer w2k Drivers and up */ |
| versioned_printer = TRUE; |
| else if( (vffi->dwFileSubtype == VFT2_DRV_PRINTER) && |
| (vffi->dwFileVersionMS != vffi->dwProductVersionMS) && |
| (vffi->dwFileVersionMS > 0) && |
| (vffi->dwFileVersionMS <= 3) ) |
| /* found this on NT 3.51, NT4.0 and old w2k Drivers */ |
| versioned_printer = TRUE; |
| } |
| |
| TRACE("structversion=%u.%u, ", |
| HIWORD(vffi->dwStrucVersion),LOWORD(vffi->dwStrucVersion)); |
| if(versioned_printer) |
| { |
| WORD mode = LOWORD(vffi->dwFileVersionMS); |
| WORD ver_rev = HIWORD(vffi->dwFileVersionLS); |
| TRACE("fileversion=%u.%u.%u.%u (%s.major.minor.release), ", |
| (vffi->dwFileVersionMS), |
| HIBYTE(ver_rev), LOBYTE(ver_rev), LOWORD(vffi->dwFileVersionLS), |
| (mode == 3) ? "Usermode" : ((mode <= 2) ? "Kernelmode" : "?") ); |
| } |
| else |
| { |
| TRACE("fileversion=%u.%u.%u.%u, ", |
| HIWORD(vffi->dwFileVersionMS),LOWORD(vffi->dwFileVersionMS), |
| HIWORD(vffi->dwFileVersionLS),LOWORD(vffi->dwFileVersionLS)); |
| } |
| TRACE("productversion=%u.%u.%u.%u\n", |
| HIWORD(vffi->dwProductVersionMS),LOWORD(vffi->dwProductVersionMS), |
| HIWORD(vffi->dwProductVersionLS),LOWORD(vffi->dwProductVersionLS)); |
| |
| TRACE("flagmask=0x%x, flags=0x%x %s%s%s%s%s%s\n", |
| vffi->dwFileFlagsMask, vffi->dwFileFlags, |
| (vffi->dwFileFlags & VS_FF_DEBUG) ? "DEBUG," : "", |
| (vffi->dwFileFlags & VS_FF_PRERELEASE) ? "PRERELEASE," : "", |
| (vffi->dwFileFlags & VS_FF_PATCHED) ? "PATCHED," : "", |
| (vffi->dwFileFlags & VS_FF_PRIVATEBUILD) ? "PRIVATEBUILD," : "", |
| (vffi->dwFileFlags & VS_FF_INFOINFERRED) ? "INFOINFERRED," : "", |
| (vffi->dwFileFlags & VS_FF_SPECIALBUILD) ? "SPECIALBUILD," : ""); |
| |
| TRACE("("); |
| |
| TRACE("OS=0x%x.0x%x ", HIWORD(vffi->dwFileOS), LOWORD(vffi->dwFileOS)); |
| |
| switch (vffi->dwFileOS&0xFFFF0000) |
| { |
| case VOS_DOS:TRACE("DOS,");break; |
| case VOS_OS216:TRACE("OS/2-16,");break; |
| case VOS_OS232:TRACE("OS/2-32,");break; |
| case VOS_NT:TRACE("NT,");break; |
| case VOS_UNKNOWN: |
| default: |
| TRACE("UNKNOWN(0x%x),",vffi->dwFileOS&0xFFFF0000);break; |
| } |
| |
| switch (LOWORD(vffi->dwFileOS)) |
| { |
| case VOS__BASE:TRACE("BASE");break; |
| case VOS__WINDOWS16:TRACE("WIN16");break; |
| case VOS__WINDOWS32:TRACE("WIN32");break; |
| case VOS__PM16:TRACE("PM16");break; |
| case VOS__PM32:TRACE("PM32");break; |
| default: |
| TRACE("UNKNOWN(0x%x)",LOWORD(vffi->dwFileOS));break; |
| } |
| |
| TRACE(")\n"); |
| |
| switch (vffi->dwFileType) |
| { |
| case VFT_APP:TRACE("filetype=APP");break; |
| case VFT_DLL: |
| TRACE("filetype=DLL"); |
| if(vffi->dwFileSubtype != 0) |
| { |
| if(versioned_printer) /* NT3.x/NT4.0 or old w2k Driver */ |
| TRACE(",PRINTER"); |
| TRACE(" (subtype=0x%x)", vffi->dwFileSubtype); |
| } |
| break; |
| case VFT_DRV: |
| TRACE("filetype=DRV,"); |
| switch(vffi->dwFileSubtype) |
| { |
| case VFT2_DRV_PRINTER:TRACE("PRINTER");break; |
| case VFT2_DRV_KEYBOARD:TRACE("KEYBOARD");break; |
| case VFT2_DRV_LANGUAGE:TRACE("LANGUAGE");break; |
| case VFT2_DRV_DISPLAY:TRACE("DISPLAY");break; |
| case VFT2_DRV_MOUSE:TRACE("MOUSE");break; |
| case VFT2_DRV_NETWORK:TRACE("NETWORK");break; |
| case VFT2_DRV_SYSTEM:TRACE("SYSTEM");break; |
| case VFT2_DRV_INSTALLABLE:TRACE("INSTALLABLE");break; |
| case VFT2_DRV_SOUND:TRACE("SOUND");break; |
| case VFT2_DRV_COMM:TRACE("COMM");break; |
| case VFT2_DRV_INPUTMETHOD:TRACE("INPUTMETHOD");break; |
| case VFT2_DRV_VERSIONED_PRINTER:TRACE("VERSIONED_PRINTER");break; |
| case VFT2_UNKNOWN: |
| default: |
| TRACE("UNKNOWN(0x%x)",vffi->dwFileSubtype);break; |
| } |
| break; |
| case VFT_FONT: |
| TRACE("filetype=FONT,"); |
| switch (vffi->dwFileSubtype) |
| { |
| case VFT2_FONT_RASTER:TRACE("RASTER");break; |
| case VFT2_FONT_VECTOR:TRACE("VECTOR");break; |
| case VFT2_FONT_TRUETYPE:TRACE("TRUETYPE");break; |
| default:TRACE("UNKNOWN(0x%x)",vffi->dwFileSubtype);break; |
| } |
| break; |
| case VFT_VXD:TRACE("filetype=VXD");break; |
| case VFT_STATIC_LIB:TRACE("filetype=STATIC_LIB");break; |
| case VFT_UNKNOWN: |
| default: |
| TRACE("filetype=Unknown(0x%x)",vffi->dwFileType);break; |
| } |
| |
| TRACE("\n"); |
| TRACE("filedate=0x%x.0x%x\n",vffi->dwFileDateMS,vffi->dwFileDateLS); |
| } |
| |
| /*********************************************************************** |
| * Version Info Structure |
| */ |
| |
| typedef struct |
| { |
| WORD wLength; |
| WORD wValueLength; |
| CHAR szKey[1]; |
| #if 0 /* variable length structure */ |
| /* DWORD aligned */ |
| BYTE Value[]; |
| /* DWORD aligned */ |
| VS_VERSION_INFO_STRUCT16 Children[]; |
| #endif |
| } VS_VERSION_INFO_STRUCT16; |
| |
| typedef struct |
| { |
| WORD wLength; |
| WORD wValueLength; |
| WORD wType; |
| WCHAR szKey[1]; |
| #if 0 /* variable length structure */ |
| /* DWORD aligned */ |
| BYTE Value[]; |
| /* DWORD aligned */ |
| VS_VERSION_INFO_STRUCT32 Children[]; |
| #endif |
| } VS_VERSION_INFO_STRUCT32; |
| |
| #define VersionInfoIs16( ver ) \ |
| ( ((const VS_VERSION_INFO_STRUCT16 *)ver)->szKey[0] >= ' ' ) |
| |
| #define DWORD_ALIGN( base, ptr ) \ |
| ( (LPBYTE)(base) + ((((LPBYTE)(ptr) - (LPBYTE)(base)) + 3) & ~3) ) |
| |
| #define VersionInfo16_Value( ver ) \ |
| DWORD_ALIGN( (ver), (ver)->szKey + strlen((ver)->szKey) + 1 ) |
| #define VersionInfo32_Value( ver ) \ |
| DWORD_ALIGN( (ver), (ver)->szKey + strlenW((ver)->szKey) + 1 ) |
| |
| #define VersionInfo16_Children( ver ) \ |
| (const VS_VERSION_INFO_STRUCT16 *)( VersionInfo16_Value( ver ) + \ |
| ( ( (ver)->wValueLength + 3 ) & ~3 ) ) |
| #define VersionInfo32_Children( ver ) \ |
| (const VS_VERSION_INFO_STRUCT32 *)( VersionInfo32_Value( ver ) + \ |
| ( ( (ver)->wValueLength * \ |
| ((ver)->wType? 2 : 1) + 3 ) & ~3 ) ) |
| |
| #define VersionInfo16_Next( ver ) \ |
| (VS_VERSION_INFO_STRUCT16 *)( (LPBYTE)ver + (((ver)->wLength + 3) & ~3) ) |
| #define VersionInfo32_Next( ver ) \ |
| (VS_VERSION_INFO_STRUCT32 *)( (LPBYTE)ver + (((ver)->wLength + 3) & ~3) ) |
| |
| /*********************************************************************** |
| * VERSION_GetFileVersionInfo_PE [internal] |
| * |
| * NOTE: returns size of the PE VERSION resource or 0xFFFFFFFF |
| * in the case the file is a PE module, but VERSION_INFO not found. |
| */ |
| static DWORD VERSION_GetFileVersionInfo_PE( LPCWSTR filename, DWORD datasize, LPVOID data ) |
| { |
| const VS_FIXEDFILEINFO *vffi; |
| DWORD len; |
| BYTE *buf; |
| HMODULE hModule; |
| HRSRC hRsrc; |
| HGLOBAL hMem; |
| |
| TRACE("%s\n", debugstr_w(filename)); |
| |
| hModule = GetModuleHandleW(filename); |
| if(!hModule) |
| hModule = LoadLibraryExW(filename, 0, LOAD_LIBRARY_AS_DATAFILE); |
| else |
| hModule = LoadLibraryExW(filename, 0, 0); |
| if(!hModule) |
| { |
| WARN("Could not load %s\n", debugstr_w(filename)); |
| return 0; |
| } |
| hRsrc = FindResourceW(hModule, |
| MAKEINTRESOURCEW(VS_VERSION_INFO), |
| MAKEINTRESOURCEW(VS_FILE_INFO)); |
| if(!hRsrc) |
| { |
| WARN("Could not find VS_VERSION_INFO in %s\n", debugstr_w(filename)); |
| FreeLibrary(hModule); |
| return 0xFFFFFFFF; |
| } |
| len = SizeofResource(hModule, hRsrc); |
| hMem = LoadResource(hModule, hRsrc); |
| if(!hMem) |
| { |
| WARN("Could not load VS_VERSION_INFO from %s\n", debugstr_w(filename)); |
| FreeLibrary(hModule); |
| return 0xFFFFFFFF; |
| } |
| buf = LockResource(hMem); |
| |
| vffi = (VS_FIXEDFILEINFO *)VersionInfo32_Value( (VS_VERSION_INFO_STRUCT32 *)buf ); |
| |
| if ( vffi->dwSignature != VS_FFI_SIGNATURE ) |
| { |
| WARN("vffi->dwSignature is 0x%08x, but not 0x%08lx!\n", |
| vffi->dwSignature, VS_FFI_SIGNATURE ); |
| len = 0xFFFFFFFF; |
| goto END; |
| } |
| |
| if ( TRACE_ON(ver) ) |
| print_vffi_debug( vffi ); |
| |
| if(data) |
| { |
| if(datasize < len) |
| len = datasize; /* truncate data */ |
| if(len) |
| memcpy(data, buf, len); |
| else |
| len = 0xFFFFFFFF; |
| } |
| END: |
| FreeResource(hMem); |
| FreeLibrary(hModule); |
| |
| return len; |
| } |
| |
| /*********************************************************************** |
| * VERSION_GetFileVersionInfo_16 [internal] |
| * |
| * NOTE: returns size of the 16-bit VERSION resource or 0xFFFFFFFF |
| * in the case the file exists, but VERSION_INFO not found. |
| */ |
| static DWORD VERSION_GetFileVersionInfo_16( LPCSTR filename, DWORD datasize, LPVOID data ) |
| { |
| const VS_FIXEDFILEINFO *vffi; |
| DWORD len, offset; |
| BYTE *buf; |
| HMODULE16 hModule; |
| HRSRC16 hRsrc; |
| HGLOBAL16 hMem; |
| char dllname[20], owner[20], *p; |
| const char *basename; |
| BOOL is_builtin = FALSE; |
| |
| TRACE("%s\n", debugstr_a(filename)); |
| |
| /* strip path information */ |
| |
| basename = filename; |
| if (basename[0] && basename[1] == ':') basename += 2; /* strip drive specification */ |
| if ((p = strrchr( basename, '\\' ))) basename = p + 1; |
| if ((p = strrchr( basename, '/' ))) basename = p + 1; |
| |
| if (strlen(basename) < sizeof(dllname)-4) |
| { |
| int file_exists; |
| |
| strcpy( dllname, basename ); |
| p = strrchr( dllname, '.' ); |
| if (!p) strcat( dllname, ".dll" ); |
| for (p = dllname; *p; p++) if (*p >= 'A' && *p <= 'Z') *p += 32; |
| |
| if (wine_dll_get_owner( dllname, owner, sizeof(owner), &file_exists ) == 0) |
| is_builtin = TRUE; |
| } |
| |
| /* first try without loading a 16-bit module */ |
| if (is_builtin) |
| len = 0; |
| else |
| len = GetFileResourceSize16( filename, |
| MAKEINTRESOURCEA(VS_FILE_INFO), |
| MAKEINTRESOURCEA(VS_VERSION_INFO), |
| &offset ); |
| if (len) |
| { |
| if (!data) return len; |
| |
| len = GetFileResource16( filename, |
| MAKEINTRESOURCEA(VS_FILE_INFO), |
| MAKEINTRESOURCEA(VS_VERSION_INFO), |
| offset, datasize, data ); |
| if (len) |
| { |
| vffi = (VS_FIXEDFILEINFO *)VersionInfo16_Value( (VS_VERSION_INFO_STRUCT16 *)data ); |
| |
| if ( vffi->dwSignature == VS_FFI_SIGNATURE ) |
| { |
| if ( ((VS_VERSION_INFO_STRUCT16 *)data)->wLength < len ) |
| len = ((VS_VERSION_INFO_STRUCT16 *)data)->wLength; |
| |
| if ( TRACE_ON(ver) ) |
| print_vffi_debug( vffi ); |
| |
| return len; |
| } |
| } |
| } |
| |
| /* this might be a builtin 16-bit module */ |
| hModule = LoadLibrary16(filename); |
| if(hModule < 32) |
| { |
| WARN("Could not load %s\n", debugstr_a(filename)); |
| return 0; |
| } |
| hRsrc = FindResource16(hModule, |
| MAKEINTRESOURCEA(VS_VERSION_INFO), |
| MAKEINTRESOURCEA(VS_FILE_INFO)); |
| if(!hRsrc) |
| { |
| WARN("Could not find VS_VERSION_INFO in %s\n", debugstr_a(filename)); |
| FreeLibrary16(hModule); |
| return 0xFFFFFFFF; |
| } |
| len = SizeofResource16(hModule, hRsrc); |
| hMem = LoadResource16(hModule, hRsrc); |
| if(!hMem) |
| { |
| WARN("Could not load VS_VERSION_INFO from %s\n", debugstr_a(filename)); |
| FreeLibrary16(hModule); |
| return 0xFFFFFFFF; |
| } |
| buf = LockResource16(hMem); |
| |
| if(!VersionInfoIs16(buf)) |
| { |
| len = 0xFFFFFFFF; |
| goto END; |
| } |
| |
| vffi = (VS_FIXEDFILEINFO *)VersionInfo16_Value( (VS_VERSION_INFO_STRUCT16 *)buf ); |
| |
| if ( vffi->dwSignature != VS_FFI_SIGNATURE ) |
| { |
| WARN("vffi->dwSignature is 0x%08x, but not 0x%08lx!\n", |
| vffi->dwSignature, VS_FFI_SIGNATURE ); |
| len = 0xFFFFFFFF; |
| goto END; |
| } |
| |
| if ( TRACE_ON(ver) ) |
| print_vffi_debug( vffi ); |
| |
| if(data) |
| { |
| if(datasize < len) |
| len = datasize; /* truncate data */ |
| if(len) |
| memcpy(data, buf, len); |
| else |
| len = 0xFFFFFFFF; |
| } |
| END: |
| FreeResource16(hMem); |
| FreeLibrary16(hModule); |
| |
| return len; |
| } |
| |
| /*********************************************************************** |
| * GetFileVersionInfoSizeW [VERSION.@] |
| */ |
| DWORD WINAPI GetFileVersionInfoSizeW( LPCWSTR filename, LPDWORD handle ) |
| { |
| DWORD len; |
| |
| TRACE("(%s,%p)\n", debugstr_w(filename), handle ); |
| |
| if (handle) *handle = 0; |
| |
| if (!filename) |
| { |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return 0; |
| } |
| if (!*filename) |
| { |
| SetLastError(ERROR_BAD_PATHNAME); |
| return 0; |
| } |
| |
| len = VERSION_GetFileVersionInfo_PE(filename, 0, NULL); |
| /* 0xFFFFFFFF means: file is a PE module, but VERSION_INFO not found */ |
| if(len == 0xFFFFFFFF) |
| { |
| SetLastError(ERROR_RESOURCE_DATA_NOT_FOUND); |
| return 0; |
| } |
| |
| if (!len) |
| { |
| LPSTR filenameA; |
| |
| len = WideCharToMultiByte( CP_ACP, 0, filename, -1, NULL, 0, NULL, NULL ); |
| filenameA = HeapAlloc( GetProcessHeap(), 0, len ); |
| WideCharToMultiByte( CP_ACP, 0, filename, -1, filenameA, len, NULL, NULL ); |
| |
| len = VERSION_GetFileVersionInfo_16(filenameA, 0, NULL); |
| HeapFree( GetProcessHeap(), 0, filenameA ); |
| /* 0xFFFFFFFF means: file exists, but VERSION_INFO not found */ |
| if (!len) |
| { |
| SetLastError(ERROR_FILE_NOT_FOUND); |
| return 0; |
| } |
| if (len == 0xFFFFFFFF) |
| { |
| SetLastError(ERROR_RESOURCE_DATA_NOT_FOUND); |
| return 0; |
| } |
| |
| /* We have a 16bit resource. |
| * |
| * XP/W2K/W2K3 uses a buffer which is more than the actual needed space: |
| * |
| * (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4 |
| * |
| * This extra buffer is used for ANSI to Unicode conversions in W-Calls. |
| * info->wLength should be the same as len. Currently it isn't but that |
| * doesn't seem to be a problem (len is bigger than info->wLength). |
| */ |
| len = (len - sizeof(VS_FIXEDFILEINFO)) * 4; |
| } |
| else |
| { |
| /* We have a 32bit resource. |
| * |
| * XP/W2K/W2K3 uses a buffer which is 2 times the actual needed space + 4 bytes "FE2X" |
| * This extra buffer is used for Unicode to ANSI conversions in A-Calls |
| */ |
| len = (len * 2) + 4; |
| } |
| |
| SetLastError(0); |
| return len; |
| } |
| |
| /*********************************************************************** |
| * GetFileVersionInfoSizeA [VERSION.@] |
| */ |
| DWORD WINAPI GetFileVersionInfoSizeA( LPCSTR filename, LPDWORD handle ) |
| { |
| UNICODE_STRING filenameW; |
| DWORD retval; |
| |
| TRACE("(%s,%p)\n", debugstr_a(filename), handle ); |
| |
| if(filename) |
| RtlCreateUnicodeStringFromAsciiz(&filenameW, filename); |
| else |
| filenameW.Buffer = NULL; |
| |
| retval = GetFileVersionInfoSizeW(filenameW.Buffer, handle); |
| |
| RtlFreeUnicodeString(&filenameW); |
| |
| return retval; |
| } |
| |
| /*********************************************************************** |
| * GetFileVersionInfoW [VERSION.@] |
| */ |
| BOOL WINAPI GetFileVersionInfoW( LPCWSTR filename, DWORD handle, |
| DWORD datasize, LPVOID data ) |
| { |
| DWORD len; |
| VS_VERSION_INFO_STRUCT32* vvis = (VS_VERSION_INFO_STRUCT32*)data; |
| |
| TRACE("(%s,%d,size=%d,data=%p)\n", |
| debugstr_w(filename), handle, datasize, data ); |
| |
| if (!data) |
| { |
| SetLastError(ERROR_INVALID_DATA); |
| return FALSE; |
| } |
| len = VERSION_GetFileVersionInfo_PE(filename, datasize, data); |
| /* 0xFFFFFFFF means: file is a PE module, but VERSION_INFO not found */ |
| if (len == 0xFFFFFFFF) |
| { |
| SetLastError(ERROR_RESOURCE_DATA_NOT_FOUND); |
| return FALSE; |
| } |
| |
| if (!len) |
| { |
| LPSTR filenameA; |
| |
| len = WideCharToMultiByte( CP_ACP, 0, filename, -1, NULL, 0, NULL, NULL ); |
| filenameA = HeapAlloc( GetProcessHeap(), 0, len ); |
| WideCharToMultiByte( CP_ACP, 0, filename, -1, filenameA, len, NULL, NULL ); |
| |
| len = VERSION_GetFileVersionInfo_16(filenameA, datasize, data); |
| HeapFree( GetProcessHeap(), 0, filenameA ); |
| /* 0xFFFFFFFF means: file exists, but VERSION_INFO not found */ |
| if (!len || len == 0xFFFFFFFF) |
| { |
| SetLastError(ERROR_RESOURCE_DATA_NOT_FOUND); |
| return FALSE; |
| } |
| /* We have a 16bit resource. */ |
| } |
| else |
| { |
| static const char signature[] = "FE2X"; |
| DWORD bufsize = vvis->wLength + strlen(signature); |
| DWORD convbuf; |
| |
| /* We have a 32bit resource. |
| * |
| * XP/W2K/W2K3 uses a buffer which is 2 times the actual needed space + 4 bytes "FE2X" |
| * This extra buffer is used for Unicode to ANSI conversions in A-Calls |
| */ |
| |
| /* information is truncated to datasize bytes */ |
| if (datasize >= bufsize) |
| { |
| convbuf = datasize - vvis->wLength; |
| memcpy( ((char*)(data))+vvis->wLength, signature, convbuf > 4 ? 4 : convbuf ); |
| } |
| } |
| |
| SetLastError(0); |
| return TRUE; |
| } |
| |
| /*********************************************************************** |
| * GetFileVersionInfoA [VERSION.@] |
| */ |
| BOOL WINAPI GetFileVersionInfoA( LPCSTR filename, DWORD handle, |
| DWORD datasize, LPVOID data ) |
| { |
| UNICODE_STRING filenameW; |
| BOOL retval; |
| |
| TRACE("(%s,%d,size=%d,data=%p)\n", |
| debugstr_a(filename), handle, datasize, data ); |
| |
| if(filename) |
| RtlCreateUnicodeStringFromAsciiz(&filenameW, filename); |
| else |
| filenameW.Buffer = NULL; |
| |
| retval = GetFileVersionInfoW(filenameW.Buffer, handle, datasize, data); |
| |
| RtlFreeUnicodeString(&filenameW); |
| |
| return retval; |
| } |
| |
| /*********************************************************************** |
| * VersionInfo16_FindChild [internal] |
| */ |
| static const VS_VERSION_INFO_STRUCT16 *VersionInfo16_FindChild( const VS_VERSION_INFO_STRUCT16 *info, |
| LPCSTR szKey, UINT cbKey ) |
| { |
| const VS_VERSION_INFO_STRUCT16 *child = VersionInfo16_Children( info ); |
| |
| while ((char *)child < (char *)info + info->wLength ) |
| { |
| if (!strncasecmp( child->szKey, szKey, cbKey ) && !child->szKey[cbKey]) |
| return child; |
| |
| if (!(child->wLength)) return NULL; |
| child = VersionInfo16_Next( child ); |
| } |
| |
| return NULL; |
| } |
| |
| /*********************************************************************** |
| * VersionInfo32_FindChild [internal] |
| */ |
| static const VS_VERSION_INFO_STRUCT32 *VersionInfo32_FindChild( const VS_VERSION_INFO_STRUCT32 *info, |
| LPCWSTR szKey, UINT cbKey ) |
| { |
| const VS_VERSION_INFO_STRUCT32 *child = VersionInfo32_Children( info ); |
| |
| while ((char *)child < (char *)info + info->wLength ) |
| { |
| if (!strncmpiW( child->szKey, szKey, cbKey ) && !child->szKey[cbKey]) |
| return child; |
| |
| child = VersionInfo32_Next( child ); |
| } |
| |
| return NULL; |
| } |
| |
| /*********************************************************************** |
| * VersionInfo16_QueryValue [internal] |
| * |
| * Gets a value from a 16-bit NE resource |
| */ |
| static BOOL WINAPI VersionInfo16_QueryValue( const VS_VERSION_INFO_STRUCT16 *info, LPCSTR lpSubBlock, |
| LPVOID *lplpBuffer, UINT *puLen ) |
| { |
| while ( *lpSubBlock ) |
| { |
| /* Find next path component */ |
| LPCSTR lpNextSlash; |
| for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ ) |
| if ( *lpNextSlash == '\\' ) |
| break; |
| |
| /* Skip empty components */ |
| if ( lpNextSlash == lpSubBlock ) |
| { |
| lpSubBlock++; |
| continue; |
| } |
| |
| /* We have a non-empty component: search info for key */ |
| info = VersionInfo16_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock ); |
| if ( !info ) |
| { |
| if (puLen) *puLen = 0 ; |
| SetLastError( ERROR_RESOURCE_TYPE_NOT_FOUND ); |
| return FALSE; |
| } |
| |
| /* Skip path component */ |
| lpSubBlock = lpNextSlash; |
| } |
| |
| /* Return value */ |
| *lplpBuffer = VersionInfo16_Value( info ); |
| if (puLen) |
| *puLen = info->wValueLength; |
| |
| return TRUE; |
| } |
| |
| /*********************************************************************** |
| * VersionInfo32_QueryValue [internal] |
| * |
| * Gets a value from a 32-bit PE resource |
| */ |
| static BOOL WINAPI VersionInfo32_QueryValue( const VS_VERSION_INFO_STRUCT32 *info, LPCWSTR lpSubBlock, |
| LPVOID *lplpBuffer, UINT *puLen ) |
| { |
| TRACE("lpSubBlock : (%s)\n", debugstr_w(lpSubBlock)); |
| |
| while ( *lpSubBlock ) |
| { |
| /* Find next path component */ |
| LPCWSTR lpNextSlash; |
| for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ ) |
| if ( *lpNextSlash == '\\' ) |
| break; |
| |
| /* Skip empty components */ |
| if ( lpNextSlash == lpSubBlock ) |
| { |
| lpSubBlock++; |
| continue; |
| } |
| |
| /* We have a non-empty component: search info for key */ |
| info = VersionInfo32_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock ); |
| if ( !info ) |
| { |
| if (puLen) *puLen = 0 ; |
| SetLastError( ERROR_RESOURCE_TYPE_NOT_FOUND ); |
| return FALSE; |
| } |
| |
| /* Skip path component */ |
| lpSubBlock = lpNextSlash; |
| } |
| |
| /* Return value */ |
| *lplpBuffer = VersionInfo32_Value( info ); |
| if (puLen) |
| *puLen = info->wValueLength; |
| |
| return TRUE; |
| } |
| |
| /*********************************************************************** |
| * VerQueryValueA [VERSION.@] |
| */ |
| BOOL WINAPI VerQueryValueA( LPCVOID pBlock, LPCSTR lpSubBlock, |
| LPVOID *lplpBuffer, PUINT puLen ) |
| { |
| static const char rootA[] = "\\"; |
| static const char varfileinfoA[] = "\\VarFileInfo\\Translation"; |
| const VS_VERSION_INFO_STRUCT16 *info = (const VS_VERSION_INFO_STRUCT16 *)pBlock; |
| |
| TRACE("(%p,%s,%p,%p)\n", |
| pBlock, debugstr_a(lpSubBlock), lplpBuffer, puLen ); |
| |
| if (!pBlock) |
| return FALSE; |
| |
| if ( !VersionInfoIs16( info ) ) |
| { |
| BOOL ret; |
| INT len; |
| LPWSTR lpSubBlockW; |
| |
| len = MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, NULL, 0); |
| lpSubBlockW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); |
| |
| if (!lpSubBlockW) |
| return FALSE; |
| |
| MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, lpSubBlockW, len); |
| |
| ret = VersionInfo32_QueryValue(pBlock, lpSubBlockW, lplpBuffer, puLen); |
| |
| HeapFree(GetProcessHeap(), 0, lpSubBlockW); |
| |
| if (ret && strcasecmp( lpSubBlock, rootA ) && strcasecmp( lpSubBlock, varfileinfoA )) |
| { |
| /* Set lpBuffer so it points to the 'empty' area where we store |
| * the converted strings |
| */ |
| LPSTR lpBufferA = (LPSTR)pBlock + info->wLength + 4; |
| DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock; |
| |
| len = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)*lplpBuffer, -1, |
| lpBufferA + pos, info->wLength - pos, NULL, NULL); |
| *lplpBuffer = lpBufferA + pos; |
| *puLen = len; |
| } |
| return ret; |
| } |
| |
| return VersionInfo16_QueryValue(info, lpSubBlock, lplpBuffer, puLen); |
| } |
| |
| /*********************************************************************** |
| * VerQueryValueW [VERSION.@] |
| */ |
| BOOL WINAPI VerQueryValueW( LPCVOID pBlock, LPCWSTR lpSubBlock, |
| LPVOID *lplpBuffer, PUINT puLen ) |
| { |
| static const WCHAR rootW[] = { '\\', 0 }; |
| static const WCHAR varfileinfoW[] = { '\\','V','a','r','F','i','l','e','I','n','f','o', |
| '\\','T','r','a','n','s','l','a','t','i','o','n', 0 }; |
| |
| const VS_VERSION_INFO_STRUCT32 *info = (const VS_VERSION_INFO_STRUCT32 *)pBlock; |
| |
| TRACE("(%p,%s,%p,%p)\n", |
| pBlock, debugstr_w(lpSubBlock), lplpBuffer, puLen ); |
| |
| if (!pBlock) |
| return FALSE; |
| |
| if ( VersionInfoIs16( info ) ) |
| { |
| BOOL ret; |
| int len; |
| LPSTR lpSubBlockA; |
| |
| len = WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, NULL, 0, NULL, NULL); |
| lpSubBlockA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char)); |
| |
| if (!lpSubBlockA) |
| return FALSE; |
| |
| WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, lpSubBlockA, len, NULL, NULL); |
| |
| ret = VersionInfo16_QueryValue(pBlock, lpSubBlockA, lplpBuffer, puLen); |
| |
| HeapFree(GetProcessHeap(), 0, lpSubBlockA); |
| |
| if (ret && strcmpiW( lpSubBlock, rootW ) && strcmpiW( lpSubBlock, varfileinfoW )) |
| { |
| /* Set lpBuffer so it points to the 'empty' area where we store |
| * the converted strings |
| */ |
| LPWSTR lpBufferW = (LPWSTR)((LPSTR)pBlock + info->wLength); |
| DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock; |
| DWORD max = (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4 - info->wLength; |
| |
| len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)*lplpBuffer, -1, |
| lpBufferW + pos, max/sizeof(WCHAR) - pos ); |
| *lplpBuffer = lpBufferW + pos; |
| *puLen = len; |
| } |
| return ret; |
| } |
| |
| return VersionInfo32_QueryValue(info, lpSubBlock, lplpBuffer, puLen); |
| } |