Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1499 lines
39 KiB

//+--------------------------------------------------------------------------
//
// 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);
}
}