Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

681 lines
16 KiB

//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1994 - 2001.
//
// File: msibase.cpp
//
// Contents: msi database abstractions
//
// History: 4-14-2000 adamed Created
//
//---------------------------------------------------------------------------
#include "precomp.hxx"
CMsiState::CMsiState() :
_MsiHandle( NULL )
{
//
// The MSIHANDLE encapsulates the state for
// all msi operations / data -- clearing this
// member is akin to clearing the state.
//
}
CMsiState::~CMsiState()
{
//
// The lifetime of the object is the lifetime
// of the underlying state -- be sure to release it
//
MsiCloseHandle( _MsiHandle );
}
void
CMsiState::SetState( MSIHANDLE MsiHandle )
{
//
// Set the state of this object based on
// a handle retrieved from an MSI operation --
// note that this should only be done if this
// object has an empty state
//
ASSERT( ! _MsiHandle );
_MsiHandle = MsiHandle;
}
MSIHANDLE
CMsiState::GetState()
{
//
// Allow callers that need to perform explicit MSI
// operations to retrieve state compatible with MSI
//
return _MsiHandle;
}
CMsiValue::CMsiValue() :
_dwDiscriminant( TYPE_NOT_SET ),
_wszValue( NULL ),
_cchSize( sizeof( _wszDefaultBuf ) / sizeof( *_wszDefaultBuf ) )
{
//
// The goal of this initialization is to set this object to
// an "empty" state -- consumers must explicitly invoke methods
// on this object to alter this condition so that Get methods
// will succeed.
//
}
CMsiValue::~CMsiValue()
{
//
// Setting the type to "none" implicitly clears our state
// (e.g. allocated memory, any other resources)
//
SetType( TYPE_NOT_SET );
}
DWORD
CMsiValue::GetDWORDValue()
{
ASSERT( TYPE_DWORD == _dwDiscriminant );
//
// Retrieve this value as a DWORD -- note that this
// does not coerce non-DWORD values to DWORD -- the
// value must already be a DWORD for this to have meaning
//
return _dwValue;
}
WCHAR*
CMsiValue::GetStringValue()
{
ASSERT( TYPE_STRING == _dwDiscriminant );
//
// Retrieve this value as a string -- note that this
// does not coerce non-string values to string -- the
// value must already be a string for this to have meaning.
// Note that the value is returned as a reference to the address
// at which this value actually stores the string -- thus, this
// may also be used to retrieve the value's buffer so that its
// contents may be edited outside the strictures of this class.
//
return _wszValue;
}
WCHAR*
CMsiValue::DuplicateString()
{
WCHAR* wszResult;
ASSERT( TYPE_STRING == _dwDiscriminant );
//
// The caller requires ownership of a duplicate
// of this string's data.
//
//
// First, allocate memory for this
//
wszResult = (WCHAR*) LocalAlloc(
0,
sizeof(WCHAR*) * (lstrlen ( _wszValue ) + 1 ) );
//
// If we successfully obtained room for the string,
// copy it
//
if ( wszResult )
{
lstrcpy( wszResult, _wszValue);
}
return wszResult;
}
void
CMsiValue::SetDWORDValue( DWORD dwValue )
{
//
// This operation will implicitly set the type
// of this value to DWORD
//
SetType( TYPE_DWORD );
//
// Now we can safely set the value
//
_dwValue = dwValue;
}
LONG
CMsiValue::SetStringValue( WCHAR* wszValue )
{
DWORD cchSize;
LONG Status;
Status = ERROR_SUCCESS;
//
// This operation will implicitly set the
// type of this value to string
//
SetType( TYPE_STRING );
//
// We need to determine the size of this string,
// in chars, without the null terminator, in order to
// allow this value to represent it
//
cchSize = lstrlen( wszValue );
if ( cchSize > _cchSize )
{
//
// Attempt to get space for this string
// by setting its size -- if this fails,
// our type will be implicitly set to none
//
Status = SetStringSize( cchSize );
if ( ERROR_SUCCESS != Status )
{
return Status;
}
//
// We have room for the string, so copy it
// into its newly allocated space
//
lstrcpy( _wszValue, wszValue );
}
return Status;
}
DWORD
CMsiValue::GetStringSize()
{
ASSERT( TYPE_STRING == _dwDiscriminant );
//
// Retrieve the size of this string in chars,
// WITHOUT the null terminator
//
return _cchSize;
}
LONG
CMsiValue::SetStringSize( DWORD cchSize )
{
ASSERT( TYPE_STRING == _dwDiscriminant );
//
// This method only makes sense if the
// type of this object is already string
//
//
// If the requested size is less than or
// equal to our current size, we already have
// enough space -- we can exit now. We do
// not "shrink" space, only expand as necessary
//
if ( cchSize <= _cchSize )
{
return ERROR_SUCCESS;
}
//
// At this point, we know we don't have enough
// space, so we'll have to allocate it. Before we
// do so, reset our type to none so that if we fail
// to get space, we can indicate the indeterminate
// state.
//
SetType( TYPE_NOT_SET );
//
// Allocate space, and include the zero terminator
//
_wszValue = new WCHAR [ cchSize + 1 ];
if ( ! _wszValue )
{
return ERROR_NOT_ENOUGH_MEMORY;
}
//
// We are successful, remember the current size
//
_cchSize = cchSize;
//
// Change the type back to string since we can
// safely represent a string of this size
//
SetType( TYPE_STRING );
return ERROR_SUCCESS;
}
void
CMsiValue::SetType( DWORD dwType )
{
//
// Setting the type to a new type implicitly clears
// state associated with the new type
//
//
// If the current type and requested type are the same
// this is a no op and we are done.
//
if ( dwType == _dwDiscriminant )
{
return;
}
//
// If the requested type is string, we need to
// set this object to have appropriate state
//
if ( TYPE_STRING == dwType )
{
//
// If we have no space for a string
//
if ( ! _wszValue )
{
//
// Use the default buffer...
//
_wszValue = _wszDefaultBuf;
//
// ... and set the size accordingly
//
_cchSize = sizeof( _wszDefaultBuf ) / sizeof( *_wszDefaultBuf );
}
//
// We are done -- this object can now represent a string, though
// at this point it must be a string of size _cchSize -- the size
// will have to be increased through SetStringSize if there's
// a need to represent a larger string
//
return;
}
//
// If the current type is string, we use the fact that the requested
// type is not string as a hint to free the state associated with
// the string. This is a heuristic designed to ensure that we
// do not continue to hold memory of which we are not actively making
// use.
//
if ( TYPE_STRING == _dwDiscriminant )
{
//
// If the string's current storage is not that of our default
// buffer (which is part of the object itself), we
// release that storage as it was allocated on the heap.
//
if ( _wszValue != _wszDefaultBuf )
{
delete [] _wszValue;
_wszValue = NULL;
}
}
//
// We may now set the type to that requested by the caller
//
_dwDiscriminant = dwType;
}
LONG
CMsiRecord::GetValue(
DWORD dwType,
DWORD dwValue,
CMsiValue* pMsiValue)
{
LONG Status = ERROR_SUCCESS;
//
// Values are the properties of the column of an
// msi record -- we are retrieving members of the
// record
//
//
// The value is our out parameter -- set it
// to the type desired by the caller
//
pMsiValue->SetType( dwType );
switch ( dwType )
{
case CMsiValue::TYPE_STRING:
DWORD cchSize;
//
// We must determine the maximum size of the
// string that can be represented by the value
// so we can pass it to the msi api
//
cchSize = pMsiValue->GetStringSize();
//
// Attempt to retrieve the string by storing
// it in the buffer of the value
//
Status = MsiRecordGetString(
GetState(),
dwValue,
pMsiValue->GetStringValue(),
&cchSize);
//
// Our attempt to retrieve the string data will
// fail if the value's string buffer is not sufficiently
// large.
//
if ( ERROR_MORE_DATA == Status )
{
//
// In the case where the value's buffer is not large enough,
// we explicitly set the size of the value to that of the
// size returned by the msi api PLUS a zero terminator --
// this is because the size returned by MSI does NOT
// include the zero terminator.
//
cchSize++;
Status = pMsiValue->SetStringSize( cchSize );
//
// We now retry the string retrieval since we have the
// correct size now.
//
if ( ERROR_SUCCESS == Status )
{
Status = MsiRecordGetString(
GetState(),
dwValue,
pMsiValue->GetStringValue(),
&cchSize);
}
}
break;
case CMsiValue::TYPE_DWORD:
Status = ERROR_INVALID_PARAMETER;
int IntegerValue;
//
// Retrieve an integer by calling the msi api
//
IntegerValue = MsiRecordGetInteger(
GetState(),
dwValue);
if ( MSI_NULL_INTEGER != IntegerValue )
{
//
// We now set the value to that retrieved by the api
//
pMsiValue->SetDWORDValue( (DWORD) IntegerValue );
Status = ERROR_SUCCESS;
}
break;
default:
ASSERT( FALSE );
break;
}
return Status;
}
LONG
CMsiQuery::GetNextRecord( CMsiRecord* pMsiRecord)
{
LONG Status;
MSIHANDLE MsiHandle;
//
// The MsiViewFetch api will retrieve a record from a query --
// it does this in an enumeration style, so we are retrieving
// the next record in the query
//
Status = MsiViewFetch(
GetState(),
&MsiHandle);
if ( ERROR_SUCCESS == Status )
{
//
// We successfully obtained an MSIHANDLE corresponding to the
// retrieved record, so we use this to set the state of our
// abstraction of the record
//
pMsiRecord->SetState( MsiHandle );
}
return Status;
}
LONG
CMsiQuery::UpdateQueryFromFilter( CMsiRecord* pFilterRecord )
{
LONG Status;
//
// The MsiViewExecute api causes the results of the query to
// be computed. The filter record passed in allows us to
// specify a filter for the query results
//
Status = MsiViewExecute(
GetState(),
pFilterRecord ? pFilterRecord->GetState() : NULL );
return Status;
}
LONG
CMsiDatabase::Open(
WCHAR* wszPath,
DWORD cTransforms,
WCHAR** rgwszTransforms)
{
MSIHANDLE DatabaseHandle;
LONG Status;
//
// The MsiOpenDatabase api abstracts an .msi package
//
Status = MsiOpenDatabase(
wszPath,
MSIDBOPEN_READONLY,
&DatabaseHandle);
if ( ERROR_SUCCESS == Status )
{
DWORD iTransform;
//
// The successful open above does not include transforms --
// we need to add each transform to generate a resultant
// database that includes the changes of each transform
//
//
// We apply the transforms in the order in which they are
// stored in the vector -- this order conforms to that
// specified by the administrator, and since order affects
// the result, we must honor the administrator's ordering
//
for ( iTransform = 0; iTransform < cTransforms; iTransform++ )
{
if ( ERROR_SUCCESS == Status )
{
//
// This api adds the effects of the transform to the
// database.
//
Status = MsiDatabaseApplyTransform(
DatabaseHandle,
rgwszTransforms[iTransform],
0);
}
if ( ERROR_SUCCESS != Status )
{
//
// If we failed to apply a transform, we bail
//
break;
}
}
if ( ERROR_SUCCESS == Status )
{
//
// We have successfully created an database of the
// package + transforms, so we allow the lifetime of its state
// to be controlled by this object
//
SetState( DatabaseHandle );
}
else
{
//
// If we failed to apply a transform, the database
// resource is useless, so we free it
//
MsiCloseHandle( DatabaseHandle );
}
}
return Status;
}
LONG
CMsiDatabase::OpenQuery(
WCHAR* wszQuery,
CMsiQuery* pQuery )
{
LONG Status;
MSIHANDLE MsiHandle;
//
// This api will initialize a query without comoputing its
// results. This will allow the caller finer control over result
// computation later, which distinguishes this method from GetQueryResults
//
Status = MsiDatabaseOpenView(
GetState(),
wszQuery,
&MsiHandle);
if ( ERROR_SUCCESS == Status )
{
//
// Give the caller's query object the state for the query
// so that it can control its lifetime
//
pQuery->SetState( MsiHandle );
}
return Status;
}
LONG
CMsiDatabase::GetQueryResults(
WCHAR* wszQuery,
CMsiQuery* pQuery )
{
LONG Status;
MSIHANDLE MsiHandle;
//
// This api will initialize a query without computing the results
//
Status = MsiDatabaseOpenView(
GetState(),
wszQuery,
&MsiHandle);
if ( ERROR_SUCCESS == Status )
{
//
// The semantics of this method are that the caller may also
// enumerate results after calling the method, so we must
// now computer the results so that the caller may enumerate --
// the api below will do this
//
Status = MsiViewExecute(
MsiHandle,
NULL);
if ( ERROR_SUCCESS == Status )
{
//
// In the success case, we give the lifetime of the msi
// state to the query object
//
pQuery->SetState( MsiHandle );
}
else
{
//
// On failure, we must clear the msi query state
// since it is useless now.
//
MsiCloseHandle( MsiHandle );
}
}
return Status;
}
LONG
CMsiDatabase::TableExists(
WCHAR* wszTableName,
BOOL* pbTableExists )
{
MSICONDITION TableState;
TableState = MsiDatabaseIsTablePersistent( GetState(), wszTableName );
if ( MSICONDITION_ERROR == TableState )
{
return ERROR_INVALID_PARAMETER;
}
*pbTableExists = MSICONDITION_TRUE == TableState;
return ERROR_SUCCESS;
}