RDP Dynamic Virtual Channels - Creating multiple Listeners

Category: visual studio vclanguage

Question

whatsyoursteezo on Tue, 12 Mar 2019 17:21:08


I have recently began working with Dynamic Virtual Channels.

https://docs.microsoft.com/en-us/windows/desktop/termserv/dvc-implementation-details

I have used Static channels for a long time and was able to develop a single plugin that could listen on several channels.

My problem is I am not able to get the Dynamic Virtual Channels to work with multiple listeners. I need to use DVCs to solve some concurrency issues I am having with static channels.

Here is my Initialize method for a single channel:

HRESULT DVCPlugin::Initialize(__in IWTSVirtualChannelManager *pChannelMgr)
{
    HRESULT hr;

    CComObject<ListenerCallback> *pListenerCallback;
    CComPtr<ListenerCallback> ptrListenerCallback;
    CComPtr<IWTSListener> ptrListener;

    // Create an instance of the DVCPlugin object.
    hr = CComObject<ListenerCallback>::CreateInstance(&pListenerCallback);
    ptrListenerCallback = pListenerCallback;

    // Attach the callback to the endpoint.
    hr = pChannelMgr->CreateListener(
        DVC_CHANNEL,
        0,
        (ListenerCallback*)ptrListenerCallback,
        &ptrListener);

    if (hr == S_OK) {
        MessageBox(NULL, L"DVC plugin is enabled!", L"Info...", MB_OK | MB_ICONWARNING);
    }
    else {
        MessageBox(NULL, L"DVC plugin initialize failure!", L"Info...", MB_OK | MB_ICONWARNING);
    }

    return hr;
}

This works perfect for a SINGLE channel. When I try to add additional listeners, only the LAST one will be active. Anyone know how to add an additional channel to the initialize method?

My code is based on this: https://docs.microsoft.com/en-us/windows/desktop/termserv/dvc-client-plug-in-example



Replies

Jack Zhang - AAA on Wed, 13 Mar 2019 08:05:12


Hi whatsyoursteezo,

MSDN shows that IWTSListener interface manages configuration settings for each listener for the dynamic virtual channel (DVC) connection. This interface is implemented by the Remote Desktop Connection (RDC) client. A reference is kept typically by the Remote Desktop Connection (RDC) client plug-in. The methods can be executed in any thread that the plug-in chooses. An instance is retrieved by calling the IWTSVirtualChannelManager::CreateListener method.
Maybe you could think about using multiple thread to add additional listeners.

Best Wishes,

Jack

RLWA32 on Wed, 13 Mar 2019 11:52:00


Just an observation -- the docs at https://docs.microsoft.com/en-us/windows/desktop/termserv/starting-a-dvc-listener are a bit vague about a DVC and multiple listeners.

It says (Emphasis added) "A plug-in can instantiate as many listeners as it needs to. Any incoming connection will be handled by IWTSListenerCallback, which is supplied in the CreateListener method of IWTSVirtualChannelManager. For an example, see the implementation of CDVCSamplePlugin::Initialize in the DVC Client Plug-in Example sample code."

whatsyoursteezo on Wed, 13 Mar 2019 17:04:32


It is definitely vague but I think i solved it. It requires a bit more "duplicated-ish" code but it works.

I first needed to create an additional ListenerCallback and ChannelCallback for each channel I want to add. These also come with additional OnNewChannelConnection and OnDataReceived functions for each channel.

The Initialize method then needed to be modified a bit. Likely can be cleaned up more, but works.

HRESULT DVCPlugin::Initialize(__in IWTSVirtualChannelManager *pChannelMgr)
{
	HRESULT hr = S_OK;

	// chanA
	CComObject<ListenerCallback_chanA> *pListenerCallback_chanA;
	CComPtr<ListenerCallback_chanA> ptrListenerCallback_chanA;
	CComPtr<IWTSListener> ptrListener_chanA;

	// Create an instance of the DVCPlugin object.
	HRESULT hr_chanA = CComObject<ListenerCallback_chanA>::CreateInstance(&pListenerCallback_chanA);
	ptrListenerCallback_chanA = pListenerCallback_chanA;

	// Attach the callback to the endpoint.
	hr_chanA = pChannelMgr->CreateListener(
		DVC_chanA,
		0,
		(ListenerCallback_chanA*)ptrListenerCallback_chanA,
		&ptrListener_chanA);

	if (hr_chanA != S_OK) {
		hr = hr_chanA;
	}

	// chanB
	CComObject<ListenerCallback_chanB> *pListenerCallback_chanB;
	CComPtr<ListenerCallback_chanB> ptrListenerCallback_chanB;
	CComPtr<IWTSListener> ptrListener_chanB;

	// Create an instance of the DVCPlugin object.
	HRESULT hr_chanB = CComObject<ListenerCallback_chanB>::CreateInstance(&pListenerCallback_chanB);
	ptrListenerCallback_chanB = pListenerCallback_chanB;

	hr_chanB = pChannelMgr->CreateListener(
		DVC_chanB,
		0,
		(ListenerCallback_chanB*)ptrListenerCallback_chanB,
		&ptrListener_chanB);

	if (hr_chanB != S_OK) {
		hr = hr_chanB;


	// All listeners created check
	if (hr == S_OK) {
		MessageBox(NULL, L"DVC plugin is enabled!", L"Info...", MB_OK | MB_ICONWARNING);
	}
	else {
		MessageBox(NULL, L"DVC plugin is NOT ENABLED!", L"Info...", MB_OK | MB_ICONWARNING);
	}

	return hr;
}