wine/dlls/advpack/files.c
Francois Gouget be6d891f0d advpack: Prepare the unicodification of advpack.dll.
All functions that manipulate strings should have an Ansi and a
Unicode variant.
Forward the unqualified dll entry points to the Ansi variant for
backward compatibility.
We're not allowed to use unqualified entry points in Wine, so change
RegInstall() calls to RegInstallA().
Update win32.api.
2006-02-27 15:57:33 +01:00

829 lines
23 KiB
C

/*
* Advpack file functions
*
* Copyright 2006 James Hawkins
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdarg.h>
#include <stdlib.h>
#include "windef.h"
#include "winbase.h"
#include "winuser.h"
#include "winreg.h"
#include "winver.h"
#include "setupapi.h"
#include "advpub.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(advpack);
/***********************************************************************
* AddDelBackupEntryA (ADVPACK.@)
*
* Either appends the files in the file list to the backup section of
* the specified INI, or deletes the entries from the INI file.
*
* PARAMS
* lpcszFileList [I] NULL-separated list of filenames.
* lpcszBackupDir [I] Path of the backup directory.
* lpcszBaseName [I] Basename of the INI file.
* dwFlags [I] AADBE_ADD_ENTRY adds the entries in the file list
* to the INI file, while AADBE_DEL_ENTRY removes
* the entries from the INI file.
*
* RETURNS
* S_OK in all cases.
*
* NOTES
* If the INI file does not exist before adding entries to it, the file
* will be created.
*
* If lpcszBackupDir is NULL, the INI file is assumed to exist in
* c:\windows or created there if it does not exist.
*/
HRESULT WINAPI AddDelBackupEntryA(LPCSTR lpcszFileList, LPCSTR lpcszBackupDir,
LPCSTR lpcszBaseName, DWORD dwFlags)
{
CHAR szIniPath[MAX_PATH];
LPSTR szString = NULL;
const char szBackupEntry[] = "-1,0,0,0,0,0,-1";
TRACE("(%p, %p, %p, %ld)\n", lpcszFileList, lpcszBackupDir,
lpcszBaseName, dwFlags);
if (!lpcszFileList || !*lpcszFileList)
return S_OK;
if (lpcszBackupDir)
lstrcpyA(szIniPath, lpcszBackupDir);
else
GetWindowsDirectoryA(szIniPath, MAX_PATH);
lstrcatA(szIniPath, "\\");
lstrcatA(szIniPath, lpcszBaseName);
lstrcatA(szIniPath, ".ini");
SetFileAttributesA(szIniPath, FILE_ATTRIBUTE_NORMAL);
if (dwFlags & AADBE_ADD_ENTRY)
szString = (LPSTR)szBackupEntry;
else if (dwFlags & AADBE_DEL_ENTRY)
szString = NULL;
/* add or delete the INI entries */
while (*lpcszFileList)
{
WritePrivateProfileStringA("backup", lpcszFileList, szString, szIniPath);
lpcszFileList += lstrlenA(lpcszFileList) + 1;
}
/* hide the INI file */
SetFileAttributesA(szIniPath, FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN);
return S_OK;
}
/* FIXME: this is only for the local case, X:\ */
#define ROOT_LENGTH 3
UINT CALLBACK pQuietQueueCallback(PVOID Context, UINT Notification,
UINT_PTR Param1, UINT_PTR Param2)
{
return 1;
}
UINT CALLBACK pQueueCallback(PVOID Context, UINT Notification,
UINT_PTR Param1, UINT_PTR Param2)
{
/* only be verbose for error notifications */
if (!Notification ||
Notification == SPFILENOTIFY_RENAMEERROR ||
Notification == SPFILENOTIFY_DELETEERROR ||
Notification == SPFILENOTIFY_COPYERROR)
{
return SetupDefaultQueueCallbackA(Context, Notification,
Param1, Param2);
}
return 1;
}
/***********************************************************************
* AdvInstallFileA (ADVPACK.@)
*
* Copies a file from the source to a destination.
*
* PARAMS
* hwnd [I] Handle to the window used for messages.
* lpszSourceDir [I] Source directory.
* lpszSourceFile [I] Source filename.
* lpszDestDir [I] Destination directory.
* lpszDestFile [I] Optional destination filename.
* dwFlags [I] See advpub.h.
* dwReserved [I] Reserved. Must be 0.
*
* RETURNS
* Success: S_OK.
* Failure: E_FAIL.
*
* NOTES
* If lpszDestFile is NULL, the destination filename is the same as
* lpszSourceFIle.
*/
HRESULT WINAPI AdvInstallFileA(HWND hwnd, LPCSTR lpszSourceDir, LPCSTR lpszSourceFile,
LPCSTR lpszDestDir, LPCSTR lpszDestFile,
DWORD dwFlags, DWORD dwReserved)
{
PSP_FILE_CALLBACK_A pFileCallback;
LPSTR szPath, szDestFilename;
char szRootPath[ROOT_LENGTH];
DWORD dwLen, dwLastError;
HSPFILEQ fileQueue;
PVOID pContext;
TRACE("(%p,%p,%p,%p,%p,%ld,%ld)\n", hwnd, debugstr_a(lpszSourceDir),
debugstr_a(lpszSourceFile), debugstr_a(lpszDestDir),
debugstr_a(lpszDestFile), dwFlags, dwReserved);
if (!lpszSourceDir || !lpszSourceFile || !lpszDestDir)
return E_INVALIDARG;
fileQueue = SetupOpenFileQueue();
if (fileQueue == INVALID_HANDLE_VALUE)
return HRESULT_FROM_WIN32(GetLastError());
pContext = NULL;
dwLastError = ERROR_SUCCESS;
lstrcpynA(szRootPath, lpszSourceDir, ROOT_LENGTH);
szPath = (LPSTR)lpszSourceDir + ROOT_LENGTH;
/* use lpszSourceFile as destination filename if lpszDestFile is NULL */
if (lpszDestFile)
{
dwLen = lstrlenA(lpszDestFile);
szDestFilename = HeapAlloc(GetProcessHeap(), 0, dwLen);
lstrcpyA(szDestFilename, lpszDestFile);
}
else
{
dwLen = lstrlenA(lpszSourceFile);
szDestFilename = HeapAlloc(GetProcessHeap(), 0, dwLen);
lstrcpyA(szDestFilename, lpszSourceFile);
}
/* add the file copy operation to the setup queue */
if (!SetupQueueCopyA(fileQueue, szRootPath, szPath, lpszSourceFile, NULL,
NULL, lpszDestDir, szDestFilename, dwFlags))
{
dwLastError = GetLastError();
goto done;
}
pContext = SetupInitDefaultQueueCallbackEx(hwnd, INVALID_HANDLE_VALUE,
0, 0, NULL);
if (!pContext)
{
dwLastError = GetLastError();
goto done;
}
/* don't output anything for AIF_QUIET */
if (dwFlags & AIF_QUIET)
pFileCallback = pQuietQueueCallback;
else
pFileCallback = pQueueCallback;
/* perform the file copy */
if (!SetupCommitFileQueueA(hwnd, fileQueue, pFileCallback, pContext))
{
dwLastError = GetLastError();
goto done;
}
done:
SetupTermDefaultQueueCallback(pContext);
SetupCloseFileQueue(fileQueue);
HeapFree(GetProcessHeap(), 0, szDestFilename);
return HRESULT_FROM_WIN32(dwLastError);
}
static HRESULT DELNODE_recurse_dirtree(LPSTR fname, DWORD flags)
{
DWORD fattrs = GetFileAttributesA(fname);
HRESULT ret = E_FAIL;
if (fattrs & FILE_ATTRIBUTE_DIRECTORY)
{
HANDLE hFindFile;
WIN32_FIND_DATAA w32fd;
BOOL done = TRUE;
int fname_len = lstrlenA(fname);
/* Generate a path with wildcard suitable for iterating */
if (CharPrevA(fname, fname + fname_len) != "\\")
{
lstrcpyA(fname + fname_len, "\\");
++fname_len;
}
lstrcpyA(fname + fname_len, "*");
if ((hFindFile = FindFirstFileA(fname, &w32fd)) != INVALID_HANDLE_VALUE)
{
/* Iterate through the files in the directory */
for (done = FALSE; !done; done = !FindNextFileA(hFindFile, &w32fd))
{
TRACE("%s\n", w32fd.cFileName);
if (lstrcmpA(".", w32fd.cFileName) != 0 &&
lstrcmpA("..", w32fd.cFileName) != 0)
{
lstrcpyA(fname + fname_len, w32fd.cFileName);
if (DELNODE_recurse_dirtree(fname, flags) != S_OK)
{
break; /* Failure */
}
}
}
FindClose(hFindFile);
}
/* We're done with this directory, so restore the old path without wildcard */
*(fname + fname_len) = '\0';
if (done)
{
TRACE("%s: directory\n", fname);
if (SetFileAttributesA(fname, FILE_ATTRIBUTE_NORMAL) && RemoveDirectoryA(fname))
{
ret = S_OK;
}
}
}
else
{
TRACE("%s: file\n", fname);
if (SetFileAttributesA(fname, FILE_ATTRIBUTE_NORMAL) && DeleteFileA(fname))
{
ret = S_OK;
}
}
return ret;
}
/***********************************************************************
* DelNodeA (ADVPACK.@)
*
* Deletes a file or directory
*
* PARAMS
* pszFileOrDirName [I] Name of file or directory to delete
* dwFlags [I] Flags; see include/advpub.h
*
* RETURNS
* Success: S_OK
* Failure: E_FAIL
*
* BUGS
* - Ignores flags
* - Native version apparently does a lot of checking to make sure
* we're not trying to delete a system directory etc.
*/
HRESULT WINAPI DelNodeA( LPCSTR pszFileOrDirName, DWORD dwFlags )
{
CHAR fname[MAX_PATH];
HRESULT ret = E_FAIL;
TRACE("(%s, 0x%08lx)\n", debugstr_a(pszFileOrDirName), dwFlags);
if (dwFlags)
FIXME("Flags ignored!\n");
if (pszFileOrDirName && *pszFileOrDirName)
{
lstrcpyA(fname, pszFileOrDirName);
/* TODO: Should check for system directory deletion etc. here */
ret = DELNODE_recurse_dirtree(fname, dwFlags);
}
return ret;
}
/* returns the parameter at dwIndex in a list of parameters
* separated by the cSeparator character
*/
static LPSTR get_parameter(LPSTR szParameters, CHAR cSeparator, DWORD dwIndex)
{
LPSTR szParam = NULL;
DWORD i = 0;
while (*szParameters && i < dwIndex)
{
if (*szParameters == cSeparator)
i++;
szParameters++;
}
if (!*szParameters)
return NULL;
szParam = HeapAlloc(GetProcessHeap(), 0, lstrlenA(szParameters));
lstrcpyA(szParam, szParameters);
return szParam;
}
/***********************************************************************
* DelNodeRunDLL32A (ADVPACK.@)
*
* Deletes a file or directory, WinMain style.
*
* PARAMS
* hWnd [I] Handle to the window used for the display.
* hInst [I] Instance of the process.
* cmdline [I] Contains parameters in the order FileOrDirName,Flags.
* show [I] How the window should be shown.
*
* RETURNS
* Success: S_OK.
* Failure: E_FAIL.
*/
HRESULT WINAPI DelNodeRunDLL32A( HWND hWnd, HINSTANCE hInst, LPSTR cmdline, INT show )
{
LPSTR szFilename, szFlags;
DWORD dwFlags;
HRESULT res;
TRACE("(%s)\n", debugstr_a(cmdline));
/* get the parameters at indexes 0 and 1 respectively */
szFilename = get_parameter(cmdline, ',', 0);
szFlags = get_parameter(cmdline, ',', 1);
dwFlags = atol(szFlags);
res = DelNodeA(szFilename, dwFlags);
HeapFree(GetProcessHeap(), 0, szFilename);
HeapFree(GetProcessHeap(), 0, szFlags);
return res;
}
/* The following defintions were copied from dlls/cabinet/cabinet.h */
/* EXTRACTdest flags */
#define EXTRACT_FILLFILELIST 0x00000001
#define EXTRACT_EXTRACTFILES 0x00000002
struct ExtractFileList {
LPSTR filename;
struct ExtractFileList *next;
BOOL unknown; /* always 1L */
} ;
/* the first parameter of the function Extract */
typedef struct {
long result1; /* 0x000 */
long unknown1[3]; /* 0x004 */
struct ExtractFileList *filelist; /* 0x010 */
long filecount; /* 0x014 */
DWORD flags; /* 0x018 */
char directory[0x104]; /* 0x01c */
char lastfile[0x20c]; /* 0x120 */
} EXTRACTdest;
static HRESULT (WINAPI *pExtract)(EXTRACTdest*, LPCSTR);
/* removes legal characters before and after file list, and
* converts the file list to a NULL-separated list
*/
static LPSTR convert_file_list(LPCSTR FileList, DWORD *dwNumFiles)
{
DWORD dwLen;
char *first = (char *)FileList;
char *last = (char *)FileList + strlen(FileList) - 1;
LPSTR szConvertedList, temp;
/* any number of these chars before the list is OK */
while (first < last && (*first == ' ' || *first == '\t' || *first == ':'))
first++;
/* any number of these chars after the list is OK */
while (last > first && (*last == ' ' || *last == '\t' || *last == ':'))
last--;
if (first == last)
return NULL;
dwLen = last - first + 3; /* room for double-null termination */
szConvertedList = HeapAlloc(GetProcessHeap(), 0, dwLen);
lstrcpynA(szConvertedList, first, dwLen - 1);
szConvertedList[dwLen - 1] = '\0';
szConvertedList[dwLen] = '\0';
/* empty list */
if (!lstrlenA(szConvertedList))
return NULL;
*dwNumFiles = 1;
/* convert the colons to double-null termination */
temp = szConvertedList;
while (*temp)
{
if (*temp == ':')
{
*temp = '\0';
(*dwNumFiles)++;
}
temp++;
}
return szConvertedList;
}
static void free_file_node(struct ExtractFileList *pNode)
{
HeapFree(GetProcessHeap(), 0, pNode->filename);
HeapFree(GetProcessHeap(), 0, pNode);
}
/* determines whether szFile is in the NULL-separated szFileList */
static BOOL file_in_list(LPSTR szFile, LPSTR szFileList)
{
DWORD dwLen = lstrlenA(szFile);
DWORD dwTestLen;
while (*szFileList)
{
dwTestLen = lstrlenA(szFileList);
if (dwTestLen == dwLen)
{
if (!lstrcmpiA(szFile, szFileList))
return TRUE;
}
szFileList += dwTestLen + 1;
}
return FALSE;
}
/* removes nodes from the linked list that aren't specified in szFileList
* returns the number of files that are in both the linked list and szFileList
*/
static DWORD fill_file_list(EXTRACTdest *extractDest, LPCSTR szCabName, LPSTR szFileList)
{
DWORD dwNumFound = 0;
struct ExtractFileList *pNode;
struct ExtractFileList *prev = NULL;
extractDest->flags |= EXTRACT_FILLFILELIST;
if (pExtract(extractDest, szCabName))
{
extractDest->flags &= ~EXTRACT_FILLFILELIST;
return -1;
}
pNode = extractDest->filelist;
while (pNode)
{
if (file_in_list(pNode->filename, szFileList))
{
prev = pNode;
pNode = pNode->next;
dwNumFound++;
}
else if (prev)
{
prev->next = pNode->next;
free_file_node(pNode);
pNode = prev->next;
}
else
{
extractDest->filelist = pNode->next;
free_file_node(pNode);
pNode = extractDest->filelist;
}
}
extractDest->flags &= ~EXTRACT_FILLFILELIST;
return dwNumFound;
}
/***********************************************************************
* ExtractFilesA (ADVPACK.@)
*
* Extracts the specified files from a cab archive into
* a destination directory.
*
* PARAMS
* CabName [I] Filename of the cab archive.
* ExpandDir [I] Destination directory for the extracted files.
* Flags [I] Reserved.
* FileList [I] Optional list of files to extract. See NOTES.
* LReserved [I] Reserved. Must be NULL.
* Reserved [I] Reserved. Must be 0.
*
* RETURNS
* Success: S_OK.
* Failure: E_FAIL.
*
* NOTES
* FileList is a colon-separated list of filenames. If FileList is
* non-NULL, only the files in the list will be extracted from the
* cab file, otherwise all files will be extracted. Any number of
* spaces, tabs, or colons can be before or after the list, but
* the list itself must only be separated by colons.
*/
HRESULT WINAPI ExtractFilesA( LPCSTR CabName, LPCSTR ExpandDir, DWORD Flags,
LPCSTR FileList, LPVOID LReserved, DWORD Reserved)
{
EXTRACTdest extractDest;
HMODULE hCabinet;
HRESULT res = S_OK;
DWORD dwFileCount = 0;
DWORD dwFilesFound = 0;
LPSTR szConvertedList = NULL;
TRACE("(%p %p %ld %p %p %ld)\n", CabName, ExpandDir, Flags,
FileList, LReserved, Reserved);
if (!CabName || !ExpandDir)
return E_INVALIDARG;
if (GetFileAttributesA(ExpandDir) == INVALID_FILE_ATTRIBUTES)
return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
hCabinet = LoadLibraryA("cabinet.dll");
if (!hCabinet)
return E_FAIL;
pExtract = (void *)GetProcAddress(hCabinet, "Extract");
if (!pExtract)
{
res = E_FAIL;
goto done;
}
ZeroMemory(&extractDest, sizeof(EXTRACTdest));
lstrcpyA(extractDest.directory, ExpandDir);
if (FileList)
{
szConvertedList = convert_file_list(FileList, &dwFileCount);
if (!szConvertedList || dwFileCount == -1)
{
res = E_FAIL;
goto done;
}
dwFilesFound = fill_file_list(&extractDest, CabName, szConvertedList);
if (dwFilesFound != dwFileCount)
{
res = E_FAIL;
goto done;
}
}
else
extractDest.flags |= EXTRACT_FILLFILELIST;
extractDest.flags |= EXTRACT_EXTRACTFILES;
res = pExtract(&extractDest, CabName);
done:
FreeLibrary(hCabinet);
HeapFree(GetProcessHeap(), 0, szConvertedList);
return res;
}
/***********************************************************************
* FileSaveMarkNotExistA (ADVPACK.@)
*
* Marks the files in the file list as not existing so they won't be
* backed up during a save.
*
* PARAMS
* pszFileList [I] NULL-separated list of filenames.
* pszDir [I] Path of the backup directory.
* pszBaseName [I] Basename of the INI file.
*
* RETURNS
* Success: S_OK.
* Failure: E_FAIL.
*/
HRESULT WINAPI FileSaveMarkNotExistA(LPSTR pszFileList, LPSTR pszDir, LPSTR pszBaseName)
{
TRACE("(%p, %p, %p)\n", pszFileList, pszDir, pszBaseName);
return AddDelBackupEntryA(pszFileList, pszDir, pszBaseName, AADBE_DEL_ENTRY);
}
/***********************************************************************
* FileSaveRestoreA (ADVPACK.@)
*
* Saves or restores the files in the specified file list.
*
* PARAMS
* hDlg [I] Handle to the dialog used for the display.
* pszFileList [I] NULL-separated list of filenames.
* pszDir [I] Path of the backup directory.
* pszBaseName [I] Basename of the backup files.
* dwFlags [I] See advpub.h.
*
* RETURNS
* Success: S_OK.
* Failure: E_FAIL.
*
* NOTES
* If pszFileList is NULL on restore, all files will be restored.
*
* BUGS
* Unimplemented.
*/
HRESULT WINAPI FileSaveRestoreA(HWND hDlg, LPSTR pszFileList, LPSTR pszDir,
LPSTR pszBaseName, DWORD dwFlags)
{
FIXME("(%p, %p, %p, %p, %ld) stub\n", hDlg, pszFileList, pszDir,
pszBaseName, dwFlags);
return E_FAIL;
}
/***********************************************************************
* FileSaveRestoreOnINFA (ADVPACK.@)
*
*
* PARAMS
* hWnd [I] Handle to the window used for the display.
* pszTitle [I] Title of the window.
* pszINF [I] Fully-qualified INF filename.
* pszSection [I] GenInstall INF section name.
* pszBackupDir [I] Directory to store the backup file.
* pszBaseBackupFile [I] Basename of the backup files.
* dwFlags [I] See advpub.h
*
* RETURNS
* Success: S_OK.
* Failure: E_FAIL.
*
* NOTES
* If pszSection is NULL, the default section will be used.
*
* BUGS
* Unimplemented.
*/
HRESULT WINAPI FileSaveRestoreOnINFA(HWND hWnd, LPCSTR pszTitle, LPCSTR pszINF,
LPCSTR pszSection, LPCSTR pszBackupDir,
LPCSTR pszBaseBackupFile, DWORD dwFlags)
{
FIXME("(%p, %p, %p, %p, %p, %p, %ld) stub\n", hWnd, pszTitle, pszINF,
pszSection, pszBackupDir, pszBaseBackupFile, dwFlags);
return E_FAIL;
}
/***********************************************************************
* GetVersionFromFileA (ADVPACK.@)
*
* See GetVersionFromFileEx.
*/
HRESULT WINAPI GetVersionFromFileA(LPCSTR Filename, LPDWORD MajorVer,
LPDWORD MinorVer, BOOL Version )
{
TRACE("(%s, %p, %p, %d)\n", Filename, MajorVer, MinorVer, Version);
return GetVersionFromFileExA(Filename, MajorVer, MinorVer, Version);
}
/* data for GetVersionFromFileEx */
typedef struct tagLANGANDCODEPAGE
{
WORD wLanguage;
WORD wCodePage;
} LANGANDCODEPAGE;
/***********************************************************************
* GetVersionFromFileExA (ADVPACK.@)
*
* Gets the files version or language information.
*
* PARAMS
* lpszFilename [I] The file to get the info from.
* pdwMSVer [O] Major version.
* pdwLSVer [O] Minor version.
* bVersion [I] Whether to retrieve version or language info.
*
* RETURNS
* Always returns S_OK.
*
* NOTES
* If bVersion is TRUE, version information is retrieved, else
* pdwMSVer gets the language ID and pdwLSVer gets the codepage ID.
*/
HRESULT WINAPI GetVersionFromFileExA(LPCSTR lpszFilename, LPDWORD pdwMSVer,
LPDWORD pdwLSVer, BOOL bVersion )
{
VS_FIXEDFILEINFO *pFixedVersionInfo;
LANGANDCODEPAGE *pLangAndCodePage;
DWORD dwHandle, dwInfoSize;
CHAR szWinDir[MAX_PATH];
CHAR szFile[MAX_PATH];
LPVOID pVersionInfo = NULL;
BOOL bFileCopied = FALSE;
UINT uValueLen;
TRACE("(%s, %p, %p, %d)\n", lpszFilename, pdwMSVer, pdwLSVer, bVersion);
*pdwLSVer = 0;
*pdwMSVer = 0;
lstrcpynA(szFile, lpszFilename, MAX_PATH);
dwInfoSize = GetFileVersionInfoSizeA(szFile, &dwHandle);
if (!dwInfoSize)
{
/* check that the file exists */
if (GetFileAttributesA(szFile) == INVALID_FILE_ATTRIBUTES)
return S_OK;
/* file exists, but won't be found by GetFileVersionInfoSize,
* so copy it to the temp dir where it will be found.
*/
GetWindowsDirectoryA(szWinDir, MAX_PATH);
GetTempFileNameA(szWinDir, NULL, 0, szFile);
CopyFileA(lpszFilename, szFile, FALSE);
bFileCopied = TRUE;
dwInfoSize = GetFileVersionInfoSizeA(szFile, &dwHandle);
if (!dwInfoSize)
goto done;
}
pVersionInfo = HeapAlloc(GetProcessHeap(), 0, dwInfoSize);
if (!pVersionInfo)
goto done;
if (!GetFileVersionInfoA(szFile, dwHandle, dwInfoSize, pVersionInfo))
goto done;
if (bVersion)
{
if (!VerQueryValueA(pVersionInfo, "\\",
(LPVOID *)&pFixedVersionInfo, &uValueLen))
goto done;
if (!uValueLen)
goto done;
*pdwMSVer = pFixedVersionInfo->dwFileVersionMS;
*pdwLSVer = pFixedVersionInfo->dwFileVersionLS;
}
else
{
if (!VerQueryValueA(pVersionInfo, "\\VarFileInfo\\Translation",
(LPVOID *)&pLangAndCodePage, &uValueLen))
goto done;
if (!uValueLen)
goto done;
*pdwMSVer = pLangAndCodePage->wLanguage;
*pdwLSVer = pLangAndCodePage->wCodePage;
}
done:
HeapFree(GetProcessHeap(), 0, pVersionInfo);
if (bFileCopied)
DeleteFileA(szFile);
return S_OK;
}