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.
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:
- Call WTSQuerySessionInformation. If that returns 1 or 2 (ICA or RDP), you are done.
- If WTSQuerySessionInformation returns 0 (Console), dynamically load wfapi.dll (64-bit processes load wfapi64.dll instead) and get the address of WFGetActiveProtocol
- Call WFGetActiveProtocol with a parameter of WF_CURRENT_SESSION, which is defined as ((DWORD)-1)
- 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>
6 Comments
Hello,
it is great material. For almost one year an issue almost made me crazy. In our company DOORS is accessed via Citrix. I need a script/ tool to detect if DOORS is running via Citrix, and to order the execution of a dxl script. Would be great if you could offer me any hints.
Nice one Helge. Do you know if there are any other lengacy API functions like WFGetActiveProtocol that will get the client ip address? I would like to be able to do what you’ve done but also output the Client IP.
Cheers,
Jeremy
Ha…more searching and I found it!
http://stackoverflow.com/questions/9603289/c-sharp-used-to-determine-the-client-ip-adress-when-running-inside-a-citrix-ses
Is there a way to do this via browser/javascript. I need to optimize my javascript if the browser is opened over ICA..
Is there a way the client side application can get the same information?
Hi, I have problem with missing wfapi.dll on xenDesktop 7.15 – is anybody had same issue?