RemoteApp and Desktop Connection Management Extensibility for provisioning apps via RD Web Access

Earlier, we described publishing in Windows Server 2008 R2 and how RDP resources can be published by using the RemoteApp and Desktop Connection Management service to users in RD Web Access. This blog post describes the third-party extensibility support available in the RemoteApp and Desktop Connection Management service, and how third parties (Enterprises or ISVs) can leverage it to publish any resources (RDP or non-RDP) in Windows Server 2008 R2.

The RemoteApp and Desktop Connection Management service, which is part of the RD Connection Broker role service, is a new publishing feature in Windows Server 2008 R2. By default, the following three plug-ins are provided in the service:

  • LegacyTS plug-in: Aggregates RemoteApp programs from Remote Desktop Session Host servers.

  • VMFarm plug-in: Aggregates virtual desktop pools.

  • MyDesktop plug-in: Provides the user with the assigned personal domain desktop.

All three plug-ins provide only RDP files, in the following scenarios:

  • RDP files for RemoteApp programs

  • RDP files for virtual desktops in virtual desktop pools

  • RDP files for per-user assigned virtual desktops

In order to provide RDP or non-RDP files in any scenario other than the ones described above, you can write a plug-in to provide the list of resources, register it with the RemoteApp and Desktop Connection Management service, and display the resources in RD Web Access. For example, if you want to serve several different files (for example, some hand-crafted RDP files and some .osd (app-v) files that are on a physical drive on the machine running the RemoteApp and Desktop Connection Management service), you can write a simple plug-in that will aggregate the files from the location and pass them on to the service, which will then pass them on to RD Web Access. As a result, users will be able to access these files via the web page.

Writing a third-party plug-in

Create a DLL that implements the ItsPubPlugin COM interface as described here:

  • HRESULT  mySamplePlugin::GetResourceList(LPCWSTR userID, __out LONG *pceAppListSize, __deref_out_ecount(*pceAppListSize)pluginResource ** resourceList)

    typedef struct {
    WCHAR alias[256];
    WCHAR name[256];
    [string, unique] WCHAR *resourceFileContents;
    WCHAR fileExtension[24];
    WCHAR resourcePluginType[256];
    boolean isDiscoverable;
    long resourceType;
    unsigned long pceIconSize;
    [size_is(pceIconSize)] byte *iconContents;
    unsigned long pcePluginBlobSize;
    [size_is(pcePluginBlobSize)] byte *blobContents;
    } pluginResource;


This interface is the one that the RemoteApp and Desktop Connection Management service will call into, when a user request comes in, to get the list of resources.

  • STDMETHODIMP mySamplePlugin::GetResource(LPCWSTR alias, LONG flags, __out pluginResource * resource)

This interface is a placeholder and does not need to be implemented. You can simply return E_NOTIMPL.

  • STDMETHODIMP mySamplePlugin::GetCacheLastUpdateTime(__out unsigned long long * lastUpdateTime)

This interface is called by the RemoteApp and Desktop Connection Management service to get the last cache updated time. If you don’t plan to implement caching, returning the current system time in Windows File Time format for the lastUpdateTime will indicate to the service that it needs to call the GetResourceList function (discussed above) to get the latest list of resources, instead of using its cache. However, this is not the recommended way of implementing the plug-in because this will cause RD Web Access to not use its cache, which will cause more data to be transferred across the network and slow user response time. The recommended way to implement this routine is to return the latest time that a resource has changed.

  • STDMETHODIMP mySamplePlugin::get_pluginName(__deref_out BSTR * pVal)

This interface is used to get the plug-in name.

  • STDMETHODIMP mySamplePlugin::get_pluginVersion(__deref_out BSTR * pVal)

This interface is used to get the plug-in version.

  • STDMETHODIMP mySamplePlugin::ResolveResource(     __RPC__out DWORD *resourceType,
    __RPC__out_ecount_full_string(256) wchar_t resourceLocation[ 256 ],
    __RPC__out_ecount_full_string(256) wchar_t endPointName[ 256 ],
    __RPC__in_string wchar_t *userID, __RPC__in_string wchar_t *alias)


This interface is used by the Remote Desktop Connection Broker service to resolve which virtual desktop is assigned to the user. It is not essential to have this implemented, and you can simply return E_NOTIMPL, unless you plan to have a custom implementation for providing the Personal Domain Desktop functionality.

Registering the third-party plugin:

After you have written the plug-in, complete the following steps:

  1. Register the plug-in.
    regsvr32 mySamplePlugin.dll

  2. Create the following registry entries:

    1. Create a sub-key under HKLM\software\Microsoft\Windows NT\CurrentVersion\TerminalServer\Centralized Publishing\ with the CLSID of the mySamplePlugin

    2. Create a DWORD under HKLM\software\Microsoft\Windows NT\CurrentVersion\TerminalServer\Centralized Publishing\{mySamplePlugin-CLSID} called IsEnabled and set it to 1. (Setting it to 0 is equivalent to disabling the plugin)

  3. The publishing service will pick up the registry changes automatically and restart the publishing service, or you can restart the publishing service manually by using the following command:
    net stop tscpubrpc & net start tscpubrpc

After completing these steps, you are finished creating the third-party plug-in. When a client connects, in addition to the enabled default plug-ins, the publishing service will also aggregate the applications provided by your own plug-in and display them in RD Web Access. Note that this will work only for the RD Web Access scenario. In order to make extensibility work in the RemoteApp and Desktop Connections scenario so that you can download the published applications onto your Start menu, you must complete some additional steps, which will be covered in the next post.