|
|
//+--------------------------------------------------------------------------
//
// 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
//
ULONG ulNoChars = lstrlen ( _wszValue ) + 1; wszResult = (WCHAR*) LocalAlloc( 0, sizeof(WCHAR*) * ulNoChars );
//
// If we successfully obtained room for the string,
// copy it
//
if ( wszResult ) { HRESULT hr;
hr = StringCchCopy ( wszResult, ulNoChars, _wszValue); ASSERT(SUCCEEDED(hr)); }
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
// Here, allocating one extra byte than required.
//
Status = SetStringSize( cchSize );
if ( ERROR_SUCCESS != Status ) { return Status; }
//
// We have room for the string, so copy it
// into its newly allocated space
//
HRESULT hr;
hr = StringCchCopy( _wszValue, cchSize+1, wszValue ); ASSERT(SUCCEEDED(hr)); }
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; }
|