// Copyright (c) 1998-1999 Microsoft Corporation #include "stdafx.h" #include "MSICAB.h" extern "C" { #include "uni2utf.h" }; #ifndef IDS_CAB_DIR_NAME #include "resource.h" #endif LPCTSTR cszDirSeparator = _T("\\"); //--------------------------------------------------------------------------- // DirectorySearch is used to locate all of the files in a directory or // one of its subdirectories which match a file spec. //--------------------------------------------------------------------------- void DirectorySearch(const CString & strSpec, const CString & strDir, CStringList &results) { // Look for all of the files which match the file spec in the directory // specified by strDir. WIN32_FIND_DATA finddata; CString strSearch, strDirectory; strDirectory = strDir; if (strDirectory.Right(1) != CString(cszDirSeparator)) strDirectory += CString(cszDirSeparator); strSearch = strDirectory + strSpec; HANDLE hFind = FindFirstFile(strSearch, &finddata); if (hFind != INVALID_HANDLE_VALUE) { do { results.AddHead(strDirectory + CString(finddata.cFileName)); } while (FindNextFile(hFind, &finddata)); FindClose(hFind); } // Now call this function recursively, with each of the subdirectories. strSearch = strDirectory + CString(_T("*")); hFind = FindFirstFile(strSearch, &finddata); if (hFind != INVALID_HANDLE_VALUE) { do { if (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) if (::_tcscmp(finddata.cFileName, _T(".")) != 0 && ::_tcscmp(finddata.cFileName, _T("..")) != 0) DirectorySearch(strSpec, strDirectory + CString(finddata.cFileName), results); } while (FindNextFile(hFind, &finddata)); FindClose(hFind); } } //--------------------------------------------------------------------------- // This function gets the directory in which to put exploded CAB files. // This will be the same directory each time, so this function will create // the directory (if necessary) and delete any files in the directory. //--------------------------------------------------------------------------- BOOL GetCABExplodeDir(CString &destination, BOOL fDeleteFiles, const CString & strDontDelete) { CString strMSInfoDir, strExplodeTo, strSubDirName; // Determine the temporary path and add on a subdir name. TCHAR szTempDir[_MAX_PATH]; if (GetTempPath(_MAX_PATH, szTempDir) > _MAX_PATH) { // MSIError(IDS_GENERAL_ERROR, "couldn't get temporary path"); destination = _T(""); return FALSE; } strSubDirName.LoadString(IDS_CAB_DIR_NAME); strExplodeTo = szTempDir; if (strExplodeTo.Right(1) == CString(cszDirSeparator)) strExplodeTo = strExplodeTo + strSubDirName; else strExplodeTo = strExplodeTo + CString(cszDirSeparator) + strSubDirName; // Kill the directory if it already exists. if (fDeleteFiles) KillDirectory(strExplodeTo, strDontDelete); // Create the subdirectory. if (!CreateDirectoryEx(szTempDir, strExplodeTo, NULL)) { if (GetLastError() != ERROR_ALREADY_EXISTS) { // MSIError(IDS_GENERAL_ERROR, "couldn't create the target directory"); destination = ""; return FALSE; } } destination = strExplodeTo; return TRUE; } //--------------------------------------------------------------------------- // This functions kills a directory by recursively deleting files and // subdirectories. //--------------------------------------------------------------------------- void KillDirectory(const CString & strDir, const CString & strDontDelete) { CString strDirectory = strDir; if (strDirectory.Right(1) == CString(cszDirSeparator)) strDirectory = strDirectory.Left(strDirectory.GetLength() - 1); // Delete any files in directory. CString strFilesToDelete = strDirectory + CString(_T("\\*.*")); CString strDeleteFile; WIN32_FIND_DATA filedata; BOOL bFound = TRUE; HANDLE hFindFile = FindFirstFile(strFilesToDelete, &filedata); while (hFindFile != INVALID_HANDLE_VALUE && bFound) { if ((filedata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0L) { strDeleteFile = strDirectory + CString(cszDirSeparator) + filedata.cFileName; if (strDontDelete.CompareNoCase(strDeleteFile) != 0) { ::SetFileAttributes(strDeleteFile, FILE_ATTRIBUTE_NORMAL); ::DeleteFile(strDeleteFile); } } bFound = FindNextFile(hFindFile, &filedata); } FindClose(hFindFile); // Now call this function on any subdirectories in this directory. CString strSearch = strDirectory + CString(_T("\\*")); hFindFile = FindFirstFile(strSearch, &filedata); if (hFindFile != INVALID_HANDLE_VALUE) { do { if (filedata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) if (::_tcscmp(filedata.cFileName, _T(".")) != 0 && ::_tcscmp(filedata.cFileName, _T("..")) != 0) KillDirectory(strDirectory + CString(cszDirSeparator) + CString(filedata.cFileName)); } while (FindNextFile(hFindFile, &filedata)); FindClose(hFindFile); } // Finally, remove this directory. ::RemoveDirectory(strDirectory); } //--------------------------------------------------------------------------- // This function will expand the specified CAB file, putting all of the // files in the specified destination directory. //--------------------------------------------------------------------------- BOOL OpenCABFile(const CString & filename, const CString & destination) { char szFilebuffer[MAX_UTF_LENGTH]; char szDestination[MAX_UTF_LENGTH]; // If the filename has Unicode characters which can't be represented // directly by ANSI characters, we need to make a copy of it to the // temp directory, and give it an ANSI name. The code we've borrowed // for expanding CAB files can't open Unicode named files. BOOL fNonANSICharacter = FALSE; const TCHAR * pChar = (LPCTSTR) filename; while (pChar && *pChar) if (*pChar++ >= (TCHAR)0x0080) { fNonANSICharacter = TRUE; break; } CString strFilename(filename); BOOL fMadeCopy = FALSE; if (fNonANSICharacter) { TCHAR szNewFile[MAX_PATH + 10]; DWORD dwLength = 0; dwLength = ::GetTempPath(MAX_PATH, szNewFile); if (dwLength != 0 && dwLength < MAX_PATH) { _tcscat(szNewFile, _T("msitemp.cab")); fMadeCopy = ::CopyFile((LPCTSTR) strFilename, szNewFile, FALSE); strFilename = szNewFile; } } char * szFilename = Unicode2UTF((LPCTSTR)strFilename); ::strcpy(szFilebuffer, szFilename); szFilename = Unicode2UTF((LPCTSTR)destination); ::strcpy(szDestination, szFilename); BOOL fResult = explode_cab(szFilebuffer, szDestination); // If we made a copy of the CAB file, we should delete it now. if (fMadeCopy) ::DeleteFile((LPCTSTR)strFilename); return fResult; } //--------------------------------------------------------------------------- // This function looks in the specified directory for an NFO file. If it // finds one, it assigns it to filename and returns TRUE. This function // will only find the first NFO file in a directory. // // If an NFO file cannot be found, then we'll look for another file type // to open. Grab the string entry in the registry = "cabdefaultopen". An // example value would be "*.nfo|hwinfo.dat|*.dat|*.txt" which would be // interpreted as follows: // // 1. First look for any NFO file to open. // 2. Then try to open a file called "hwinfo.dat". // 3. Then try to open any file with a DAT extension. // 4. Then try for any TXT file. // 5. Finally, if none of these can be found, present an open dialog // to the user. //--------------------------------------------------------------------------- LPCTSTR VAL_CABDEFAULTOPEN = _T("cabdefaultopen"); BOOL FindFileToOpen(const CString & destination, CString & filename) { CString strCABDefaultOpen, strRegBase, strDirectory; HKEY hkey; filename.Empty(); strDirectory = destination; if (strDirectory.Right(1) != CString(cszDirSeparator)) strDirectory += CString(cszDirSeparator); // Set up a fallback string of the NFO file type, in case we can't // find the registry entry. strCABDefaultOpen.LoadString(IDS_MSI_FILE_EXTENSION); strCABDefaultOpen = CString("*.") + strCABDefaultOpen; // Load the string of files and file types to open from the registry. strRegBase.LoadString(IDS_MSI_REG_BASE); if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, strRegBase, 0, KEY_READ, &hkey) == ERROR_SUCCESS) { char szData[MAX_PATH]; DWORD dwType, dwSize = MAX_PATH; if (RegQueryValueEx(hkey, VAL_CABDEFAULTOPEN, NULL, &dwType, (LPBYTE) szData, &dwSize) == ERROR_SUCCESS) if (dwType == REG_SZ) strCABDefaultOpen = szData; RegCloseKey(hkey); } // Look through each of the potential files and file types. If we find // a match, return TRUE after setting filename appropriately. Note that // we need to recurse down through directories. CString strFileSpec; CStringList filesfound; POSITION pos; while (!strCABDefaultOpen.IsEmpty()) { if (strCABDefaultOpen.Find('|') == -1) strFileSpec = strCABDefaultOpen; else strFileSpec = strCABDefaultOpen.Left(strCABDefaultOpen.Find('|')); filesfound.RemoveAll(); DirectorySearch(strFileSpec, strDirectory, filesfound); pos = filesfound.GetHeadPosition(); if (pos != NULL) { filename = filesfound.GetNext(pos); return TRUE; } strCABDefaultOpen = strCABDefaultOpen.Right(strCABDefaultOpen.GetLength() - strFileSpec.GetLength()); if (strCABDefaultOpen.Find('|') == 0) strCABDefaultOpen = strCABDefaultOpen.Right(strCABDefaultOpen.GetLength() - 1); } return FALSE; }