Example #1
0
HRESULT CpiGetCatalogCollection(
	ICatalogCollection* piColl,
	ICatalogObject* piObj,
	LPCWSTR pwzName,
	ICatalogCollection** ppiColl
	)
{
	HRESULT hr = S_OK;

	ICOMAdminCatalog* piCatalog = NULL;
	IDispatch* piDisp = NULL;

	BSTR bstrName = NULL;

	VARIANT vtKey;
	::VariantInit(&vtKey);

	// copy name string
	bstrName = ::SysAllocString(pwzName);
	ExitOnNull(bstrName, hr, E_OUTOFMEMORY, "Failed to allocate BSTR for collection name");

	// get catalog
	hr = CpiGetAdminCatalog(&piCatalog);
	ExitOnFailure(hr, "Failed to get COM+ admin catalog");

	// get key
	hr = piObj->get_Key(&vtKey);
	ExitOnFailure(hr, "Failed to get object key");

	// get collecton from catalog
	hr = piColl->GetCollection(bstrName, vtKey, &piDisp);
	ExitOnFailure(hr, "Failed to get collection");

	hr = piDisp->QueryInterface(IID_ICatalogCollection, (void**)ppiColl);
	ExitOnFailure(hr, "Failed to get IID_ICatalogCollection interface");

	// populate collection
	hr = (*ppiColl)->Populate();
	if (COMADMIN_E_OBJECTERRORS == hr)
		CpiLogCatalogErrorInfo();
	ExitOnFailure(hr, "Failed to populate collection");

	hr = S_OK;

LExit:
	// clean up
	ReleaseObject(piCatalog);
	ReleaseObject(piDisp);
	ReleaseBSTR(bstrName);
	::VariantClear(&vtKey);

	return hr;
}
Example #2
0
HRESULT CpiGetCatalogCollection(
    LPCWSTR pwzName,
    ICatalogCollection** ppiColl
    )
{
    HRESULT hr = S_OK;

    ICOMAdminCatalog* piCatalog = NULL;
    IDispatch* piDisp = NULL;
    BSTR bstrName = NULL;

    // copy name string
    bstrName = ::SysAllocString(pwzName);
    ExitOnNull(bstrName, hr, E_OUTOFMEMORY, "Failed to allocate BSTR for collection name");

    // get catalog
    hr = CpiGetAdminCatalog(&piCatalog);
    ExitOnFailure(hr, "Failed to get COM+ admin catalog");

    // get collecton from catalog
    hr = piCatalog->GetCollection(bstrName, &piDisp);
    ExitOnFailure(hr, "Failed to get collection");

    hr = piDisp->QueryInterface(IID_ICatalogCollection, (void**)ppiColl);
    ExitOnFailure(hr, "Failed to get IID_ICatalogCollection interface");

    // populate collection
    hr = (*ppiColl)->Populate();
    ExitOnFailure(hr, "Failed to populate collection");

    hr = S_OK;

LExit:
    // clean up
    ReleaseObject(piCatalog);
    ReleaseObject(piDisp);
    ReleaseBSTR(bstrName);

    return hr;
}
Example #3
0
HRESULT CpiGetApplicationsCollection(
    ICatalogCollection** ppiAppColl
    )
{
    HRESULT hr = S_OK;

    ICOMAdminCatalog* piCatalog = NULL;
    ICOMAdminCatalog2* piCatalog2 = NULL;
    ICatalogCollection* piPartColl = NULL;
    ICatalogObject* piPartObj = NULL;
    BSTR bstrGlobPartID = NULL;

    if (!gpiAppColl)
    {
        // get catalog
        hr = CpiGetAdminCatalog(&piCatalog);
        ExitOnFailure(hr, "Failed to get COM+ admin catalog");

        // get ICOMAdminCatalog2 interface
        hr = piCatalog->QueryInterface(IID_ICOMAdminCatalog2, (void**)&piCatalog2);

        // COM+ 1.5 or later
        if (E_NOINTERFACE != hr)
        {
            ExitOnFailure(hr, "Failed to get IID_ICOMAdminCatalog2 interface");

            // get global partition id
            hr = piCatalog2->get_GlobalPartitionID(&bstrGlobPartID);
            ExitOnFailure(hr, "Failed to get global partition id");

            // get partitions collection
            hr = CpiGetPartitionsCollection(&piPartColl);
            ExitOnFailure(hr, "Failed to get partitions collection");

            // find object
            hr = CpiFindCollectionObject(piPartColl, bstrGlobPartID, NULL, &piPartObj);
            ExitOnFailure(hr, "Failed to find collection object");

            if (S_FALSE == hr)
                ExitFunction(); // partition not found, exit with hr = S_FALSE

            // get applications collection
            hr = CpiGetCatalogCollection(piPartColl, piPartObj, L"Applications", &gpiAppColl);
            ExitOnFailure(hr, "Failed to get applications collection");
        }

        // COM+ pre 1.5
        else
        {
            // get applications collection
            hr = CpiGetCatalogCollection(L"Applications", &gpiAppColl);
            ExitOnFailure(hr, "Failed to get applications collection");
        }
    }

    // return value
    gpiAppColl->AddRef();
    *ppiAppColl = gpiAppColl;

    hr = S_OK;

LExit:
    // clean up
    ReleaseObject(piCatalog);
    ReleaseObject(piCatalog2);
    ReleaseObject(piPartColl);
    ReleaseObject(piPartObj);
    ReleaseBSTR(bstrGlobPartID);

    return hr;
}
Example #4
0
HRESULT CpiLogCatalogErrorInfo()
{
	HRESULT hr = S_OK;

	ICOMAdminCatalog* piCatalog = NULL;
	ICatalogCollection* piErrColl = NULL;
	IDispatch* piDisp = NULL;
	ICatalogObject* piObj = NULL;

	LPWSTR pwzName = NULL;
	LPWSTR pwzErrorCode = NULL;
	LPWSTR pwzMajorRef = NULL;
	LPWSTR pwzMinorRef = NULL;

	// get catalog
	hr = CpiGetAdminCatalog(&piCatalog);
	ExitOnFailure(hr, "Failed to get COM+ admin catalog");

	// get error info collection
	hr = CpiGetCatalogCollection(L"ErrorInfo", &piErrColl);
	ExitOnFailure(hr, "Failed to get error info collection");

	// loop objects
	long lCnt;
	hr = piErrColl->get_Count(&lCnt);
	ExitOnFailure(hr, "Failed to get to number of items in collection");

	for (long i = 0; i < lCnt; i++)
	{
		// get ICatalogObject interface
		hr = piErrColl->get_Item(i, &piDisp);
		ExitOnFailure(hr, "Failed to get item from partitions collection");

		hr = piDisp->QueryInterface(IID_ICatalogObject, (void**)&piObj);
		ExitOnFailure(hr, "Failed to get IID_ICatalogObject interface");

		// get properties
		hr = CpiGetCollectionObjectValue(piObj, L"Name", &pwzName);
		ExitOnFailure(hr, "Failed to get name");
		hr = CpiGetCollectionObjectValue(piObj, L"ErrorCode", &pwzErrorCode);
		ExitOnFailure(hr, "Failed to get error code");
		hr = CpiGetCollectionObjectValue(piObj, L"MajorRef", &pwzMajorRef);
		ExitOnFailure(hr, "Failed to get major ref");
		hr = CpiGetCollectionObjectValue(piObj, L"MinorRef", &pwzMinorRef);
		ExitOnFailure(hr, "Failed to get minor ref");

		// write to log
		WcaLog(LOGMSG_STANDARD, "ErrorInfo: Name='%S', ErrorCode='%S', MajorRef='%S', MinorRef='%S'",
			pwzName, pwzErrorCode, pwzMajorRef, pwzMinorRef);

		// clean up
		ReleaseNullObject(piDisp);
		ReleaseNullObject(piObj);
	}

	hr = S_OK;

LExit:
	// clean up
	ReleaseObject(piCatalog);
	ReleaseObject(piErrColl);
	ReleaseObject(piDisp);
	ReleaseObject(piObj);

	ReleaseStr(pwzName);
	ReleaseStr(pwzErrorCode);
	ReleaseStr(pwzMajorRef);
	ReleaseStr(pwzMinorRef);

	return hr;
}
Example #5
0
HRESULT CpiGetApplicationsCollection(
	LPCWSTR pwzPartID,
	ICatalogCollection** ppiAppColl
	)
{
	HRESULT hr = S_OK;

	ICOMAdminCatalog* piCatalog = NULL;
	ICOMAdminCatalog2* piCatalog2 = NULL;
	BSTR bstrGlobPartID = NULL;

	ICatalogCollection* piPartColl = NULL;
	ICatalogObject* piPartObj = NULL;

	// get catalog
	hr = CpiGetAdminCatalog(&piCatalog);
	ExitOnFailure(hr, "Failed to get COM+ admin catalog");

	// get ICOMAdminCatalog2 interface
	hr = piCatalog->QueryInterface(IID_ICOMAdminCatalog2, (void**)&piCatalog2);

	// COM+ 1.5 or later
	if (E_NOINTERFACE != hr)
	{
		ExitOnFailure(hr, "Failed to get IID_ICOMAdminCatalog2 interface");

		// partition id
		if (!pwzPartID || !*pwzPartID)
		{
			// get global partition id
			hr = piCatalog2->get_GlobalPartitionID(&bstrGlobPartID);
			ExitOnFailure(hr, "Failed to get global partition id");
		}

		// get partitions collection
		hr = CpiGetPartitionsCollection(&piPartColl);
		ExitOnFailure(hr, "Failed to get partitions collection");

		// find object
		hr = CpiFindCollectionObjectByStringKey(piPartColl, bstrGlobPartID ? bstrGlobPartID : pwzPartID, &piPartObj);
		ExitOnFailure(hr, "Failed to find collection object");

		if (S_FALSE == hr)
			ExitFunction(); // partition not found, exit with hr = S_FALSE

		// get applications collection
		hr = CpiGetCatalogCollection(piPartColl, piPartObj, L"Applications", ppiAppColl);
		ExitOnFailure(hr, "Failed to get catalog collection for partition");
	}

	// COM+ pre 1.5
	else
	{
		// this version of COM+ does not support partitions, make sure a partition was not specified
		if (pwzPartID && *pwzPartID)
			ExitOnFailure(hr = E_FAIL, "Partitions are not supported by this version of COM+");

		// get applications collection
		hr = CpiGetCatalogCollection(L"Applications", ppiAppColl);
		ExitOnFailure(hr, "Failed to get catalog collection");
	}

	hr = S_OK;

LExit:
	// clean up
	ReleaseObject(piCatalog);
	ReleaseObject(piCatalog2);
	ReleaseBSTR(bstrGlobPartID);

	ReleaseObject(piPartColl);
	ReleaseObject(piPartObj);

	return hr;
}
Example #6
0
/********************************************************************
 ConfigureComPlusInstall - CUSTOM ACTION ENTRY POINT for installing COM+ components

********************************************************************/
extern "C" UINT __stdcall ConfigureComPlusInstall(MSIHANDLE hInstall)
{
    HRESULT hr = S_OK;
    UINT er = ERROR_SUCCESS;

    BOOL fInitializedCom = FALSE;

    ICOMAdminCatalog* piCatalog = NULL;

    CPI_PARTITION_LIST partList;
    CPI_PARTITION_ROLE_LIST partRoleList;
    CPI_USER_IN_PARTITION_ROLE_LIST usrInPartRoleList;
    CPI_PARTITION_USER_LIST partUsrList;
    CPI_APPLICATION_LIST appList;
    CPI_APPLICATION_ROLE_LIST appRoleList;
    CPI_USER_IN_APPLICATION_ROLE_LIST usrInAppRoleList;
    CPI_ASSEMBLY_LIST asmList;
    CPI_SUBSCRIPTION_LIST subList;

    LPWSTR pwzRollbackFileName = NULL;
    LPWSTR pwzActionData = NULL;
    LPWSTR pwzRollbackActionData = NULL;
    LPWSTR pwzCommitActionData = NULL;

    int iVersionNT = 0;
    int iProgress = 0;
    int iCommitProgress = 0;

    ::ZeroMemory(&partList, sizeof(CPI_PARTITION_LIST));
    ::ZeroMemory(&partRoleList, sizeof(CPI_PARTITION_ROLE_LIST));
    ::ZeroMemory(&usrInPartRoleList, sizeof(CPI_USER_IN_PARTITION_ROLE_LIST));
    ::ZeroMemory(&partUsrList, sizeof(CPI_PARTITION_USER_LIST));
    ::ZeroMemory(&appList, sizeof(CPI_APPLICATION_LIST));
    ::ZeroMemory(&appRoleList, sizeof(CPI_APPLICATION_ROLE_LIST));
    ::ZeroMemory(&usrInAppRoleList, sizeof(CPI_USER_IN_APPLICATION_ROLE_LIST));
    ::ZeroMemory(&asmList, sizeof(CPI_ASSEMBLY_LIST));
    ::ZeroMemory(&subList, sizeof(CPI_SUBSCRIPTION_LIST));

    // initialize
    hr = WcaInitialize(hInstall, "ConfigureComPlusInstall");
    ExitOnFailure(hr, "Failed to initialize");

    hr = ::CoInitialize(NULL);
    ExitOnFailure(hr, "Failed to initialize COM");
    fInitializedCom = TRUE;

    CpiInitialize();

    // check for the prerequsite tables
    if (!CpiTableExists(cptComPlusPartition) && !CpiTableExists(cptComPlusApplication) && !CpiTableExists(cptComPlusAssembly))
    {
        WcaLog(LOGMSG_VERBOSE, "skipping install COM+ CustomAction, no ComPlusPartition, ComPlusApplication or ComPlusAssembly table present");
        ExitFunction1(hr = S_FALSE);
    }

    // make sure we can access the COM+ admin catalog
    do {
        hr = CpiGetAdminCatalog(&piCatalog);
        if (FAILED(hr))
        {
            WcaLog(LOGMSG_STANDARD, "Failed to get COM+ admin catalog");
            er = WcaErrorMessage(msierrComPlusCannotConnect, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0);
            switch (er)
            {
            case IDABORT:
                ExitFunction(); // exit with hr from CpiGetAdminCatalog() to kick off a rollback
            case IDRETRY:
                hr = S_FALSE;
                break;
            case IDIGNORE:
            default:
                ExitFunction1(hr = S_OK); // pretend everything is okay and bail
            }
        }
    } while (S_FALSE == hr);

    // get NT version
    hr = WcaGetIntProperty(L"VersionNT", &iVersionNT);
    ExitOnFailure(hr, "Failed to get VersionNT property");

    // read elements
    if (502 <= iVersionNT && CpiTableExists(cptComPlusPartition))
    {
        hr = CpiPartitionsRead(&partList);
        MessageExitOnFailure(hr, msierrComPlusPartitionReadFailed, "Failed to read ComPlusPartitions table");
    }

    if (502 <= iVersionNT && CpiTableExists(cptComPlusPartitionRole))
    {
        hr = CpiPartitionRolesRead(&partList, &partRoleList);
        MessageExitOnFailure(hr, msierrComPlusPartitionRoleReadFailed, "Failed to read ComPlusPartitionRole table");
    }

    if (502 <= iVersionNT && (CpiTableExists(cptComPlusUserInPartitionRole) || CpiTableExists(cptComPlusGroupInPartitionRole)))
    {
        hr = CpiUsersInPartitionRolesRead(&partRoleList, &usrInPartRoleList);
        MessageExitOnFailure(hr, msierrComPlusUserInPartitionRoleReadFailed, "Failed to read ComPlusUserInPartitionRole table");
    }

    if (502 <= iVersionNT && CpiTableExists(cptComPlusPartitionUser))
    {
        hr = CpiPartitionUsersRead(&partList, &partUsrList);
        MessageExitOnFailure(hr, msierrComPlusPartitionUserReadFailed, "Failed to read ComPlusPartitionUser table");
    }

    if (CpiTableExists(cptComPlusApplication))
    {
        hr = CpiApplicationsRead(&partList, &appList);
        MessageExitOnFailure(hr, msierrComPlusApplicationReadFailed, "Failed to read ComPlusApplication table");
    }

    if (CpiTableExists(cptComPlusApplicationRole))
    {
        hr = CpiApplicationRolesRead(&appList, &appRoleList);
        MessageExitOnFailure(hr, msierrComPlusApplicationRoleReadFailed, "Failed to read ComPlusApplicationRole table");
    }

    if (CpiTableExists(cptComPlusUserInApplicationRole) || CpiTableExists(cptComPlusGroupInApplicationRole))
    {
        hr = CpiUsersInApplicationRolesRead(&appRoleList, &usrInAppRoleList);
        MessageExitOnFailure(hr, msierrComPlusUserInApplicationRoleReadFailed, "Failed to read ComPlusUserInApplicationRole table");
    }

    if (CpiTableExists(cptComPlusAssembly))
    {
        hr = CpiAssembliesRead(&appList, &appRoleList, &asmList);
        MessageExitOnFailure(hr, msierrComPlusAssembliesReadFailed, "Failed to read ComPlusAssembly table");
    }

    if (CpiTableExists(cptComPlusSubscription))
    {
        hr = CpiSubscriptionsRead(&asmList, &subList);
        MessageExitOnFailure(hr, msierrComPlusSubscriptionReadFailed, "Failed to read ComPlusSubscription table");
    }

    // verify elements
    hr = CpiPartitionsVerifyInstall(&partList);
    ExitOnFailure(hr, "Failed to verify partitions");

    hr = CpiApplicationsVerifyInstall(&appList);
    ExitOnFailure(hr, "Failed to verify applications");

    hr = CpiApplicationRolesVerifyInstall(&appRoleList);
    ExitOnFailure(hr, "Failed to verify application roles");

    hr = CpiAssembliesVerifyInstall(&asmList);
    ExitOnFailure(hr, "Failed to verify assemblies");

    if (subList.iInstallCount)
    {
        hr = CpiSubscriptionsVerifyInstall(&subList);
        ExitOnFailure(hr, "Failed to verify subscriptions");
    }

    // schedule
    if (partList.iInstallCount || appList.iInstallCount || usrInAppRoleList.iInstallCount ||
        appRoleList.iInstallCount || asmList.iInstallCount || asmList.iRoleInstallCount || subList.iInstallCount)
    {
        // create rollback file name
        hr = CpiGetTempFileName(&pwzRollbackFileName);
        ExitOnFailure(hr, "Failed to get rollback file name");

        // schedule rollback prepare custom action
        hr = WcaDoDeferredAction(CP_COMPLUSROLLBACKINSTALLPREPARE, pwzRollbackFileName, 0);
        ExitOnFailure(hr, "Failed to schedule ComPlusRollbackInstallPrepare");

        // schedule prepare custom action
        hr = WcaDoDeferredAction(CP_COMPLUSINSTALLPREPARE, pwzRollbackFileName, 0);
        ExitOnFailure(hr, "Failed to schedule ComPlusInstallPrepare");

        // schedule rollback custom action
        hr = WcaWriteStringToCaData(pwzRollbackFileName, &pwzRollbackActionData);
        ExitOnFailure(hr, "Failed to add rollback file name to rollback custom action data");

        hr = CpiSubscriptionsInstall(&subList, rmRollback, &pwzRollbackActionData, NULL);
        ExitOnFailure(hr, "Failed to install subscriptions");
        hr = CpiRoleAssignmentsInstall(&asmList, rmRollback, &pwzRollbackActionData, NULL);
        ExitOnFailure(hr, "Failed to install assemblies");
        hr = CpiAssembliesInstall(&asmList, rmRollback, &pwzRollbackActionData, NULL);
        ExitOnFailure(hr, "Failed to install assemblies");
        hr = CpiUsersInApplicationRolesInstall(&usrInAppRoleList, rmRollback, &pwzRollbackActionData, NULL);
        ExitOnFailure(hr, "Failed to install users in application roles");
        hr = CpiApplicationRolesInstall(&appRoleList, rmRollback, &pwzRollbackActionData, NULL);
        ExitOnFailure(hr, "Failed to install application roles");
        hr = CpiApplicationsInstall(&appList, rmRollback, &pwzRollbackActionData, NULL);
        ExitOnFailure(hr, "Failed to install applications");
        hr = CpiPartitionUsersInstall(&partUsrList, rmRollback, &pwzRollbackActionData, NULL);
        ExitOnFailure(hr, "Failed to install partition users");
        hr = CpiUsersInPartitionRolesInstall(&usrInPartRoleList, rmRollback, &pwzRollbackActionData, NULL);
        ExitOnFailure(hr, "Failed to install users in partition roles");
        hr = CpiPartitionsInstall(&partList, rmRollback, &pwzRollbackActionData, NULL);
        ExitOnFailure(hr, "Failed to install partitions");

        hr = WcaDoDeferredAction(CP_COMPLUSROLLBACKINSTALLEXECUTE, pwzRollbackActionData, 0);
        ExitOnFailure(hr, "Failed to schedule ComPlusRollbackInstallExecute");

        // schedule install custom action
        hr = WcaWriteStringToCaData(pwzRollbackFileName, &pwzActionData);
        ExitOnFailure(hr, "Failed to add rollback file name to custom action data");

        hr = CpiPartitionsInstall(&partList, rmDeferred, &pwzActionData, &iProgress);
        ExitOnFailure(hr, "Failed to install partitions");
        hr = CpiUsersInPartitionRolesInstall(&usrInPartRoleList, rmDeferred, &pwzActionData, &iProgress);
        ExitOnFailure(hr, "Failed to install users in partition roles");
        hr = CpiPartitionUsersInstall(&partUsrList, rmDeferred, &pwzActionData, &iProgress);
        ExitOnFailure(hr, "Failed to install partition users");
        hr = CpiApplicationsInstall(&appList, rmDeferred, &pwzActionData, &iProgress);
        ExitOnFailure(hr, "Failed to install applications");
        hr = CpiApplicationRolesInstall(&appRoleList, rmDeferred, &pwzActionData, &iProgress);
        ExitOnFailure(hr, "Failed to install application roles");
        hr = CpiUsersInApplicationRolesInstall(&usrInAppRoleList, rmDeferred, &pwzActionData, &iProgress);
        ExitOnFailure(hr, "Failed to install users in application roles");
        hr = CpiAssembliesInstall(&asmList, rmDeferred, &pwzActionData, &iProgress);
        ExitOnFailure(hr, "Failed to install assemblies");
        hr = CpiRoleAssignmentsInstall(&asmList, rmDeferred, &pwzActionData, &iProgress);
        ExitOnFailure(hr, "Failed to install assemblies");
        hr = CpiSubscriptionsInstall(&subList, rmDeferred, &pwzActionData, &iProgress);
        ExitOnFailure(hr, "Failed to install subscriptions");

        hr = WcaDoDeferredAction(CP_COMPLUSINSTALLEXECUTE, pwzActionData, iProgress);
        ExitOnFailure(hr, "Failed to schedule ComPlusInstallExecute");

        // schedule install commit custom action
        hr = WcaWriteStringToCaData(pwzRollbackFileName, &pwzCommitActionData);
        ExitOnFailure(hr, "Failed to add rollback file name to commit custom action data");

        hr = CpiAssembliesInstall(&asmList, rmCommit, &pwzCommitActionData, &iCommitProgress);
        ExitOnFailure(hr, "Failed to install assemblies");
        hr = CpiRoleAssignmentsInstall(&asmList, rmCommit, &pwzCommitActionData, &iCommitProgress);
        ExitOnFailure(hr, "Failed to install assemblies");
        hr = CpiSubscriptionsInstall(&subList, rmCommit, &pwzCommitActionData, &iCommitProgress);
        ExitOnFailure(hr, "Failed to install subscriptions");

        hr = WcaDoDeferredAction(CP_COMPLUSINSTALLEXECUTECOMMIT, pwzCommitActionData, iCommitProgress);
        ExitOnFailure(hr, "Failed to schedule ComPlusInstallExecuteCommit");

        // schedule commit custom action
        hr = WcaDoDeferredAction(CP_COMPLUSINSTALLCOMMIT, pwzRollbackFileName, 0);
        ExitOnFailure(hr, "Failed to schedule ComPlusInstallCommit");
    }

    hr = S_OK;

LExit:
    // clean up
    ReleaseObject(piCatalog);

    ReleaseStr(pwzRollbackFileName);
    ReleaseStr(pwzActionData);
    ReleaseStr(pwzRollbackActionData);
    ReleaseStr(pwzCommitActionData);

    CpiPartitionListFree(&partList);
    CpiPartitionRoleListFree(&partRoleList);
    CpiUserInPartitionRoleListFree(&usrInPartRoleList);
    CpiPartitionUserListFree(&partUsrList);
    CpiApplicationListFree(&appList);
    CpiApplicationRoleListFree(&appRoleList);
    CpiUserInApplicationRoleListFree(&usrInAppRoleList);
    CpiAssemblyListFree(&asmList);
    CpiSubscriptionListFree(&subList);

    // unitialize
    CpiFinalize();

    if (fInitializedCom)
        ::CoUninitialize();

    er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
    return WcaFinalize(er);
}