windows-nt/Source/XPSP1/NT/enduser/troubleshoot/msinfo/msicab.cpp
2020-09-26 16:20:57 +08:00

299 lines
9.2 KiB
C++

// 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;
}