by: Helge, published: Aug 6, 2014, updated: Aug 18, 2014, in

Is my App Running on Citrix XenDesktop/XenApp?

How do you programmatically determine if an application is running in a session accessed over a remoting protocol (i.e. ICA aka HDX or RDP)? It may be Citrix’ strategy to completely hide the fact that a session is remoted – which makes sense in many ways – but in some cases developers simply need to know in order to optimize their applications. It is surprisingly difficult to find official documentation about this. Here is what you need to know.

Machine or Protocol?

Often, when people pose a question related to our topic they ask how to find out whether an application is “running on Citrix”. I freely admit that I did the same in the title – in order to be found by people asking the question in exactly that way.

But actually the question is wrong. It should be: “is the session my application is running in being remoted?”

Typically what developers want to know is whether to optimize their applications for remoting protocols, e.g. by replacing animations with static images and other similar things. It certainly is a good enough approximation to assume that all sessions on Citrix XenApp or Microsoft RDS are remoted (console sessions should be very rare and not really relevant since they are limited to administrators). The same is true of (traditional) XenDesktop sessions, too. But there are variants of XenDesktop, called Remote PC, where the ICA/HDX remoting protocol is being used to access physical PCs normally used directly via the console. In other words, with Remote PC employees sometimes sit directly in front of their PCs, and sometimes they use their PCs via ICA. Same machine, same user, same session. You want to be able to distinguish between the two.

SessionProtolInfo - ICA, RDP and Console

Citrix XenApp and Microsoft RDS

How to Find Out if a Session is Being Remoted

The Windows API function WTSQuerySessionInformation tells you whether a specific session is displayed locally on the console or remoted via Citrix ICA/HDX or Microsoft RDS.

Call WTSQuerySessionInformation with the third parameter set to WTSClientProtocolType. The function returns:

  • 0 for console sessions
  • 1 for ICA sessions
  • 2 for RDP sessions

Interestingly the return value of 1 is not documented as WTS_PROTOCOL_TYPE_ICA on MSDN any more, but as “This value is retained for legacy purposes.” If I remember correctly that was different 5-10 years ago.

Where it Works

WTSQuerySessionInformation works on any version of Windows beginning with XP / Server 2003, but it does not correctly identify ICA with XenDesktop (see below).

Citrix XenDesktop

XenDesktop’s architecture is different from XenApp in that it makes the OS think the session is displayed locally instead of being remoted. From the point of view of Windows the session is being displayed on the console. It does not know that the console is “redirected” by means of the ICA/HDX protocol. That is the reason why WTSQuerySessionInformation returns a value of 0 (console) for XenDesktop sessions.

How to Find Out if a Session is Being Remoted

To determine whether a XenDesktop session is being remoted we need to dig deep and utilize one of Citrix’ legacy API functions. The WinFrame API SDK (WFAPI) comes with function the WFGetActiveProtocol, a function so totally undocumented that a Google search for its name returns only a single result (as of 2014-07-31). But at least it is officially part of the SDK and very easy to use. It returns the same values as WTSQuerySessionInformation:

  • 0 for console sessions
  • 1 for ICA sessions
  • 2 for RDP sessions

Where it Works

WFGetActiveProtocol correctly identifies the protocol in all the configurations we tested except Windows 7 with the XenDesktop 5.6 VDA accessed via RDP. In that case WFGetActiveProtocol returns 0 (console) instead of 2 (RDP).

Of course, being part of a Citrix API, WFGetActiveProtocol requires Citrix XenApp or XenDesktop to be installed. To be more precise it needs wfapi.dll (wfapi64.dll for 64-bit processes).

Putting it All Together

If you want to be able to identify the remoting protocol on any version of Windows, with and without Citrix XenApp or XenDesktop installed, you need to do the following:

  1. Call WTSQuerySessionInformation. If that returns 1 or 2 (ICA or RDP), you are done.
  2. If WTSQuerySessionInformation returns 0 (Console), dynamically load wfapi.dll (64-bit processes load wfapi64.dll instead) and get the address of WFGetActiveProtocol
  3. Call WFGetActiveProtocol with a parameter of WF_CURRENT_SESSION, which is defined as ((DWORD)-1)
  4. The return value of WFGetActiveProtocol is the session type. It should be either 0 (Console) or 1 (ICA)

C++ Example Code

The following is essentially the source code of SessionProtocolInfo. If you are looking for a compiled tool instead you can simply download SessionProtocolInfo here.

#include "stdafx.h"

#pragma comment (lib, "wtsapi32.lib")

#define WF_CURRENT_SESSION ((DWORD)-1)

int _tmain(int argc, _TCHAR* argv[])
{
   UNREFERENCED_PARAMETER (argc);
   UNREFERENCED_PARAMETER (argv);

   LPTSTR   buffer = 0;
   DWORD    bytesReturned = 0;
   DWORD    retVal = ERROR_SUCCESS;
   DWORD    protocolId = 0;

   // First use Microsoft's official API function
   if (! WTSQuerySessionInformation (WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSClientProtocolType, &buffer, &bytesReturned))
   {
      retVal = GetLastError ();
      goto CleanUp;
   }
   protocolId = (USHORT) *buffer;

   // If the WTS API returns "console" check Citrix' API, it might be a  XenDesktop session
   if (protocolId == 0)
   {
      typedef int (WINAPI* WFGetActiveProtocol) (IN DWORD SessionId);
      WFGetActiveProtocol funcWFGetActiveProtocol = NULL;

      // Dynamically load wfapi.dll
      HMODULE dll = LoadLibrary (L"wfapi.dll");
      if (dll == NULL)
      {
         retVal = GetLastError ();
         if (retVal == ERROR_MOD_NOT_FOUND)
            goto ProtocolDetermined;
         else
            goto CleanUp;
      }

      // Get the address of the function needed
      funcWFGetActiveProtocol = (WFGetActiveProtocol) GetProcAddress (dll, "WFGetActiveProtocol");
      if (funcWFGetActiveProtocol == NULL)
      {
         retVal = ERROR_INVALID_FUNCTION;
         goto CleanUp;
      }

      protocolId = funcWFGetActiveProtocol (WF_CURRENT_SESSION);
   }

ProtocolDetermined:

   if (protocolId == 0)
      _tprintf (L"Console\n");
   else if (protocolId == 1)
      _tprintf (L"ICA\n");
   else if (protocolId == 2)
      _tprintf (L"RDP\n");
   else
      _tprintf (L"Unknown: %d\n", protocolId);

CleanUp:

   if (buffer)
   {
      WTSFreeMemory (buffer);
      buffer = NULL;
   }

	return protocolId;
}

For completeness sake here is also the content of stdafx.h:

#pragma once

#include "targetver.h"

#include <stdio.h>
#include <tchar.h>

#include <Windows.h>
#include <WtsApi32.h>

Tools Using WFGetActiveProtocol

Download SessionProtocolInfo here

Previous Article Windows 8 Client Hyper-V: Internet Access Without 2nd NIC
Next Article Conferences and Community Meetings I Attend(ed) 2014