How to Configure COM+ Application Server on Windows Server 2022

COM+ (Component Object Model Plus) is a Windows application services layer built on top of COM and MTS (Microsoft Transaction Server). It provides distributed application infrastructure including automatic transaction management, object pooling, role-based security, and event services. On Windows Server 2022, COM+ is part of the Application Server role and is used to host enterprise components, especially legacy .NET Framework and classic COM components that require managed lifecycle and transactional support.

COM+ Overview and Component Services

COM+ extends COM by adding services that COM components can use without implementing that logic themselves. When a COM+ component is activated, the COM+ runtime intercepts the creation call, applies the configured services (transactions, security, pooling), and returns a proxy to the component rather than the component directly.

The key services COM+ provides include:

Automatic transactions: COM+ can automatically enlist components in DTC (Distributed Transaction Coordinator) transactions based on metadata, eliminating manual Begin/Commit/Abort calls in most cases.

Object pooling: COM+ maintains a pool of pre-created object instances to reduce activation cost for expensive components.

Role-based security: Access to components and interfaces can be restricted to defined roles populated with Windows users and groups.

Queued Components: COM+ can use MSMQ to asynchronously invoke component methods, recording the call in a queue to be played back when the component is available.

COM+ Events: A loosely-coupled event system where publishers fire events and COM+ delivers them to registered subscribers without the publisher knowing who the subscribers are.

Installing and Enabling COM+ Application Server

COM+ is part of the Windows operating system and is enabled by default on Windows Server 2022. The underlying COM+ System Application runs automatically. However, the Application Server role components (IIS, .NET Framework, DTC) may need to be installed separately depending on what you need to host.

# Install Application Server role with all sub-features
Install-WindowsFeature Application-Server -IncludeAllSubFeature -IncludeManagementTools

# Or install individual components as needed
Install-WindowsFeature NET-Framework-45-Features    # .NET Framework 4.5+
Install-WindowsFeature MSDTC-Server                  # Distributed Transaction Coordinator

# Verify the COM+ System Application service is running
Get-Service COMSysApp | Select-Object Name, Status

# Start it if stopped
Start-Service COMSysApp

The Distributed Transaction Coordinator (MSDTC) is a dependency for COM+ transactional applications. Verify it is running:

Get-Service MSDTC
Start-Service MSDTC

# Set MSDTC to start automatically
Set-Service MSDTC -StartupType Automatic

The Component Services Console (dcomcnfg)

The primary administrative interface for COM+ is the Component Services MMC snap-in. Open it via:

dcomcnfg

Or navigate to: Start > Administrative Tools > Component Services.

The console tree structure is: Component Services > Computers > My Computer > COM+ Applications. Under each COM+ Application you will find: Components (the registered COM/COM+ classes), Roles (security groups), Subscriptions, and Legacy Components.

Key nodes in the console:

COM+ Applications: Lists all COM+ applications installed on the server, including the built-in COM+ System Application and COM+ Utilities.

DCOM Config: Lists all DCOM servers registered on the computer. This is where you configure remote activation, launch permissions, and access permissions for individual COM/DCOM components.

Distributed Transaction Coordinator: Shows DTC transaction statistics and configuration.

Creating COM+ Applications: Library vs Server

COM+ applications run in one of two activation modes: Library Application or Server Application.

Library Application: The component runs in-process within the client’s process. Lower overhead, but if the component crashes it takes down the client. No isolation. Cannot be activated remotely directly. Shares the client’s security context.

Server Application: The component runs in a dedicated surrogate process (dllhost.exe). Provides process isolation — a crash in the component does not affect the client. Can be activated remotely via DCOM. Runs under a configurable identity.

To create a COM+ Server Application using PowerShell via the COM+ Admin API:

# Create a COM+ Server Application via the COMAdmin COM API
$catalog = New-Object -ComObject COMAdmin.COMAdminCatalog

# Get the Applications collection
$apps = $catalog.GetCollection("Applications")
$apps.Populate()

# Add a new application
$newApp = $apps.Add()
$newApp.Value("Name") = "MyOrderProcessor"
$newApp.Value("Description") = "Order processing COM+ application"
$newApp.Value("Activation") = 1   # 1 = Server Application, 0 = Library Application
$newApp.Value("Identity") = "CORPsvc-complus"
$newApp.Value("Password") = "ServiceAccountPassword123!"

$apps.SaveChanges()

Write-Host "COM+ application created successfully."

Alternatively, use the Component Services GUI: right-click COM+ Applications > New > Application, choose “Create an empty application”, name it, select Server Application, and configure the identity under which it runs.

Registering COM Components with regsvr32

Classic COM components (DLLs implementing IUnknown) are registered in the Windows Registry using regsvr32. This writes entries to HKEY_CLASSES_ROOTCLSID so the COM runtime can locate and load the DLL.

# Register a COM DLL
regsvr32 "C:componentsOrderProcessor.dll"

# Register silently (no dialog box)
regsvr32 /s "C:componentsOrderProcessor.dll"

# Unregister a COM DLL
regsvr32 /u "C:componentsOrderProcessor.dll"

# Unregister silently
regsvr32 /u /s "C:componentsOrderProcessor.dll"

After registering the DLL, add the component to a COM+ Application via the Component Services console: right-click the Components node under the target application, select New > Component > Install New Component(s), and browse to the DLL.

Registering .NET Components with regasm

regasm.exe (Assembly Registration Utility) registers .NET Framework assemblies for COM interop. It writes COM-compatible CLSID and ProgID entries to the registry, enabling COM clients to create .NET objects as if they were native COM components.

# Register a .NET assembly for COM interop
# Use the correct .NET version path
C:WindowsMicrosoft.NETFramework64v4.0.30319regasm.exe ^
    "C:componentsOrderService.dll" ^
    /tlb:"C:componentsOrderService.tlb" ^
    /codebase

# The /codebase flag registers the full path (required if not in the GAC)
# The /tlb flag generates and registers a type library

# Unregister
C:WindowsMicrosoft.NETFramework64v4.0.30319regasm.exe ^
    "C:componentsOrderService.dll" /unregister

For .NET assemblies to be COM-visible, they must have [ComVisible(true)] on the class and implement a parameterless constructor. The class should also have a [Guid] attribute specifying a fixed CLSID to avoid regenerating it on each registration.

Install the assembly into the Global Assembly Cache (GAC) for production deployments to avoid the /codebase requirement:

C:WindowsMicrosoft.NETFramework64v4.0.30319gacutil.exe /i "C:componentsOrderService.dll"

COM+ Roles and Security

COM+ role-based security lets you restrict access to an application, component, interface, or method to specific Windows users and groups by defining roles. The COM+ runtime enforces these roles during activation.

Enable authorization checking on a COM+ application and create roles using the COMAdmin API:

$catalog = New-Object -ComObject COMAdmin.COMAdminCatalog
$apps = $catalog.GetCollection("Applications")
$apps.Populate()

# Find the target application by name
$app = $null
for ($i = 0; $i -lt $apps.Count; $i++) {
    if ($apps.Item($i).Value("Name") -eq "MyOrderProcessor") {
        $app = $apps.Item($i)
        break
    }
}

# Enable security checking
$app.Value("ApplicationAccessChecksEnabled") = $true
$app.Value("AccessChecksLevel") = 2   # 2 = Component/interface/method level
$apps.SaveChanges()

# Add a role to the application
$roles = $catalog.GetCollection("Roles", $app.Value("ID"))
$roles.Populate()

$newRole = $roles.Add()
$newRole.Value("Name") = "OrderManagers"
$newRole.Value("Description") = "Users who can submit and view orders"
$roles.SaveChanges()

Write-Host "Role 'OrderManagers' created."

After creating roles, populate them with Windows users or groups from the Component Services console: expand the application, expand Roles > OrderManagers > Users, right-click Add Windows User/Group, and add the appropriate domain group.

Configuring Transactions in COM+

Each COM+ component has a transaction attribute that tells the COM+ runtime how to handle DTC transactions for that component. There are five transaction support levels:

Disabled: The component ignores the transaction context entirely. No DTC involvement.

Not Supported: The component does not participate in transactions. If called within a transaction context, it runs outside it.

Supported: The component participates in an existing transaction if one exists, but does not require one. If no transaction context is present, it runs without one.

Required: The component must run within a transaction. If the caller already has a transaction, it joins it. If not, COM+ creates a new transaction. This is the most common setting for components that perform database operations.

Requires New: The component always runs in a new, independent transaction, regardless of whether the caller already has one. The existing transaction is suspended while the new transaction runs.

Set the transaction attribute via the Component Services console: right-click the component, select Properties > Transactions, choose the transaction support level. Or via COMAdmin API:

$catalog = New-Object -ComObject COMAdmin.COMAdminCatalog
$apps = $catalog.GetCollection("Applications")
$apps.Populate()

# Find the application
$appID = $null
for ($i = 0; $i -lt $apps.Count; $i++) {
    if ($apps.Item($i).Value("Name") -eq "MyOrderProcessor") {
        $appID = $apps.Item($i).Value("ID")
        break
    }
}

# Get components in the application
$components = $catalog.GetCollection("Components", $appID)
$components.Populate()

for ($i = 0; $i -lt $components.Count; $i++) {
    $comp = $components.Item($i)
    if ($comp.Value("ProgID") -eq "OrderService.OrderManager") {
        # 0=Ignored, 1=NotSupported, 2=Supported, 3=Required, 4=RequiresNew
        $comp.Value("Transaction") = 3   # Required
    }
}
$components.SaveChanges()

COM+ Object Pooling

Object pooling keeps a set of pre-initialized component instances alive between client calls. When a client requests a component, COM+ returns an instance from the pool rather than creating a new one, and when the client releases it, the instance returns to the pool rather than being destroyed. This significantly reduces activation overhead for expensive objects (database connections, heavy initialization).

To enable object pooling, the component must implement the IObjectControl interface in COM+, or in .NET, extend System.EnterpriseServices.ServicedComponent and apply the [ObjectPooling] attribute.

Configure pooling via Component Services: right-click the component, Properties > Activation > Enable object pooling. Set:

Minimum pool size: Number of instances kept alive even when idle.

Maximum pool size: Maximum concurrent instances.

Creation timeout (ms): How long a client waits for a pooled instance before receiving an error.

For .NET ServicedComponent pooling configuration in code:

// Example .NET C# ServicedComponent with pooling
using System.EnterpriseServices;

[ObjectPooling(MinPoolSize = 5, MaxPoolSize = 50, CreationTimeout = 5000)]
[Transaction(TransactionOption.Required)]
public class OrderManager : ServicedComponent
{
    protected override bool CanBePooled()
    {
        // Return true to allow this instance to return to the pool
        return true;
    }

    protected override void Activate()
    {
        // Called when an instance is taken from the pool
    }

    protected override void Deactivate()
    {
        // Called when an instance is returned to the pool
        // Reset state here
    }

    public bool SubmitOrder(int orderId)
    {
        // Business logic here
        ContextUtil.SetComplete();
        return true;
    }
}

COM+ Events and Subscribers

The COM+ Events system (Loosely Coupled Events) provides publish/subscribe messaging between COM+ components. Publishers fire events; the COM+ runtime delivers them to registered subscribers without the publisher having a direct reference to any subscriber.

To use COM+ Events: create an Event Class in Component Services (a special COM+ component that defines the event interface), register subscriber components against it, and have publishers call methods on the event class. COM+ intercepts the call and dispatches it to all registered subscribers.

Create a subscription via the Component Services console: navigate to COM+ Applications > [YourApp] > Subscriptions, add a new subscription specifying the Event Class CLSID, the subscriber component CLSID, and the interface/method to subscribe to. Transient subscriptions exist only while the subscriber is running; persistent subscriptions are stored in the COM+ catalog and survive reboots.

DCOM Configuration

DCOM (Distributed COM) allows COM components to be activated and called across the network. Configure machine-wide and per-component DCOM settings in Component Services under My Computer > Properties (Default Properties, Default Protocols, COM Security tabs) or per-component under DCOM Config.

# Enable DCOM on a machine via registry (machine-wide)
Set-ItemProperty -Path "HKLM:SOFTWAREMicrosoftOle" -Name "EnableDCOM" -Value "Y"

# Configure Windows Firewall to allow DCOM
netsh advfirewall firewall add rule name="DCOM" dir=in action=allow protocol=TCP localport=135
netsh advfirewall firewall add rule name="DCOM Dynamic" dir=in action=allow protocol=TCP localport=1024-65535

# Or use the predefined rules
Enable-NetFirewallRule -DisplayGroup "COM+ Remote Administration"

For fine-grained DCOM control, use dcomcnfg or the OLEView tool to inspect and configure individual component launch permissions and access permissions.

Troubleshooting COM+ Activation Errors (Event 10016)

Event ID 10016 in the System event log indicates a DCOM activation permission error: “The application-specific permission settings do not grant Local Activation permission for the COM Server application.”

This commonly occurs with built-in components (RuntimeBroker, Windows Search) when system account permissions are misconfigured. To resolve for a specific component:

# View Event 10016 in the System log
Get-WinEvent -LogName System |
    Where-Object { $_.Id -eq 10016 } |
    Select-Object -First 10 TimeCreated, Message |
    Format-List

The event message contains the AppID GUID and the account that was denied. To fix:

1. Note the AppID GUID from the event (e.g., {GUID}).

2. Open dcomcnfg, navigate to Component Services > Computers > My Computer > DCOM Config.

3. Find the application matching the AppID GUID (right-click, Properties, AppID).

4. Go to the Security tab, click Edit under Launch and Activation Permissions.

5. Add the account shown in the event (e.g., NT SERVICECDPSvc) with Local Activation permission.

For system-owned AppIDs, you must first take ownership of the registry key:

# Take ownership of a DCOM AppID registry key (run as Administrator)
$appID = "{}"
$regPath = "HKCR:AppID$appID"

# Take ownership
$acl = Get-Acl $regPath
$owner = New-Object System.Security.Principal.NTAccount("BUILTINAdministrators")
$acl.SetOwner($owner)
Set-Acl $regPath $acl

# Grant full control
$rule = New-Object System.Security.AccessControl.RegistryAccessRule(
    "BUILTINAdministrators",
    "FullControl",
    "Allow"
)
$acl.AddAccessRule($rule)
Set-Acl $regPath $acl

Summary

COM+ Application Server on Windows Server 2022 provides a mature hosting environment for COM and .NET Framework components requiring enterprise services. Use Component Services (dcomcnfg) for administration, register COM components with regsvr32 and .NET components with regasm, configure transaction levels (Required being the most common for database-integrated components), and use object pooling to optimize performance for expensive components. Event ID 10016 errors are resolved by granting appropriate DCOM launch and activation permissions via the Component Services console.