Questions? Call Us
(202) 499-2900  M-F 6am-6pm ET
Home Support Blog How to enable Remote Desktop programmatically
How to enable Remote Desktop programmatically PDF Print E-mail
User Rating: / 19
PoorBest 

There is a lot out there about enabling Remote Desktop remotely with different methods (registry or script), but not much about enabling Remote Desktop within your application (programmatically), especially for C++ developers . All we want to achieve here is Enable/Disable Remote Desktop as we would do in the System Properties.

This would be useful for admin softwares or installers that need to enable/disable Terminal Services.

Let's see the other methods first.

Using the Registry

This solution is simple, all you need to do is connect to the registry on the computer you need to update. Browse to

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server

In the right panel, select fDenyTSConnection (REG_DWORD). Change the value data from 1 (Remote Desktop disabled) to 0 (Remote Desktop enabled).

This is the most widely used solution as it is the simplest, unfortunately things are not that simple:

  1. Nothing happens by magic, changing a value in the registry is not enough, Terminal Server must be notified of the change to start/stop waiting for connection. That means that you will also have to force a restart of the remote computer. If you don't, you might end up with the Windows System Properties saying RDP is enabled, but without being able to connect until you restart.
  2. Even if Remote Desktop is enable, it might still be blocked by the firewall as changing the registry key does not add/enable Remote Desktop in the firewall exception list.

That is a nice hack, but too limited for a real use.

Using a WMI script

Windows Management Instrumentation (WMI) is the infrastructure for management data and operations on Windows-based operating systems. You can write WMI scripts or applications to automate administrative tasks on remote computers. This is the proper way to enable Remote Desktop as it also configures the firewall. We will use SetAllowTSConnections Method of the Win32_TerminalServiceSetting Class.

The easier way to access WMI providers is through the use of WMIC To query TS Connections setting:

wmic RDToggle get AllowTSConnections

To set Terminal Services connections setting:

wmic RDToggle where servername=‚ÄĚServerName" call SetAllowTSConnections 1

We can use the WMIC global switches for the remote case: To query the TS connections setting:

wmic /node:"RemoteServer" /user:"domain\AdminUser" /password:"password"
RDToggle where servername="RemoteServer" get AllowTSConnections

To enable Terminal Services connections:

wmic /node:"RemoteServer" /user:"domain\AdminUser" /password:"password"
RDToggle where servername="RemoteServer" call SetAllowTSConnections 1

To Enable Remote Desktop on the local computer:

wmic PATH win32_terminalservicesetting WHERE (__Class!="") CALL
SetAllowTSConnections 1

That is a good solution, all we need to do now is see how to do this in C++ rather than in a command line.

Using VC++

To create an application for WMI using C++: you must initialize COM, access and set WMI protocols, and perform a manual cleanup. However, C++ does have the advantage of flexibility and power. Here are the steps involved:

1) Header

You need to add the following references and #include statements to compile correctly.
#define _WIN32_DCOM
#include
using namespace std;
#include
# pragma comment(lib, "wbemuuid.lib")

2) Initialize COM

Because WMI is based on COM technology, you must perform calls to the CoInitializeEx and CoInitializeSecurity functions to access WMI.
// Initialize COM.
hr = CoInitializeEx(0, COINIT_MULTITHREADED);
if (SUCCEEDED(hr))
{
// Initialize Security
hr = CoInitializeSecurity(
NULL,
-1, // COM negotiates service
NULL, // Authentication services
NULL, // Reserved
RPC_C_AUTHN_LEVEL_PKT_PRIVACY, // authentication
RPC_C_IMP_LEVEL_IMPERSONATE, // Impersonation
NULL, // Authentication info
EOAC_NONE, // Additional capabilities
NULL // Reserved
);
}

Note: To connect to the \root\CIMV2\TerminalServices namespace, the authentication level must include packet privacy. For C/C++ calls, this is an authentication level of RPC_C_AUTHN_LEVEL_PKT_PRIVACY.

3) Create a connection to a WMI namespace

By definition, WMI runs in a different process than your application. Therefore, you must create a connection between your application and WMI.

Note: To connect to a remote computer, for example Computer_B, use the following parameters:

_bstr_t(L"\\COMPUTER_B\ROOT\\CIMV2") // WMI namespace
_bstr_t(L"DOMAIN\\MyUserName") // User name
_bstr_t(L"MyPassword") // User password
Here is the main code:
IWbemLocator *pLocator = NULL;
if (SUCCEEDED(hr))
{
// Obtain the initial locator to Windows Management
// on a particular host computer.

hr = CoCreateInstance(
CLSID_WbemLocator,
0,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator, (LPVOID *) &pLocator);
}

IWbemServices *pNamespace = NULL;
_bstr_t sNamespace(L"ROOT\\CIMV2");
if (SUCCEEDED(hr))
{
// Connect to the root\cimv2 namespace with the
// current user and obtain pointer pSvc
// to make IWbemServices calls.

hr = pLocator->ConnectServer(
sNamespace, // WMI namespace
NULL, // User name
NULL, // User password
0, // Locale
NULL, // Security flags
0, // Authority
0, // Context object
&pNamespace // IWbemServices proxy
);
}

if(SUCCEEDED(hr))
wprintf(L"Connected to %s WMI namespace.\n", static_cast(sNamespace));

Note: The code above works for Win2k3/XP, for Vista, just update the namespace from "root/cimv2" to "root/cimv2/TerminalServices".

4) Set the security levels on the WMI connection

To use the connection you create to WMI, you must set the impersonation and authentication levels for your application.
if (SUCCEEDED(hr))
{
// Set the IWbemServices proxy so that impersonation
// of the user (client) occurs.

hr = CoSetProxyBlanket(
pNamespace, // the proxy to set
RPC_C_AUTHN_WINNT, // authentication service
RPC_C_AUTHZ_NONE, // authorization service
NULL, // Server principal name
RPC_C_AUTHN_LEVEL_PKT_PRIVACY, // authentication level
RPC_C_IMP_LEVEL_IMPERSONATE, // impersonation level
NULL, // client identity
EOAC_NONE // proxy capabilities
);
}

5) Make the WMI Request

Locate Win32_TerminalServiceSettings using IWbemServices.
IEnumWbemClassObject* pEnumerator = NULL;
if(SUCCEEDED(hr))
{
hr = pNamespace->ExecQuery(
bstr_t("WQL"),
bstr_t("SELECT * FROM Win32_TerminalServiceSetting"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
}

6) Check and Update TerminalServiceSettings

Check the current value of AllowTSConnection flag and call SetAllowTSConnection.
IWbemClassObject * pObject = NULL;
ULONG uReturn = 0;
while (pEnumerator)
{
hr = pEnumerator->Next(WBEM_INFINITE, 1,
&pObject, &uReturn);

if(0 == uReturn)
{
break;
}

if(SUCCEEDED(hr))
{
// Get the current Value of AllowTSConnections
VARIANT v;
hr = pObject->Get(L"AllowTSConnections",0,&v,0,0);

if (SUCCEEDED(hr) && (V_VT(&v) == VT_I4))
{
wprintf(L"AllowTSConnections = %d.\n", V_I4(&v));
}
else
{
wprintf(L"Error in getting specified object\n");
}
VariantClear(&v);
}

if(SUCCEEDED(hr))
{
// Call the method SetAllowTSConnections(1)
_bstr_t sMethodName = L"SetAllowTSConnections";
_bstr_t sClassName = L"Win32_TerminalServiceSetting";

IWbemClassObject* pClass = NULL;
hr = pNamespace->GetObject(sClassName, 0, NULL, &pClass, NULL);

IWbemClassObject* pInParamsDefinition = NULL;
hr = pClass->GetMethod(sMethodName, 0, &pInParamsDefinition, NULL);

IWbemClassObject* pClassInstance = NULL;
hr = pInParamsDefinition->SpawnInstance(0, &pClassInstance);

// Create the values for the in parameters
VARIANT v;
V_VT(&v) = VT_I4;
V_I4(&v) = 1;

// Store the value for the in parameters
hr = pClassInstance->Put(L"AllowTSConnections", 0, &v, 0);

// Get the context from which to execute the method
CIMTYPE pType;
LONG pFlavor;
VARIANT var;
hr = pObject->Get(L"__PATH", 0, &var, &pType, &pFlavor);

// Execute Method
IWbemClassObject* pOutParams = NULL;
hr = pNamespace->ExecMethod(var.bstrVal, sMethodName, 0,
NULL, pClassInstance, &pOutParams, NULL);

if (SUCCEEDED(hr))
{
wprintf(L"SetAllowTSConnections(%d) OK.\n", V_I4(&v));
}
else
{
wprintf(L"SetAllowTSConnections(%d) FAILED.\n", V_I4(&v));
}

VariantClear(&v);
VariantClear(&var);
}

7) Cleanup and shutdown

Destroy all COM pointers and shut down your application correctly.
if(pLocator != NULL)
pLocator->Release();
if(pNamespace != NULL)
pNamespace->Release();
CoUninitialize();

Conclusion

Enabling Remote Desktop locally or remotely is of great use for administrators or to automate installers (NSIS for instance). But it needs to be done properly (not just with the registry settings). WMI is the best way to achieve this and you I have described how to achieve this in VC++. You can find attached the full .cpp file of the example I will probably be discussing here about a plugin for NSIS if people are interrested.
 
Discover Vedivi

Vedivi is the latest generation of secure remote access solution, it combines a VPN with Remote Desktop and Remote Assistance integration.

With Vedivi you can:

  • Setup a VPN and provide secure remote access for your business in minutes
  • Offer full network connectivity to your office network to collaborators wherever they connect from
  • Allow users to connect (Remote Desktop) to their computer or a Windows Server session from any web browser
  • Provide Remote Assistance to any client or user from a web browser without software installation

Get started with Vedivi free trial so you can see for yourself why so many businesses trust Vedivi for VPN & remote access.