|
|
//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1994 - 2001.
//
// File: msiclass.cpp
//
// Contents: msi class collection abstraction
//
// Classes:
//
//
// History: 4-14-2000 adamed Created
//
//---------------------------------------------------------------------------
#include "precomp.hxx"
WCHAR* CClassCollection::_wszQueries[ TYPE_COUNT ] = { QUERY_EXTENSIONS, QUERY_CLSIDS, QUERY_VERSION_INDEPENDENT_PROGIDS };
CClassCollection::CClassCollection( PACKAGEDETAIL* pPackageDetail ) : _pPackageDetail( pPackageDetail ), _cMaxClsids( 0 ), _cMaxExtensions( 0 ), _InstallLevel( 0 ) { //
// All memory referenced by the pPackageDetail must be
// freed by the caller after the GetClasses method is called,
// even if the call fails.
//
//
// We need to clear any existing class information in the
// PACKAGEDETAIL structure since we are going to overwrite
// it eventually anyway
//
//
// First clear the clsid's
//
DWORD iClass;
//
// Free each individual class
//
for ( iClass = 0; iClass < _pPackageDetail->pActInfo->cClasses; iClass++ ) { FreeClassDetail( &(_pPackageDetail->pActInfo->pClasses[ iClass ]) ); }
//
// Now free the vector that held the classes
//
LocalFree( _pPackageDetail->pActInfo->pClasses );
//
// Set our vector reference to the initial state of none
//
_pPackageDetail->pActInfo->pClasses = NULL;
//
// Set the initial state of no clsid's since they have all been freed
//
_pPackageDetail->pActInfo->cClasses = 0;
//
// Now clear the extensions
//
DWORD iExtension;
//
// For each individual extension
//
for ( iExtension = 0; iExtension < _pPackageDetail->pActInfo->cShellFileExt; iExtension++ ) { LocalFree( _pPackageDetail->pActInfo->prgShellFileExt[ iExtension ] ); }
//
// Free the vector that held the extensions
//
LocalFree( _pPackageDetail->pActInfo->prgShellFileExt );
//
// Also destroy the vector that held extension priorities
//
LocalFree( _pPackageDetail->pActInfo->prgPriority );
//
// Set our vector references to the initial state of none
//
_pPackageDetail->pActInfo->prgShellFileExt = NULL; _pPackageDetail->pActInfo->prgPriority = NULL;
//
// Set the initial state of no file extensions since they have all been freed
//
_pPackageDetail->pActInfo->cShellFileExt = 0; }
HRESULT CClassCollection::GetClasses( BOOL bFileExtensionsOnly ) { HRESULT hr; LONG Status; DWORD cTransforms;
//
// This method obtains the class metadata from an msi package + transforms.
// The goal is to approximate the set of class data that would be advertised
// on any system (regardless of system configuration) if the package were
// advertised.
//
//
// The classes will all be stored in the PACKAGEDETAIL structure. The caller
// must free this memory after finishing with the structure, even if this
// method fails
//
//
// First, we must create a database representation of the package + transforms
//
//
// The source list vector contains the package + transforms in application order --
// we must subtract one source since the original package is included in the list
//
cTransforms = _pPackageDetail->cSources - 1;
//
// Now we create a database out of the package plus transforms. Since the
// first item in the source list is the package, we pass that in as the package,
// and all other items after it in the vector are passed in as the transform vector
//
Status = _Database.Open( _pPackageDetail->pszSourceList[0], cTransforms, cTransforms ? &(_pPackageDetail->pszSourceList[1]) : NULL );
if ( ERROR_SUCCESS == Status ) { //
// We've successfully opened the package, now obtain its friendly name.
//
Status = GetFriendlyName();
if (ERROR_SUCCESS == Status) { //
// Now obtain its install level.
// The install level affects whether or not a class will get advertised
//
Status = GetInstallLevel();
}
if ( ERROR_SUCCESS == Status ) { //
// Now that we know the install level of the package, we have
// enough information to flag each advertisable feature in the database.
// We need this because a class is only advertised if its associated
// feature is advertised.
//
Status = FlagAdvertisableFeatures(); }
//
// We may now retrieve the set of classes that will be advertised based
// on the set of advertised features we flagged earlier. We care only
// about 3 types of classes: Clsid's, ProgId's, and File Extenions.
//
if ( ! bFileExtensionsOnly ) { if ( ERROR_SUCCESS == Status ) { Status = GetClsids(); }
if ( ERROR_SUCCESS == Status ) { Status = GetProgIds(); } }
if ( ERROR_SUCCESS == Status ) { Status = GetExtensions(); }
LONG StatusFree;
//
// We must remove the scratch flags we added to the database
//
StatusFree = RemoveAdvertisableFeatureFlags();
if ( ERROR_SUCCESS == Status ) { //
// Take care to preserve the return value -- a failure
// before cleaning up the database takes precedence over
// a failure in cleaning up the database.
//
Status = StatusFree; } }
return HRESULT_FROM_WIN32(Status); }
LONG CClassCollection::GetExtensions() { LONG Status; BOOL bTableExists;
//
// First check to see if we even have an extension table to query
//
Status = _Database.TableExists( TABLE_FILE_EXTENSIONS, &bTableExists );
if ( ( ERROR_SUCCESS == Status ) && bTableExists ) { //
// Set up a destination in the user's PACKAGEDETAIL structure
// for the shell extension class data
//
DataDestination Destination( TYPE_EXTENSION, (void**)&(_pPackageDetail->pActInfo->prgShellFileExt), &(_pPackageDetail->pActInfo->cShellFileExt), (UINT*) &_cMaxExtensions);
//
// Now retrieve the shell extensions
//
Status = GetElements( TYPE_EXTENSION, &Destination );
if ( ERROR_SUCCESS == Status ) { //
// We've successfully retrieved the shell extensions --
// the caller also expects a parallel array of priorities
// with each shell extension -- the values are unimportant
// since the caller will fill those in, but the memory must
// exist, so we will allocate it.
//
_pPackageDetail->pActInfo->prgPriority = (UINT*) LocalAlloc( 0, sizeof(UINT) * _pPackageDetail->pActInfo->cShellFileExt );
if ( ! _pPackageDetail->pActInfo->prgPriority ) { Status = ERROR_NOT_ENOUGH_MEMORY; } } }
return Status; }
LONG CClassCollection::GetClsids() { LONG Status; BOOL bTableExists;
//
// First check to see if we even have a clsid table to query
//
Status = _Database.TableExists( TABLE_CLSIDS, &bTableExists );
if ( ( ERROR_SUCCESS == Status ) && bTableExists ) { //
// Set the destination for the clsid's to a location
// in the caller's PACKAGEDETAIL structure
//
DataDestination Destination( TYPE_CLSID, (void**)&(_pPackageDetail->pActInfo->pClasses), &(_pPackageDetail->pActInfo->cClasses), (UINT*) &_cMaxClsids);
//
// Now retrieve the clsid's for each package
//
Status = GetElements( TYPE_CLSID, &Destination ); }
return Status; }
LONG CClassCollection::GetProgIds() { LONG Status; BOOL bTableExists;
//
// First check to see if we even have a ProgId table to query
//
Status = _Database.TableExists( TABLE_PROGIDS, &bTableExists );
if ( ( ERROR_SUCCESS == Status ) && bTableExists ) { //
// This method MUST be called AFTER GetClsids -- progid's
// are stored within their associated clsid's, so we will
// not have a place to store the progid's unless we've
// already obtained the clsid's.
//
//
// At this point, we know only that we want to retrieve ProgId's --
// we do not know their destination because this differs for
// each progid depending on the associated clsid -- the NULL
// parameters indicate that some callee will need to determine
// the location for this data.
//
DataDestination Destination( TYPE_PROGID, NULL, NULL, NULL);
//
// Retrieve the progid's into the appropriate locations in the structure
//
Status = GetElements( TYPE_PROGID, &Destination ); }
return ERROR_SUCCESS; }
LONG CClassCollection::GetElements( DWORD dwType, DataDestination* pDestination ) { LONG Status;
CMsiQuery ElementQuery;
//
// Perform the query for the class elements
//
Status = _Database.GetQueryResults( _wszQueries[ dwType ], &ElementQuery);
if ( ERROR_SUCCESS != Status ) { return Status; }
for (;;) { //
// We've obtained the results -- now we enumerate them so
// that we can persist them in the caller's PACKAGEDETAIL
// structure.
//
//
// Note that we start a new scope so that our record object
// will automatically free its resources
//
{ CMsiRecord CurrentRecord;
//
// Enumerate the next record in the query result set
//
Status = ElementQuery.GetNextRecord( &CurrentRecord );
if ( ERROR_SUCCESS != Status ) { if ( ERROR_NO_MORE_ITEMS == Status ) { Status = ERROR_SUCCESS; }
break; }
//
// Now attempt to add the class data from this record into
// the PACKAGEDETAIL structure
//
Status = ProcessElement( dwType, &CurrentRecord, pDestination); }
if ( ERROR_SUCCESS != Status ) { break; } }
return Status; }
LONG CClassCollection::FlagAdvertisableFeatures() { LONG Status;
CMsiQuery FeatureQueryCreate;
//
// We will attempt to mark each feature in the database
// with a flag indicating whether or not it will be advertised
//
//
// First, add a column to the feature table of the database
// so that we can use the column to flag whether or not the
// feature is advertised.
//
Status = _Database.GetQueryResults( QUERY_ADVERTISED_FEATURES_CREATE, &FeatureQueryCreate);
CMsiQuery FeatureQueryInit;
//
// Now intialize the new column's flags to 0 which
// indicates that no features will be advertised (yet)
//
if ( ERROR_SUCCESS == Status ) { Status = _Database.GetQueryResults( QUERY_ADVERTISED_FEATURES_INIT, &FeatureQueryInit); }
CMsiQuery AllFeatures;
//
// Now we perform the query to retrieve all features --
// records in this query will contain the newly added
// flag column.
//
if ( ERROR_SUCCESS == Status ) { Status = _Database.GetQueryResults( QUERY_ADVERTISED_FEATURES_RESULT, &AllFeatures); }
CMsiQuery SetAdvertised;
//
// Create a query that will allow us to set the flag --
// this query is not yet computed, simply initialized
//
if ( ERROR_SUCCESS == Status ) { Status = _Database.OpenQuery( QUERY_FEATURES_SET, &SetAdvertised); }
//
// Now we enumerate through all the features and
// set the flag for each feature that passes the tests
// for advertisability.
//
for (; ERROR_SUCCESS == Status ;) { CMsiRecord CurrentRecord; BOOL bAdvertised;
//
// Retrieve the current feature
//
Status = AllFeatures.GetNextRecord( &CurrentRecord);
if ( ERROR_SUCCESS != Status ) { if ( ERROR_NO_MORE_ITEMS == Status ) { Status = ERROR_SUCCESS; }
break; }
//
// Determine whether or not this feature should be advertised
//
Status = GetFeatureAdvertiseState( &CurrentRecord, &bAdvertised );
if ( ( ERROR_SUCCESS == Status ) && bAdvertised ) { //
// This feature is advertisable -- use our SetAdvertised query
// to set the advertisability flag to true.
//
Status = SetAdvertised.UpdateQueryFromFilter( &CurrentRecord ); } }
return Status; }
LONG CClassCollection::RemoveAdvertisableFeatureFlags() { LONG Status;
CMsiQuery FreeQuery;
//
// Retrieving the results of this query will
// eliminate the extra flag column we added
// to the feature table to flag advertisable
// features.
//
Status = _Database.GetQueryResults( QUERY_ADVERTISED_FEATURES_DESTROY, &FreeQuery);
return Status; }
LONG CClassCollection::GetInstallLevel() { LONG Status;
CMsiQuery InstallLevelQuery;
//
// Perform a query which retrieves the install level
// property from the package's property table
//
Status = _Database.GetQueryResults( QUERY_INSTALLLEVEL, &InstallLevelQuery);
CMsiRecord InstallLevelRecord;
//
// This query should only have one record in it since
// it was targeted at the specific record for install level --
// we now read that record
//
if ( ERROR_SUCCESS == Status ) { Status = InstallLevelQuery.GetNextRecord( &InstallLevelRecord); }
if ( ERROR_SUCCESS == Status ) { CMsiValue InstallLevelProperty;
//
// We now attempt to obtain the installlevel property value
// from the retrieved record.
//
Status = InstallLevelRecord.GetValue( CMsiValue::TYPE_DWORD, PROPERTY_COLUMN_VALUE, &InstallLevelProperty);
if ( ERROR_SUCCESS == Status ) { //
// We've successfully obtained the value, so we set it
//
_InstallLevel = InstallLevelProperty.GetDWORDValue(); } } else if ( ERROR_NO_MORE_ITEMS == Status ) { //
// This will only happen if the install level property
// is not present. As fundamental as this property is,
// some packages do not specify it. The Darwin engine
// treats this case as an implicit install level of 1, so
// we must do the same here
//
_InstallLevel = 1;
Status = ERROR_SUCCESS; }
return Status; }
LONG CClassCollection::GetFriendlyName() { LONG Status;
CMsiQuery FriendlyNameQuery;
//
// Perform a query which retrieves the install level
// property from the package's property table
//
Status = _Database.GetQueryResults( QUERY_FRIENDLYNAME, &FriendlyNameQuery);
CMsiRecord FriendlyNameRecord;
//
// This query should only have one record in it since
// it was targeted at the specific record
// we now read that record
//
if ( ERROR_SUCCESS == Status ) { Status = FriendlyNameQuery.GetNextRecord( &FriendlyNameRecord); }
if ( ERROR_SUCCESS == Status ) { CMsiValue FriendlyNameProperty;
//
// We now attempt to obtain the property value
// from the retrieved record.
//
Status = FriendlyNameRecord.GetValue( CMsiValue::TYPE_STRING, PROPERTY_COLUMN_VALUE, &FriendlyNameProperty);
if ( ERROR_SUCCESS == Status ) { //
// We've successfully obtained the value, so we set it
//
CString szName = FriendlyNameProperty.GetStringValue(); OLESAFE_DELETE(_pPackageDetail->pszPackageName); OLESAFE_COPYSTRING(_pPackageDetail->pszPackageName, szName); } }
return Status; }
LONG CClassCollection::GetFeatureAdvertiseState( CMsiRecord* pFeatureRecord, BOOL* pbAdvertised ) { LONG Status; CMsiValue Attributes; CMsiValue InstallLevel;
//
// Set the out paramter's initial value to FALSE,
// indicating that the feature is not advertised
//
*pbAdvertised = FALSE;
//
// The Attributes column of the feature table
// contains a flag indicating that a feature
// should be not advertised
//
Status = pFeatureRecord->GetValue( CMsiValue::TYPE_DWORD, FEATURE_COLUMN_ATTRIBUTES, &Attributes);
if ( ERROR_SUCCESS == Status ) { //
// If the disable advertise flag is set, this feature
// cannot be advertised
//
if ( Attributes.GetDWORDValue() & MSI_DISABLEADVERTISE ) { return ERROR_SUCCESS; }
//
// The disable flag was not set -- that still does not mean that
// the feature is advertised -- we must check the install level.
// We retrieve the install level for this feature here
//
Status = pFeatureRecord->GetValue( CMsiValue::TYPE_DWORD, FEATURE_COLUMN_LEVEL, &InstallLevel); }
if ( ERROR_SUCCESS == Status ) { DWORD dwInstallLevel;
//
// Obtain the value for the install level so
// we can compare against the package install level
//
dwInstallLevel = InstallLevel.GetDWORDValue();
//
// An install level of 0 indicates that the package will
// not be advertised. The install level of the feature
// must be no higher than the package's global install
// level
//
if ( ( 0 != dwInstallLevel ) && ( dwInstallLevel <= _InstallLevel ) ) { //
// This feature passes the tests -- set the out parameter
// to TRUE to indicate that the feature should be advertised
//
*pbAdvertised = TRUE; } }
return Status; }
LONG CClassCollection::AddElement( void* pvDataSource, DataDestination* pDataDestination) { DWORD* pcMax; BYTE* pNewResults; DWORD cCurrent;
//
// We attempt to add an element to a vector
//
//
// Set the count for how many elements are stored in the vector to
// that specified by the caller
//
cCurrent = *(pDataDestination->_pcCurrent);
//
// Set the element count for the maximum number of elements that
// will fit in the vector currently to that specified by the caller
//
pcMax = (DWORD*) pDataDestination->_pcMax;
//
// Set our results to point to the vector specified by the caller
//
pNewResults = (BYTE*) pDataDestination->_ppvData;
//
// If we already have the maximum number of elements in the vector,
// we will have to make room for more
//
if ( *pcMax >= cCurrent) { DWORD cbSize;
//
// Calculate the new size in bytes so that we can ask the system
// for memory. We take our current size in elements and add on a fixed
// allocation increment. The caller has specified the size
// of each individual element, so we use that to turn the number
// of elements to a memory size.
//
cbSize = ( *pcMax + CLASS_ALLOC_SIZE ) * pDataDestination->_cbElementSize;
//
// Make the request for memory
//
pNewResults = (BYTE*) LocalAlloc( 0, cbSize );
if ( ! pNewResults ) { return ERROR_NOT_ENOUGH_MEMORY; }
//
// Clear the memory -- any data structures embedded in the element
// will have NULL references and thus will be properly initialized
//
memset( pNewResults, 0, cbSize );
//
// If the original maximum size of the vector was nonzero, then we must
// copy to original contents of the vector to the newly allocated memory
// location.
//
if ( *pcMax ) { memcpy( pNewResults, *(pDataDestination->_ppvData), *pcMax * pDataDestination->_cbElementSize); }
//
// Free the original vector as it is no longer needed
//
LocalFree( *(pDataDestination->_ppvData) );
//
// Change the caller's reference to point to the new vector
//
*(pDataDestination->_ppvData) = pNewResults;
//
// Set the new maximum size (in elements) to that of the newly allocated vector
//
*pcMax += CLASS_ALLOC_SIZE; }
//
// At this point, we know we have a memory location in the vector into
// which we can safely copy the new element
//
memcpy( pNewResults + ( cCurrent * pDataDestination->_cbElementSize ), pvDataSource, pDataDestination->_cbElementSize);
//
// Update the count of elements currently stored in the vector
//
*(pDataDestination->_pcCurrent) = cCurrent + 1;
return ERROR_SUCCESS; }
LONG CClassCollection::ProcessElement( DWORD dwType, CMsiRecord* pRecord, DataDestination* pDataDestination) { LONG Status = ERROR_SUCCESS; void* pvData; WCHAR* wszData; CLASSDETAIL ClassDetail;
pvData = NULL; wszData = NULL;
//
// We attempt to create a new class element based
// on the record passed in by the caller, and then
// add that element to the caller's PACKAGEDETAIL structure
//
//
// The type of element to be added depends on the type
// of class requested by the caller. The pvData variable
// will point to the element to be added if we can successfully
// create a representation for it.
//
switch ( dwType ) { case TYPE_EXTENSION:
//
// Get a file extension representation from the record --
// note that wszData points to memory allocated by the callee
// on success, so it must be freed by this function.
//
Status = ProcessExtension( pRecord, &wszData);
if ( ERROR_SUCCESS == Status ) { pvData = &wszData; }
break;
case TYPE_CLSID:
//
// Get a clsid representation from the record --
// in this case, the ClassDetail itself does not
// need to be freed since it does not contain any references
// to memory after this call
//
BOOL bIgnoreClsid;
Status = ProcessClsid( pRecord, &ClassDetail, &bIgnoreClsid);
if ( ERROR_SUCCESS == Status ) { //
// Check to see if we should add this clsid -- we may be prohibited from
// this because it is a duplicate of an exsting clsid, which would be
// redundant and furthermore the PACKAGEDETAIL format requires
// that all (clsid, clsctx) pairs be unique. Or the clsid itself
// may have an unsupported clsctx. This is not a failure
// case, so we return success here and simply avoid addding this
// class
//
if ( bIgnoreClsid ) { return ERROR_SUCCESS; }
pvData = &ClassDetail; }
break;
case TYPE_PROGID:
//
// Get a progid representation from the record. In addition
// to retrieving the progid in the form of an allocated string
// which must be freed by this funciton, we also retrieve the
// location at which to add the progid to the caller's
// PACKAGEDETAIL structure. This is necessary since the
// ProgId must be part of the CLASSDETAIL structure with which
// it is associated.
//
Status = ProcessProgId( pRecord, pDataDestination, &wszData);
if ( ( ERROR_SUCCESS == Status ) && wszData ) { pvData = &wszData; }
break;
default: ASSERT(FALSE); break; }
//
// If we were successful in obtaining a representation of the record
// that can be stored in the caller's PACKAGEDETAIL structure, attempt
// to add it to the structure
//
if ( pvData ) { Status = AddElement( pvData, pDataDestination); }
//
// Be sure that in the failure case, we free any memory
// that may have been allocated.
//
if ( ERROR_SUCCESS != Status ) { if (wszData ) { LocalFree( wszData ); } }
return Status; }
LONG CClassCollection::ProcessExtension( CMsiRecord* pRecord, WCHAR** ppwszExtension) { LONG Status; CMsiValue FileExtension;
*ppwszExtension = NULL;
//
// We retrieve the actual file extension string
//
Status = pRecord->GetValue( CMsiValue::TYPE_STRING, EXTENSION_COLUMN_EXTENSION, &FileExtension);
if ( ERROR_SUCCESS == Status ) { ULONG ulNoChars; HRESULT hr;
//
// We have the value. Note that it does not contain
// an initial '.', but the usage of the PACKAGEDETAIL
// structure mandates that file extensions begin with the '.'
// char, so we will have to prepend the '.' here.
//
//
// First, get space for a copy of the string that includes
// the '.' as well as the zero terminator.
//
ulNoChars = FileExtension.GetStringSize() + 1 + 1; *ppwszExtension = (WCHAR*) LocalAlloc( 0, ulNoChars * sizeof(WCHAR) );
if ( ! *ppwszExtension ) { Status = ERROR_NOT_ENOUGH_MEMORY; return Status; }
//
// Set the first char to be '.'
//
**ppwszExtension = L'.';
//
// Now append the actual extension to the '.'
//
hr = StringCchCopy( *ppwszExtension + 1, ulNoChars - 1, FileExtension.GetStringValue() ); ASSERT(SUCCEEDED(hr)); }
return Status; }
LONG CClassCollection::ProcessClsid( CMsiRecord* pRecord, CLASSDETAIL* pClsid, BOOL* pbIgnoreClsid) { LONG Status; DWORD dwClsCtx; CMsiValue GuidString; CMsiValue ClassContext;
//
// Clear the clsid to a safe state
//
memset( pClsid, 0, sizeof( *pClsid ) );
//
// Reset out parameters
//
*pbIgnoreClsid = FALSE;
dwClsCtx = 0;
//
// Retrieve the actual clsid
//
Status = pRecord->GetValue( CMsiValue::TYPE_STRING, CLSID_COLUMN_CLSID, &GuidString);
if ( ERROR_SUCCESS == Status ) { //
// Get the clsctx for this clsid
//
Status = pRecord->GetValue( CMsiValue::TYPE_STRING, CLSID_COLUMN_CONTEXT, &ClassContext); }
if ( ERROR_SUCCESS == Status ) { CMsiValue Attribute; WCHAR* wszClassContext; DWORD dwInprocClsCtx;
dwInprocClsCtx = 0;
//
// Retrieve a string representation of the clsctx for this clsid
//
wszClassContext = ClassContext.GetStringValue();
//
// Now map the clsctx strings to COM CLSCTX_* values
//
if (CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, wszClassContext, -1, COM_INPROC_CONTEXT, -1) == CSTR_EQUAL) { dwInprocClsCtx |= CLSCTX_INPROC_SERVER; } else if (CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, wszClassContext, -1, COM_INPROCHANDLER_CONTEXT, -1) == CSTR_EQUAL) { dwInprocClsCtx |= CLSCTX_INPROC_HANDLER; } else if (CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, wszClassContext, -1, COM_LOCALSERVER_CONTEXT, -1) == CSTR_EQUAL) { dwClsCtx |= CLSCTX_LOCAL_SERVER; } else if (CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, wszClassContext, -1, COM_REMOTESERVER_CONTEXT, -1) == CSTR_EQUAL) { dwClsCtx |= CLSCTX_REMOTE_SERVER; } else { //
// If the clsctx is one we do not support, we will ignore it
//
*pbIgnoreClsid = TRUE;
return ERROR_SUCCESS; }
BOOL b64Bit;
b64Bit = FALSE;
//
// We must disginguish between 32-bit and 64-bit in-process servers, since
// 64-bit Windows does not allows modules of different bitness to coexist in the
// same process. If this is an in-process component, we will also check to see
// whether it is 64-bit or not.
//
if ( ( dwInprocClsCtx & CLSCTX_INPROC_HANDLER ) || ( dwInprocClsCtx & CLSCTX_INPROC_SERVER ) ) { //
// The Attributes column of the record has a flag indicating bitness -- this
// will only fail if the property is NULL
//
Status = pRecord->GetValue( CMsiValue::TYPE_DWORD, CLSID_COLUMN_ATTRIBUTES, &Attribute);
//
// Check the flag to see if this is 64-bit
//
if ( ERROR_SUCCESS == Status ) { b64Bit = Attribute.GetDWORDValue() & MSI_64BIT_CLASS; } else { //
// This means the property is NULL, so we interpret that as
// meaning the application is not 64 bit
//
Status = ERROR_SUCCESS; }
//
// Map this 64-bit clsctx to a custom (non-COM) CLSCTX that
// indicates that this is a 64-bit-only in-process class.
//
if ( ( ERROR_SUCCESS == Status ) && b64Bit ) { if ( dwInprocClsCtx & CLSCTX_INPROC_SERVER ) { dwClsCtx |= CLSCTX64_INPROC_SERVER; }
if ( dwInprocClsCtx & CLSCTX_INPROC_HANDLER ) { dwClsCtx |= CLSCTX64_INPROC_HANDLER; } } }
//
// In the 32-bit case, just or in the values we already computed for
// inproc case
//
if ( ! b64Bit ) { dwClsCtx |= dwInprocClsCtx; } } //
// Check to see if this is a duplicate -- we do this because our query
// returned results distinct in (clsid, clsctx, attribute). Since we
// are mapping attribute to clsctx above and we only support 1 attribute
// flag (the 64-bit flag) out of several, we may end up with duplicate
// (clsid, clsctx) pairs, and the PACKAGEDETAIL format requires that
// we have unique (clsid, clsctx) pairs. Another way to get this would
// be if COM introduced new clsctx types which we did not support -- these
// would map to zero, and again we could have duplicates
//
if ( ERROR_SUCCESS == Status ) { CLASSDETAIL* pClassDetail;
pClassDetail = NULL;
Status = FindClass( GuidString.GetStringValue(), &pClassDetail);
//
// If we already have an entry for this clsid, check to see if
// it has the same clsctx bits -- if so it is a duplicate entry
// and we will cease processing it
//
if ( ( ERROR_SUCCESS == Status ) && pClassDetail ) { *pbIgnoreClsid = ( dwClsCtx & pClassDetail->dwComClassContext );
if ( *pbIgnoreClsid ) { return ERROR_SUCCESS; } } }
//
// Convert the clsid string to a guid as mandated by the
// CLASSDETAIL structure
//
if ( ERROR_SUCCESS == Status ) { HRESULT hr;
hr = CLSIDFromString( GuidString.GetStringValue(), &(pClsid->Clsid));
if ( FAILED(hr) ) { Status = ERROR_GEN_FAILURE; } }
//
// Set the clsctx we computed above.
//
if ( ERROR_SUCCESS == Status ) { pClsid->dwComClassContext = dwClsCtx; }
return Status; }
LONG CClassCollection::ProcessProgId( CMsiRecord* pRecord, DataDestination* pDataDestination, WCHAR** ppwszProgId) { LONG Status;
CMsiValue ProgIdString; CMsiValue ClsidString;
CLASSDETAIL* pClassDetail;
//
// We attempt to map a progid record to a
// clsid that we've already processed, since
// the progid will eventually need to go
// inside the clsid's structure.
//
*ppwszProgId = NULL;
pClassDetail = NULL;
//
// Retrieve the value for the progid itself
//
Status = pRecord->GetValue( CMsiValue::TYPE_STRING, PROGID_COLUMN_PROGID, &ProgIdString);
//
// Retrieve the value of the clsid associated with
// the progid
//
if ( ERROR_SUCCESS == Status ) { Status = pRecord->GetValue( CMsiValue::TYPE_STRING, PROGID_COLUMN_CLSID, &ClsidString); }
//
// We must find the existing CLASSDETAIL structure
// that we are maintaining for the progid since the
// progid must eventually be referenced in that structure.
//
if ( ERROR_SUCCESS == Status ) { Status = FindClass( ClsidString.GetStringValue(), &pClassDetail); }
if ( ERROR_SUCCESS == Status ) { //
// If we have successfully found the class,
//
if ( pClassDetail ) { //
// Give the caller the progid string since
// we know that we have a class in which
// to place it
//
*ppwszProgId = ProgIdString.DuplicateString();
if ( ! *ppwszProgId ) { Status = ERROR_NOT_ENOUGH_MEMORY; } else { //
// Set the caller's data destination to that of the
// progid vector within the clsid associated with this progid
//
pDataDestination->_ppvData = (void**) &( pClassDetail->prgProgId );
pDataDestination->_pcCurrent = (UINT*) &( pClassDetail->cProgId );
pDataDestination->_pcMax = (UINT*) &( pClassDetail->cMaxProgId ); } } }
//
// On failure, free any resources we've allocated
//
if ( ( ERROR_SUCCESS != Status ) && *ppwszProgId ) { LocalFree( *ppwszProgId ); }
return Status; }
LONG CClassCollection::FindClass( WCHAR* wszClsid, CLASSDETAIL** ppClass) { CLSID Clsid; HRESULT hr;
//
// Attempt to find a CLASSDETAIL structure in the PACKAGEDETAIL structure
// for the clsid given in string form in wszClsid
//
*ppClass = NULL;
//
// The PACKAGEDETAIL structure stores the clsid in guid form,
// so we must convert the string to that form before searching
//
hr = CLSIDFromString( wszClsid, &Clsid);
if ( FAILED(hr) ) { return ERROR_GEN_FAILURE; }
UINT iClsid;
//
// We now perform a simple linear search for the clsid
//
for ( iClsid = 0; iClsid < _pPackageDetail->pActInfo->cClasses; iClsid++) { if ( IsEqualGUID( _pPackageDetail->pActInfo->pClasses[iClsid].Clsid, Clsid) ) { *ppClass = &(_pPackageDetail->pActInfo->pClasses[iClsid]); return ERROR_SUCCESS; } }
return ERROR_SUCCESS; }
void CClassCollection::FreeClassDetail( CLASSDETAIL* pClass ) { DWORD iProgId;
//
// Free each individual progid string
//
for ( iProgId = 0; iProgId < pClass->cProgId; iProgId++ ) { LocalFree( pClass->prgProgId[ iProgId ] ); }
//
// Free the array of progid strings
//
LocalFree( pClass->prgProgId ); }
DataDestination::DataDestination( DWORD dwType, void** prgpvDestination, UINT* pcCurrent, UINT* pcMax ) : _pcCurrent( pcCurrent ), _ppvData( prgpvDestination ), _pcMax ( pcMax ) { //
// The size of the elements stored by
// the vector referenced from this class
// depend on the type of element --
// clsid, file extension, or progid
//
switch ( dwType ) { case TYPE_EXTENSION: _cbElementSize = sizeof( WCHAR* ); break;
case TYPE_CLSID: _cbElementSize = sizeof( CLASSDETAIL ); break;
case TYPE_PROGID: _cbElementSize = sizeof( WCHAR* ); break;
default: ASSERT(FALSE); } }
|