Бизнес-робот
Бизнес
робот

Темы:

Архив:

Каталог рекламы и объявлений

Ящик для предложений: sales@blogslov.ru

Wix-commits Digest, Vol 11, Issue 5

Send Wix-commits mailing list submissions to


To subscribe or unsubscribe via the World Wide Web, visit
https://lists.sourceforge.net/lists/listinfo/wix-commits
or, via email, send a message with subject or body 'help' to


You can reach the person managing the list at


When replying, please edit your Subject line so it is more specific
than "Re: Contents of Wix-commits digest..."


Today's Topics:

1. wix/src/ext/ComPlusExtension/ca/cpsched cpapprolesched.cpp,
NONE, 1.1 cpapprolesched.h, NONE, 1.1 cpappsched.cpp, NONE, 1.1
cpappsched.h, NONE, 1.1 cpasmsched.cpp, NONE, 1.1 cpasmsched.h,
NONE, 1.1 cppartrolesched.cpp, NONE, 1.1 cppartrolesched.h, NONE,
1.1 cppartsched.cpp, NONE, 1.1 cppartsched.h, NONE, 1.1
cpsched.build, NONE, 1.1 cpsched.cpp, NONE, 1.1 cpsched.def,
NONE, 1.1 cpsched.rc, NONE, 1.1 cpsubssched.cpp, NONE, 1.1
cpsubssched.h, NONE, 1.1 cputilsched.cpp, NONE, 1.1
cputilsched.h, NONE, 1.1 precomp.h, NONE, 1.1 (Rob Mensching)


----------------------------------------------------------------------

Message: 1
Date: Wed, 27 Jun 2007 05:36:32 +0000
From: Rob Mensching
Subject: [WiX-commits] wix/src/ext/ComPlusExtension/ca/cpsched
cpapprolesched.cpp, NONE, 1.1 cpapprolesched.h, NONE, 1.1
cpappsched.cpp, NONE, 1.1 cpappsched.h, NONE, 1.1 cpasmsched.cpp,
NONE, 1.1 cpasmsched.h, NONE, 1.1 cppartrolesched.cpp, NONE, 1.1
cppartrolesched.h, NONE, 1.1 cppartsched.cpp, NONE, 1.1 cppartsched.h,
NONE, 1.1 cpsched.build, NONE, 1.1 cpsched.cpp, NONE, 1.1 cpsched.def,
NONE, 1.1 cpsched.rc, NONE, 1.1 cpsubssched.cpp, NONE, 1.1
cpsubssched.h, NONE, 1.1 cputilsched.cpp, NONE, 1.1 cputilsched.h,
NONE, 1.1 precomp.h, NONE, 1.1
To:
Message-ID:

Update of /cvsroot/wix/wix/src/ext/ComPlusExtension/ca/cpsched
In directory sc8-pr-cvs9.sourceforge.net:/tmp/cvs-serv1332/src/ext/ComPlusExtension/ca/cpsched

Added Files:
cpapprolesched.cpp cpapprolesched.h cpappsched.cpp
cpappsched.h cpasmsched.cpp cpasmsched.h cppartrolesched.cpp
cppartrolesched.h cppartsched.cpp cppartsched.h cpsched.build
cpsched.cpp cpsched.def cpsched.rc cpsubssched.cpp
cpsubssched.h cputilsched.cpp cputilsched.h precomp.h
Log Message:
Benxing: Number of patch related bug fix
*Update FeatureComponent table when ComponentRef in the selected PatchFamily
*File Sequence problem

PMarcu: Allowing extension attributes on DirectoryRef and PropertyRef
Adding extension attributes to the VSExtension Refs

PMarcu: Adding UIRef and DirectoryRef to possible PatchFamily Children
Allowing extension attributes on CustomAction elements.

JRock: Added support for setting pre- and post-build event command lines via a
property page in Votive. This gets me one step closer to restoring the
inter-project variables that we had in V2 but had to pull in V3 to get
it out the door (e.g. $(var.MyProject.TargetPath)).

JRock: Added support for SolutionX variables in the wix.targets file. So, you
can now use $(var.SolutionDir) in your .wxs files and have the variables
automatically added to the preprocessor definitions when building with
wix.targets and MSBuild.

Benxing: Regardless of differences in the MST, we will compare underlying files before
copying data into the patch. Extensions can override CompareFiles to provide
custom file diffing behavior.

AaronSte: Updating Heat to use HKCU instead of HKLM when harvesting registry
information on Windows Vista to avoid UAC issues

PMarcu: Making XsdStitch only output a single prefix for each extension namespace.

Benxing: Fixing null reference exception in the binder when the file table is empty.

RobMen: Default File/@Name to File/@Id.

MikeHo: Fix bug with Setup.exe when trying to install and TEMP and AppData
folders are not on same drive, setup fails

MikeHo: Add fallback to Caching the MSI.

RobMen: SFBUG:1680666 - Correctly modularize RemoveIniFile.DirProperites.

PMarcu: Removing FragmentRef's

AaronSte: SFBUG:1675664 - Marking ComboBox value attribute as localizable.

PMarcu: Adding BinaryRef as a child of PatchFamily.

Benxing: Give warning when removing component from feature during the patch build.

PMarcu: Changing namespace keys in the VSExtension help tables to not be modularized.

PMarcu: Adding some targeted checks to patch transforms to catch possible error
conditions as early as possible.

AaronSte: Adding Visual Studio Codename Orcas detection properties.

AaronSte: SFBUG:1687207 - Update Heat so DllRegisterServer captures will work
on Windows Vista from an elevated cmd prompt.

PMarcu: Fix for DocCompiler to handle ref attributes in attribute definitions under
elements.

BobArnso: Resize and combine some controls to better fit localized strings
(affects all UI sets, dialogs UserExit, OutOfRbDiskDlg, OutOfDiskDlg)

Benxing: Skipping unreal tables when binding transforms to improve patch build
perfomance.
Adding active substorage into binder extension to give the ability to
access corresponding transform information.

JRock: SFBUG:1673425 - Cannot access the VS menu using the alt key
SFBUG:1576283 - Unable to enter 'c' or 'm' in proj properties screen fields
SFBUG:1570392 - Project Designer - Index was outside the bounds of the array
SFBUG:1566296 - Setting 'Cultures' field in project properties has no effect
SFBUG:1576287 - Modifying project properties does not force rebuild

Benxing: Ignore rows without section id in ReduceTransform.

MikeHo: Allow for multiple files to be extracted from chainer.

AaronSte: Adding Visual Studio Team Test project system detection properties.

FGrohn: Support for PubCA in WiX v3.

HeathS: Added extension support to the Validator.
Exposed extension support for the Validator through light.exe.
Exposed extension support for the Validator through smoke.exe.
Exposed multiple .cub file support through smoke.exe.

HeathS: Exposed multiple .cub file support through light.exe.
Added a test for the Validator and multiple .cub support in light.exe.

MikeHo: Add error messages for Windows Installer service can't start or Install
blocked by system policy

BobArnso: Add WixQueryOsInfo CA to detect system suite info and "special
folders" as properties over and above the MSI set

BobArnso: Default to removing library rows from decompiled output in
WixUtilExtension

PMarcu: Refactoring patch buld system to use Pyro instead of Light for filtering
and binding. Other target patch specific bug fixes are in the mix as well.

PMarcu: Adding more command line options to Pyro, specifically the ones that
provide settings for the binder.

PMarcu: Adding error and warning preprocessor instructions.

PMarcu: Fixing an exception thrown when Dark is run and no extensions are defined
in the config file or the config file is absent.
Also, bug where Customtable columns that are not foreign keys have the
keytable attribute defined on the column as keytable="". This results in
an invalid table reference to "".

PMarcu: Switching order on Pyro commandline.

Jordanf: Adding support to WixUnit for Pyro. Fixing the qtest patch.minorupgrade
to use Pyro.

BrianRe: Added code to detect a namespace prefix that is already in use and if
so it will append it with its' position within the "duplicates" for
that specific prefix; eg. sql, sql2, sql3, and so on.

RobMen: Add support for PerformanceCounters (including managed code).

FGrohn: Added COM+ and MSMQ extensions to the zip file.

FGrohn: Move to Windows Vista SDK.

JRock: SFBUG:1566807 - Display full version in about dialog.
SFBUG:1697089 - MSBuild WiX taskscannot resolve WiX tool paths automatically
SFBUG:1689830 - Error when using Wix3 when not installed on C drive

Benxing: Handle Sequence tables when building the patch.
Don't allow empty patch.

HeathS: Added an extension for installing PowerShell snap-ins.

PMarcu: Adding Melt as a tool to decompile MSM's to ComponentGroups.

PMarcu: Adding warning for when a non keypath file is updated and the keypath file
of that component is not.

Benxing: Adding information into _SummaryInformation table for patch build.

PMarcu: Adding a check to assure no duplicate fragment Id's exist.

JKuhne: Fixing a GC issue with the Validator. (Locals are rooted until last reference, not
end of scope.)

BobArnso: Add DiskId attribute to Directory and DirectoryRef to provide default
DiskId for contained components and files.

MikeHo: Fix language matching in Setup.exe

PMarcu: Making sdut and tsa defaults when passing xo to light.exe

AaronSte: Adding more CSIDL values to WixQueryOsDirs custom action

RobMen: Reverse integrate WiX v2 CustomAction fixes.

BobArnso: Add element-extensibility points to Directory and DirectoryRef.

Jordanf: Add a new test for preprocessor .

Mikeho: Add NewFolder UIText element.

AaronSte: Adding custom actions to run devenv.exe /InstallVSTemplates to the
WixVSExtension

BobArnso: Pass directory ID to Directory and DirectoryRef extension elements.

RobMen: Introducing smart-cabbing.

RobMen: SFBUG:1707259 - fix nasty memory violation
HeathS: Added patch-specific property to identify client patches and
if they can be removed.

Mikeho: Fix manifests for setupbld.exe/setup.exe

JordanF: Add the -update option to automatically update a test. Make MSI/MSI
validation explicit in the tests.

JRock: Adding back project references to Votive V3! Actually, I'm adding them
to the wix.targets MSBuild file to be exact. Votive uses it, but you get
the goodness without using Votive also. Basically, this is the feature
where if you reference other projects in your Visual Studio solution,
you can reference the output of those projects from within your wixproj
project. For example, $(var.MyCSharpApp.TargetPath). This will work for
any managed project in Visual Studio (at least it does for VC#, VB, and
VC++ managed). You have to build from within Visual Studio or from the
command line. If you build just the .wixproj, then you won't get the
project variables defined.

SFBUG:1585281 - Add solution and project variables back to Votive v3

MikeHo: Fix support for more than 10 MSIs/MSTs

JRock: SFBUG:1588291 - Support response files for MSBuild candle/light/lit
tasks

BobArnso: Have heat generate a default ComponentGroup when harvesting
directories in a fragment.

JRock: SFBUG:1717966 - Solution Build Issues (build 2911)
When a wixproj is the only thing in the solution, the SolutionX
variables aren't defined when building within Visual Studio. This
is because normally C#/VB define these variables for us. The fix
is to define them ourself.

HeathS: SFBUG:1716160 - ICE03 string overflow error, xmlFile

RobMen: SFBUG:1716160 - fix string overflow error for XmlConfig

BrianRe: Fixing wix.xsd to use W3C recognized regular expressions.

PMarcu: Defaulting Media\@Source to a form of the patchId if not specified
when Media is a child of a patch.

RobMen: SFBUG:1724535 - correctly integrate a few more fixes from WiX v2 to
WiX v3.

JordanF: WixUnit now compares the transforms inside a patch when it is diffing
two patches. Previously, only the tables in the patch were compared.

AaronSte: Added documentation for properties and custom actions in the
WixVSExtension.

PMarcu: Fix for ServiceConfig CA's to call correct rollback entrypoint.

BMurri: Add support for bound wixouts/wixmsts to torch and pyro.

PMarcu: Fix for ServiceConfig CA's to call correct rollback entrypoint.

RobMen: Schema tweaks to enable simple references on FeatureRef and
FeatureGroupRef plus tweaks to enable floating Components.
AaronSte: Added documentation for Votive functionality.

AaronSte: Fixing variable resolution problem in WiX Product item template
in Votive.

PMarcu: Updating flags in XmlConfig to match with CA after the 2.0 to
3.0 integration.

JRock: Integrated the VS SDK 4.0 into Votive.

AaronSte: Merged WiX 2.0 documentation changes into 3.0. Updated instances
of deprecated src attributes in examples in the docs.

MikeHo: Add reinstall support & logging to Chainer + fix bug when using
transforms other than Chinese.

HeathS: SFBUG:1739868 - Pyro does not find .AllowRemoval property

PMarcu: Removing primary key from EnsureTable to support patching.
Fixing documentation for XmlConfig.

RobMen: SFBUG:1739194 - Preserve whitespace when using XmlFile or XmlConfig

JKuhne: Fix a GC related bug in the cab enumeration callback. (Cab.WixEnumerateCab.Enumerate)


--- NEW FILE: cppartrolesched.h ---
#pragma once
//-------------------------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
//
//
//
// COM+ partition role functions for CustomActions
//
//-------------------------------------------------------------------------------------------------


// structs

struct CPI_PARTITION_ROLE
{
WCHAR wzKey[MAX_DARWIN_KEY + 1];
WCHAR wzName[MAX_DARWIN_COLUMN + 1];

CPI_PARTITION* pPartition;

ICatalogCollection* piUsersColl;

CPI_PARTITION_ROLE* pNext;
};

struct CPI_PARTITION_ROLE_LIST
{
CPI_PARTITION_ROLE* pFirst;
};

struct CPI_USER_IN_PARTITION_ROLE
{
WCHAR wzKey[MAX_DARWIN_KEY + 1];
LPWSTR pwzAccount;

INSTALLSTATE isInstalled, isAction;

CPI_PARTITION_ROLE* pPartitionRole;

CPI_USER_IN_PARTITION_ROLE* pNext;
};

struct CPI_USER_IN_PARTITION_ROLE_LIST
{
CPI_USER_IN_PARTITION_ROLE* pFirst;

int iInstallCount;
int iUninstallCount;
};


// function prototypes

void CpiPartitionRoleListFree(
CPI_PARTITION_ROLE_LIST* pList
);
HRESULT CpiPartitionRolesRead(
CPI_PARTITION_LIST* pPartList,
CPI_PARTITION_ROLE_LIST* pPartRoleList
);
HRESULT CpiPartitionRoleFindByKey(
CPI_PARTITION_ROLE_LIST* pList,
LPCWSTR pwzKey,
CPI_PARTITION_ROLE** ppPartRole
);

void CpiUserInPartitionRoleListFree(
CPI_USER_IN_PARTITION_ROLE_LIST* pList
);
HRESULT CpiUsersInPartitionRolesRead(
CPI_PARTITION_ROLE_LIST* pPartRoleList,
CPI_USER_IN_PARTITION_ROLE_LIST* pUsrInPartRoleList
);
HRESULT CpiUsersInPartitionRolesInstall(
CPI_USER_IN_PARTITION_ROLE_LIST* pList,
int iRunMode,
LPWSTR* ppwzActionData,
int* piProgress
);
HRESULT CpiUsersInPartitionRolesUninstall(
CPI_USER_IN_PARTITION_ROLE_LIST* pList,
int iRunMode,
LPWSTR* ppwzActionData,
int* piProgress
);

--- NEW FILE: cpappsched.cpp ---
//-------------------------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
//
//
//
// COM+ application functions for CustomActions
//
//-------------------------------------------------------------------------------------------------

#include "precomp.h"


// sql queries

LPCWSTR vcsApplicationQuery =
L"SELECT `Application`, `Component_`, `Partition_`, `Id`, `Name` FROM `ComPlusApplication`";
enum eApplicationQuery { aqApplication = 1, aqComponent, aqPartition, aqID, aqName };

LPCWSTR vcsApplicationPropertyQuery =
L"SELECT `Name`, `Value` FROM `ComPlusApplicationProperty` WHERE `Application_` = ?";


// property definitions

CPI_PROPERTY_DEFINITION pdlApplicationProperties[] =
{
{L"3GigSupportEnabled", cpptBoolean, 500},
{L"AccessChecksLevel", cpptInteger, 500},
{L"Activation", cpptInteger, 500},
{L"ApplicationAccessChecksEnabled", cpptBoolean, 500},
{L"ApplicationDirectory", cpptString, 501},
{L"Authentication", cpptInteger, 500},
{L"AuthenticationCapability", cpptInteger, 500},
{L"Changeable", cpptBoolean, 500},
{L"CommandLine", cpptString, 500},
{L"ConcurrentApps", cpptInteger, 501},
{L"CreatedBy", cpptString, 500},
{L"CRMEnabled", cpptBoolean, 500},
{L"CRMLogFile", cpptString, 500},
{L"Deleteable", cpptBoolean, 500},
{L"Description", cpptString, 500},
{L"DumpEnabled", cpptBoolean, 501},
{L"DumpOnException", cpptBoolean, 501},
{L"DumpOnFailfast", cpptBoolean, 501},
{L"DumpPath", cpptString, 501},
{L"EventsEnabled", cpptBoolean, 500},
{L"Identity", cpptString, 500},
{L"ImpersonationLevel", cpptInteger, 500},
{L"IsEnabled", cpptBoolean, 501},
{L"MaxDumpCount", cpptInteger, 501},
{L"Password", cpptString, 500},
{L"QCAuthenticateMsgs", cpptInteger, 501},
{L"QCListenerMaxThreads", cpptInteger, 501},
{L"QueueListenerEnabled", cpptBoolean, 500},
{L"QueuingEnabled", cpptBoolean, 500},
{L"RecycleActivationLimit", cpptInteger, 501},
{L"RecycleCallLimit", cpptInteger, 501},
{L"RecycleExpirationTimeout", cpptInteger, 501},
{L"RecycleLifetimeLimit", cpptInteger, 501},
{L"RecycleMemoryLimit", cpptInteger, 501},
{L"Replicable", cpptBoolean, 501},
{L"RunForever", cpptBoolean, 500},
{L"ShutdownAfter", cpptInteger, 500},
{L"SoapActivated", cpptBoolean, 502},
{L"SoapBaseUrl", cpptString, 502},
{L"SoapMailTo", cpptString, 502},
{L"SoapVRoot", cpptString, 502},
{L"SRPEnabled", cpptBoolean, 501},
{L"SRPTrustLevel", cpptInteger, 501},
{NULL, cpptNone, 0}
};


// prototypes for private helper functions

static void FreeApplication(
CPI_APPLICATION* pItm
);
static HRESULT FindObjectForApplication(
CPI_APPLICATION* pItm,
BOOL fFindId,
BOOL fFindName,
ICatalogObject** ppiAppObj
);
static HRESULT AddApplicationToActionData(
CPI_APPLICATION* pItm,
int iActionType,
int iActionCost,
LPWSTR* ppwzActionData
);


// function definitions

void CpiApplicationListFree(
CPI_APPLICATION_LIST* pList
)
{
CPI_APPLICATION* pItm = pList->pFirst;

while (pItm)
{
CPI_APPLICATION* pDelete = pItm;
pItm = pItm->pNext;
FreeApplication(pDelete);
}
}

HRESULT CpiApplicationsRead(
CPI_PARTITION_LIST* pPartList,
CPI_APPLICATION_LIST* pAppList
)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;

int iVersionNT = 0;

PMSIHANDLE hView, hRec;

CPI_APPLICATION* pItm = NULL;
LPWSTR pwzData = NULL;

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

// loop through all applications
hr = WcaOpenExecuteView(vcsApplicationQuery, &hView);
ExitOnFailure(hr, "Failed to execute view on ComPlusApplication table");

while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
{
// create entry
pItm = (CPI_APPLICATION*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_APPLICATION));
if (!pItm)
ExitFunction1(hr = E_OUTOFMEMORY);

// get key
hr = WcaGetRecordString(hRec, aqApplication, &pwzData);
ExitOnFailure(hr, "Failed to get key");
StringCchCopyW(pItm->wzKey, countof(pItm->wzKey), pwzData);

// get component
hr = WcaGetRecordString(hRec, aqComponent, &pwzData);
ExitOnFailure(hr, "Failed to get component");

if (pwzData && *pwzData)
{
pItm->fHasComponent = TRUE;

// get component install state
er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &pItm->isInstalled, &pItm->isAction);
ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to get component state");
}

// get partition
if (502 pPartition);
ExitOnFailure1(hr, "Failed to find partition, key: %S", pwzData);
}
}

// get id
hr = WcaGetRecordFormattedString(hRec, aqID, &pwzData);
ExitOnFailure(hr, "Failed to get id");

if (pwzData && *pwzData)
{
hr = PcaGuidToRegFormat(pwzData, pItm->wzID, countof(pItm->wzID));
ExitOnFailure2(hr, "Failed to parse id guid value, key: %S, value: '%S'", pItm->wzKey, pwzData);
}

// get name
hr = WcaGetRecordFormattedString(hRec, aqName, &pwzData);
ExitOnFailure(hr, "Failed to get name");
StringCchCopyW(pItm->wzName, countof(pItm->wzName), pwzData);

// if application is a locater, either an id or a name must be provided
if (!pItm->fHasComponent && !*pItm->wzID && !*pItm->wzName)
ExitOnFailure1(hr = E_FAIL, "An application locater must have either an id or a name associated, key: %S", pItm->wzKey);

// if application is not a locater, an name must be provided
if (pItm->fHasComponent && !*pItm->wzName)
ExitOnFailure1(hr = E_FAIL, "An application must have a name associated, key: %S", pItm->wzKey);

// get properties
if (CpiTableExists(cptComPlusApplicationProperty) && pItm->fHasComponent)
{
hr = CpiPropertiesRead(vcsApplicationPropertyQuery, pItm->wzKey, pdlApplicationProperties, &pItm->pProperties, &pItm->iPropertyCount);
ExitOnFailure(hr, "Failed to get properties");
}

// set references & increment counters
if (pItm->fHasComponent)
{
if (WcaIsInstalling(pItm->isInstalled, pItm->isAction))
{
if (pItm->pPartition)
CpiPartitionAddReferenceInstall(pItm->pPartition);
pAppList->iInstallCount++;
}
if (WcaIsUninstalling(pItm->isInstalled, pItm->isAction))
{
if (pItm->pPartition)
CpiPartitionAddReferenceUninstall(pItm->pPartition);
pAppList->iUninstallCount++;
}
}

// add entry
if (pAppList->pFirst)
pItm->pNext = pAppList->pFirst;
pAppList->pFirst = pItm;
pItm = NULL;
}

if (E_NOMOREITEMS == hr)
hr = S_OK;

LExit:
// clean up
if (pItm)
FreeApplication(pItm);

ReleaseStr(pwzData);

return hr;
}

HRESULT CpiApplicationsVerifyInstall(
CPI_APPLICATION_LIST* pList
)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;

ICatalogObject* piAppObj = NULL;

for (CPI_APPLICATION* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
{
// referenced locaters or applications that are being installed
if (!pItm->fReferencedForInstall && !(pItm->fHasComponent && WcaIsInstalling(pItm->isInstalled, pItm->isAction)))
continue;

// if the application is referensed and is not a locater, it must be installed
if (pItm->fReferencedForInstall && pItm->fHasComponent && !CpiWillBeInstalled(pItm->isInstalled, pItm->isAction))
MessageExitOnFailure1(hr = E_FAIL, msierrComPlusApplicationDependency, "An application is used by another entity being installed, but is not installed itself, key: %S", pItm->wzKey);

// application is supposed to exist
if (!pItm->fHasComponent || CpiIsInstalled(pItm->isInstalled))
{
// get collection object for application
hr = FindObjectForApplication(pItm, 0 != *pItm->wzID, 0 == *pItm->wzID, &piAppObj);
ExitOnFailure(hr, "Failed to find collection object for application");

// if the application was found
if (S_OK == hr)
{
// if we don't have an id, copy id from object
if (!*pItm->wzID)
{
hr = CpiGetKeyForObject(piAppObj, pItm->wzID, countof(pItm->wzID));
ExitOnFailure(hr, "Failed to get id");
}
}

// if the application was not found
else
{
// if the application is a locater, this is an error
if (!pItm->fHasComponent)
MessageExitOnFailure1(hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND), msierrComPlusApplicationNotFound, "An application required by this installation was not found, key: %S", pItm->wzKey);

// create a new id if one is missing
if (!*pItm->wzID)
{
hr = CpiCreateId(pItm->wzID, countof(pItm->wzID));
ExitOnFailure(hr, "Failed to create id");
}
}
}

// application is supposed to be created
else
{
// check for conflicts
do {
if (*pItm->wzID)
{
// find applications with conflicting id
hr = FindObjectForApplication(pItm, TRUE, FALSE, &piAppObj);
ExitOnFailure(hr, "Failed to find collection object for application");

if (S_FALSE == hr)
{
// find applications with conflicting name
hr = FindObjectForApplication(pItm, FALSE, TRUE, &piAppObj);
ExitOnFailure(hr, "Failed to find collection object for application");

if (S_OK == hr)
// "A application with a conflictiong name exists. retry cancel"
er = WcaErrorMessage(msierrComPlusApplicationNameConflict, hr, INSTALLMESSAGE_ERROR | MB_RETRYCANCEL, 0);
else
break; // no conflicting entry found, break loop
}
else
// "A application with a conflicting id exists. abort retry ignore"
er = WcaErrorMessage(msierrComPlusApplicationIdConflict, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0);
}
else
{
// find applications with conflicting name
hr = FindObjectForApplication(pItm, FALSE, TRUE, &piAppObj);
ExitOnFailure(hr, "Failed to find collection object for application");

if (S_OK == hr)
// "A subscription with a conflictiong name exists. abort retry ignore"
er = WcaErrorMessage(msierrComPlusApplicationNameConflict, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0);
else
break; // no conflicting entry found, break loop
}

switch (er)
{
case IDCANCEL:
case IDABORT:
ExitOnFailure1(hr = E_FAIL, "An application with a conflictiong name or id exists, key: %S", pItm->wzKey);
break;
case IDRETRY:
break;
case IDIGNORE:
// if we don't have an id, copy id from object
if (!*pItm->wzID)
{
hr = CpiGetKeyForObject(piAppObj, pItm->wzID, countof(pItm->wzID));
ExitOnFailure(hr, "Failed to get id");
}
hr = S_FALSE; // indicate that this is not a conflict
}
} while (S_OK == hr); // hr = S_FALSE if we don't have any conflicts

// create a new id if one is missing
if (!*pItm->wzID)
{
hr = CpiCreateId(pItm->wzID, countof(pItm->wzID));
ExitOnFailure(hr, "Failed to create id");
}
}

// clean up
ReleaseNullObject(piAppObj);
}

hr = S_OK;

LExit:
// clean up
ReleaseObject(piAppObj);

return hr;
}

HRESULT CpiApplicationsVerifyUninstall(
CPI_APPLICATION_LIST* pList
)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;

ICatalogObject* piAppObj = NULL;

for (CPI_APPLICATION* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
{
// referenced locaters or applications that are being installed
if (!pItm->fReferencedForUninstall && !(pItm->fHasComponent && WcaIsUninstalling(pItm->isInstalled, pItm->isAction)))
continue;

// get collection object for application
hr = FindObjectForApplication(pItm, 0 != *pItm->wzID, 0 == *pItm->wzID, &piAppObj);
ExitOnFailure(hr, "Failed to find collection object for application");

// if the application was found
if (S_OK == hr)
{
// if we don't have an id, copy id from object
if (!*pItm->wzID)
{
hr = CpiGetKeyForObject(piAppObj, pItm->wzID, countof(pItm->wzID));
ExitOnFailure(hr, "Failed to get id");
}
}

// if the application was not found
else
{
pItm->fObjectNotFound = TRUE;
if (pItm->fHasComponent)
pList->iUninstallCount--; // elements with the fObjectNotFound flag set will not be scheduled for uninstall
}

// clean up
ReleaseNullObject(piAppObj);
}

hr = S_OK;

LExit:
// clean up
ReleaseObject(piAppObj);

return hr;
}

void CpiApplicationAddReferenceInstall(
CPI_APPLICATION* pItm
)
{
pItm->fReferencedForInstall = TRUE;
if (pItm->pPartition)
CpiPartitionAddReferenceInstall(pItm->pPartition);
}

void CpiApplicationAddReferenceUninstall(
CPI_APPLICATION* pItm
)
{
pItm->fReferencedForUninstall = TRUE;
if (pItm->pPartition)
CpiPartitionAddReferenceUninstall(pItm->pPartition);
}

HRESULT CpiApplicationsInstall(
CPI_APPLICATION_LIST* pList,
int iRunMode,
LPWSTR* ppwzActionData,
int* piProgress
)
{
HRESULT hr = S_OK;

int iActionType;

// add action text
hr = CpiAddActionTextToActionData(L"CreateComPlusApplications", ppwzActionData);
ExitOnFailure(hr, "Failed to add action text to custom action data");

// add applicaton count to action data
hr = WcaWriteIntegerToCaData(pList->iInstallCount, ppwzActionData);
ExitOnFailure(hr, "Failed to add count to custom action data");

// add applications to custom action data
for (CPI_APPLICATION* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
{
// applications that are being installed only
if (!pItm->fHasComponent || !WcaIsInstalling(pItm->isInstalled, pItm->isAction))
continue;

// action type
if (rmRollback == iRunMode)
{
if (CpiIsInstalled(pItm->isInstalled))
iActionType = atNoOp;
else
iActionType = atRemove;
}
else
iActionType = atCreate;

// add to action data
hr = AddApplicationToActionData(pItm, iActionType, COST_APPLICATION_CREATE, ppwzActionData);
ExitOnFailure1(hr, "Failed to add applicaton to custom action data, key: %S", pItm->wzKey);
}

// add progress tics
if (piProgress)
*piProgress += COST_APPLICATION_CREATE * pList->iInstallCount;

hr = S_OK;

LExit:
return hr;
}

HRESULT CpiApplicationsUninstall(
CPI_APPLICATION_LIST* pList,
int iRunMode,
LPWSTR* ppwzActionData,
int* piProgress
)
{
HRESULT hr = S_OK;

int iActionType;

// add action text
hr = CpiAddActionTextToActionData(L"RemoveComPlusApplications", ppwzActionData);
ExitOnFailure(hr, "Failed to add action text to custom action data");

// add applicaton count to action data
hr = WcaWriteIntegerToCaData(pList->iUninstallCount, ppwzActionData);
ExitOnFailure(hr, "Failed to add count to custom action data");

// add applications to custom action data
for (CPI_APPLICATION* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
{
// applications that are being uninstalled only
if (!pItm->fHasComponent || !WcaIsUninstalling(pItm->isInstalled, pItm->isAction))
continue;

// action type
if (rmRollback == iRunMode)
iActionType = atCreate;
else
iActionType = atRemove;

// add to action data
hr = AddApplicationToActionData(pItm, iActionType, COST_APPLICATION_DELETE, ppwzActionData);
ExitOnFailure1(hr, "Failed to add applicaton to custom action data, key: %S", pItm->wzKey);
}

// add progress tics
if (piProgress)
*piProgress += COST_APPLICATION_DELETE * pList->iUninstallCount;

hr = S_OK;

LExit:
return hr;
}

HRESULT CpiApplicationFindByKey(
CPI_APPLICATION_LIST* pList,
LPCWSTR pwzKey,
CPI_APPLICATION** ppApp
)
{
for (CPI_APPLICATION* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
{
if (0 == lstrcmpW(pItm->wzKey, pwzKey))
{
*ppApp = pItm;
return S_OK;
}
}

return S_FALSE;
}

HRESULT CpiGetRolesCollForApplication(
CPI_APPLICATION* pApp,
ICatalogCollection** ppiRolesColl
)
{
HRESULT hr = S_OK;

ICatalogCollection* piAppColl = NULL;
ICatalogObject* piAppObj = NULL;

// if a previous attempt to locate the collection object failed
if (pApp->fObjectNotFound)
ExitFunction1(hr = S_FALSE);

// get applications collection
if (!pApp->piRolesColl)
{
// get applications collection
if (pApp->pPartition)
hr = CpiGetApplicationsCollForPartition(pApp->pPartition, &piAppColl);
else
hr = CpiGetApplicationsCollection(&piAppColl);
ExitOnFailure(hr, "Failed to get applications collection");

if (S_FALSE == hr)
ExitFunction(); // exit with hr = S_FALSE

// find application object
hr = CpiFindCollectionObject(piAppColl, pApp->wzID, *pApp->wzID ? NULL : pApp->wzName, &piAppObj);
ExitOnFailure(hr, "Failed to find application object");

if (S_FALSE == hr)
ExitFunction(); // exit with hr = S_FALSE

// get roles collection
hr = CpiGetCatalogCollection(piAppColl, piAppObj, L"Roles", &pApp->piRolesColl);
ExitOnFailure(hr, "Failed to get roles collection");
}

// return value
*ppiRolesColl = pApp->piRolesColl;
(*ppiRolesColl)->AddRef();

hr = S_OK;

LExit:
// clean up
ReleaseObject(piAppColl);
ReleaseObject(piAppObj);

return hr;
}

HRESULT CpiGetComponentsCollForApplication(
CPI_APPLICATION* pApp,
ICatalogCollection** ppiCompsColl
)
{
HRESULT hr = S_OK;

ICatalogCollection* piAppColl = NULL;
ICatalogObject* piAppObj = NULL;

// if a previous attempt to locate the collection object failed
if (pApp->fObjectNotFound)
ExitFunction1(hr = S_FALSE);

// get applications collection
if (!pApp->piCompsColl)
{
// get applications collection
if (pApp->pPartition)
hr = CpiGetApplicationsCollForPartition(pApp->pPartition, &piAppColl);
else
hr = CpiGetApplicationsCollection(&piAppColl);
ExitOnFailure(hr, "Failed to get applications collection");

if (S_FALSE == hr)
ExitFunction(); // exit with hr = S_FALSE

// find application object
hr = CpiFindCollectionObject(piAppColl, pApp->wzID, *pApp->wzID ? NULL : pApp->wzName, &piAppObj);
ExitOnFailure(hr, "Failed to find application object");

if (S_FALSE == hr)
ExitFunction(); // exit with hr = S_FALSE

// get roles collection
hr = CpiGetCatalogCollection(piAppColl, piAppObj, L"Components", &pApp->piCompsColl);
ExitOnFailure(hr, "Failed to get components collection");
}

// return value
*ppiCompsColl = pApp->piCompsColl;
(*ppiCompsColl)->AddRef();

hr = S_OK;

LExit:
// clean up
ReleaseObject(piAppColl);
ReleaseObject(piAppObj);

return hr;
}


// helper function definitions

static void FreeApplication(
CPI_APPLICATION* pItm
)
{
if (pItm->pProperties)
CpiPropertiesFreeList(pItm->pProperties);

ReleaseObject(pItm->piRolesColl);
ReleaseObject(pItm->piCompsColl);

::HeapFree(::GetProcessHeap(), 0, pItm);
}

static HRESULT FindObjectForApplication(
CPI_APPLICATION* pItm,
BOOL fFindId,
BOOL fFindName,
ICatalogObject** ppiAppObj
)
{
HRESULT hr = S_OK;

ICatalogCollection* piAppColl = NULL;

// get applications collection
if (pItm->pPartition)
hr = CpiGetApplicationsCollForPartition(pItm->pPartition, &piAppColl);
else
hr = CpiGetApplicationsCollection(&piAppColl);
ExitOnFailure(hr, "Failed to get applications collection");

if (S_FALSE == hr)
ExitFunction(); // exit with hr = S_FALSE

// find application object
hr = CpiFindCollectionObject(piAppColl, fFindId ? pItm->wzID : NULL, fFindName ? pItm->wzName : NULL, ppiAppObj);
ExitOnFailure(hr, "Failed to find application object");

// exit with hr from CpiFindCollectionObject()

LExit:
// clean up
ReleaseObject(piAppColl);

return hr;
}

static HRESULT AddApplicationToActionData(
CPI_APPLICATION* pItm,
int iActionType,
int iActionCost,
LPWSTR* ppwzActionData
)
{
HRESULT hr = S_OK;

// add action information to custom action data
hr = WcaWriteIntegerToCaData(iActionType, ppwzActionData);
ExitOnFailure(hr, "Failed to add action type to custom action data");
hr = WcaWriteIntegerToCaData(iActionCost, ppwzActionData);
ExitOnFailure(hr, "Failed to add action cost to custom action data");

// add application information to custom action data
hr = WcaWriteStringToCaData(pItm->wzKey, ppwzActionData);
ExitOnFailure(hr, "Failed to add application key to custom action data");
hr = WcaWriteStringToCaData(pItm->wzID, ppwzActionData);
ExitOnFailure(hr, "Failed to add application id to custom action data");
hr = WcaWriteStringToCaData(pItm->wzName, ppwzActionData);
ExitOnFailure(hr, "Failed to add application name to custom action data");

// add partition information to custom action data
hr = WcaWriteStringToCaData(pItm->pPartition ? pItm->pPartition->wzID : L"", ppwzActionData);
ExitOnFailure(hr, "Failed to add partition id to custom action data");

// add properties to custom action data
hr = CpiAddPropertiesToActionData(atCreate == iActionType ? pItm->iPropertyCount : 0, pItm->pProperties, ppwzActionData);
ExitOnFailure(hr, "Failed to add properties to custom action data");

hr = S_OK;

LExit:
return hr;
}

--- NEW FILE: cpsched.rc ---
//--------------------------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
//
//
//
// WiX shim to native code resource file.
//
//--------------------------------------------------------------------------------------------------

#define VER_APP
#define VER_LANG_NEUTRAL
#define VER_ORIGINAL_FILENAME "cpsched.dll"
#define VER_INTERNAL_NAME "cpsched"
#define VER_PRODUCT_NAME "WiX COM+ Custom Actions"
#define VER_FILE_DESCRIPTION "WiX COM+ Custom Actions"
#include "wix.rc"

// Additional resources here

--- NEW FILE: cpasmsched.h ---
#pragma once
//-------------------------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
//
//
//
// COM+ assembly functions for CustomActions
//
//-------------------------------------------------------------------------------------------------


// constants

enum eAssemblyAttributes
{
aaEventClass = (1 wzName), pwzData);

// if partition is a locater, either an id or a name must be provided
if (!pItm->fHasComponent && !*pItm->wzID && !*pItm->wzName)
ExitOnFailure1(hr = E_FAIL, "A partition locater must have either an id or a name associated, key: %S", pItm->wzKey);

// if partition is not a locater, an name must be provided
if (pItm->fHasComponent && !*pItm->wzName)
ExitOnFailure1(hr = E_FAIL, "A partition must have a name associated, key: %S", pItm->wzKey);

// get properties
if (CpiTableExists(cptComPlusPartitionProperty) && pItm->fHasComponent)
{
hr = CpiPropertiesRead(vcsPartitionPropertyQuery, pItm->wzKey, pdlPartitionProperties, &pItm->pProperties, &pItm->iPropertyCount);
ExitOnFailure(hr, "Failed to get properties");
}

// increment counters
if (pItm->fHasComponent && WcaIsInstalling(pItm->isInstalled, pItm->isAction))
pPartList->iInstallCount++;
if (pItm->fHasComponent && WcaIsUninstalling(pItm->isInstalled, pItm->isAction))
pPartList->iUninstallCount++;

// add entry
if (pPartList->pFirst)
pItm->pNext = pPartList->pFirst;
pPartList->pFirst = pItm;
pItm = NULL;
}

if (E_NOMOREITEMS == hr)
hr = S_OK;

LExit:
// clean up
if (pItm)
FreePartition(pItm);

ReleaseStr(pwzData);

return hr;
}

HRESULT CpiPartitionsVerifyInstall(
CPI_PARTITION_LIST* pList
)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;

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

for (CPI_PARTITION* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
{
// referenced locaters or partitions that are being installed
if (!pItm->fReferencedForInstall && !(pItm->fHasComponent && WcaIsInstalling(pItm->isInstalled, pItm->isAction)))
continue;

// if the partition is referensed and is not a locater, it must be installed
if (pItm->fReferencedForInstall && pItm->fHasComponent && !CpiWillBeInstalled(pItm->isInstalled, pItm->isAction))
MessageExitOnFailure1(hr = E_FAIL, msierrComPlusPartitionDependency, "A partition is used by another entity being installed, but is not installed itself, key: %S", pItm->wzKey);

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

// partition is supposed to exist
if (!pItm->fHasComponent || CpiIsInstalled(pItm->isInstalled))
{
// get collection object for partition
hr = CpiFindCollectionObject(piPartColl, pItm->wzID, *pItm->wzID ? NULL : pItm->wzName, &piPartObj);
ExitOnFailure(hr, "Failed to find collection object for partition");

// if the partition was found
if (S_OK == hr)
{
// if we don't have an id, copy id from object
if (!*pItm->wzID)
{
hr = CpiGetKeyForObject(piPartObj, pItm->wzID, countof(pItm->wzID));
ExitOnFailure(hr, "Failed to get id");
}
}

// if the partition was not found
else
{
// if the application is a locater, this is an error
if (!pItm->fHasComponent)
MessageExitOnFailure1(hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND), msierrComPlusPartitionNotFound, "A partition required by this installation was not found, key: %S", pItm->wzKey);

// create a new id if one is missing
if (!*pItm->wzID)
{
hr = CpiCreateId(pItm->wzID, countof(pItm->wzID));
ExitOnFailure(hr, "Failed to create id");
}
}
}

// partition is supposed to be created
else
{
// check for conflicts
do {
if (*pItm->wzID)
{
// find partitions with conflicting id
hr = CpiFindCollectionObject(piPartColl, pItm->wzID, NULL, &piPartObj);
ExitOnFailure(hr, "Failed to find collection object for partition");

if (S_FALSE == hr)
{
// find partitions with conflicting name
hr = CpiFindCollectionObject(piPartColl, NULL, pItm->wzName, &piPartObj);
ExitOnFailure(hr, "Failed to find collection object for partition");

if (S_OK == hr)
// "A partition with a conflictiong name exists. retry cancel"
er = WcaErrorMessage(msierrComPlusPartitionNameConflict, hr, INSTALLMESSAGE_ERROR | MB_RETRYCANCEL, 0);
else
break; // no conflicting entry found, break loop
}
else
// "A partition with a conflicting id exists. abort retry ignore"
er = WcaErrorMessage(msierrComPlusPartitionIdConflict, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0);
}
else
{
// find partitions with conflicting name
hr = CpiFindCollectionObject(piPartColl, NULL, pItm->wzName, &piPartObj);
ExitOnFailure(hr, "Failed to find collection object for partition");

if (S_OK == hr)
// "A partition with a conflictiong name exists. abort retry ignore"
er = WcaErrorMessage(msierrComPlusPartitionNameConflict, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0);
else
break; // no conflicting entry found, break loop
}

switch (er)
{
case IDCANCEL:
case IDABORT:
ExitOnFailure1(hr = E_FAIL, "A partition with a conflictiong name or id exists, key: %S", pItm->wzKey);
break;
case IDRETRY:
break;
case IDIGNORE:
// if we don't have an id, copy id from object
if (!*pItm->wzID)
{
hr = CpiGetKeyForObject(piPartObj, pItm->wzID, countof(pItm->wzID));
ExitOnFailure(hr, "Failed to get id");
}
hr = S_FALSE; // indicate that this is not a conflict
}
} while (S_OK == hr); // hr = S_FALSE if we don't have any conflicts

// create a new id if one is missing
if (!*pItm->wzID)
{
hr = CpiCreateId(pItm->wzID, countof(pItm->wzID));
ExitOnFailure(hr, "Failed to create id");
}
}

// clean up
ReleaseNullObject(piPartObj);
}

hr = S_OK;

LExit:
// clean up
ReleaseObject(piPartColl);
ReleaseObject(piPartObj);

return hr;
}

HRESULT CpiPartitionsVerifyUninstall(
CPI_PARTITION_LIST* pList
)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;

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

for (CPI_PARTITION* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
{
// referenced locaters or partitions that are being uninstalled
if (!pItm->fReferencedForUninstall && !(pItm->fHasComponent && WcaIsUninstalling(pItm->isInstalled, pItm->isAction)))
continue;

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

// get collection object for partition
hr = CpiFindCollectionObject(piPartColl, pItm->wzID, *pItm->wzID ? NULL : pItm->wzName, &piPartObj);
ExitOnFailure(hr, "Failed to find collection object for partition");

// if the partition was found
if (S_OK == hr)
{
// if we don't have an id, copy id from object
if (!*pItm->wzID)
{
hr = CpiGetKeyForObject(piPartObj, pItm->wzID, countof(pItm->wzID));
ExitOnFailure(hr, "Failed to get id");
}
}

// if the partition was not found
else
{
pItm->fObjectNotFound = TRUE;
if (pItm->fHasComponent)
pList->iUninstallCount--; // elements with the fObjectNotFound flag set will not be scheduled for uninstall
}

// clean up
ReleaseNullObject(piPartObj);
}

hr = S_OK;

LExit:
// clean up
ReleaseObject(piPartColl);
ReleaseObject(piPartObj);

return hr;
}

void CpiPartitionAddReferenceInstall(
CPI_PARTITION* pItm
)
{
pItm->fReferencedForInstall = TRUE;
}

void CpiPartitionAddReferenceUninstall(
CPI_PARTITION* pItm
)
{
pItm->fReferencedForUninstall = TRUE;
}

HRESULT CpiPartitionsInstall(
CPI_PARTITION_LIST* pList,
int iRunMode,
LPWSTR* ppwzActionData,
int* piProgress
)
{
HRESULT hr = S_OK;

int iActionType;

// add action text
hr = CpiAddActionTextToActionData(L"CreateComPlusPartitions", ppwzActionData);
ExitOnFailure(hr, "Failed to add action text to custom action data");

// add partition count to action data
hr = WcaWriteIntegerToCaData(pList->iInstallCount, ppwzActionData);
ExitOnFailure(hr, "Failed to add count to custom action data");

// add applications to custom action data
for (CPI_PARTITION* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
{
// partitions that are being installed only
if (!pItm->fHasComponent || !WcaIsInstalling(pItm->isInstalled, pItm->isAction))
continue;

// action type
if (rmRollback == iRunMode)
{
if (CpiIsInstalled(pItm->isInstalled))
iActionType = atNoOp;
else
iActionType = atRemove;
}
else
iActionType = atCreate;

// add to action data
hr = AddPartitionToActionData(pItm, iActionType, COST_PARTITION_CREATE, ppwzActionData);
ExitOnFailure1(hr, "Failed to add partition to custom action data, key: %S", pItm->wzKey);
}

// add progress tics
if (piProgress)
*piProgress += COST_PARTITION_CREATE * pList->iInstallCount;

hr = S_OK;

LExit:
return hr;
}

HRESULT CpiPartitionsUninstall(
CPI_PARTITION_LIST* pList,
int iRunMode,
LPWSTR* ppwzActionData,
int* piProgress
)
{
HRESULT hr = S_OK;

int iActionType;

// add action text
hr = CpiAddActionTextToActionData(L"RemoveComPlusPartitions", ppwzActionData);
ExitOnFailure(hr, "Failed to add action text to custom action data");

// add partition count to action data
hr = WcaWriteIntegerToCaData(pList->iUninstallCount, ppwzActionData);
ExitOnFailure(hr, "Failed to add count to custom action data");

// add partitions to custom action data
for (CPI_PARTITION* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
{
// partitions that are being uninstalled only
if (!pItm->fHasComponent || pItm->fObjectNotFound || !WcaIsUninstalling(pItm->isInstalled, pItm->isAction))
continue;

// action type
if (rmRollback == iRunMode)
iActionType = atCreate;
else
iActionType = atRemove;

// add to action data
hr = AddPartitionToActionData(pItm, iActionType, COST_PARTITION_DELETE, ppwzActionData);
ExitOnFailure1(hr, "Failed to add partition to custom action data, key:", pItm->wzKey);
}

// add progress tics
if (piProgress)
*piProgress += COST_PARTITION_DELETE * pList->iUninstallCount;

hr = S_OK;

LExit:
return hr;
}

HRESULT CpiPartitionFindByKey(
CPI_PARTITION_LIST* pList,
LPCWSTR wzKey,
CPI_PARTITION** ppItm
)
{
for (CPI_PARTITION* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
{
if (0 == lstrcmpW(pItm->wzKey, wzKey))
{
*ppItm = pItm;
return S_OK;
}
}

return S_FALSE;
}

HRESULT CpiGetApplicationsCollForPartition(
CPI_PARTITION* pPart,
ICatalogCollection** ppiAppColl
)
{
HRESULT hr = S_OK;

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

// if a previous attempt to locate the collection object failed
if (pPart->fObjectNotFound)
ExitFunction1(hr = S_FALSE);

// get applications collection
if (!pPart->piApplicationsColl)
{
// get partitions collection from catalog
hr = CpiGetPartitionsCollection(&piPartColl);
ExitOnFailure(hr, "Failed to get partitions collection");

// find application object
hr = CpiFindCollectionObject(piPartColl, pPart->wzID, *pPart->wzID ? NULL : pPart->wzName, &piPartObj);
ExitOnFailure(hr, "Failed to find partition object");

if (S_FALSE == hr)
{
pPart->fObjectNotFound = TRUE;
ExitFunction(); // exit with hr = S_FALSE
}

// get roles collection
hr = CpiGetCatalogCollection(piPartColl, piPartObj, L"Applications", &pPart->piApplicationsColl);
ExitOnFailure(hr, "Failed to get applications collection");
}

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

hr = S_OK;

LExit:
// clean up
ReleaseObject(piPartColl);
ReleaseObject(piPartObj);

return hr;
}

HRESULT CpiGetRolesCollForPartition(
CPI_PARTITION* pPart,
ICatalogCollection** ppiRolesColl
)
{
HRESULT hr = S_OK;

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

// if a previous attempt to locate the collection object failed
if (pPart->fObjectNotFound)
ExitFunction1(hr = S_FALSE);

// get applications collection
if (!pPart->piRolesColl)
{
// get partitions collection from catalog
hr = CpiGetPartitionsCollection(&piPartColl);
ExitOnFailure(hr, "Failed to get partitions collection");

// find partition object
hr = CpiFindCollectionObject(piPartColl, pPart->wzID, *pPart->wzID ? NULL : pPart->wzName, &piPartObj);
ExitOnFailure(hr, "Failed to find partition object");

if (S_FALSE == hr)
ExitFunction(); // exit with hr = S_FALSE

// get roles collection
hr = CpiGetCatalogCollection(piPartColl, piPartObj, L"RolesForPartition", &pPart->piRolesColl);
ExitOnFailure(hr, "Failed to get roles collection");
}

// return value
*ppiRolesColl = pPart->piRolesColl;
(*ppiRolesColl)->AddRef();

hr = S_OK;

LExit:
// clean up
ReleaseObject(piPartColl);
ReleaseObject(piPartObj);

return hr;
}

void CpiPartitionUserListFree(
CPI_PARTITION_USER_LIST* pList
)
{
CPI_PARTITION_USER* pItm = pList->pFirst;

while (pItm)
{
CPI_PARTITION_USER* pDelete = pItm;
pItm = pItm->pNext;
FreePartitionUser(pDelete);
}
}

HRESULT CpiPartitionUsersRead(
CPI_PARTITION_LIST* pPartList,
CPI_PARTITION_USER_LIST* pPartUsrList
)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;

PMSIHANDLE hView, hRec;

CPI_PARTITION_USER* pItm = NULL;
LPWSTR pwzData = NULL;
LPWSTR pwzDomain = NULL;
LPWSTR pwzName = NULL;

// loop through all partition users
hr = WcaOpenExecuteView(vcsPartitionUserQuery, &hView);
ExitOnFailure(hr, "Failed to execute view on ComPlusPartitionUser table");

while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
{
// create entry
pItm = (CPI_PARTITION_USER*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_PARTITION_USER));
if (!pItm)
ExitFunction1(hr = E_OUTOFMEMORY);

// get key
hr = WcaGetRecordString(hRec, puqPartitionUser, &pwzData);
ExitOnFailure(hr, "Failed to get key");
StringCchCopyW(pItm->wzKey, countof(pItm->wzKey), pwzData);

// get component
hr = WcaGetRecordString(hRec, puqComponent, &pwzData);
ExitOnFailure(hr, "Failed to get component");

// get component install state
er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &pItm->isInstalled, &pItm->isAction);
ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to get component state");

// get partition
hr = WcaGetRecordString(hRec, puqPartition, &pwzData);
ExitOnFailure(hr, "Failed to get partition");

hr = CpiPartitionFindByKey(pPartList, pwzData, &pItm->pPartition);
if (S_FALSE == hr)
hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
ExitOnFailure1(hr, "Failed to find partition, key: %S", pwzData);

// get user domain
hr = WcaGetRecordFormattedString(hRec, puqDomain, &pwzDomain);
ExitOnFailure(hr, "Failed to get user domain");

// get user name
hr = WcaGetRecordFormattedString(hRec, puqName, &pwzName);
ExitOnFailure(hr, "Failed to get user name");

// build account name
hr = CpiBuildAccountName(pwzDomain, pwzName, &pItm->pwzAccount);
ExitOnFailure(hr, "Failed to build account name");

// set references & increment counters
if (WcaIsInstalling(pItm->isInstalled, pItm->isAction))
{
pItm->pPartition->fReferencedForInstall = TRUE;
pPartUsrList->iInstallCount++;
}
if (WcaIsUninstalling(pItm->isInstalled, pItm->isAction))
{
pItm->pPartition->fReferencedForUninstall = TRUE;
pPartUsrList->iUninstallCount++;
}

// add entry
if (pPartUsrList->pFirst)
pItm->pNext = pPartUsrList->pFirst;
pPartUsrList->pFirst = pItm;
pItm = NULL;
}

if (E_NOMOREITEMS == hr)
hr = S_OK;

LExit:
// clean up
if (pItm)
FreePartitionUser(pItm);

ReleaseStr(pwzData);
ReleaseStr(pwzDomain);
ReleaseStr(pwzName);

return hr;
}

HRESULT CpiPartitionUsersInstall(
CPI_PARTITION_USER_LIST* pList,
int iRunMode,
LPWSTR* ppwzActionData,
int* piProgress
)
{
HRESULT hr = S_OK;

int iActionType;

// add action text
hr = CpiAddActionTextToActionData(L"AddComPlusPartitionUsers", ppwzActionData);
ExitOnFailure(hr, "Failed to add action text to custom action data");

// add partition count to action data
hr = WcaWriteIntegerToCaData(pList->iInstallCount, ppwzActionData);
ExitOnFailure(hr, "Failed to add count to custom action data");

// add applications to custom action data
for (CPI_PARTITION_USER* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
{
// partitions that are being installed only
if (!WcaIsInstalling(pItm->isInstalled, pItm->isAction))
continue;

// action type
if (rmRollback == iRunMode)
{
if (CpiIsInstalled(pItm->isInstalled))
iActionType = atNoOp;
else
iActionType = atRemove;
}
else
iActionType = atCreate;

// add to action data
hr = AddPartitionUserToActionData(pItm, iActionType, COST_PARTITION_USER_CREATE, ppwzActionData);
ExitOnFailure1(hr, "Failed to add partition user to custom action data, key: %S", pItm->wzKey);
}

// add progress tics
if (piProgress)
*piProgress += COST_PARTITION_USER_CREATE * pList->iInstallCount;

hr = S_OK;

LExit:
return hr;
}

HRESULT CpiPartitionUsersUninstall(
CPI_PARTITION_USER_LIST* pList,
int iRunMode,
LPWSTR* ppwzActionData,
int* piProgress
)
{
HRESULT hr = S_OK;

int iActionType;

// add action text
hr = CpiAddActionTextToActionData(L"RemoveComPlusPartitionUsers", ppwzActionData);
ExitOnFailure(hr, "Failed to add action text to custom action data");

// add partition count to action data
hr = WcaWriteIntegerToCaData(pList->iUninstallCount, ppwzActionData);
ExitOnFailure(hr, "Failed to add count to custom action data");

// add partitions to custom action data
for (CPI_PARTITION_USER* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
{
// partitions that are being uninstalled only
if (!WcaIsUninstalling(pItm->isInstalled, pItm->isAction))
continue;

// action type
if (rmRollback == iRunMode)
iActionType = atCreate;
else
iActionType = atRemove;

// add to action data
hr = AddPartitionUserToActionData(pItm, iActionType, COST_PARTITION_USER_DELETE, ppwzActionData);
ExitOnFailure1(hr, "Failed to add partition user to custom action data, key: %S", pItm->wzKey);
}

// add progress tics
if (piProgress)
*piProgress += COST_PARTITION_USER_DELETE * pList->iUninstallCount;

hr = S_OK;

LExit:
return hr;
}


// helper function definitions

static void FreePartition(
CPI_PARTITION* pItm
)
{
if (pItm->pProperties)
CpiPropertiesFreeList(pItm->pProperties);

ReleaseObject(pItm->piApplicationsColl);
ReleaseObject(pItm->piRolesColl);

::HeapFree(::GetProcessHeap(), 0, pItm);
}

static void FreePartitionUser(
CPI_PARTITION_USER* pItm
)
{
ReleaseStr(pItm->pwzAccount);

::HeapFree(::GetProcessHeap(), 0, pItm);
}

static HRESULT AddPartitionToActionData(
CPI_PARTITION* pItm,
int iActionType,
int iActionCost,
LPWSTR* ppwzActionData
)
{
HRESULT hr = S_OK;

// add action information to custom action data
hr = WcaWriteIntegerToCaData(iActionType, ppwzActionData);
ExitOnFailure(hr, "Failed to add action type to custom action data");
hr = WcaWriteIntegerToCaData(iActionCost, ppwzActionData);
ExitOnFailure(hr, "Failed to add action cost to custom action data");

// add partition information to custom action data
hr = WcaWriteStringToCaData(pItm->wzKey, ppwzActionData);
ExitOnFailure(hr, "Failed to add partition key to custom action data");
hr = WcaWriteStringToCaData(pItm->wzID, ppwzActionData);
ExitOnFailure(hr, "Failed to add partition id to custom action data");
hr = WcaWriteStringToCaData(pItm->wzName, ppwzActionData);
ExitOnFailure(hr, "Failed to add partition name to custom action data");

// add properties to custom action data
hr = CpiAddPropertiesToActionData(atCreate == iActionType ? pItm->iPropertyCount : 0, pItm->pProperties, ppwzActionData);
ExitOnFailure(hr, "Failed to add properties to custom action data");

hr = S_OK;

LExit:
return hr;
}

static HRESULT AddPartitionUserToActionData(
CPI_PARTITION_USER* pItm,
int iActionType,
int iActionCost,
LPWSTR* ppwzActionData
)
{
HRESULT hr = S_OK;

// add action information to custom action data
hr = WcaWriteIntegerToCaData(iActionType, ppwzActionData);
ExitOnFailure(hr, "Failed to add action type to custom action data");
hr = WcaWriteIntegerToCaData(iActionCost, ppwzActionData);
ExitOnFailure(hr, "Failed to add action cost to custom action data");

// add partition user information to custom action data
hr = WcaWriteStringToCaData(pItm->wzKey, ppwzActionData);
ExitOnFailure(hr, "Failed to add partition user key to custom action data");
hr = WcaWriteStringToCaData(pItm->pwzAccount, ppwzActionData);
ExitOnFailure(hr, "Failed to add user account to custom action data");

// add partition information to custom action data
hr = WcaWriteStringToCaData(atCreate == iActionType ? pItm->pPartition->wzID : L"", ppwzActionData);
ExitOnFailure(hr, "Failed to add partition id to custom action data");

hr = S_OK;

LExit:
return hr;
}

--- NEW FILE: cpsubssched.cpp ---
//-------------------------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
//
//
//
// COM+ subscription functions for CustomActions
//
//-------------------------------------------------------------------------------------------------

#include "precomp.h"


// sql queries

LPCWSTR vcsSubscriptionQuery =
L"SELECT `Subscription`, `ComPlusComponent_`, `Component_`, `Id`, `Name`, `EventCLSID`, `PublisherID` FROM `ComPlusSubscription`";
enum eSubscriptionQuery { sqSubscription = 1, sqComPlusComponent, sqComponent, sqID, sqName, sqEventCLSID, sqPublisherID };

LPCWSTR vcsSubscriptionPropertyQuery =
L"SELECT `Name`, `Value` FROM `ComPlusSubscriptionProperty` WHERE `Subscription_` = ?";


// property definitions

CPI_PROPERTY_DEFINITION pdlSubscriptionProperties[] =
{
{L"Description", cpptString, 500},
{L"Enabled", cpptBoolean, 500},
{L"EventClassPartitionID", cpptString, 502},
{L"FilterCriteria", cpptString, 500},
{L"InterfaceID", cpptString, 500},
{L"MachineName", cpptString, 500},
{L"MethodName", cpptString, 500},
{L"PerUser", cpptBoolean, 500},
{L"Queued", cpptBoolean, 500},
{L"SubscriberMoniker", cpptString, 500},
{L"UserName", cpptUser, 500},
{NULL, cpptNone, 0}
};


// prototypes for private helper functions

static void FreeSubscription(
CPI_SUBSCRIPTION* pItm
);
static HRESULT FindObjectForSubscription(
CPI_SUBSCRIPTION* pItm,
BOOL fFindId,
BOOL fFindName,
ICatalogObject** ppiSubsObj
);
static HRESULT AddSubscriptionToActionData(
CPI_SUBSCRIPTION* pItm,
int iActionType,
int iActionCost,
LPWSTR* ppwzActionData
);
static HRESULT ComponentFindByKey(
CPI_ASSEMBLY_LIST* pAsmList,
LPCWSTR pwzKey,
CPI_ASSEMBLY** ppAsmItm,
CPI_COMPONENT** ppCompItm
);


// function definitions

void CpiSubscriptionListFree(
CPI_SUBSCRIPTION_LIST* pList
)
{
CPI_SUBSCRIPTION* pItm = pList->pFirst;

while (pItm)
{
CPI_SUBSCRIPTION* pDelete = pItm;
pItm = pItm->pNext;
FreeSubscription(pDelete);
}
}

HRESULT CpiSubscriptionsRead(
CPI_ASSEMBLY_LIST* pAsmList,
CPI_SUBSCRIPTION_LIST* pSubList
)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;

PMSIHANDLE hView, hRec;

CPI_SUBSCRIPTION* pItm = NULL;
LPWSTR pwzData = NULL;

// loop through all applications
hr = WcaOpenExecuteView(vcsSubscriptionQuery, &hView);
ExitOnFailure(hr, "Failed to execute view on ComPlusSubscription table");

while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
{
// create entry
pItm = (CPI_SUBSCRIPTION*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_SUBSCRIPTION));
if (!pItm)
ExitFunction1(hr = E_OUTOFMEMORY);

// get key
hr = WcaGetRecordString(hRec, sqSubscription, &pwzData);
ExitOnFailure(hr, "Failed to get key");
StringCchCopyW(pItm->wzKey, countof(pItm->wzKey), pwzData);

// get com+ component
hr = WcaGetRecordString(hRec, sqComPlusComponent, &pwzData);
ExitOnFailure(hr, "Failed to get COM+ component");

hr = ComponentFindByKey(pAsmList, pwzData, &pItm->pAssembly, &pItm->pComponent);

if (S_FALSE == hr)
{
// component not found
ExitOnFailure1(hr = E_FAIL, "Failed to find component, key: %S", pwzData);
}

// get msi component
hr = WcaGetRecordString(hRec, sqComponent, &pwzData);
ExitOnFailure(hr, "Failed to get component");

// get msi component install state
er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &pItm->isInstalled, &pItm->isAction);
ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to get component state");

// get id
hr = WcaGetRecordFormattedString(hRec, sqID, &pwzData);
ExitOnFailure(hr, "Failed to get id");

if (pwzData && *pwzData)
{
hr = PcaGuidToRegFormat(pwzData, pItm->wzID, countof(pItm->wzID));
ExitOnFailure2(hr, "Failed to parse id guid value, key: %S, value: '%S'", pItm->wzKey, pwzData);
}

// get name
hr = WcaGetRecordFormattedString(hRec, sqName, &pwzData);
ExitOnFailure(hr, "Failed to get name");
StringCchCopyW(pItm->wzName, countof(pItm->wzName), pwzData);

// get event clsid
hr = WcaGetRecordFormattedString(hRec, sqEventCLSID, &pwzData);
ExitOnFailure(hr, "Failed to get event clsid");
StringCchCopyW(pItm->wzEventCLSID, countof(pItm->wzEventCLSID), pwzData);

// get publisher id
hr = WcaGetRecordFormattedString(hRec, sqPublisherID, &pwzData);
ExitOnFailure(hr, "Failed to get publisher id");
StringCchCopyW(pItm->wzPublisherID, countof(pItm->wzPublisherID), pwzData);

// get properties
if (CpiTableExists(cptComPlusSubscriptionProperty))
{
hr = CpiPropertiesRead(vcsSubscriptionPropertyQuery, pItm->wzKey, pdlSubscriptionProperties, &pItm->pProperties, &pItm->iPropertyCount);
ExitOnFailure(hr, "Failed to get subscription properties");
}

// set references & increment counters
if (WcaIsInstalling(pItm->isInstalled, pItm->isAction))
{
CpiApplicationAddReferenceInstall(pItm->pAssembly->pApplication);
pItm->pAssembly->fReferencedForInstall = TRUE;
pSubList->iInstallCount++;
if (pItm->pAssembly->iAttributes & aaRunInCommit)
pSubList->iCommitCount++;
}
if (WcaIsUninstalling(pItm->isInstalled, pItm->isAction))
{
CpiApplicationAddReferenceUninstall(pItm->pAssembly->pApplication);
pItm->pAssembly->fReferencedForUninstall = TRUE;
pSubList->iUninstallCount++;
}

// add entry
if (pSubList->pFirst)
pItm->pNext = pSubList->pFirst;
pSubList->pFirst = pItm;
pItm = NULL;
}

if (E_NOMOREITEMS == hr)
hr = S_OK;

LExit:
// clean up
if (pItm)
FreeSubscription(pItm);

ReleaseStr(pwzData);

return hr;
}

HRESULT CpiSubscriptionsVerifyInstall(
CPI_SUBSCRIPTION_LIST* pList
)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;

ICatalogObject* piSubsObj = NULL;

for (CPI_SUBSCRIPTION* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
{
// subscriptions that are being installed
if (!WcaIsInstalling(pItm->isInstalled, pItm->isAction))
continue;

// subscription is supposed to exist
if (CpiIsInstalled(pItm->isInstalled))
{
// if we don't have an id
if (!*pItm->wzID)
{
// find subscriptions with conflicting name
hr = FindObjectForSubscription(pItm, FALSE, TRUE, &piSubsObj);
ExitOnFailure(hr, "Failed to find collection object for subscription");

// if the subscription was found
if (S_OK == hr)
{
// get id from subscription object
hr = CpiGetKeyForObject(piSubsObj, pItm->wzID, countof(pItm->wzID));
ExitOnFailure(hr, "Failed to get id");
}

// if the subscription was not found
else
{
// create a new id
hr = CpiCreateId(pItm->wzID, countof(pItm->wzID));
ExitOnFailure(hr, "Failed to create id");
}
}
}

// subscription is supposed to be created
else
{
// check for conflicts
do {
if (*pItm->wzID)
{
// find subscriptions with conflicting id
hr = FindObjectForSubscription(pItm, TRUE, FALSE, &piSubsObj);
ExitOnFailure(hr, "Failed to find collection object for subscription");

if (S_FALSE == hr)
{
// find subscriptions with conflicting name
hr = FindObjectForSubscription(pItm, FALSE, TRUE, &piSubsObj);
ExitOnFailure(hr, "Failed to find collection object for subscription");

if (S_OK == hr)
// "A subscription with a conflictiong name exists. retry cancel"
er = WcaErrorMessage(msierrComPlusSubscriptionNameConflict, hr, INSTALLMESSAGE_ERROR | MB_RETRYCANCEL, 0);
else
break; // no conflicting entry found, break loop
}
else
// "A subscription with a conflicting id exists. abort retry ignore"
er = WcaErrorMessage(msierrComPlusSubscriptionIdConflict, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0);
}
else
{
// find subscriptions with conflicting name
hr = FindObjectForSubscription(pItm, FALSE, TRUE, &piSubsObj);
ExitOnFailure(hr, "Failed to find collection object for subscription");

if (S_OK == hr)
// "A subscription with a conflictiong name exists. abort retry ignore"
er = WcaErrorMessage(msierrComPlusSubscriptionNameConflict, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0);
else
break; // no conflicting entry found, break loop
}

switch (er)
{
case IDCANCEL:
case IDABORT:
ExitOnFailure1(hr = E_FAIL, "A subscription with a conflictiong name or id exists, key: %S", pItm->wzKey);
break;
case IDRETRY:
break;
case IDIGNORE:
// if we don't have an id, copy id from object
if (!*pItm->wzID)
{
hr = CpiGetKeyForObject(piSubsObj, pItm->wzID, countof(pItm->wzID));
ExitOnFailure(hr, "Failed to get id");
}
hr = S_FALSE; // indicate that this is not a conflict
}
} while (S_OK == hr); // hr = S_FALSE if we don't have any conflicts

// create a new id if one is missing
if (!*pItm->wzID)
{
hr = CpiCreateId(pItm->wzID, countof(pItm->wzID));
ExitOnFailure(hr, "Failed to create id");
}
}

// clean up
ReleaseNullObject(piSubsObj);
}

hr = S_OK;

LExit:
// clean up
ReleaseObject(piSubsObj);

return hr;
}

HRESULT CpiSubscriptionsVerifyUninstall(
CPI_SUBSCRIPTION_LIST* pList
)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;

ICatalogObject* piSubsObj = NULL;

for (CPI_SUBSCRIPTION* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
{
// subscriptions that are being installed
if (!WcaIsUninstalling(pItm->isInstalled, pItm->isAction))
continue;

// find subscriptions with conflicting name
hr = FindObjectForSubscription(pItm, 0 != *pItm->wzID, 0 == *pItm->wzID, &piSubsObj);
ExitOnFailure(hr, "Failed to find collection object for subscription");

// if the subscription was found
if (S_OK == hr)
{
// if we don't have an id, copy id from object
if (!*pItm->wzID)
{
hr = CpiGetKeyForObject(piSubsObj, pItm->wzID, countof(pItm->wzID));
ExitOnFailure(hr, "Failed to get id");
}
}

// if the subscription was not found
else
{
pItm->fObjectNotFound = TRUE;
pList->iUninstallCount--; // elements with the fObjectNotFound flag set will not be scheduled for uninstall
}

// clean up
ReleaseNullObject(piSubsObj);
}

hr = S_OK;

LExit:
// clean up
ReleaseObject(piSubsObj);

return hr;
}

HRESULT CpiSubscriptionsInstall(
CPI_SUBSCRIPTION_LIST* pList,
int iRunMode,
LPWSTR* ppwzActionData,
int* piProgress
)
{
HRESULT hr = S_OK;

int iActionType;
int iCount = 0;

// add action text
hr = CpiAddActionTextToActionData(L"CreateSubscriptionsComPlusComponents", ppwzActionData);
ExitOnFailure(hr, "Failed to add action text to custom action data");

// subscription count
switch (iRunMode)
{
case rmDeferred:
iCount = pList->iInstallCount - pList->iCommitCount;
break;
case rmCommit:
iCount = pList->iCommitCount;
break;
case rmRollback:
iCount = pList->iInstallCount;
break;
}

// add subscription count to action data
hr = WcaWriteIntegerToCaData(iCount, ppwzActionData);
ExitOnFailure(hr, "Failed to add count to custom action data");

// add assemblies to custom action data in forward order
for (CPI_SUBSCRIPTION* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
{
// roles that are being installed only
if ((rmCommit == iRunMode && !(pItm->pAssembly->iAttributes & aaRunInCommit)) ||
(rmDeferred == iRunMode && (pItm->pAssembly->iAttributes & aaRunInCommit)) ||
!WcaIsInstalling(pItm->isInstalled, pItm->isAction))
continue;

// action type
if (rmRollback == iRunMode)
{
if (CpiIsInstalled(pItm->isInstalled))
iActionType = atNoOp;
else
iActionType = atRemove;
}
else
iActionType = atCreate;

// add to action data
hr = AddSubscriptionToActionData(pItm, iActionType, COST_SUBSCRIPTION_CREATE, ppwzActionData);
ExitOnFailure1(hr, "Failed to add subscription to custom action data, key: %S", pItm->wzKey);
}

// add progress tics
if (piProgress)
*piProgress += COST_SUBSCRIPTION_CREATE * pList->iInstallCount;

hr = S_OK;

LExit:
return hr;
}

HRESULT CpiSubscriptionsUninstall(
CPI_SUBSCRIPTION_LIST* pList,
int iRunMode,
LPWSTR* ppwzActionData,
int* piProgress
)
{
HRESULT hr = S_OK;

int iActionType;

// add action text
hr = CpiAddActionTextToActionData(L"RemoveSubscriptionsComPlusComponents", ppwzActionData);
ExitOnFailure(hr, "Failed to add action text to custom action data");

// add subscription count to action data
hr = WcaWriteIntegerToCaData(pList->iUninstallCount, ppwzActionData);
ExitOnFailure(hr, "Failed to add count to custom action data");

// add assemblies to custom action data in reverse order
for (CPI_SUBSCRIPTION* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
{
// roles that are being uninstalled only
if (pItm->fObjectNotFound || !WcaIsUninstalling(pItm->isInstalled, pItm->isAction))
continue;

// action type
if (rmRollback == iRunMode)
iActionType = atCreate;
else
iActionType = atRemove;

// add to action data
hr = AddSubscriptionToActionData(pItm, iActionType, COST_SUBSCRIPTION_DELETE, ppwzActionData);
ExitOnFailure1(hr, "Failed to add subscription to custom action data, key: %S", pItm->wzKey);
}

// add progress tics
if (piProgress)
*piProgress += COST_SUBSCRIPTION_DELETE * pList->iUninstallCount;

hr = S_OK;

LExit:
return hr;
}


// helper function definitions

static void FreeSubscription(
CPI_SUBSCRIPTION* pItm
)
{
if (pItm->pProperties)
CpiPropertiesFreeList(pItm->pProperties);

::HeapFree(::GetProcessHeap(), 0, pItm);
}

static HRESULT FindObjectForSubscription(
CPI_SUBSCRIPTION* pItm,
BOOL fFindId,
BOOL fFindName,
ICatalogObject** ppiSubsObj
)
{
HRESULT hr = S_OK;

ICatalogCollection* piSubsColl = NULL;

// get applications collection
hr = CpiGetSubscriptionsCollForComponent(pItm->pAssembly, pItm->pComponent, &piSubsColl);
ExitOnFailure(hr, "Failed to get collection");

if (S_FALSE == hr)
ExitFunction(); // exit with hr = S_FALSE

// find application object
hr = CpiFindCollectionObject(piSubsColl, fFindId ? pItm->wzID : NULL, fFindName ? pItm->wzName : NULL, ppiSubsObj);
ExitOnFailure(hr, "Failed to find object");

// exit with hr from CpiFindCollectionObject()

LExit:
// clean up
ReleaseObject(piSubsColl);

return hr;
}

static HRESULT AddSubscriptionToActionData(
CPI_SUBSCRIPTION* pItm,
int iActionType,
int iActionCost,
LPWSTR* ppwzActionData
)
{
HRESULT hr = S_OK;

// add action information to custom action data
hr = WcaWriteIntegerToCaData(iActionType, ppwzActionData);
ExitOnFailure(hr, "Failed to add action type to custom action data");
hr = WcaWriteIntegerToCaData(iActionCost, ppwzActionData);
ExitOnFailure(hr, "Failed to add action cost to custom action data");

// add application role information to custom action data
hr = WcaWriteStringToCaData(pItm->wzKey, ppwzActionData);
ExitOnFailure(hr, "Failed to add subscription key to custom action data");
hr = WcaWriteStringToCaData(pItm->wzID, ppwzActionData);
ExitOnFailure(hr, "Failed to add subscription id to custom action data");
hr = WcaWriteStringToCaData(pItm->wzName, ppwzActionData);
ExitOnFailure(hr, "Failed to add subscription name to custom action data");
hr = WcaWriteStringToCaData(atCreate == iActionType ? pItm->wzEventCLSID : L"", ppwzActionData);
ExitOnFailure(hr, "Failed to add assembly tlb path to custom action data");
hr = WcaWriteStringToCaData(atCreate == iActionType ? pItm->wzPublisherID : L"", ppwzActionData);
ExitOnFailure(hr, "Failed to add assembly proxy-stub dll path to custom action data");

// add component information to custom action data
hr = WcaWriteStringToCaData(pItm->pComponent->wzCLSID, ppwzActionData);
ExitOnFailure(hr, "Failed to add application id to custom action data");

// add application information to custom action data
hr = WcaWriteStringToCaData(pItm->pAssembly->pApplication->wzID, ppwzActionData);
ExitOnFailure(hr, "Failed to add application id to custom action data");

// add partition information to custom action data
LPCWSTR pwzPartID = pItm->pAssembly->pApplication->pPartition ? pItm->pAssembly->pApplication->pPartition->wzID : L"";
hr = WcaWriteStringToCaData(pwzPartID, ppwzActionData);
ExitOnFailure(hr, "Failed to add partition id to custom action data");

// add properties to custom action data
hr = CpiAddPropertiesToActionData(atCreate == iActionType ? pItm->iPropertyCount : 0, pItm->pProperties, ppwzActionData);
ExitOnFailure(hr, "Failed to add properties to custom action data");

hr = S_OK;

LExit:
return hr;
}

static HRESULT ComponentFindByKey(
CPI_ASSEMBLY_LIST* pAsmList,
LPCWSTR pwzKey,
CPI_ASSEMBLY** ppAsmItm,
CPI_COMPONENT** ppCompItm
)
{
for (CPI_ASSEMBLY* pAsmItm = pAsmList->pFirst; pAsmItm; pAsmItm = pAsmItm->pNext)
{
for (CPI_COMPONENT* pCompItm = pAsmItm->pComponents; pCompItm; pCompItm = pCompItm->pNext)
{
if (0 == lstrcmpW(pCompItm->wzKey, pwzKey))
{
*ppAsmItm = pAsmItm;
*ppCompItm = pCompItm;
return S_OK;
}
}
}

return S_FALSE;
}

--- NEW FILE: cpapprolesched.cpp ---
//-------------------------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
//
//
//
// COM+ application role functions for CustomActions
//
//-------------------------------------------------------------------------------------------------

#include "precomp.h"


// sql queries

LPCWSTR vcsApplicationRoleQuery =
L"SELECT `ApplicationRole`, `Application_`, `Component_`, `Name` FROM `ComPlusApplicationRole`";
enum eApplicationRoleQuery { arqApplicationRole = 1, arqApplication, arqComponent, arqName };

LPCWSTR vcsUserInApplicationRoleQuery =
L"SELECT `UserInApplicationRole`, `ApplicationRole_`, `ComPlusUserInApplicationRole`.`Component_`, `Domain`, `Name` FROM `ComPlusUserInApplicationRole`, `User` WHERE `User_` = `User`";
LPCWSTR vcsGroupInApplicationRoleQuery =
L"SELECT `GroupInApplicationRole`, `ApplicationRole_`, `ComPlusGroupInApplicationRole`.`Component_`, `Domain`, `Name` FROM `ComPlusGroupInApplicationRole`, `Group` WHERE `Group_` = `Group`";
enum eTrusteeInApplicationRoleQuery { tiarqUserInApplicationRole = 1, tiarqApplicationRole, tiarqComponent, tiarqDomain, tiarqName };

LPCWSTR vcsApplicationRolePropertyQuery =
L"SELECT `Name`, `Value` FROM `ComPlusApplicationRoleProperty` WHERE `ApplicationRole_` = ?";


// property definitions

CPI_PROPERTY_DEFINITION pdlApplicationRoleProperties[] =
{
{L"Description", cpptString, 500},
{NULL, cpptNone, 0}
};


// prototypes for private helper functions

static HRESULT TrusteesInApplicationRolesRead(
LPCWSTR pwzQuery,
CPI_APPLICATION_ROLE_LIST* pAppRoleList,
CPI_USER_IN_APPLICATION_ROLE_LIST* pUsrInAppRoleList
);
static void FreeApplicationRole(
CPI_APPLICATION_ROLE* pItm
);
static void FreeUserInApplicationRole(
CPI_USER_IN_APPLICATION_ROLE* pItm
);
static HRESULT GetUsersCollForApplicationRole(
CPI_APPLICATION_ROLE* pAppRole,
ICatalogCollection** ppiUsersColl
);
static HRESULT FindObjectForApplicationRole(
CPI_APPLICATION_ROLE* pItm,
ICatalogObject** ppiRoleObj
);
static HRESULT AddApplicationRoleToActionData(
CPI_APPLICATION_ROLE* pItm,
int iActionType,
int iActionCost,
LPWSTR* ppwzActionData
);
static HRESULT AddUserInApplicationRoleToActionData(
CPI_USER_IN_APPLICATION_ROLE* pItm,
int iActionType,
int iActionCost,
LPWSTR* ppwzActionData
);


// function definitions

void CpiApplicationRoleListFree(
CPI_APPLICATION_ROLE_LIST* pList
)
{
CPI_APPLICATION_ROLE* pItm = pList->pFirst;

while (pItm)
{
CPI_APPLICATION_ROLE* pDelete = pItm;
pItm = pItm->pNext;
FreeApplicationRole(pDelete);
}
}

HRESULT CpiApplicationRolesRead(
CPI_APPLICATION_LIST* pAppList,
CPI_APPLICATION_ROLE_LIST* pAppRoleList
)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;

PMSIHANDLE hView, hRec;

CPI_APPLICATION_ROLE* pItm = NULL;
LPWSTR pwzData = NULL;

// loop through all application roles
hr = WcaOpenExecuteView(vcsApplicationRoleQuery, &hView);
ExitOnFailure(hr, "Failed to execute view on ComPlusApplicationRole table");

while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
{
// create entry
pItm = (CPI_APPLICATION_ROLE*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_APPLICATION_ROLE));
if (!pItm)
ExitFunction1(hr = E_OUTOFMEMORY);

// get key
hr = WcaGetRecordString(hRec, arqApplicationRole, &pwzData);
ExitOnFailure(hr, "Failed to get key");
StringCchCopyW(pItm->wzKey, countof(pItm->wzKey), pwzData);

// get component
hr = WcaGetRecordString(hRec, arqComponent, &pwzData);
ExitOnFailure(hr, "Failed to get component");

if (pwzData && *pwzData)
{
pItm->fHasComponent = TRUE;

// get component install state
er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &pItm->isInstalled, &pItm->isAction);
ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to get component state");
}

// get application
hr = WcaGetRecordString(hRec, arqApplication, &pwzData);
ExitOnFailure(hr, "Failed to get application");

hr = CpiApplicationFindByKey(pAppList, pwzData, &pItm->pApplication);
if (S_FALSE == hr)
hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
ExitOnFailure1(hr, "Failed to find application, key: %S", pwzData);

// get name
hr = WcaGetRecordFormattedString(hRec, arqName, &pwzData);
ExitOnFailure(hr, "Failed to get name");
StringCchCopyW(pItm->wzName, countof(pItm->wzName), pwzData);

// get properties
if (CpiTableExists(cptComPlusApplicationRoleProperty))
{
hr = CpiPropertiesRead(vcsApplicationRolePropertyQuery, pItm->wzKey, pdlApplicationRoleProperties, &pItm->pProperties, &pItm->iPropertyCount);
ExitOnFailure(hr, "Failed to get properties");
}

// set references & increment counters
if (pItm->fHasComponent)
{
if (WcaIsInstalling(pItm->isInstalled, pItm->isAction))
{
CpiApplicationAddReferenceInstall(pItm->pApplication);
pAppRoleList->iInstallCount++;
}
if (WcaIsUninstalling(pItm->isInstalled, pItm->isAction))
{
CpiApplicationAddReferenceUninstall(pItm->pApplication);
pAppRoleList->iUninstallCount++;
}
}

// add entry
if (pAppRoleList->pFirst)
pItm->pNext = pAppRoleList->pFirst;
pAppRoleList->pFirst = pItm;
pItm = NULL;
}

if (E_NOMOREITEMS == hr)
hr = S_OK;

LExit:
// clean up
if (pItm)
FreeApplicationRole(pItm);

ReleaseStr(pwzData);

return hr;
}

HRESULT CpiApplicationRolesVerifyInstall(
CPI_APPLICATION_ROLE_LIST* pList
)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;

ICatalogObject* piRoleObj = NULL;

for (CPI_APPLICATION_ROLE* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
{
// referenced locaters or roles that are being installed
if (!pItm->fReferencedForInstall && !(pItm->fHasComponent && WcaIsInstalling(pItm->isInstalled, pItm->isAction)))
continue;

// if the role is referensed and is not a locater, it must be installed
if (pItm->fReferencedForInstall && pItm->fHasComponent && !CpiWillBeInstalled(pItm->isInstalled, pItm->isAction))
MessageExitOnFailure1(hr = E_FAIL, msierrComPlusApplicationRoleDependency, "An application role is used by another entity being installed, but is not installed itself, key: %S", pItm->wzKey);

// role is a locater
if (!pItm->fHasComponent)
{
// get collection object for role
hr = FindObjectForApplicationRole(pItm, &piRoleObj);
ExitOnFailure(hr, "Failed to find collection object for role");

// if the role was not found
if (S_FALSE == hr)
MessageExitOnFailure1(hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND), msierrComPlusApplicationRoleNotFound, "An application role required by this installation was not found, key: %S", pItm->wzKey);
}

// role is supposed to be created
else if (!CpiIsInstalled(pItm->isInstalled))
{
do {
// find roles with conflicting name or id
hr = FindObjectForApplicationRole(pItm, NULL);
ExitOnFailure(hr, "Failed to find collection object for role");

if (S_OK == hr)
{
er = WcaErrorMessage(msierrComPlusApplicationRoleConflict, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0);
switch (er)
{
case IDABORT:
ExitOnFailure1(hr = E_FAIL, "An application with a conflictiong name exists, key: %S", pItm->wzKey);
break;
case IDRETRY:
break;
case IDIGNORE:
hr = S_FALSE; // indicate that this is not a conflict
}
}
} while (S_OK == hr); // hr = S_FALSE if we don't have any conflicts
}

// clean up
ReleaseNullObject(piRoleObj);
}

hr = S_OK;

LExit:
// clean up
ReleaseObject(piRoleObj);

return hr;
}

HRESULT CpiApplicationRolesVerifyUninstall(
CPI_APPLICATION_ROLE_LIST* pList
)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;

for (CPI_APPLICATION_ROLE* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
{
// referenced locaters or roles that are being installed
if (!pItm->fReferencedForUninstall && !(pItm->fHasComponent && WcaIsUninstalling(pItm->isInstalled, pItm->isAction)))
continue;

// get collection object for role
hr = FindObjectForApplicationRole(pItm, NULL);
ExitOnFailure(hr, "Failed to find collection object for role");

// if the role was not found
if (S_FALSE == hr)
{
pItm->fObjectNotFound = TRUE;
if (pItm->fHasComponent)
pList->iUninstallCount--; // elements with the fObjectNotFound flag set will not be scheduled for uninstall
}
}

hr = S_OK;

LExit:
return hr;
}

void CpiApplicationRoleAddReferenceInstall(
CPI_APPLICATION_ROLE* pItm
)
{
pItm->fReferencedForInstall = TRUE;
CpiApplicationAddReferenceInstall(pItm->pApplication);
}

void CpiApplicationRoleAddReferenceUninstall(
CPI_APPLICATION_ROLE* pItm
)
{
pItm->fReferencedForUninstall = TRUE;
CpiApplicationAddReferenceUninstall(pItm->pApplication);
}

HRESULT CpiApplicationRolesInstall(
CPI_APPLICATION_ROLE_LIST* pList,
int iRunMode,
LPWSTR* ppwzActionData,
int* piProgress
)
{
HRESULT hr = S_OK;

int iActionType;

// add action text
hr = CpiAddActionTextToActionData(L"CreateComPlusApplicationRoles", ppwzActionData);
ExitOnFailure(hr, "Failed to add action text to custom action data");

// add count to action data
hr = WcaWriteIntegerToCaData(pList->iInstallCount, ppwzActionData);
ExitOnFailure(hr, "Failed to add count to custom action data");

// add roles to custom action data
for (CPI_APPLICATION_ROLE* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
{
// roles that are being installed only
if (!WcaIsInstalling(pItm->isInstalled, pItm->isAction))
continue;

// action type
if (rmRollback == iRunMode)
{
if (CpiIsInstalled(pItm->isInstalled))
iActionType = atNoOp;
else
iActionType = atRemove;
}
else
iActionType = atCreate;

// add to action data
hr = AddApplicationRoleToActionData(pItm, iActionType, COST_APPLICATION_ROLE_CREATE, ppwzActionData);
ExitOnFailure1(hr, "Failed to add application role to custom action data, key: %S", pItm->wzKey);
}

// add progress tics
if (piProgress)
*piProgress += COST_APPLICATION_ROLE_CREATE * pList->iInstallCount;

hr = S_OK;

LExit:
return hr;
}

HRESULT CpiApplicationRolesUninstall(
CPI_APPLICATION_ROLE_LIST* pList,
int iRunMode,
LPWSTR* ppwzActionData,
int* piProgress
)
{
HRESULT hr = S_OK;

int iActionType;

// add action text
hr = CpiAddActionTextToActionData(L"RemoveComPlusApplicationRoles", ppwzActionData);
ExitOnFailure(hr, "Failed to add action text to custom action data");

// add count to action data
hr = WcaWriteIntegerToCaData(pList->iUninstallCount, ppwzActionData);
ExitOnFailure(hr, "Failed to add count to custom action data");

// add roles to custom action data
for (CPI_APPLICATION_ROLE* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
{
// roles that are being uninstalled only
if (pItm->fObjectNotFound || !WcaIsUninstalling(pItm->isInstalled, pItm->isAction))
continue;

// action type
if (rmRollback == iRunMode)
iActionType = atCreate;
else
iActionType = atRemove;

// add to action data
hr = AddApplicationRoleToActionData(pItm, iActionType, COST_APPLICATION_ROLE_DELETE, ppwzActionData);
ExitOnFailure1(hr, "Failed to add application role to custom action data, key: %S", pItm->wzKey);
}

// add progress tics
if (piProgress)
*piProgress += COST_APPLICATION_ROLE_DELETE * pList->iUninstallCount;

hr = S_OK;

LExit:
return hr;
}

HRESULT CpiApplicationRoleFindByKey(
CPI_APPLICATION_ROLE_LIST* pList,
LPCWSTR pwzKey,
CPI_APPLICATION_ROLE** ppAppRole
)
{
for (CPI_APPLICATION_ROLE* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
{
if (0 == lstrcmpW(pItm->wzKey, pwzKey))
{
*ppAppRole = pItm;
return S_OK;
}
}

return S_FALSE;
}

void CpiUserInApplicationRoleListFree(
CPI_USER_IN_APPLICATION_ROLE_LIST* pList
)
{
CPI_USER_IN_APPLICATION_ROLE* pItm = pList->pFirst;

while (pItm)
{
CPI_USER_IN_APPLICATION_ROLE* pDelete = pItm;
pItm = pItm->pNext;
FreeUserInApplicationRole(pDelete);
}
}

HRESULT CpiUsersInApplicationRolesRead(
CPI_APPLICATION_ROLE_LIST* pAppRoleList,
CPI_USER_IN_APPLICATION_ROLE_LIST* pUsrInAppRoleList
)
{
HRESULT hr = S_OK;

// read users in application roles
if (CpiTableExists(cptComPlusUserInApplicationRole))
{
hr = TrusteesInApplicationRolesRead(vcsUserInApplicationRoleQuery, pAppRoleList, pUsrInAppRoleList);
ExitOnFailure(hr, "Failed to read users in application roles");
}

// read groups in application roles
if (CpiTableExists(cptComPlusGroupInApplicationRole))
{
hr = TrusteesInApplicationRolesRead(vcsGroupInApplicationRoleQuery, pAppRoleList, pUsrInAppRoleList);
ExitOnFailure(hr, "Failed to read groups in application roles");
}

hr = S_OK;

LExit:
return hr;
}

HRESULT CpiUsersInApplicationRolesInstall(
CPI_USER_IN_APPLICATION_ROLE_LIST* pList,
int iRunMode,
LPWSTR* ppwzActionData,
int* piProgress
)
{
HRESULT hr = S_OK;

int iActionType;

// add action text
hr = CpiAddActionTextToActionData(L"AddUsersToComPlusApplicationRoles", ppwzActionData);
ExitOnFailure(hr, "Failed to add action text to custom action data");

// add count to action data
hr = WcaWriteIntegerToCaData(pList->iInstallCount, ppwzActionData);
ExitOnFailure(hr, "Failed to add count to custom action data");

// add roles to custom action data
for (CPI_USER_IN_APPLICATION_ROLE* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
{
// roles that are being installed only
if (!WcaIsInstalling(pItm->isInstalled, pItm->isAction))
continue;

// action type
if (rmRollback == iRunMode)
{
if (CpiIsInstalled(pItm->isInstalled))
iActionType = atNoOp;
else
iActionType = atRemove;
}
else
iActionType = atCreate;

// add to action data
hr = AddUserInApplicationRoleToActionData(pItm, iActionType, COST_USER_IN_APPLICATION_ROLE_CREATE, ppwzActionData);
ExitOnFailure1(hr, "Failed to add user in application role to custom action data, key: %S", pItm->wzKey);
}

// add progress tics
if (piProgress)
*piProgress += COST_USER_IN_APPLICATION_ROLE_CREATE * pList->iInstallCount;

hr = S_OK;

LExit:
return hr;
}

HRESULT CpiUsersInApplicationRolesUninstall(
CPI_USER_IN_APPLICATION_ROLE_LIST* pList,
int iRunMode,
LPWSTR* ppwzActionData,
int* piProgress
)
{
HRESULT hr = S_OK;

int iActionType;

// add action text
hr = CpiAddActionTextToActionData(L"RemoveUsersFromComPlusApplicationRoles", ppwzActionData);
ExitOnFailure(hr, "Failed to add action text to custom action data");

// add count to action data
hr = WcaWriteIntegerToCaData(pList->iUninstallCount, ppwzActionData);
ExitOnFailure(hr, "Failed to add count to custom action data");

// add roles to custom action data
for (CPI_USER_IN_APPLICATION_ROLE* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
{
// roles that are being uninstalled only
if (!WcaIsUninstalling(pItm->isInstalled, pItm->isAction))
continue;

// action type
if (rmRollback == iRunMode)
iActionType = atCreate;
else
iActionType = atRemove;

// add to action data
hr = AddUserInApplicationRoleToActionData(pItm, iActionType, COST_USER_IN_APPLICATION_ROLE_DELETE, ppwzActionData);
ExitOnFailure1(hr, "Failed to add user in application role to custom action data, key: %S", pItm->wzKey);
}

// add progress tics
if (piProgress)
*piProgress += COST_USER_IN_APPLICATION_ROLE_DELETE * pList->iUninstallCount;

hr = S_OK;

LExit:
return hr;
}


// helper function definitions

static HRESULT TrusteesInApplicationRolesRead(
LPCWSTR pwzQuery,
CPI_APPLICATION_ROLE_LIST* pAppRoleList,
CPI_USER_IN_APPLICATION_ROLE_LIST* pUsrInAppRoleList
)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;

PMSIHANDLE hView, hRec;

CPI_USER_IN_APPLICATION_ROLE* pItm = NULL;
LPWSTR pwzData = NULL;
LPWSTR pwzDomain = NULL;
LPWSTR pwzName = NULL;

// loop through all application roles
hr = WcaOpenExecuteView(pwzQuery, &hView);
ExitOnFailure(hr, "Failed to execute view on table");

while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
{
// create entry
pItm = (CPI_USER_IN_APPLICATION_ROLE*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_USER_IN_APPLICATION_ROLE));
if (!pItm)
ExitFunction1(hr = E_OUTOFMEMORY);

// get key
hr = WcaGetRecordString(hRec, tiarqUserInApplicationRole, &pwzData);
ExitOnFailure(hr, "Failed to get key");
StringCchCopyW(pItm->wzKey, countof(pItm->wzKey), pwzData);

// get component
hr = WcaGetRecordString(hRec, tiarqComponent, &pwzData);
ExitOnFailure(hr, "Failed to get component");

// get component install state
er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &pItm->isInstalled, &pItm->isAction);
ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to get component state");

// get application role
hr = WcaGetRecordString(hRec, tiarqApplicationRole, &pwzData);
ExitOnFailure(hr, "Failed to get application role");

hr = CpiApplicationRoleFindByKey(pAppRoleList, pwzData, &pItm->pApplicationRole);
if (S_FALSE == hr)
hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
ExitOnFailure1(hr, "Failed to find application role, key: %S", pwzData);

// get user domain
hr = WcaGetRecordFormattedString(hRec, tiarqDomain, &pwzDomain);
ExitOnFailure(hr, "Failed to get domain");

// get user name
hr = WcaGetRecordFormattedString(hRec, tiarqName, &pwzName);
ExitOnFailure(hr, "Failed to get name");

// build account name
hr = CpiBuildAccountName(pwzDomain, pwzName, &pItm->pwzAccount);
ExitOnFailure(hr, "Failed to build account name");

// set references & increment counters
if (WcaIsInstalling(pItm->isInstalled, pItm->isAction))
{
CpiApplicationRoleAddReferenceInstall(pItm->pApplicationRole);
pUsrInAppRoleList->iInstallCount++;
}
if (WcaIsUninstalling(pItm->isInstalled, pItm->isAction))
{
CpiApplicationRoleAddReferenceUninstall(pItm->pApplicationRole);
pUsrInAppRoleList->iUninstallCount++;
}

// add entry
if (pUsrInAppRoleList->pFirst)
pItm->pNext = pUsrInAppRoleList->pFirst;
pUsrInAppRoleList->pFirst = pItm;
pItm = NULL;
}

if (E_NOMOREITEMS == hr)
hr = S_OK;

LExit:
// clean up
if (pItm)
FreeUserInApplicationRole(pItm);

ReleaseStr(pwzData);
ReleaseStr(pwzDomain);
ReleaseStr(pwzName);

return hr;
}

static void FreeApplicationRole(
CPI_APPLICATION_ROLE* pItm
)
{
if (pItm->pProperties)
CpiPropertiesFreeList(pItm->pProperties);

ReleaseObject(pItm->piUsersColl);

::HeapFree(::GetProcessHeap(), 0, pItm);
}

static void FreeUserInApplicationRole(
CPI_USER_IN_APPLICATION_ROLE* pItm
)
{
ReleaseStr(pItm->pwzAccount);

::HeapFree(::GetProcessHeap(), 0, pItm);
}

static HRESULT GetUsersCollForApplicationRole(
CPI_APPLICATION_ROLE* pAppRole,
ICatalogCollection** ppiUsersColl
)
{
HRESULT hr = S_OK;

ICatalogCollection* piRoleColl = NULL;
ICatalogObject* piRoleObj = NULL;

// if a previous attempt to locate the collection object failed
if (pAppRole->fObjectNotFound)
ExitFunction1(hr = S_FALSE);

// get applications collection
if (!pAppRole->piUsersColl)
{
// get collection object for role
hr = FindObjectForApplicationRole(pAppRole, &piRoleObj);
ExitOnFailure(hr, "Failed to find collection object for role");

if (S_FALSE == hr)
ExitFunction(); // exit with hr = S_FALSE

// get users collection
hr = CpiGetCatalogCollection(piRoleColl, piRoleObj, L"UsersInRole", &pAppRole->piUsersColl);
ExitOnFailure(hr, "Failed to get users in role collection");
}

// return value
*ppiUsersColl = pAppRole->piUsersColl;
(*ppiUsersColl)->AddRef();

hr = S_OK;

LExit:
// clean up
ReleaseObject(piRoleColl);
ReleaseObject(piRoleObj);

return hr;
}

static HRESULT FindObjectForApplicationRole(
CPI_APPLICATION_ROLE* pItm,
ICatalogObject** ppiRoleObj
)
{
HRESULT hr = S_OK;

ICatalogCollection* piRoleColl = NULL;

// get roles collection
hr = CpiGetRolesCollForApplication(pItm->pApplication, &piRoleColl);
ExitOnFailure(hr, "Failed to get collection");

if (S_FALSE == hr)
ExitFunction(); // exit with hr = S_FALSE

// find role object
hr = CpiFindCollectionObject(piRoleColl, NULL, pItm->wzName, ppiRoleObj);
ExitOnFailure(hr, "Failed to find object");

// exit with hr from CpiFindCollectionObject()

LExit:
// clean up
ReleaseObject(piRoleColl);

return hr;
}

static HRESULT AddApplicationRoleToActionData(
CPI_APPLICATION_ROLE* pItm,
int iActionType,
int iActionCost,
LPWSTR* ppwzActionData
)
{
HRESULT hr = S_OK;

// add action information to custom action data
hr = WcaWriteIntegerToCaData(iActionType, ppwzActionData);
ExitOnFailure(hr, "Failed to add action type to custom action data");
hr = WcaWriteIntegerToCaData(iActionCost, ppwzActionData);
ExitOnFailure(hr, "Failed to add action cost to custom action data");

// add application role information to custom action data
hr = WcaWriteStringToCaData(pItm->wzKey, ppwzActionData);
ExitOnFailure(hr, "Failed to add application role key to custom action data");
hr = WcaWriteStringToCaData(pItm->wzName, ppwzActionData);
ExitOnFailure(hr, "Failed to add application role name to custom action data");

// add application information to custom action data
hr = WcaWriteStringToCaData(pItm->pApplication->wzID, ppwzActionData);
ExitOnFailure(hr, "Failed to add application id to custom action data");

// add partition information to custom action data
hr = WcaWriteStringToCaData(pItm->pApplication->pPartition ? pItm->pApplication->pPartition->wzID : L"", ppwzActionData);
ExitOnFailure(hr, "Failed to add partition id to custom action data");

// add properties to custom action data
hr = CpiAddPropertiesToActionData(atCreate == iActionType ? pItm->iPropertyCount : 0, pItm->pProperties, ppwzActionData);
ExitOnFailure(hr, "Failed to add properties to custom action data");

hr = S_OK;

LExit:
return hr;
}

static HRESULT AddUserInApplicationRoleToActionData(
CPI_USER_IN_APPLICATION_ROLE* pItm,
int iActionType,
int iActionCost,
LPWSTR* ppwzActionData
)
{
HRESULT hr = S_OK;

// add action information to custom action data
hr = WcaWriteIntegerToCaData(iActionType, ppwzActionData);
ExitOnFailure(hr, "Failed to add action type to custom action data");
hr = WcaWriteIntegerToCaData(iActionCost, ppwzActionData);
ExitOnFailure(hr, "Failed to add action cost to custom action data");

// add application role information to custom action data
hr = WcaWriteStringToCaData(pItm->wzKey, ppwzActionData);
ExitOnFailure(hr, "Failed to add key to custom action data");
hr = WcaWriteStringToCaData(pItm->pApplicationRole->wzName, ppwzActionData);
ExitOnFailure(hr, "Failed to add role name to custom action data");
hr = WcaWriteStringToCaData(pItm->pwzAccount, ppwzActionData);
ExitOnFailure(hr, "Failed to add user account to custom action data");

// add application information to custom action data
CPI_APPLICATION* pApplication = pItm->pApplicationRole->pApplication;
hr = WcaWriteStringToCaData(pApplication->wzID, ppwzActionData);
ExitOnFailure(hr, "Failed to add application id to custom action data");

// add partition information to custom action data
hr = WcaWriteStringToCaData(pApplication->pPartition ? pApplication->pPartition->wzID : L"", ppwzActionData);
ExitOnFailure(hr, "Failed to add partition id to custom action data");

hr = S_OK;

LExit:
return hr;
}

--- NEW FILE: precomp.h ---
#pragma once
//-------------------------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
//
//
//
// Precompiled header for Public Scheduling CustomActions
//
//-------------------------------------------------------------------------------------------------

#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0500
#endif

#include
#include
#include
#include

#include "wcautil.h"
#include "memutil.h"
#include "strutil.h"

#include "CustomMsiErrors.h"

#include "..\inc\cpcost.h"
#include "cputilsched.h"
#include "cppartsched.h"
#include "cppartrolesched.h"
#include "cpappsched.h"
#include "cpapprolesched.h"
#include "cpasmsched.h"
#include "cpsubssched.h"

--- NEW FILE: cpapprolesched.h ---
#pragma once
//-------------------------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
//
//
//
// COM+ application role functions for CustomActions
//
//-------------------------------------------------------------------------------------------------


// structs

struct CPI_APPLICATION_ROLE
{
WCHAR wzKey[MAX_DARWIN_KEY + 1];
WCHAR wzName[MAX_DARWIN_COLUMN + 1];

int iPropertyCount;
CPI_PROPERTY* pProperties;

BOOL fHasComponent;
BOOL fReferencedForInstall;
BOOL fReferencedForUninstall;
BOOL fObjectNotFound;

INSTALLSTATE isInstalled, isAction;

CPI_APPLICATION* pApplication;

ICatalogCollection* piUsersColl;

CPI_APPLICATION_ROLE* pNext;
};

struct CPI_APPLICATION_ROLE_LIST
{
CPI_APPLICATION_ROLE* pFirst;

int iInstallCount;
int iUninstallCount;
};

struct CPI_USER_IN_APPLICATION_ROLE
{
WCHAR wzKey[MAX_DARWIN_KEY + 1];
LPWSTR pwzAccount;

INSTALLSTATE isInstalled, isAction;

CPI_APPLICATION_ROLE* pApplicationRole;

CPI_USER_IN_APPLICATION_ROLE* pNext;
};

struct CPI_USER_IN_APPLICATION_ROLE_LIST
{
CPI_USER_IN_APPLICATION_ROLE* pFirst;

int iInstallCount;
int iUninstallCount;
};


// function prototypes

void CpiApplicationRoleListFree(
CPI_APPLICATION_ROLE_LIST* pList
);
HRESULT CpiApplicationRolesRead(
CPI_APPLICATION_LIST* pAppList,
CPI_APPLICATION_ROLE_LIST* pAppRoleList
);
HRESULT CpiApplicationRolesVerifyInstall(
CPI_APPLICATION_ROLE_LIST* pList
);
HRESULT CpiApplicationRolesVerifyUninstall(
CPI_APPLICATION_ROLE_LIST* pList
);
void CpiApplicationRoleAddReferenceInstall(
CPI_APPLICATION_ROLE* pItm
);
void CpiApplicationRoleAddReferenceUninstall(
CPI_APPLICATION_ROLE* pItm
);
HRESULT CpiApplicationRolesInstall(
CPI_APPLICATION_ROLE_LIST* pList,
int iRunMode,
LPWSTR* ppwzActionData,
int* piProgress
);
HRESULT CpiApplicationRolesUninstall(
CPI_APPLICATION_ROLE_LIST* pList,
int iRunMode,
LPWSTR* ppwzActionData,
int* piProgress
);
HRESULT CpiApplicationRoleFindByKey(
CPI_APPLICATION_ROLE_LIST* pList,
LPCWSTR pwzKey,
CPI_APPLICATION_ROLE** ppAppRole
);
void CpiUserInApplicationRoleListFree(
CPI_USER_IN_APPLICATION_ROLE_LIST* pList
);
HRESULT CpiUsersInApplicationRolesRead(
CPI_APPLICATION_ROLE_LIST* pAppRoleList,
CPI_USER_IN_APPLICATION_ROLE_LIST* pUsrInAppRoleList
);
HRESULT CpiUsersInApplicationRolesInstall(
CPI_USER_IN_APPLICATION_ROLE_LIST* pList,
int iRunMode,
LPWSTR* ppwzActionData,
int* piProgress
);
HRESULT CpiUsersInApplicationRolesUninstall(
CPI_USER_IN_APPLICATION_ROLE_LIST* pList,
int iRunMode,
LPWSTR* ppwzActionData,
int* piProgress
);

--- NEW FILE: cppartsched.h ---
#pragma once
//-------------------------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
//
//
//
// COM+ partition functions for CustomActions
//
//-------------------------------------------------------------------------------------------------


// structs

struct CPI_PARTITION
{
WCHAR wzKey[MAX_DARWIN_KEY + 1];
WCHAR wzID[CPI_MAX_GUID + 1];
WCHAR wzName[MAX_DARWIN_COLUMN + 1];

int iPropertyCount;
CPI_PROPERTY* pProperties;

BOOL fHasComponent;
BOOL fReferencedForInstall;
BOOL fReferencedForUninstall;
BOOL fObjectNotFound;

INSTALLSTATE isInstalled, isAction;

ICatalogCollection* piApplicationsColl;
ICatalogCollection* piRolesColl;

CPI_PARTITION* pNext;
};

struct CPI_PARTITION_LIST
{
CPI_PARTITION* pFirst;

int iInstallCount;
int iUninstallCount;
};

struct CPI_PARTITION_USER
{
WCHAR wzKey[MAX_DARWIN_KEY + 1];
LPWSTR pwzAccount;

BOOL fNoFind;

INSTALLSTATE isInstalled, isAction;

CPI_PARTITION* pPartition;

CPI_PARTITION_USER* pNext;
};

struct CPI_PARTITION_USER_LIST
{
CPI_PARTITION_USER* pFirst;

int iInstallCount;
int iUninstallCount;
};


// function prototypes

void CpiPartitionListFree(
CPI_PARTITION_LIST* pList
);
HRESULT CpiPartitionsRead(
CPI_PARTITION_LIST* pPartList
);
HRESULT CpiPartitionsVerifyInstall(
CPI_PARTITION_LIST* pList
);
HRESULT CpiPartitionsVerifyUninstall(
CPI_PARTITION_LIST* pList
);
void CpiPartitionAddReferenceInstall(
CPI_PARTITION* pItm
);
void CpiPartitionAddReferenceUninstall(
CPI_PARTITION* pItm
);
HRESULT CpiPartitionsInstall(
CPI_PARTITION_LIST* pList,
int iRunMode,
LPWSTR* ppwzActionData,
int* piProgress
);
HRESULT CpiPartitionsUninstall(
CPI_PARTITION_LIST* pList,
int iRunMode,
LPWSTR* ppwzActionData,
int* piProgress
);
HRESULT CpiPartitionFindByKey(
CPI_PARTITION_LIST* pList,
LPCWSTR wzKey,
CPI_PARTITION** ppItm
);
HRESULT CpiGetApplicationsCollForPartition(
CPI_PARTITION* pPart,
ICatalogCollection** ppiAppColl
);
HRESULT CpiGetPartitionUsersCollection(
CPI_PARTITION* pPart,
ICatalogCollection** ppiPartUsrColl
);
HRESULT CpiGetRolesCollForPartition(
CPI_PARTITION* pPart,
ICatalogCollection** ppiRolesColl
);
void CpiPartitionUserListFree(
CPI_PARTITION_USER_LIST* pList
);
HRESULT CpiPartitionUsersRead(
CPI_PARTITION_LIST* pPartList,
CPI_PARTITION_USER_LIST* pPartUsrList
);
HRESULT CpiPartitionUsersInstall(
CPI_PARTITION_USER_LIST* pList,
int iRunMode,
LPWSTR* ppwzActionData,
int* piProgress
);
HRESULT CpiPartitionUsersUninstall(
CPI_PARTITION_USER_LIST* pList,
int iRunMode,
LPWSTR* ppwzActionData,
int* piProgress
);

--- NEW FILE: cpappsched.h ---
#pragma once
//-------------------------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
//
//
//
// COM+ application functions for CustomActions
//
//-------------------------------------------------------------------------------------------------


// structs

struct CPI_APPLICATION
{
WCHAR wzKey[MAX_DARWIN_KEY + 1];
WCHAR wzID[CPI_MAX_GUID + 1];
WCHAR wzName[MAX_DARWIN_COLUMN + 1];

int iPropertyCount;
CPI_PROPERTY* pProperties;

BOOL fHasComponent;
BOOL fReferencedForInstall;
BOOL fReferencedForUninstall;
BOOL fObjectNotFound;

INSTALLSTATE isInstalled, isAction;

CPI_PARTITION* pPartition;

ICatalogCollection* piRolesColl;
ICatalogCollection* piCompsColl;

CPI_APPLICATION* pNext;
};

struct CPI_APPLICATION_LIST
{
CPI_APPLICATION* pFirst;

int iInstallCount;
int iUninstallCount;
};


// function prototypes

void CpiApplicationListFree(
CPI_APPLICATION_LIST* pList
);
HRESULT CpiApplicationsRead(
CPI_PARTITION_LIST* pPartList,
CPI_APPLICATION_LIST* pAppList
);
HRESULT CpiApplicationsVerifyInstall(
CPI_APPLICATION_LIST* pList
);
HRESULT CpiApplicationsVerifyUninstall(
CPI_APPLICATION_LIST* pList
);
void CpiApplicationAddReferenceInstall(
CPI_APPLICATION* pItm
);
void CpiApplicationAddReferenceUninstall(
CPI_APPLICATION* pItm
);
HRESULT CpiApplicationsInstall(
CPI_APPLICATION_LIST* pList,
int iRunMode,
LPWSTR* ppwzActionData,
int* piProgress
);
HRESULT CpiApplicationsUninstall(
CPI_APPLICATION_LIST* pList,
int iRunMode,
LPWSTR* ppwzActionData,
int* piProgress
);
HRESULT CpiApplicationFindByKey(
CPI_APPLICATION_LIST* pList,
LPCWSTR pwzKey,
CPI_APPLICATION** ppApp
);
HRESULT CpiGetRolesCollForApplication(
CPI_APPLICATION* pApp,
ICatalogCollection** ppiRolesColl
);
HRESULT CpiGetComponentsCollForApplication(
CPI_APPLICATION* pApp,
ICatalogCollection** ppiCompsColl
);

--- NEW FILE: cpsched.build ---
(This appears to be a binary file; contents omitted.)

--- NEW FILE: cppartrolesched.cpp ---
//-------------------------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
//
//
//
// COM+ partition role functions for CustomActions
//
//-------------------------------------------------------------------------------------------------

#include "precomp.h"


// sql queries

LPCWSTR vcsPartitionRoleQuery =
L"SELECT `PartitionRole`, `Partition_`, `Component_`, `Name` FROM `ComPlusPartitionRole`";
enum ePartitionRoleQuery { prqPartitionRole = 1, prqPartition, prqComponent, prqName };

LPCWSTR vcsUserInPartitionRoleQuery =
L"SELECT `UserInPartitionRole`, `PartitionRole_`, `ComPlusUserInPartitionRole`.`Component_`, `Domain`, `Name` FROM `ComPlusUserInPartitionRole`, `User` WHERE `User_` = `User`";
LPCWSTR vcsGroupInPartitionRoleQuery =
L"SELECT `GroupInPartitionRole`, `PartitionRole_`, `ComPlusGroupInPartitionRole`.`Component_`, `Domain`, `Name` FROM `ComPlusGroupInPartitionRole`, `Group` WHERE `Group_` = `Group`";
enum eTrusteeInPartitionRoleQuery { tiprqUserInPartitionRole = 1, tiprqPartitionRole, tiprqComponent, tiprqDomain, tiprqName };


// prototypes for private helper functions

static HRESULT TrusteesInPartitionRolesRead(
LPCWSTR pwzQuery,
CPI_PARTITION_ROLE_LIST* pPartRoleList,
CPI_USER_IN_PARTITION_ROLE_LIST* pUsrInPartRoleList
);
static void FreePartitionRole(
CPI_PARTITION_ROLE* pItm
);
static void FreeUserInPartitionRole(
CPI_USER_IN_PARTITION_ROLE* pItm
);
static HRESULT AddUserInPartitionRoleToActionData(
CPI_USER_IN_PARTITION_ROLE* pItm,
int iActionType,
int iActionCost,
LPWSTR* ppwzActionData
);


// function definitions

void CpiPartitionRoleListFree(
CPI_PARTITION_ROLE_LIST* pList
)
{
CPI_PARTITION_ROLE* pItm = pList->pFirst;

while (pItm)
{
CPI_PARTITION_ROLE* pDelete = pItm;
pItm = pItm->pNext;
FreePartitionRole(pDelete);
}
}

HRESULT CpiPartitionRolesRead(
CPI_PARTITION_LIST* pPartList,
CPI_PARTITION_ROLE_LIST* pPartRoleList
)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;

PMSIHANDLE hView, hRec;

CPI_PARTITION_ROLE* pItm = NULL;
LPWSTR pwzData = NULL;

// loop through all application roles
hr = WcaOpenExecuteView(vcsPartitionRoleQuery, &hView);
ExitOnFailure(hr, "Failed to execute view on ComPlusPartitionRole table");

while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
{
// create entry
pItm = (CPI_PARTITION_ROLE*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_PARTITION_ROLE));
if (!pItm)
ExitFunction1(hr = E_OUTOFMEMORY);

// get key
hr = WcaGetRecordString(hRec, prqPartitionRole, &pwzData);
ExitOnFailure(hr, "Failed to get key");
StringCchCopyW(pItm->wzKey, countof(pItm->wzKey), pwzData);

// get partition
hr = WcaGetRecordString(hRec, prqPartition, &pwzData);
ExitOnFailure(hr, "Failed to get application");

hr = CpiPartitionFindByKey(pPartList, pwzData, &pItm->pPartition);
if (S_FALSE == hr)
hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
ExitOnFailure1(hr, "Failed to find partition, key: %S", pwzData);

// get name
hr = WcaGetRecordFormattedString(hRec, prqName, &pwzData);
ExitOnFailure(hr, "Failed to get name");
StringCchCopyW(pItm->wzName, countof(pItm->wzName), pwzData);

// add entry
if (pPartRoleList->pFirst)
pItm->pNext = pPartRoleList->pFirst;
pPartRoleList->pFirst = pItm;
pItm = NULL;
}

if (E_NOMOREITEMS == hr)
hr = S_OK;

LExit:
// clean up
if (pItm)
FreePartitionRole(pItm);

ReleaseStr(pwzData);

return hr;
}

HRESULT CpiPartitionRoleFindByKey(
CPI_PARTITION_ROLE_LIST* pList,
LPCWSTR pwzKey,
CPI_PARTITION_ROLE** ppPartRole
)
{
for (CPI_PARTITION_ROLE* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
{
if (0 == lstrcmpW(pItm->wzKey, pwzKey))
{
*ppPartRole = pItm;
return S_OK;
}
}

return E_FAIL;
}

void CpiUserInPartitionRoleListFree(
CPI_USER_IN_PARTITION_ROLE_LIST* pList
)
{
CPI_USER_IN_PARTITION_ROLE* pItm = pList->pFirst;

while (pItm)
{
CPI_USER_IN_PARTITION_ROLE* pDelete = pItm;
pItm = pItm->pNext;
FreeUserInPartitionRole(pDelete);
}
}

HRESULT CpiUsersInPartitionRolesRead(
CPI_PARTITION_ROLE_LIST* pPartRoleList,
CPI_USER_IN_PARTITION_ROLE_LIST* pUsrInPartRoleList
)
{
HRESULT hr = S_OK;

// read users in partition roles
if (CpiTableExists(cptComPlusUserInPartitionRole))
{
hr = TrusteesInPartitionRolesRead(vcsUserInPartitionRoleQuery, pPartRoleList, pUsrInPartRoleList);
ExitOnFailure(hr, "Failed to read users in partition roles");
}

// read groups in partition roles
if (CpiTableExists(cptComPlusGroupInPartitionRole))
{
hr = TrusteesInPartitionRolesRead(vcsGroupInPartitionRoleQuery, pPartRoleList, pUsrInPartRoleList);
ExitOnFailure(hr, "Failed to read groups in partition roles");
}

hr = S_OK;

LExit:
return hr;
}

HRESULT CpiUsersInPartitionRolesInstall(
CPI_USER_IN_PARTITION_ROLE_LIST* pList,
int iRunMode,
LPWSTR* ppwzActionData,
int* piProgress
)
{
HRESULT hr = S_OK;

int iActionType;

// add action text
hr = CpiAddActionTextToActionData(L"AddUsersToComPlusPartitionRoles", ppwzActionData);
ExitOnFailure(hr, "Failed to add action text to custom action data");

// add count to action data
hr = WcaWriteIntegerToCaData(pList->iInstallCount, ppwzActionData);
ExitOnFailure(hr, "Failed to add count to custom action data");

// add roles to custom action data
for (CPI_USER_IN_PARTITION_ROLE* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
{
// roles that are being installed only
if (!WcaIsInstalling(pItm->isInstalled, pItm->isAction))
continue;

// action type
if (rmRollback == iRunMode)
{
if (CpiIsInstalled(pItm->isInstalled))
iActionType = atNoOp;
else
iActionType = atRemove;
}
else
iActionType = atCreate;

// add to action data
hr = AddUserInPartitionRoleToActionData(pItm, iActionType, COST_USER_IN_APPLICATION_ROLE_CREATE, ppwzActionData);
ExitOnFailure1(hr, "Failed to add user in partition role to custom action data, key: %S", pItm->wzKey);
}

// add progress tics
if (piProgress)
*piProgress += COST_USER_IN_APPLICATION_ROLE_CREATE * pList->iInstallCount;

hr = S_OK;

LExit:
return hr;
}

HRESULT CpiUsersInPartitionRolesUninstall(
CPI_USER_IN_PARTITION_ROLE_LIST* pList,
int iRunMode,
LPWSTR* ppwzActionData,
int* piProgress
)
{
HRESULT hr = S_OK;

int iActionType;

// add action text
hr = CpiAddActionTextToActionData(L"RemoveUsersFromComPlusPartitionRoles", ppwzActionData);
ExitOnFailure(hr, "Failed to add action text to custom action data");

// add count to action data
hr = WcaWriteIntegerToCaData(pList->iUninstallCount, ppwzActionData);
ExitOnFailure(hr, "Failed to add count to custom action data");

// add roles to custom action data
for (CPI_USER_IN_PARTITION_ROLE* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
{
// roles that are being uninstalled only
if (!WcaIsUninstalling(pItm->isInstalled, pItm->isAction))
continue;

// action type
if (rmRollback == iRunMode)
iActionType = atCreate;
else
iActionType = atRemove;

// add to action data
hr = AddUserInPartitionRoleToActionData(pItm, iActionType, COST_USER_IN_APPLICATION_ROLE_DELETE, ppwzActionData);
ExitOnFailure1(hr, "Failed to add user in partition role to custom action data, key: %S", pItm->wzKey);
}

// add progress tics
if (piProgress)
*piProgress += COST_USER_IN_APPLICATION_ROLE_DELETE * pList->iUninstallCount;

hr = S_OK;

LExit:
return hr;
}


// helper function definitions

static HRESULT TrusteesInPartitionRolesRead(
LPCWSTR pwzQuery,
CPI_PARTITION_ROLE_LIST* pPartRoleList,
CPI_USER_IN_PARTITION_ROLE_LIST* pUsrInPartRoleList
)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;

PMSIHANDLE hView, hRec;

CPI_USER_IN_PARTITION_ROLE* pItm = NULL;
LPWSTR pwzData = NULL;
LPWSTR pwzDomain = NULL;
LPWSTR pwzName = NULL;

// loop through all application roles
hr = WcaOpenExecuteView(pwzQuery, &hView);
ExitOnFailure(hr, "Failed to execute view on table");

while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
{
// create entry
pItm = (CPI_USER_IN_PARTITION_ROLE*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_USER_IN_PARTITION_ROLE));
if (!pItm)
ExitFunction1(hr = E_OUTOFMEMORY);

// get key
hr = WcaGetRecordString(hRec, tiprqUserInPartitionRole, &pwzData);
ExitOnFailure(hr, "Failed to get key");
StringCchCopyW(pItm->wzKey, countof(pItm->wzKey), pwzData);

// get component
hr = WcaGetRecordString(hRec, tiprqComponent, &pwzData);
ExitOnFailure(hr, "Failed to get component");

// get component install state
er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &pItm->isInstalled, &pItm->isAction);
ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to get component state");

// get partition role
hr = WcaGetRecordString(hRec, tiprqPartitionRole, &pwzData);
ExitOnFailure(hr, "Failed to get partition role");

hr = CpiPartitionRoleFindByKey(pPartRoleList, pwzData, &pItm->pPartitionRole);
ExitOnFailure1(hr, "Failed to find partition role, key: %S", pwzData);

// get user domain
hr = WcaGetRecordFormattedString(hRec, tiprqDomain, &pwzDomain);
ExitOnFailure(hr, "Failed to get domain");

// get user name
hr = WcaGetRecordFormattedString(hRec, tiprqName, &pwzName);
ExitOnFailure(hr, "Failed to get name");

// build account name
hr = CpiBuildAccountName(pwzDomain, pwzName, &pItm->pwzAccount);
ExitOnFailure(hr, "Failed to build account name");

// increment counters
if (WcaIsInstalling(pItm->isInstalled, pItm->isAction))
pUsrInPartRoleList->iInstallCount++;
if (WcaIsUninstalling(pItm->isInstalled, pItm->isAction))
pUsrInPartRoleList->iUninstallCount++;

// add entry
if (pUsrInPartRoleList->pFirst)
pItm->pNext = pUsrInPartRoleList->pFirst;
pUsrInPartRoleList->pFirst = pItm;
pItm = NULL;
}

if (E_NOMOREITEMS == hr)
hr = S_OK;

LExit:
// clean up
if (pItm)
FreeUserInPartitionRole(pItm);

ReleaseStr(pwzData);
ReleaseStr(pwzDomain);
ReleaseStr(pwzName);

return hr;
}

static void FreePartitionRole(
CPI_PARTITION_ROLE* pItm
)
{
::HeapFree(::GetProcessHeap(), 0, pItm);
}

static void FreeUserInPartitionRole(
CPI_USER_IN_PARTITION_ROLE* pItm
)
{
ReleaseStr(pItm->pwzAccount);

::HeapFree(::GetProcessHeap(), 0, pItm);
}

static HRESULT AddUserInPartitionRoleToActionData(
CPI_USER_IN_PARTITION_ROLE* pItm,
int iActionType,
int iActionCost,
LPWSTR* ppwzActionData
)
{
HRESULT hr = S_OK;

// add action information to custom action data
hr = WcaWriteIntegerToCaData(iActionType, ppwzActionData);
ExitOnFailure(hr, "Failed to add action type to custom action data");
hr = WcaWriteIntegerToCaData(iActionCost, ppwzActionData);
ExitOnFailure(hr, "Failed to add action cost to custom action data");

// add application role information to custom action data
hr = WcaWriteStringToCaData(pItm->wzKey, ppwzActionData);
ExitOnFailure(hr, "Failed to add key to custom action data");
hr = WcaWriteStringToCaData(pItm->pPartitionRole->wzName, ppwzActionData);
ExitOnFailure(hr, "Failed to add role name to custom action data");
hr = WcaWriteStringToCaData(pItm->pwzAccount, ppwzActionData);
ExitOnFailure(hr, "Failed to add user account to custom action data");

// add partition information to custom action data
hr = WcaWriteStringToCaData(pItm->pPartitionRole->pPartition->wzID, ppwzActionData);
ExitOnFailure(hr, "Failed to add partition id to custom action data");

hr = S_OK;

LExit:
return hr;
}

--- NEW FILE: cpasmsched.cpp ---
//-------------------------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
//
//
//
// COM+ assembly functions for CustomActions
//
//-------------------------------------------------------------------------------------------------

#include "precomp.h"
[...2097 lines suppressed...]
if (pList->pRoles)
RoleAssignmentsFreeList(pList->pRoles);

CPI_METHOD* pDelete = pList;
pList = pList->pNext;
::HeapFree(::GetProcessHeap(), 0, pDelete);
}
}

static void RoleAssignmentsFreeList(
CPI_ROLE_ASSIGNMENT* pList
)
{
while (pList)
{
CPI_ROLE_ASSIGNMENT* pDelete = pList;
pList = pList->pNext;
::HeapFree(::GetProcessHeap(), 0, pDelete);
}
}

--- NEW FILE: cputilsched.cpp ---
//-------------------------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
//
//
//
// COM+ related utility functions for CustomActions
//
//-------------------------------------------------------------------------------------------------

#include "precomp.h"


// sql queries

LPCWSTR vcsActionTextQuery =
L"SELECT `Description`, `Template` FROM `ActionText` WHERE `Action` = ?";
enum eActionTextQuery { atqDescription = 1, atqTemplate };

LPCWSTR vcsUserQuery = L"SELECT `Domain`, `Name` FROM `User` WHERE `User` = ?";
enum eUserQuery { uqDomain = 1, uqName };

enum ePropertyQuery { pqName = 1, pqValue };


// prototypes for private helper functions

static HRESULT FindPropertyDefinition(
CPI_PROPERTY_DEFINITION* pPropDefList,
LPCWSTR pwzName,
CPI_PROPERTY_DEFINITION** ppPropDef
);
static HRESULT GetUserAccountName(
LPCWSTR pwzKey,
LPWSTR* ppwzAccount
);


// variables

static ICOMAdminCatalog* gpiCatalog;
static ICatalogCollection* gpiPartColl;
static ICatalogCollection* gpiAppColl;

static int giTables;


// function definitions

void CpiInitialize()
{
// collections
gpiCatalog = NULL;
gpiPartColl = NULL;
gpiAppColl = NULL;

// tables
giTables = 0;

if (S_OK == WcaTableExists(L"ComPlusPartition")) giTables |= cptComPlusPartition;
if (S_OK == WcaTableExists(L"ComPlusPartitionProperty")) giTables |= cptComPlusPartitionProperty;
if (S_OK == WcaTableExists(L"ComPlusPartitionRole")) giTables |= cptComPlusPartitionRole;
if (S_OK == WcaTableExists(L"ComPlusUserInPartitionRole")) giTables |= cptComPlusUserInPartitionRole;
if (S_OK == WcaTableExists(L"ComPlusGroupInPartitionRole")) giTables |= cptComPlusGroupInPartitionRole;
if (S_OK == WcaTableExists(L"ComPlusPartitionUser")) giTables |= cptComPlusPartitionUser;
if (S_OK == WcaTableExists(L"ComPlusApplication")) giTables |= cptComPlusApplication;
if (S_OK == WcaTableExists(L"ComPlusApplicationProperty")) giTables |= cptComPlusApplicationProperty;
if (S_OK == WcaTableExists(L"ComPlusApplicationRole")) giTables |= cptComPlusApplicationRole;
if (S_OK == WcaTableExists(L"ComPlusApplicationRoleProperty")) giTables |= cptComPlusApplicationRoleProperty;
if (S_OK == WcaTableExists(L"ComPlusUserInApplicationRole")) giTables |= cptComPlusUserInApplicationRole;
if (S_OK == WcaTableExists(L"ComPlusGroupInApplicationRole")) giTables |= cptComPlusGroupInApplicationRole;
if (S_OK == WcaTableExists(L"ComPlusAssembly")) giTables |= cptComPlusAssembly;
if (S_OK == WcaTableExists(L"ComPlusAssemblyDependency")) giTables |= cptComPlusAssemblyDependency;
if (S_OK == WcaTableExists(L"ComPlusComponent")) giTables |= cptComPlusComponent;
if (S_OK == WcaTableExists(L"ComPlusComponentProperty")) giTables |= cptComPlusComponentProperty;
if (S_OK == WcaTableExists(L"ComPlusRoleForComponent")) giTables |= cptComPlusRoleForComponent;
if (S_OK == WcaTableExists(L"ComPlusInterface")) giTables |= cptComPlusInterface;
if (S_OK == WcaTableExists(L"ComPlusInterfaceProperty")) giTables |= cptComPlusInterfaceProperty;
if (S_OK == WcaTableExists(L"ComPlusRoleForInterface")) giTables |= cptComPlusRoleForInterface;
if (S_OK == WcaTableExists(L"ComPlusMethod")) giTables |= cptComPlusMethod;
if (S_OK == WcaTableExists(L"ComPlusMethodProperty")) giTables |= cptComPlusMethodProperty;
if (S_OK == WcaTableExists(L"ComPlusRoleForMethod")) giTables |= cptComPlusRoleForMethod;
if (S_OK == WcaTableExists(L"ComPlusSubscription")) giTables |= cptComPlusSubscription;
if (S_OK == WcaTableExists(L"ComPlusSubscriptionProperty")) giTables |= cptComPlusSubscriptionProperty;
}

void CpiFinalize()
{
// collections
ReleaseObject(gpiCatalog);
ReleaseObject(gpiPartColl);
ReleaseObject(gpiAppColl);
}

BOOL CpiTableExists(
int iTable
)
{
return (giTables & iTable) == iTable;
}

HRESULT CpiGetAdminCatalog(
ICOMAdminCatalog** ppiCatalog
)
{
HRESULT hr = S_OK;

if (!gpiCatalog)
{
// get collection
hr = ::CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_ALL, IID_ICOMAdminCatalog, (void**)&gpiCatalog);
ExitOnFailure(hr, "Failed to create COM+ admin catalog object");
}

// return value
gpiCatalog->AddRef();
*ppiCatalog = gpiCatalog;

hr = S_OK;

LExit:
return hr;
}

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;
}

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();
ExitOnFailure(hr, "Failed to populate collection");

hr = S_OK;

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

return hr;
}

HRESULT CpiGetKeyForObject(
ICatalogObject* piObj,
LPWSTR pwzKey,
SIZE_T cchKey
)
{
HRESULT hr = S_OK;

VARIANT vtKey;
::VariantInit(&vtKey);

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

// change variant type
hr = ::VariantChangeType(&vtKey, &vtKey, 0, VT_BSTR);
ExitOnFailure(hr, "Failed to change variant type");

// copy key
hr = StringCchCopyW(pwzKey, cchKey, vtKey.bstrVal);
ExitOnFailure(hr, "Failed to copy key");

hr = S_OK;

LExit:
// clean up
::VariantClear(&vtKey);

return hr;
}

HRESULT CpiFindCollectionObject(
ICatalogCollection* piColl,
LPCWSTR pwzID,
LPCWSTR pwzName,
ICatalogObject** ppiObj
)
{
HRESULT hr = S_OK;

IDispatch* piDisp = NULL;
ICatalogObject* piObj = NULL;

VARIANT vtVal;
::VariantInit(&vtVal);

long lCnt;
hr = piColl->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 = piColl->get_Item(i, &piDisp);
ExitOnFailure(hr, "Failed to get object from collection");

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

// compare id
if (pwzID && *pwzID)
{
hr = piObj->get_Key(&vtVal);
ExitOnFailure(hr, "Failed to get key");

hr = ::VariantChangeType(&vtVal, &vtVal, 0, VT_BSTR);
ExitOnFailure(hr, "Failed to change variant type");

if (0 == lstrcmpiW(vtVal.bstrVal, pwzID))
{
if (ppiObj)
{
*ppiObj = piObj;
piObj = NULL;
}
ExitFunction1(hr = S_OK);
}

::VariantClear(&vtVal);
}

// compare name
if (pwzName && *pwzName)
{
hr = piObj->get_Name(&vtVal);
ExitOnFailure(hr, "Failed to get name");

hr = ::VariantChangeType(&vtVal, &vtVal, 0, VT_BSTR);
ExitOnFailure(hr, "Failed to change variant type");

if (0 == lstrcmpW(vtVal.bstrVal, pwzName))
{
if (ppiObj)
{
*ppiObj = piObj;
piObj = NULL;
}
ExitFunction1(hr = S_OK);
}

::VariantClear(&vtVal);
}

// release interface pointers
ReleaseNullObject(piDisp);
ReleaseNullObject(piObj);
}

hr = S_FALSE;

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

::VariantClear(&vtVal);

return hr;
}

HRESULT CpiGetPartitionsCollection(
ICatalogCollection** ppiPartColl
)
{
HRESULT hr = S_OK;

if (!gpiPartColl)
{
// get collection
hr = CpiGetCatalogCollection(L"Partitions", &gpiPartColl);
ExitOnFailure(hr, "Failed to get partitions collection");
}

// return value
gpiPartColl->AddRef();
*ppiPartColl = gpiPartColl;

hr = S_OK;

LExit:
return hr;
}

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;
}

HRESULT CpiAddActionTextToActionData(
LPCWSTR pwzAction,
LPWSTR* ppwzActionData
)
{
HRESULT hr = S_OK;

PMSIHANDLE hView, hRecKey, hRec;

LPWSTR pwzDescription = NULL;
LPWSTR pwzTemplate = NULL;

if (S_OK == WcaTableExists(L"ActionText"))
{
// create parameter record
hRecKey = ::MsiCreateRecord(1);
ExitOnNull(hRecKey, hr, E_OUTOFMEMORY, "Failed to create record");
hr = WcaSetRecordString(hRecKey, 1, pwzAction);
ExitOnFailure(hr, "Failed to set record string");

// open view
hr = WcaOpenView(vcsActionTextQuery, &hView);
ExitOnFailure(hr, "Failed to open view on ActionText table");
hr = WcaExecuteView(hView, hRecKey);
ExitOnFailure(hr, "Failed to execute view on ActionText table");

// fetch record
hr = WcaFetchSingleRecord(hView, &hRec);
if (S_FALSE != hr)
{
ExitOnFailure(hr, "Failed to fetch action text record");

// get description
hr = WcaGetRecordString(hRec, atqDescription, &pwzDescription);
ExitOnFailure(hr, "Failed to get description");

// get template
hr = WcaGetRecordString(hRec, atqTemplate, &pwzTemplate);
ExitOnFailure(hr, "Failed to get template");
}
}

// add action name to action data
hr = WcaWriteStringToCaData(pwzAction, ppwzActionData);
ExitOnFailure(hr, "Failed to add action name to custom action data");

// add description to action data
hr = WcaWriteStringToCaData(pwzDescription ? pwzDescription : L"", ppwzActionData);
ExitOnFailure(hr, "Failed to add description to custom action data");

// add template to action data
hr = WcaWriteStringToCaData(pwzTemplate ? pwzTemplate : L"", ppwzActionData);
ExitOnFailure(hr, "Failed to add template to custom action data");

hr = S_OK;

LExit:
// clean up
ReleaseStr(pwzDescription);
ReleaseStr(pwzTemplate);

return hr;
}

HRESULT CpiPropertiesRead(
LPCWSTR pwzQuery,
LPCWSTR pwzKey,
CPI_PROPERTY_DEFINITION* pPropDefList,
CPI_PROPERTY** ppPropList,
int* piCount
)
{
HRESULT hr = S_OK;

PMSIHANDLE hView, hRecKey, hRec;

CPI_PROPERTY* pItm = NULL;
LPWSTR pwzData = NULL;

int iVersionNT = 0;

CPI_PROPERTY_DEFINITION* pPropDef;

*piCount = 0;

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

// create parameter record
hRecKey = ::MsiCreateRecord(1);
ExitOnNull(hRecKey, hr, E_OUTOFMEMORY, "Failed to create record");
hr = WcaSetRecordString(hRecKey, 1, pwzKey);
ExitOnFailure(hr, "Failed to set record string");

// open view
hr = WcaOpenView(pwzQuery, &hView);
ExitOnFailure(hr, "Failed to open view on property table");
hr = WcaExecuteView(hView, hRecKey);
ExitOnFailure(hr, "Failed to execute view on property table");

while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
{
// create entry
pItm = (CPI_PROPERTY*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_PROPERTY));
if (!pItm)
ExitFunction1(hr = E_OUTOFMEMORY);

// get name
hr = WcaGetRecordString(hRec, pqName, &pwzData);
ExitOnFailure(hr, "Failed to get name");
StringCchCopyW(pItm->wzName, countof(pItm->wzName), pwzData);

// get value
hr = WcaGetRecordFormattedString(hRec, pqValue, &pItm->pwzValue);
ExitOnFailure(hr, "Failed to get value");

// find property definition
hr = FindPropertyDefinition(pPropDefList, pItm->wzName, &pPropDef);
ExitOnFailure(hr, "Failed to find property definition");

if (S_FALSE == hr)
ExitOnFailure2(hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND), "Unknown property, key: %S, property: %S", pwzKey, pItm->wzName);

// check version, ignore if catalog version is too low
if (iVersionNT < pPropDef->iMinVersionNT)
{
WcaLog(LOGMSG_VERBOSE, "Skipping property since NT version is too low, key: %S, property: %S", pwzKey, pItm->wzName);
CpiPropertiesFreeList(pItm);
pItm = NULL;
continue;
}

// if the property is a user, replace the User table key with a user account name
if (cpptUser == pPropDef->iType)
{
hr = GetUserAccountName(pItm->pwzValue, &pItm->pwzValue);
ExitOnFailure(hr, "Failed to get user account name");
}

// add entry
++*piCount;
if (*ppPropList)
pItm->pNext = *ppPropList;
*ppPropList = pItm;
pItm = NULL;
}

if (E_NOMOREITEMS == hr)
hr = S_OK;

LExit:
// clean up
if (pItm)
CpiPropertiesFreeList(pItm);

ReleaseStr(pwzData);

return hr;
}

void CpiPropertiesFreeList(
CPI_PROPERTY* pList
)
{
while (pList)
{
ReleaseStr(pList->pwzValue);

CPI_PROPERTY* pDelete = pList;
pList = pList->pNext;
::HeapFree(::GetProcessHeap(), 0, pDelete);
}
}

HRESULT CpiAddPropertiesToActionData(
int iPropCount,
CPI_PROPERTY* pPropList,
LPWSTR* ppwzActionData
)
{
HRESULT hr = S_OK;

hr = WcaWriteIntegerToCaData(iPropCount, ppwzActionData);
ExitOnFailure(hr, "Failed to add count to custom action data");

if (iPropCount) // count might be 0 event thought there are elements in the list
{
for (CPI_PROPERTY* pProp = pPropList; pProp; pProp = pProp->pNext)
{
hr = WcaWriteStringToCaData(pProp->wzName, ppwzActionData);
ExitOnFailure1(hr, "Failed to add property name to custom action data, name: %S", pProp->wzName);

hr = WcaWriteStringToCaData(pProp->pwzValue, ppwzActionData);
ExitOnFailure1(hr, "Failed to add property value to custom action data, name: %S", pProp->wzName);
}
}

hr = S_OK;

LExit:
return hr;
}

HRESULT CpiBuildAccountName(
LPCWSTR pwzDomain,
LPCWSTR pwzName,
LPWSTR* ppwzAccount
)
{
HRESULT hr = S_OK;

WCHAR wzComputerName[MAX_COMPUTERNAME_LENGTH + 1];
::ZeroMemory(wzComputerName, sizeof(wzComputerName));

// if domain is '.', get computer name
if (0 == lstrcmpW(pwzDomain, L"."))
{
DWORD dwSize = countof(wzComputerName);
if (!::GetComputerNameW(wzComputerName, &dwSize))
ExitOnFailure(hr = HRESULT_FROM_WIN32(::GetLastError()), "Failed to get computer name");
}

// build account name
hr = StrAllocFormatted(ppwzAccount, L"%s\\%s", *wzComputerName ? wzComputerName : pwzDomain, pwzName);
ExitOnFailure(hr, "Failed to build domain user name");

hr = S_OK;

LExit:
return hr;
}

HRESULT CpiGetTempFileName(
LPWSTR* ppwzTempFile
)
{
HRESULT hr = S_OK;

// get temp path
WCHAR wzTempPath[MAX_PATH];
DWORD dw = ::GetTempPathW(countof(wzTempPath), wzTempPath);
if (countof(wzTempPath) pwzName; pItm++)
{
if (0 == lstrcmpW(pItm->pwzName, pwzName))
{
*ppPropDef = pItm;
return S_OK;
}
}

return S_FALSE;
}

static HRESULT GetUserAccountName(
LPCWSTR pwzKey,
LPWSTR* ppwzAccount
)
{
HRESULT hr = S_OK;

PMSIHANDLE hView, hRecKey, hRec;

LPWSTR pwzDomain = NULL;
LPWSTR pwzName = NULL;

// create parameter record
hRecKey = ::MsiCreateRecord(1);
ExitOnNull(hRecKey, hr, E_OUTOFMEMORY, "Failed to create record");
hr = WcaSetRecordString(hRecKey, 1, pwzKey);
ExitOnFailure(hr, "Failed to set record string");

// open view
hr = WcaOpenView(vcsUserQuery, &hView);
ExitOnFailure(hr, "Failed to open view on User table");
hr = WcaExecuteView(hView, hRecKey);
ExitOnFailure(hr, "Failed to execute view on User table");

// fetch record
hr = WcaFetchSingleRecord(hView, &hRec);
if (S_FALSE == hr)
ExitOnFailure1(hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND), "User not found, key: %S", pwzKey);
ExitOnFailure(hr, "Failed to fetch user record");

// get user domain
hr = WcaGetRecordFormattedString(hRec, uqDomain, &pwzDomain);
ExitOnFailure(hr, "Failed to get domain");

// get user name
hr = WcaGetRecordFormattedString(hRec, uqName, &pwzName);
ExitOnFailure(hr, "Failed to get name");

// build account name
hr = CpiBuildAccountName(pwzDomain, pwzName, ppwzAccount);
ExitOnFailure(hr, "Failed to build account name");

hr = S_OK;

LExit:
// clean up
ReleaseStr(pwzDomain);
ReleaseStr(pwzName);

return hr;
}

--- NEW FILE: cputilsched.h ---
#pragma once
//-------------------------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
//
//
//
// COM+ related utility functions for CustomActions
//
//-------------------------------------------------------------------------------------------------


// constants

#define CPI_MAX_GUID 38

enum eRunMode { rmDeferred = 1, rmCommit, rmRollback };

enum eActionType { atNoOp = 0, atCreate, atRemove };

enum eComPlusPropertyType { cpptNone = 0, cpptBoolean, cpptInteger, cpptString, cpptUser };

enum eComPlusTables
{
cptComPlusPartition = (1