2 * Advpack file functions
4 * Copyright 2006 James Hawkins
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 #include "wine/debug.h"
33 #include "wine/unicode.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(advpack);
37 /* converts an ansi double null-terminated list to a unicode list */
38 static LPWSTR ansi_to_unicode_list(LPCSTR ansi_list)
42 LPCSTR ptr = ansi_list;
44 while (*ptr) ptr += lstrlenA(ptr) + 1;
45 len = ptr + 1 - ansi_list;
46 wlen = MultiByteToWideChar(CP_ACP, 0, ansi_list, len, NULL, 0);
47 list = HeapAlloc(GetProcessHeap(), 0, wlen * sizeof(WCHAR));
48 MultiByteToWideChar(CP_ACP, 0, ansi_list, len, list, wlen);
52 /***********************************************************************
53 * AddDelBackupEntryA (ADVPACK.@)
55 * See AddDelBackupEntryW.
57 HRESULT WINAPI AddDelBackupEntryA(LPCSTR lpcszFileList, LPCSTR lpcszBackupDir,
58 LPCSTR lpcszBaseName, DWORD dwFlags)
60 UNICODE_STRING backupdir, basename;
61 LPWSTR filelist, backup;
64 TRACE("(%p, %p, %p, %ld)\n", lpcszFileList, lpcszBackupDir,
65 lpcszBaseName, dwFlags);
68 filelist = ansi_to_unicode_list(lpcszFileList);
72 RtlCreateUnicodeStringFromAsciiz(&backupdir, lpcszBackupDir);
73 RtlCreateUnicodeStringFromAsciiz(&basename, lpcszBaseName);
76 backup = backupdir.Buffer;
80 res = AddDelBackupEntryW(filelist, backup, basename.Buffer, dwFlags);
82 HeapFree(GetProcessHeap(), 0, filelist);
84 RtlFreeUnicodeString(&backupdir);
85 RtlFreeUnicodeString(&basename);
90 /***********************************************************************
91 * AddDelBackupEntryW (ADVPACK.@)
93 * Either appends the files in the file list to the backup section of
94 * the specified INI, or deletes the entries from the INI file.
97 * lpcszFileList [I] NULL-separated list of filenames.
98 * lpcszBackupDir [I] Path of the backup directory.
99 * lpcszBaseName [I] Basename of the INI file.
100 * dwFlags [I] AADBE_ADD_ENTRY adds the entries in the file list
101 * to the INI file, while AADBE_DEL_ENTRY removes
102 * the entries from the INI file.
108 * If the INI file does not exist before adding entries to it, the file
111 * If lpcszBackupDir is NULL, the INI file is assumed to exist in
112 * c:\windows or created there if it does not exist.
114 HRESULT WINAPI AddDelBackupEntryW(LPCWSTR lpcszFileList, LPCWSTR lpcszBackupDir,
115 LPCWSTR lpcszBaseName, DWORD dwFlags)
117 WCHAR szIniPath[MAX_PATH];
118 LPWSTR szString = NULL;
120 static const WCHAR szBackupEntry[] = {
121 '-','1',',','0',',','0',',','0',',','0',',','0',',','-','1',0
124 static const WCHAR backslash[] = {'\\',0};
125 static const WCHAR ini[] = {'.','i','n','i',0};
126 static const WCHAR backup[] = {'b','a','c','k','u','p',0};
128 TRACE("(%p, %p, %p, %ld)\n", lpcszFileList, lpcszBackupDir,
129 lpcszBaseName, dwFlags);
131 if (!lpcszFileList || !*lpcszFileList)
135 lstrcpyW(szIniPath, lpcszBackupDir);
137 GetWindowsDirectoryW(szIniPath, MAX_PATH);
139 lstrcatW(szIniPath, backslash);
140 lstrcatW(szIniPath, lpcszBaseName);
141 lstrcatW(szIniPath, ini);
143 SetFileAttributesW(szIniPath, FILE_ATTRIBUTE_NORMAL);
145 if (dwFlags & AADBE_ADD_ENTRY)
146 szString = (LPWSTR)szBackupEntry;
147 else if (dwFlags & AADBE_DEL_ENTRY)
150 /* add or delete the INI entries */
151 while (*lpcszFileList)
153 WritePrivateProfileStringW(backup, lpcszFileList, szString, szIniPath);
154 lpcszFileList += lstrlenW(lpcszFileList) + 1;
157 /* hide the INI file */
158 SetFileAttributesW(szIniPath, FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN);
163 /* FIXME: this is only for the local case, X:\ */
164 #define ROOT_LENGTH 3
166 UINT CALLBACK pQuietQueueCallback(PVOID Context, UINT Notification,
167 UINT_PTR Param1, UINT_PTR Param2)
172 UINT CALLBACK pQueueCallback(PVOID Context, UINT Notification,
173 UINT_PTR Param1, UINT_PTR Param2)
175 /* only be verbose for error notifications */
177 Notification == SPFILENOTIFY_RENAMEERROR ||
178 Notification == SPFILENOTIFY_DELETEERROR ||
179 Notification == SPFILENOTIFY_COPYERROR)
181 return SetupDefaultQueueCallbackW(Context, Notification,
188 /***********************************************************************
189 * AdvInstallFileA (ADVPACK.@)
191 * See AdvInstallFileW.
193 HRESULT WINAPI AdvInstallFileA(HWND hwnd, LPCSTR lpszSourceDir, LPCSTR lpszSourceFile,
194 LPCSTR lpszDestDir, LPCSTR lpszDestFile,
195 DWORD dwFlags, DWORD dwReserved)
197 UNICODE_STRING sourcedir, sourcefile;
198 UNICODE_STRING destdir, destfile;
201 TRACE("(%p,%s,%s,%s,%s,%ld,%ld)\n", hwnd, debugstr_a(lpszSourceDir),
202 debugstr_a(lpszSourceFile), debugstr_a(lpszDestDir),
203 debugstr_a(lpszDestFile), dwFlags, dwReserved);
205 if (!lpszSourceDir || !lpszSourceFile || !lpszDestDir)
208 RtlCreateUnicodeStringFromAsciiz(&sourcedir, lpszSourceDir);
209 RtlCreateUnicodeStringFromAsciiz(&sourcefile, lpszSourceFile);
210 RtlCreateUnicodeStringFromAsciiz(&destdir, lpszDestDir);
211 RtlCreateUnicodeStringFromAsciiz(&destfile, lpszDestFile);
213 res = AdvInstallFileW(hwnd, sourcedir.Buffer, sourcefile.Buffer,
214 destdir.Buffer, destfile.Buffer, dwFlags, dwReserved);
216 RtlFreeUnicodeString(&sourcedir);
217 RtlFreeUnicodeString(&sourcefile);
218 RtlFreeUnicodeString(&destdir);
219 RtlFreeUnicodeString(&destfile);
224 /***********************************************************************
225 * AdvInstallFileW (ADVPACK.@)
227 * Copies a file from the source to a destination.
230 * hwnd [I] Handle to the window used for messages.
231 * lpszSourceDir [I] Source directory.
232 * lpszSourceFile [I] Source filename.
233 * lpszDestDir [I] Destination directory.
234 * lpszDestFile [I] Optional destination filename.
235 * dwFlags [I] See advpub.h.
236 * dwReserved [I] Reserved. Must be 0.
243 * If lpszDestFile is NULL, the destination filename is the same as
246 HRESULT WINAPI AdvInstallFileW(HWND hwnd, LPCWSTR lpszSourceDir, LPCWSTR lpszSourceFile,
247 LPCWSTR lpszDestDir, LPCWSTR lpszDestFile,
248 DWORD dwFlags, DWORD dwReserved)
250 PSP_FILE_CALLBACK_W pFileCallback;
251 LPWSTR szPath, szDestFilename;
252 WCHAR szRootPath[ROOT_LENGTH];
253 DWORD dwLen, dwLastError;
257 TRACE("(%p,%s,%s,%s,%s,%ld,%ld)\n", hwnd, debugstr_w(lpszSourceDir),
258 debugstr_w(lpszSourceFile), debugstr_w(lpszDestDir),
259 debugstr_w(lpszDestFile), dwFlags, dwReserved);
261 if (!lpszSourceDir || !lpszSourceFile || !lpszDestDir)
264 fileQueue = SetupOpenFileQueue();
265 if (fileQueue == INVALID_HANDLE_VALUE)
266 return HRESULT_FROM_WIN32(GetLastError());
269 dwLastError = ERROR_SUCCESS;
271 lstrcpynW(szRootPath, lpszSourceDir, ROOT_LENGTH);
272 szPath = (LPWSTR)lpszSourceDir + ROOT_LENGTH;
274 /* use lpszSourceFile as destination filename if lpszDestFile is NULL */
277 dwLen = lstrlenW(lpszDestFile);
278 szDestFilename = HeapAlloc(GetProcessHeap(), 0, dwLen * sizeof(WCHAR));
279 lstrcpyW(szDestFilename, lpszDestFile);
283 dwLen = lstrlenW(lpszSourceFile);
284 szDestFilename = HeapAlloc(GetProcessHeap(), 0, dwLen * sizeof(WCHAR));
285 lstrcpyW(szDestFilename, lpszSourceFile);
288 /* add the file copy operation to the setup queue */
289 if (!SetupQueueCopyW(fileQueue, szRootPath, szPath, lpszSourceFile, NULL,
290 NULL, lpszDestDir, szDestFilename, dwFlags))
292 dwLastError = GetLastError();
296 pContext = SetupInitDefaultQueueCallbackEx(hwnd, INVALID_HANDLE_VALUE,
300 dwLastError = GetLastError();
304 /* don't output anything for AIF_QUIET */
305 if (dwFlags & AIF_QUIET)
306 pFileCallback = pQuietQueueCallback;
308 pFileCallback = pQueueCallback;
310 /* perform the file copy */
311 if (!SetupCommitFileQueueW(hwnd, fileQueue, pFileCallback, pContext))
313 dwLastError = GetLastError();
318 SetupTermDefaultQueueCallback(pContext);
319 SetupCloseFileQueue(fileQueue);
321 HeapFree(GetProcessHeap(), 0, szDestFilename);
323 return HRESULT_FROM_WIN32(dwLastError);
326 static HRESULT DELNODE_recurse_dirtree(LPSTR fname, DWORD flags)
328 DWORD fattrs = GetFileAttributesA(fname);
329 HRESULT ret = E_FAIL;
331 if (fattrs & FILE_ATTRIBUTE_DIRECTORY)
334 WIN32_FIND_DATAA w32fd;
336 int fname_len = lstrlenA(fname);
338 /* Generate a path with wildcard suitable for iterating */
339 if (CharPrevA(fname, fname + fname_len) != "\\")
341 lstrcpyA(fname + fname_len, "\\");
344 lstrcpyA(fname + fname_len, "*");
346 if ((hFindFile = FindFirstFileA(fname, &w32fd)) != INVALID_HANDLE_VALUE)
348 /* Iterate through the files in the directory */
349 for (done = FALSE; !done; done = !FindNextFileA(hFindFile, &w32fd))
351 TRACE("%s\n", w32fd.cFileName);
352 if (lstrcmpA(".", w32fd.cFileName) != 0 &&
353 lstrcmpA("..", w32fd.cFileName) != 0)
355 lstrcpyA(fname + fname_len, w32fd.cFileName);
356 if (DELNODE_recurse_dirtree(fname, flags) != S_OK)
362 FindClose(hFindFile);
365 /* We're done with this directory, so restore the old path without wildcard */
366 *(fname + fname_len) = '\0';
370 TRACE("%s: directory\n", fname);
371 if (SetFileAttributesA(fname, FILE_ATTRIBUTE_NORMAL) && RemoveDirectoryA(fname))
379 TRACE("%s: file\n", fname);
380 if (SetFileAttributesA(fname, FILE_ATTRIBUTE_NORMAL) && DeleteFileA(fname))
389 /***********************************************************************
390 * DelNodeA (ADVPACK.@)
392 * Deletes a file or directory
395 * pszFileOrDirName [I] Name of file or directory to delete
396 * dwFlags [I] Flags; see include/advpub.h
404 * - Native version apparently does a lot of checking to make sure
405 * we're not trying to delete a system directory etc.
407 HRESULT WINAPI DelNodeA( LPCSTR pszFileOrDirName, DWORD dwFlags )
409 CHAR fname[MAX_PATH];
410 HRESULT ret = E_FAIL;
412 TRACE("(%s, 0x%08lx)\n", debugstr_a(pszFileOrDirName), dwFlags);
415 FIXME("Flags ignored!\n");
417 if (pszFileOrDirName && *pszFileOrDirName)
419 lstrcpyA(fname, pszFileOrDirName);
421 /* TODO: Should check for system directory deletion etc. here */
423 ret = DELNODE_recurse_dirtree(fname, dwFlags);
429 /* returns the parameter at dwIndex in a list of parameters
430 * separated by the cSeparator character
432 static LPSTR get_parameter(LPSTR szParameters, CHAR cSeparator, DWORD dwIndex)
434 LPSTR szParam = NULL;
437 while (*szParameters && i < dwIndex)
439 if (*szParameters == cSeparator)
448 szParam = HeapAlloc(GetProcessHeap(), 0, lstrlenA(szParameters));
449 lstrcpyA(szParam, szParameters);
454 /***********************************************************************
455 * DelNodeRunDLL32A (ADVPACK.@)
457 * Deletes a file or directory, WinMain style.
460 * hWnd [I] Handle to the window used for the display.
461 * hInst [I] Instance of the process.
462 * cmdline [I] Contains parameters in the order FileOrDirName,Flags.
463 * show [I] How the window should be shown.
469 HRESULT WINAPI DelNodeRunDLL32A( HWND hWnd, HINSTANCE hInst, LPSTR cmdline, INT show )
471 LPSTR szFilename, szFlags;
475 TRACE("(%s)\n", debugstr_a(cmdline));
477 /* get the parameters at indexes 0 and 1 respectively */
478 szFilename = get_parameter(cmdline, ',', 0);
479 szFlags = get_parameter(cmdline, ',', 1);
481 dwFlags = atol(szFlags);
483 res = DelNodeA(szFilename, dwFlags);
485 HeapFree(GetProcessHeap(), 0, szFilename);
486 HeapFree(GetProcessHeap(), 0, szFlags);
491 /* The following defintions were copied from dlls/cabinet/cabinet.h */
493 /* EXTRACTdest flags */
494 #define EXTRACT_FILLFILELIST 0x00000001
495 #define EXTRACT_EXTRACTFILES 0x00000002
497 struct ExtractFileList {
499 struct ExtractFileList *next;
500 BOOL unknown; /* always 1L */
503 /* the first parameter of the function Extract */
505 long result1; /* 0x000 */
506 long unknown1[3]; /* 0x004 */
507 struct ExtractFileList *filelist; /* 0x010 */
508 long filecount; /* 0x014 */
509 DWORD flags; /* 0x018 */
510 char directory[0x104]; /* 0x01c */
511 char lastfile[0x20c]; /* 0x120 */
514 static HRESULT (WINAPI *pExtract)(EXTRACTdest*, LPCSTR);
516 /* removes legal characters before and after file list, and
517 * converts the file list to a NULL-separated list
519 static LPSTR convert_file_list(LPCSTR FileList, DWORD *dwNumFiles)
522 char *first = (char *)FileList;
523 char *last = (char *)FileList + strlen(FileList) - 1;
524 LPSTR szConvertedList, temp;
526 /* any number of these chars before the list is OK */
527 while (first < last && (*first == ' ' || *first == '\t' || *first == ':'))
530 /* any number of these chars after the list is OK */
531 while (last > first && (*last == ' ' || *last == '\t' || *last == ':'))
537 dwLen = last - first + 3; /* room for double-null termination */
538 szConvertedList = HeapAlloc(GetProcessHeap(), 0, dwLen);
539 lstrcpynA(szConvertedList, first, dwLen - 1);
541 szConvertedList[dwLen - 1] = '\0';
542 szConvertedList[dwLen] = '\0';
545 if (!lstrlenA(szConvertedList))
550 /* convert the colons to double-null termination */
551 temp = szConvertedList;
563 return szConvertedList;
566 static void free_file_node(struct ExtractFileList *pNode)
568 HeapFree(GetProcessHeap(), 0, pNode->filename);
569 HeapFree(GetProcessHeap(), 0, pNode);
572 /* determines whether szFile is in the NULL-separated szFileList */
573 static BOOL file_in_list(LPSTR szFile, LPSTR szFileList)
575 DWORD dwLen = lstrlenA(szFile);
580 dwTestLen = lstrlenA(szFileList);
582 if (dwTestLen == dwLen)
584 if (!lstrcmpiA(szFile, szFileList))
588 szFileList += dwTestLen + 1;
594 /* removes nodes from the linked list that aren't specified in szFileList
595 * returns the number of files that are in both the linked list and szFileList
597 static DWORD fill_file_list(EXTRACTdest *extractDest, LPCSTR szCabName, LPSTR szFileList)
599 DWORD dwNumFound = 0;
600 struct ExtractFileList *pNode;
601 struct ExtractFileList *prev = NULL;
603 extractDest->flags |= EXTRACT_FILLFILELIST;
604 if (pExtract(extractDest, szCabName))
606 extractDest->flags &= ~EXTRACT_FILLFILELIST;
610 pNode = extractDest->filelist;
613 if (file_in_list(pNode->filename, szFileList))
621 prev->next = pNode->next;
622 free_file_node(pNode);
627 extractDest->filelist = pNode->next;
628 free_file_node(pNode);
629 pNode = extractDest->filelist;
633 extractDest->flags &= ~EXTRACT_FILLFILELIST;
637 /***********************************************************************
638 * ExtractFilesA (ADVPACK.@)
640 * Extracts the specified files from a cab archive into
641 * a destination directory.
644 * CabName [I] Filename of the cab archive.
645 * ExpandDir [I] Destination directory for the extracted files.
646 * Flags [I] Reserved.
647 * FileList [I] Optional list of files to extract. See NOTES.
648 * LReserved [I] Reserved. Must be NULL.
649 * Reserved [I] Reserved. Must be 0.
656 * FileList is a colon-separated list of filenames. If FileList is
657 * non-NULL, only the files in the list will be extracted from the
658 * cab file, otherwise all files will be extracted. Any number of
659 * spaces, tabs, or colons can be before or after the list, but
660 * the list itself must only be separated by colons.
662 HRESULT WINAPI ExtractFilesA( LPCSTR CabName, LPCSTR ExpandDir, DWORD Flags,
663 LPCSTR FileList, LPVOID LReserved, DWORD Reserved)
665 EXTRACTdest extractDest;
668 DWORD dwFileCount = 0;
669 DWORD dwFilesFound = 0;
670 LPSTR szConvertedList = NULL;
672 TRACE("(%p %p %ld %p %p %ld)\n", CabName, ExpandDir, Flags,
673 FileList, LReserved, Reserved);
675 if (!CabName || !ExpandDir)
678 if (GetFileAttributesA(ExpandDir) == INVALID_FILE_ATTRIBUTES)
679 return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
681 hCabinet = LoadLibraryA("cabinet.dll");
685 pExtract = (void *)GetProcAddress(hCabinet, "Extract");
692 ZeroMemory(&extractDest, sizeof(EXTRACTdest));
693 lstrcpyA(extractDest.directory, ExpandDir);
697 szConvertedList = convert_file_list(FileList, &dwFileCount);
698 if (!szConvertedList || dwFileCount == -1)
704 dwFilesFound = fill_file_list(&extractDest, CabName, szConvertedList);
705 if (dwFilesFound != dwFileCount)
712 extractDest.flags |= EXTRACT_FILLFILELIST;
714 extractDest.flags |= EXTRACT_EXTRACTFILES;
715 res = pExtract(&extractDest, CabName);
718 FreeLibrary(hCabinet);
719 HeapFree(GetProcessHeap(), 0, szConvertedList);
724 /***********************************************************************
725 * FileSaveMarkNotExistA (ADVPACK.@)
727 * See FileSaveMarkNotExistW.
729 HRESULT WINAPI FileSaveMarkNotExistA(LPSTR pszFileList, LPSTR pszDir, LPSTR pszBaseName)
731 TRACE("(%p, %p, %p)\n", pszFileList, pszDir, pszBaseName);
733 return AddDelBackupEntryA(pszFileList, pszDir, pszBaseName, AADBE_DEL_ENTRY);
736 /***********************************************************************
737 * FileSaveMarkNotExistW (ADVPACK.@)
739 * Marks the files in the file list as not existing so they won't be
740 * backed up during a save.
743 * pszFileList [I] NULL-separated list of filenames.
744 * pszDir [I] Path of the backup directory.
745 * pszBaseName [I] Basename of the INI file.
751 HRESULT WINAPI FileSaveMarkNotExistW(LPWSTR pszFileList, LPWSTR pszDir, LPWSTR pszBaseName)
753 TRACE("(%p, %p, %p)\n", pszFileList, pszDir, pszBaseName);
755 return AddDelBackupEntryW(pszFileList, pszDir, pszBaseName, AADBE_DEL_ENTRY);
758 /***********************************************************************
759 * FileSaveRestoreA (ADVPACK.@)
761 * Saves or restores the files in the specified file list.
764 * hDlg [I] Handle to the dialog used for the display.
765 * pszFileList [I] NULL-separated list of filenames.
766 * pszDir [I] Path of the backup directory.
767 * pszBaseName [I] Basename of the backup files.
768 * dwFlags [I] See advpub.h.
775 * If pszFileList is NULL on restore, all files will be restored.
780 HRESULT WINAPI FileSaveRestoreA(HWND hDlg, LPSTR pszFileList, LPSTR pszDir,
781 LPSTR pszBaseName, DWORD dwFlags)
783 FIXME("(%p, %p, %p, %p, %ld) stub\n", hDlg, pszFileList, pszDir,
784 pszBaseName, dwFlags);
789 /***********************************************************************
790 * FileSaveRestoreOnINFA (ADVPACK.@)
794 * hWnd [I] Handle to the window used for the display.
795 * pszTitle [I] Title of the window.
796 * pszINF [I] Fully-qualified INF filename.
797 * pszSection [I] GenInstall INF section name.
798 * pszBackupDir [I] Directory to store the backup file.
799 * pszBaseBackupFile [I] Basename of the backup files.
800 * dwFlags [I] See advpub.h
807 * If pszSection is NULL, the default section will be used.
812 HRESULT WINAPI FileSaveRestoreOnINFA(HWND hWnd, LPCSTR pszTitle, LPCSTR pszINF,
813 LPCSTR pszSection, LPCSTR pszBackupDir,
814 LPCSTR pszBaseBackupFile, DWORD dwFlags)
816 FIXME("(%p, %p, %p, %p, %p, %p, %ld) stub\n", hWnd, pszTitle, pszINF,
817 pszSection, pszBackupDir, pszBaseBackupFile, dwFlags);
822 /***********************************************************************
823 * GetVersionFromFileA (ADVPACK.@)
825 * See GetVersionFromFileEx.
827 HRESULT WINAPI GetVersionFromFileA(LPCSTR Filename, LPDWORD MajorVer,
828 LPDWORD MinorVer, BOOL Version )
830 TRACE("(%s, %p, %p, %d)\n", Filename, MajorVer, MinorVer, Version);
831 return GetVersionFromFileExA(Filename, MajorVer, MinorVer, Version);
834 /* data for GetVersionFromFileEx */
835 typedef struct tagLANGANDCODEPAGE
841 /***********************************************************************
842 * GetVersionFromFileExA (ADVPACK.@)
844 * Gets the files version or language information.
847 * lpszFilename [I] The file to get the info from.
848 * pdwMSVer [O] Major version.
849 * pdwLSVer [O] Minor version.
850 * bVersion [I] Whether to retrieve version or language info.
853 * Always returns S_OK.
856 * If bVersion is TRUE, version information is retrieved, else
857 * pdwMSVer gets the language ID and pdwLSVer gets the codepage ID.
859 HRESULT WINAPI GetVersionFromFileExA(LPCSTR lpszFilename, LPDWORD pdwMSVer,
860 LPDWORD pdwLSVer, BOOL bVersion )
862 VS_FIXEDFILEINFO *pFixedVersionInfo;
863 LANGANDCODEPAGE *pLangAndCodePage;
864 DWORD dwHandle, dwInfoSize;
865 CHAR szWinDir[MAX_PATH];
866 CHAR szFile[MAX_PATH];
867 LPVOID pVersionInfo = NULL;
868 BOOL bFileCopied = FALSE;
871 TRACE("(%s, %p, %p, %d)\n", lpszFilename, pdwMSVer, pdwLSVer, bVersion);
876 lstrcpynA(szFile, lpszFilename, MAX_PATH);
878 dwInfoSize = GetFileVersionInfoSizeA(szFile, &dwHandle);
881 /* check that the file exists */
882 if (GetFileAttributesA(szFile) == INVALID_FILE_ATTRIBUTES)
885 /* file exists, but won't be found by GetFileVersionInfoSize,
886 * so copy it to the temp dir where it will be found.
888 GetWindowsDirectoryA(szWinDir, MAX_PATH);
889 GetTempFileNameA(szWinDir, NULL, 0, szFile);
890 CopyFileA(lpszFilename, szFile, FALSE);
893 dwInfoSize = GetFileVersionInfoSizeA(szFile, &dwHandle);
898 pVersionInfo = HeapAlloc(GetProcessHeap(), 0, dwInfoSize);
902 if (!GetFileVersionInfoA(szFile, dwHandle, dwInfoSize, pVersionInfo))
907 if (!VerQueryValueA(pVersionInfo, "\\",
908 (LPVOID *)&pFixedVersionInfo, &uValueLen))
914 *pdwMSVer = pFixedVersionInfo->dwFileVersionMS;
915 *pdwLSVer = pFixedVersionInfo->dwFileVersionLS;
919 if (!VerQueryValueA(pVersionInfo, "\\VarFileInfo\\Translation",
920 (LPVOID *)&pLangAndCodePage, &uValueLen))
926 *pdwMSVer = pLangAndCodePage->wLanguage;
927 *pdwLSVer = pLangAndCodePage->wCodePage;
931 HeapFree(GetProcessHeap(), 0, pVersionInfo);