You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
477 lines
13 KiB
477 lines
13 KiB
//-----------------------------------------------------------------------------
|
|
|
|
#include "stdafx.h"
|
|
#include "msitoddf.h"
|
|
|
|
static PTCHAR GetFileLongName( PTCHAR szFileName );
|
|
static PTCHAR GetErrorMsg( UINT iRet );
|
|
static bool CreateCABDDF( void );
|
|
static bool FileExists( PTCHAR szFileName );
|
|
static bool CreateCabsTableDefinitionFile( void );
|
|
static bool LookupComponentTargetFolder( PTCHAR szComponent, PTCHAR szComponentFolder );
|
|
static void Log( LPCTSTR fmt, ... );
|
|
|
|
PMSIHANDLE g_hDatabase;
|
|
PMSIHANDLE g_hInstall;
|
|
UINT g_iFileCount;
|
|
|
|
TCHAR g_MSIFile[ MAX_PATH ];
|
|
TCHAR g_LogFile[ MAX_PATH ];
|
|
TCHAR g_DDFFile[ MAX_PATH ];
|
|
TCHAR g_CABFile[ MAX_PATH ];
|
|
TCHAR g_RootDir[ MAX_PATH ];
|
|
|
|
//--------------------------------------------------------------
|
|
|
|
int __cdecl _tmain( int argc, TCHAR* argv[], TCHAR* envp[] )
|
|
{
|
|
UINT iRet = 0;
|
|
|
|
if( argc < 2 )
|
|
{
|
|
_tprintf( TEXT( "Extracts the file list from an MSI installer database and creates a DDF file\n\n" ) );
|
|
_tprintf( TEXT( "Usage: %s <MSI file path> [-L <error log file path>]\n\n" ), argv[ 0 ] );
|
|
return 1;
|
|
}
|
|
|
|
//MessageBox( NULL, TEXT( "UDDI" ), TEXT( "UDDI" ), MB_OK) ;
|
|
|
|
_tcscpy( g_MSIFile, argv[ 1 ] );
|
|
|
|
TCHAR szDrive[ _MAX_DRIVE ];
|
|
TCHAR szPath[ _MAX_DIR ];
|
|
TCHAR szFileName[ _MAX_FNAME ];
|
|
_tsplitpath( g_MSIFile, szDrive, szPath, szFileName, NULL );
|
|
|
|
_stprintf( g_DDFFile, TEXT( "%s%s%s.DDF" ), szDrive, szPath, szFileName );
|
|
_stprintf( g_CABFile, TEXT( "%s.CAB" ), szFileName );
|
|
_stprintf( g_RootDir, TEXT( "%s%s" ), szDrive, szPath );
|
|
|
|
if( argc > 3 && 0 == _tcsicmp( argv[ 2 ], TEXT( "-L" ) ) )
|
|
{
|
|
_tcsncpy( g_LogFile, argv[ 3 ], MAX_PATH );
|
|
}
|
|
else
|
|
{
|
|
*g_LogFile = NULL;
|
|
}
|
|
|
|
//
|
|
// open the database
|
|
//
|
|
if( !FileExists( g_MSIFile ) )
|
|
{
|
|
Log( TEXT( "*** Error: MSI File does not exist: %s" ), g_MSIFile );
|
|
return 1;
|
|
}
|
|
|
|
iRet = MsiOpenDatabase( g_MSIFile, MSIDBOPEN_READONLY, &g_hDatabase );
|
|
if( ERROR_SUCCESS != iRet )
|
|
{
|
|
Log( TEXT( "*** Error: MsiOpenDatabase Error: %s" ), GetErrorMsg( iRet ) );
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// get a database handle
|
|
//
|
|
TCHAR szDBHandle[ 10 ];
|
|
_stprintf( szDBHandle, TEXT( "#%d" ), (DWORD) (MSIHANDLE) g_hDatabase );
|
|
|
|
iRet = MsiOpenPackage( szDBHandle, &g_hInstall );
|
|
if( ERROR_SUCCESS != iRet )
|
|
{
|
|
Log( TEXT( "*** Error: MsiOpenPackage Error: %s" ), GetErrorMsg( iRet ) );
|
|
}
|
|
else
|
|
{
|
|
if( ERROR_SUCCESS != MsiDoAction( g_hInstall, TEXT( "CostInitialize" ) ) )
|
|
return 1;
|
|
if( ERROR_SUCCESS != MsiDoAction( g_hInstall, TEXT( "FileCost" ) ) )
|
|
return 1;
|
|
if( ERROR_SUCCESS != MsiDoAction( g_hInstall, TEXT( "CostFinalize" ) ) )
|
|
return 1;
|
|
if( !CreateCABDDF() )
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// commit the changes
|
|
//
|
|
iRet = MsiDatabaseCommit( g_hDatabase );
|
|
if( ERROR_SUCCESS != iRet )
|
|
{
|
|
Log( TEXT( "*** Error: MsiDatabaseCommit Error: %s" ), GetErrorMsg( iRet ) );
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
|
|
static bool CreateCABDDF( void )
|
|
{
|
|
UINT iRet;
|
|
PMSIHANDLE hView;
|
|
PMSIHANDLE hRecord = 0;
|
|
|
|
//
|
|
// the install dir will give us the root of the path that we
|
|
// can use later and trim it off
|
|
//
|
|
TCHAR szInstallDir[ _MAX_PATH ];
|
|
DWORD dwSize = _MAX_PATH;
|
|
iRet = MsiGetProperty( g_hInstall, TEXT("uddi"), szInstallDir, &dwSize );
|
|
if( ERROR_SUCCESS != iRet )
|
|
{
|
|
Log( TEXT( "*** Error: MsiGetProperty Error: %s" ), GetErrorMsg( iRet ) );
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// open a view and submit the SQL query
|
|
//
|
|
TCHAR szSQLQuery[ 256 ];
|
|
_stprintf( szSQLQuery, TEXT( "select File, FileName, Component_, Sequence from File order by Sequence" ) );
|
|
|
|
iRet = MsiDatabaseOpenView( g_hDatabase, szSQLQuery , &hView );
|
|
|
|
if( ERROR_SUCCESS != iRet )
|
|
{
|
|
Log( TEXT( "*** Error: MsiDatabaseOpenView Error: %s" ), GetErrorMsg( iRet ) );
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// execute the SQL query
|
|
//
|
|
iRet = MsiViewExecute( hView, hRecord );
|
|
|
|
if( ERROR_SUCCESS != iRet )
|
|
{
|
|
Log( TEXT( "*** Error: MsiViewExecute Error: %s" ), GetErrorMsg( iRet ) );
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// open the DDF file
|
|
//
|
|
FILE *file = _tfopen( g_DDFFile, TEXT( "w" ) );
|
|
if( NULL == file )
|
|
{
|
|
Log( TEXT( "*** Error: Unable to open output file: %s, Error code %d" ), g_DDFFile, GetLastError() );
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// header in DDF file used by makecab.exe
|
|
//
|
|
_fputts( TEXT( ".Option Explicit\n" ), file );
|
|
_fputts( TEXT( ".Set DiskDirectoryTemplate=.\n" ), file );
|
|
_fputts( TEXT( ".Set CabinetName1=" ), file );
|
|
_fputts( g_CABFile, file );
|
|
_fputts( TEXT( "\n" ), file );
|
|
_fputts( TEXT( ".Set RptFilename=nul\n" ), file );
|
|
_fputts( TEXT( ".Set InfFileName=nul\n" ), file );
|
|
_fputts( TEXT( ".Set InfAttr=\n" ), file );
|
|
_fputts( TEXT( ".Set MaxDiskSize=CDROM\n" ), file );
|
|
_fputts( TEXT( ".Set CompressionType=LZX\n" ), file );
|
|
_fputts( TEXT( ".Set CompressionMemory=21\n" ), file );
|
|
_fputts( TEXT( ".Set CompressionLevel=1\n" ), file );
|
|
_fputts( TEXT( ".Set Compress=ON\n" ), file );
|
|
_fputts( TEXT( ".Set Cabinet=ON\n" ), file );
|
|
_fputts( TEXT( ".Set UniqueFiles=ON\n" ), file );
|
|
_fputts( TEXT( ".Set FolderSizeThreshold=1000000\n" ), file );
|
|
_fputts( TEXT( ".Set MaxErrors=300\n" ), file );
|
|
|
|
//
|
|
// fetch the data
|
|
//
|
|
TCHAR szFile[ 256 ];
|
|
TCHAR szFileName[ 256 ];
|
|
TCHAR szComponent[ 256 ];
|
|
TCHAR szComponentFolder[ 256 ];
|
|
TCHAR szFilePath[ 256 ];
|
|
int iSequence;
|
|
DWORD cchValueBuf;
|
|
g_iFileCount = 0;
|
|
|
|
while( ERROR_NO_MORE_ITEMS != MsiViewFetch( hView, &hRecord ) )
|
|
{
|
|
g_iFileCount++;
|
|
cchValueBuf = 256;
|
|
MsiRecordGetString( hRecord, 1, szFile, &cchValueBuf );
|
|
|
|
cchValueBuf = 256;
|
|
MsiRecordGetString( hRecord, 2, szFileName, &cchValueBuf );
|
|
|
|
cchValueBuf = 256;
|
|
MsiRecordGetString( hRecord, 3, szComponent, &cchValueBuf );
|
|
|
|
iSequence = MsiRecordGetInteger( hRecord, 4 );
|
|
|
|
//
|
|
// query the MSI for the target folder for this file.
|
|
// this will tell us where it is located in the \binaries folders.
|
|
//
|
|
if( !LookupComponentTargetFolder( szComponent, szComponentFolder ) )
|
|
{
|
|
Log( TEXT( "**ERROR: Unable to find directory for component: %s" ), szComponent );
|
|
return false;
|
|
}
|
|
|
|
TCHAR szWindowsDir[ MAX_PATH ];
|
|
if( 0 == ExpandEnvironmentStrings( TEXT( "%systemroot%" ), szWindowsDir, MAX_PATH ) )
|
|
return false;
|
|
|
|
//_tprintf( TEXT( "windir=%s\n" ), szWindowsDir );
|
|
//_tprintf( TEXT( "szComponentFolder=%s\n" ), szComponentFolder );
|
|
|
|
//
|
|
// look for files that are in the windows folder
|
|
//
|
|
if( 0 == _tcsnicmp( szComponentFolder, szWindowsDir, _tcslen( szWindowsDir ) ) )
|
|
{
|
|
//
|
|
// create the full path to the file in \binaries folder. e.g. \binaries\uddi\windows\system32
|
|
//
|
|
_stprintf( szFilePath, TEXT( "%s%s%s" ),
|
|
g_RootDir, // start with the root (c:\binariesx\uddi\)
|
|
szComponentFolder + _tcslen( szWindowsDir ) + 1,
|
|
GetFileLongName( szFileName ) ); // add file name
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// create the full path to the file in \binaries folder
|
|
//
|
|
_stprintf( szFilePath, TEXT( "%s%s%s" ),
|
|
g_RootDir, // start with the root (c:\binariesx\uddi)
|
|
&szComponentFolder[ _tcslen( szInstallDir ) ], // strip installdir prefix and append
|
|
GetFileLongName( szFileName ) ); // add file name
|
|
}
|
|
|
|
//
|
|
// if it exists (and it should), trim the c:\binaries\... part on put the file
|
|
// name into the DDF file
|
|
//
|
|
if( FileExists( szFilePath ) )
|
|
{
|
|
_fputts( TEXT( "\"" ), file );
|
|
_fputts( &szFilePath[ _tcslen( g_RootDir ) ], file ); // trim the root dir
|
|
_fputts( TEXT( "\"\t\"" ), file );
|
|
_fputts( szFile, file );
|
|
_fputts( TEXT( "\"\n" ), file );
|
|
}
|
|
else
|
|
{
|
|
fclose( file );
|
|
Log( TEXT( "*** ERROR: Source File does not exist: %s" ), szFilePath );
|
|
Log( TEXT( "File=%s, %s" ), szFile, szFileName );
|
|
Log( TEXT( "Component=%s" ), szComponent );
|
|
Log( TEXT( "Component Folder=%s" ), szComponentFolder );
|
|
Log( TEXT( "systemroot=%s" ), szWindowsDir );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
fclose( file );
|
|
|
|
return true;
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
|
|
static bool LookupComponentTargetFolder( PTCHAR szComponent, PTCHAR szComponentFolder )
|
|
{
|
|
UINT iRet;
|
|
PMSIHANDLE hView;
|
|
PMSIHANDLE hRecord = 0;
|
|
//
|
|
// open a view and submit the SQL query
|
|
//
|
|
TCHAR szSQLQuery[ 256 ];
|
|
|
|
_stprintf( szSQLQuery, TEXT( "select Directory_ from Component where Component = '%s'" ), szComponent );
|
|
|
|
iRet = MsiDatabaseOpenView( g_hDatabase, szSQLQuery , &hView );
|
|
|
|
if( ERROR_SUCCESS != iRet )
|
|
{
|
|
Log( TEXT( "*** Error: MsiDatabaseOpenView Error: %s" ), GetErrorMsg( iRet ) );
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// execute the SQL query
|
|
//
|
|
iRet = MsiViewExecute( hView, hRecord );
|
|
|
|
if( ERROR_SUCCESS != iRet )
|
|
{
|
|
Log( TEXT( "*** Error: MsiViewExecute Error: %s" ), GetErrorMsg( iRet ) );
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// fetch the data
|
|
//
|
|
TCHAR szDirectoryKey[ 256 ];
|
|
DWORD cchValueBuf = 256;
|
|
if( ERROR_SUCCESS != MsiViewFetch( hView, &hRecord ) )
|
|
{
|
|
Log( TEXT( "*** ERROR: Directory not found for component [ %s ]" ), szComponent );
|
|
return false;
|
|
}
|
|
|
|
// get the key into the Directory table
|
|
MsiRecordGetString( hRecord, 1, szDirectoryKey, &cchValueBuf );
|
|
|
|
// look up the full path
|
|
DWORD cchPathBuf = 256;
|
|
iRet = MsiGetTargetPath( g_hInstall, szDirectoryKey, szComponentFolder, &cchPathBuf );
|
|
|
|
if( ERROR_SUCCESS != iRet )
|
|
{
|
|
Log( TEXT( "*** Error: MsiGetSourcePath Error component [ %s ] %s" ), szComponent, GetErrorMsg( iRet ) );
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
// returns the long file name out of a string that has both
|
|
// short and long name separated by a pipe character
|
|
static PTCHAR GetFileLongName( PTCHAR szFileName )
|
|
{
|
|
PTCHAR pStart = _tcschr( szFileName, TEXT( '|' ) );
|
|
|
|
if( pStart )
|
|
{
|
|
return pStart + 1; // add 1 to skip over the pipe symbol
|
|
}
|
|
|
|
return szFileName;
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
|
|
static bool FileExists( PTCHAR szFileName )
|
|
{
|
|
FILE *file = _tfopen( szFileName, TEXT( "rt" ) );
|
|
if( NULL == file )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
fclose( file );
|
|
|
|
return true;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
static bool SetProperty( PTCHAR szProperty, PTCHAR szValue )
|
|
{
|
|
UINT iRet = MsiSetProperty( g_hInstall, szProperty, szValue );
|
|
|
|
if( ERROR_SUCCESS != iRet )
|
|
{
|
|
Log( TEXT( "*** Error: SetProperty Error: %s, Property %s, Value %s" ),
|
|
GetErrorMsg( iRet ),
|
|
szProperty,
|
|
szValue );
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
|
|
static PTCHAR GetErrorMsg( UINT iRet )
|
|
{
|
|
static TCHAR szErrMsg[ 100 ];
|
|
|
|
FormatMessage(
|
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL,
|
|
iRet,
|
|
MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language
|
|
( LPTSTR ) szErrMsg,
|
|
100,
|
|
NULL);
|
|
|
|
return szErrMsg;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
static void Log( LPCTSTR fmt, ... )
|
|
{
|
|
TCHAR szTime[ 10 ];
|
|
TCHAR szDate[ 10 ];
|
|
::_tstrtime( szTime );
|
|
::_tstrdate( szDate );
|
|
|
|
va_list marker;
|
|
TCHAR szBuf[1024];
|
|
|
|
size_t cbSize = ( sizeof( szBuf ) / sizeof( TCHAR ) ) - 1; // one byte for null
|
|
_sntprintf( szBuf, cbSize, TEXT( "%s %s: " ), szDate, szTime );
|
|
szBuf[ 1023 ] = '\0';
|
|
cbSize -= _tcslen( szBuf );
|
|
|
|
va_start( marker, fmt );
|
|
|
|
_vsntprintf( szBuf + _tcslen( szBuf ), cbSize, fmt, marker );
|
|
szBuf[ 1023 ] = '\0';
|
|
cbSize -= _tcslen( szBuf );
|
|
|
|
va_end( marker );
|
|
|
|
_tcsncat(szBuf, TEXT("\r\n"), cbSize );
|
|
|
|
_tprintf( TEXT( "%s" ), szBuf );
|
|
|
|
if( 0 == _tcslen( g_LogFile ) )
|
|
return;
|
|
|
|
// write the data out to the log file
|
|
char szBufA[ 1024 ];
|
|
WideCharToMultiByte( CP_ACP, 0, szBuf, -1, szBufA, 1024, NULL, NULL );
|
|
|
|
HANDLE hFile = CreateFile(
|
|
g_LogFile, // file name
|
|
GENERIC_WRITE, // open for writing
|
|
0, // do not share
|
|
NULL, // no security
|
|
OPEN_ALWAYS, // open and create if not exists
|
|
FILE_ATTRIBUTE_NORMAL, // normal file
|
|
NULL); // no attr. template
|
|
|
|
if( hFile == INVALID_HANDLE_VALUE )
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// move the file pointer to the end so that we can append
|
|
//
|
|
SetFilePointer( hFile, 0, NULL, FILE_END );
|
|
|
|
DWORD dwNumberOfBytesWritten;
|
|
BOOL bOK = WriteFile(
|
|
hFile,
|
|
szBufA,
|
|
(UINT) strlen( szBufA ), // number of bytes to write
|
|
&dwNumberOfBytesWritten, // number of bytes written
|
|
NULL); // overlapped buffer
|
|
|
|
FlushFileBuffers ( hFile );
|
|
CloseHandle( hFile );
|
|
}
|