#include "msix.h"
#pragma comment(lib, "msi.lib")
// Entry point.
int _tmain(int argc, _TCHAR* argv[])
{
DWORD dwError = NOERROR;
HRESULT hr = NOERROR;
ARGS args = { 0 };
IStorage* pRootStorage = NULL;
IEnumSTATSTG* pEnum = NULL;
LPCTSTR pszPersist = (LPTSTR)MSIDBOPEN_READONLY;
STATSTG stg = { 0 };
PMSIHANDLE hDatabase = NULL;
PMSIHANDLE hView = NULL;
PMSIHANDLE hRecord = NULL;
dwError = ParseArguments(argc, argv, &args);
if (ERROR_SUCCESS != dwError)
{
return dwError;
}
// Open the root storage file and extract storages first. Storages cannot
// be extracted using MSI APIs so we must use the compound file implementation
// for IStorage.
hr = StgOpenStorage(
CT2W(args.Path),
NULL,
STGM_READ | STGM_SHARE_EXCLUSIVE,
NULL,
0,
&pRootStorage);
if (SUCCEEDED(hr) && pRootStorage)
{
// Determine if the file path specifies an MSP file.
// This will be used later to open the database with MSI APIs.
if (IsPatch(pRootStorage))
{
pszPersist = MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE;
}
hr = pRootStorage->EnumElements(0, NULL, 0, &pEnum);
if (SUCCEEDED(hr))
{
while (S_OK == (hr = pEnum->Next(1, &stg, NULL)))
{
if (STGTY_STORAGE == stg.type)
{
hr = SaveStorage(pRootStorage, args.Directory, stg.pwcsName,
args.IncludeExtension ? TEXT(".mst") : NULL);
if (FAILED(hr))
{
break;
}
}
}
SAFE_RELEASE(pEnum);
}
}
SAFE_RELEASE(pRootStorage);
// Now open the database using MSI APIs. Patches cannot be opened simultaneously
// since exclusive access is required and no MSI APIs are exported that accept
// an IStorage pointer.
if (SUCCEEDED(hr))
{
dwError = MsiOpenDatabase(args.Path, pszPersist, &hDatabase);
if (ERROR_SUCCESS == dwError)
{
dwError = MsiDatabaseOpenView(hDatabase,
TEXT("SELECT `Name`, `Data` FROM `_Streams`"), &hView);
if (ERROR_SUCCESS == dwError)
{
dwError = MsiViewExecute(hView, NULL);
if (ERROR_SUCCESS == dwError)
{
while (ERROR_SUCCESS == (dwError = MsiViewFetch(hView, &hRecord)))
{
dwError = SaveStream(hRecord, args.Directory, args.IncludeExtension);
if (ERROR_SUCCESS != dwError)
{
break;
}
}
// If there are no more records indicate success.
if (ERROR_NO_MORE_ITEMS == dwError)
{
dwError = ERROR_SUCCESS;
}
}
}
}
}
// If a Win32 error has occurred return only the win32 error portion.
if (FACILITY_WIN32 == HRESULT_FACILITY(hr))
{
dwError = HRESULT_CODE(hr);
}
else if (FAILED(hr))
{
// Just set it to the HRESULT. Many common HRESULTs
// will yield an error string from FormatMessage.
dwError = hr;
}
// Print the error to the console.
if (ERROR_SUCCESS != dwError)
{
win32_error(dwError);
}
return dwError;
}
// Parse the file path, and optionally output directory and extension guessing switch.
DWORD ParseArguments(int argc, [Pre(Null=No)] _TCHAR* argv[], [Pre(Null=No)] LPARGS args)
{
_ASSERTE(argv);
_ASSERTE(args);
int iParamIndex = 1;
// Validate arguments.
if (2 > argc)
{
error(TEXT("Error: you must specify a Windows Installer file from which to extract files.\n"));
usage(argv[0], stderr);
return ERROR_INVALID_PARAMETER;
}
if (0 == _tcsicmp(TEXT("/?"), argv[iParamIndex]) || 0 == _tcsicmp(TEXT("-?"), argv[iParamIndex]))
{
// Display the usage text.
usage(argv[0], stdout);
return ERROR_SUCCESS;
}
else if (TEXT('/') == argv[iParamIndex][0] || TEXT('-') == argv[iParamIndex][0])
{
// Filename should not begin with a command-switch character.
error(TEXT("Error: invalid file name.\n"));
usage(argv[0], stderr);
return ERROR_INVALID_PARAMETER;
}
else
{
// Set the path argument.
args->Path = const_cast<LPTSTR>(argv[iParamIndex]);
}
// Get the output directory if requested.
while (++iParamIndex < argc)
{
// The directory in which files are extracted.
if (0 == _tcsicmp(TEXT("/out"), argv[iParamIndex]) || 0 == _tcsicmp(TEXT("-out"), argv[iParamIndex]))
{
if (++iParamIndex < argc && TEXT('/') != argv[iParamIndex][0] && TEXT('-') != argv[iParamIndex][0])
{
args->Directory = const_cast<LPTSTR>(argv[iParamIndex]);
}
else
{
error(TEXT("Error: you must specify an output directory with /out.\n"));
usage(argv[0], stderr);
return ERROR_INVALID_PARAMETER;
}
}
// Whether or not to include or guess at extensions for output file names.
else if (0 == _tcsicmp(TEXT("/ext"), argv[iParamIndex]) || 0 == _tcsicmp(TEXT("-ext"), argv[iParamIndex]))
{
args->IncludeExtension = TRUE;
}
else
{
error(TEXT("Error: unknown option: %s.\n"), argv[iParamIndex]);
usage (argv[0], stderr);
return ERROR_INVALID_PARAMETER;
}
}
return ERROR_SUCCESS;
}
// Prints usage to the given output file stream.
void usage(LPCTSTR pszPath, FILE* out)
{
_ASSERTE(pszPath);
_ASSERTE(out);
LPTSTR pszName = NULL;
pszName = (LPTSTR)_tcsrchr(pszPath, TEXT('\\'));
if (pszName)
{
// Advance past the backslash.
pszName++;
}
else
{
// Set the executable name.
pszName = const_cast<LPTSTR>(pszPath);
}
_ftprintf(out, TEXT("Usage: %s <file> [/out <output>] [/ext]\n\n"), pszName);
_ftprintf(out, TEXT("\tfile - Path to an MSI, MSM, MSP, or PCP file.\n"));
_ftprintf(out, TEXT("\tout - Extract streams and storages to the <output> directory.\n"));
_ftprintf(out, TEXT("\text - Append appropriate extensions to output files.\n"));
_ftprintf(out, TEXT("\nExtracts transforms and cabinets from a Windows Installer file.\n"));
}
// Colors errors on the console red and prints the formatted error.
// You can use positional format specifiers with CRT8.
void error(LPCTSTR pszFormat, ...)
{
_ASSERTE(pszFormat);
CONSOLE_SCREEN_BUFFER_INFO csbi;
HANDLE hStdErr = INVALID_HANDLE_VALUE;
va_list args;
// Set console colors to error values.
hStdErr = GetStdHandle(STD_ERROR_HANDLE);
if (INVALID_HANDLE_VALUE != hStdErr)
{
if (GetConsoleScreenBufferInfo(hStdErr, &csbi))
{
// Set new console colors.
SetConsoleTextAttribute(hStdErr, COLOR_ERROR);
}
}
// Print error.
va_start(args, pszFormat);
_vftprintf_p(stderr, pszFormat, args);
va_end(args);
// Reset the console colors to original values.
if (INVALID_HANDLE_VALUE != hStdErr)
{
SetConsoleTextAttribute(hStdErr, csbi.wAttributes);
}
}
// Wrapper around FormatMessage for getting error text.
// Calls error() to print the error to the console.
void win32_error(DWORD dwError)
{
LPTSTR pszError;
// Format the error. Error ends with new line.
if (FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
dwError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &pszError,
0,
NULL))
{
error(TEXT("Error 0x%1$08x (%1$d): %2$s"), dwError, pszError);
LocalFree(pszError);
}
}
// Creates a patch from components, using the current working
// directory if pszDir is NULL.
// pszExt should be either NULL or start with a ".".
LPTSTR MakePath(LPTSTR pszDest, size_t cchDest, LPCTSTR pszDir, LPCTSTR pszName, LPCTSTR pszExt)
{
size_t len = 0;
_ASSERTE(pszDest);
_A
- 1
- 2
- 3
前往页