API for Determining if App is Running on Citrix or Terminal Services [closed]
Asked Answered
N

4

8

I'm looking for an API/function I can call to determine if software is running on Citrix, Terminal Services, or a stand-alone PC. Optimally, it would work something like this:

Select Case APIWhatSystem.Type.ToString
   Case "Citrix"
      bCitrix = True
   Case "TS"
      bTerminalServices = True
   Case "PC"
      bPC = True
End Select

I would prefer something that worked from an API call as opposed to looking at something in the registry as we're having more and more customers that are locking down the registry.

Thanks.

Northeast answered 15/11, 2010 at 15:57 Comment(3)
I have found this... SystemInformation.TerminalServerSession which returns Boolean value, so that answers the TS question. I'm not sure if it returns bool for Citrix. Still investigating.Northeast
I found it true for Citrix and Windows 2008 Server TS ThinApp.Housebreaking
Citrix SDK mentions WFQuerySessionInformation in their WFAPI SDK for example citrix.com/content/dam/citrix/en_us/documents/downloads/sdk/…Illuminati
E
10

According to: http://forums.citrix.com/message.jspa?messageID=1363711 you can check the SESSIONNAME environment variable.

Another simpler way is to read the system environment variable "SESSIONNAME". If it exists and starts with "ICA" then you're running within a Citrix session. If it starts with "RDP" then you're running within an RDP session.

I tested it with my PC and locally I get:

C:\>echo %SESSIONNAME%
Console

While remotely I got

C:\>echo %SESSIONNAME%
RDP-tcp1

So it seems like it might be an easy route to go, otherwise it sounds like checking for registry values or if certain dlls exist will be the next best thing.

Eolian answered 15/11, 2010 at 16:12 Comment(1)
The Citrix.com link in this post is now dead, and I cannot find an archived copy anywhere.Katharina
H
16

There is an API function that lets you determine whether a specific user session is displayed on the console (locally) or via one the the remoting protocols Citrix ICA (nowadays called HDX) or Microsoft RDP.

Call WTSQuerySessionInformation with 3rd 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 (second link above) any more, but as "This value is retained for legacy purposes.".

Update:

XenDesktop sessions cannot be detected with WTSQuerySessionInformation (it returns 0, meaning Console). If you want a universal solution:

  • Call WTSQuerySessionInformation. If that returns 1 or 2 (ICA or RDP), you are done.
  • If WTSQuerySessionInformation returns 0 (Console), dynamically load wfapi.dll 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)

I have described the process in detail here along with a C++ code sample and a working compiled tool that returns the current session's remoting protocol type.

Hybridize answered 15/11, 2010 at 21:28 Comment(12)
Do you think it would be safe to assume if that function returns ANY number greater than 0, the app is not running on a PC? ie. It's gotta be running on some kind of server. I'm thinking like 5 years in the future... either way a non-zero should always indicate something other than a PC, right?Northeast
Yes, I think if would be safe to assume that anything > 0 is ... NOT LOCAL. Please note the difference in wording. It can very well run on a PC, but with today's hot topic "VDI" aka virtual desktops served over some remoting protocol (think 2008 R2 SP1 - RemoteFX) the likelihood increases steadily of PCs becoming 1-session terminal servers soon.Hybridize
If the application is running under Citrix XenApp, then WTSClientProtocolType will return 1. If, however, the application is running under Citrix XenDesktop, then WTSClientProtocolType will return 0. I am still looking for a way to detect it that doesn't involve enumerating the devices and looking for known virtual drivers.Diazomethane
@DHW: XenApp and XenDesktop are fundamentally different in the way they interface with the OS. Whereas XenApp officially tells the OS that it uses a remoting protocol XenDesktop redirects the console session. The user session therefore is not a WTS session and WTSQuerySessionInformation does not help. You should ask another question regarding XenDesktop.Hybridize
@HelgeKlein There are a lot of websites tracing back to your solution here - and a number of those sites are asking exactly what I'm asking - so I'm thinking my not-so-neat solution will do. I checked which registry keys were present in the virtualized desktop for XenDesktop 2.0 and compared it to ones still in play in the current version. I settled on testing for the existence of HKEY_LOCAL_MACHINE\SOFTWARE\Citrix\VirtualDesktopAgent - So your solution comes back with Console, I'll check for the registry key. If it exists, I conclude its Citrix. Close enough for all practical purposes.Diazomethane
@DHW: Sounds good enough to me.Hybridize
@DHW: I now have a better solution for XenDesktop and updated my answer accordingly.Hybridize
@HelgeKlein I'm not sure on the specifics of the XenDesktop implementation - but wfapi.dll does not exist in the XenDesktop build I'm using.Diazomethane
@DHW: Which version of XenDesktop is that? To be more specific: the version of the VDA software installed on the virtual Windows machines is relevant.Hybridize
XenDesktop 5.6. Win7. Don't know if its relevant but its launched using Program Neighborhood Agent (pnagent)Diazomethane
@DHW: I just check on a Windows 7 x64 machine with the XenDesktop 5.6 VDA installed. Wfapi.dll and Wfapi64.dll are in the subdirectory "ICAService" of the VDA installation directory. It should also be in the path so you can find it on the console with "where wfapi.dll".Hybridize
@HelgeKlein Ah. See it. C:\Program Files\Citrix\ICAService\wfapi.dll I was looking in Windows tree.Diazomethane
E
10

According to: http://forums.citrix.com/message.jspa?messageID=1363711 you can check the SESSIONNAME environment variable.

Another simpler way is to read the system environment variable "SESSIONNAME". If it exists and starts with "ICA" then you're running within a Citrix session. If it starts with "RDP" then you're running within an RDP session.

I tested it with my PC and locally I get:

C:\>echo %SESSIONNAME%
Console

While remotely I got

C:\>echo %SESSIONNAME%
RDP-tcp1

So it seems like it might be an easy route to go, otherwise it sounds like checking for registry values or if certain dlls exist will be the next best thing.

Eolian answered 15/11, 2010 at 16:12 Comment(1)
The Citrix.com link in this post is now dead, and I cannot find an archived copy anywhere.Katharina
N
2

Following @Josh's answer, the code would look like this:

Select Case Environment.GetEnvironmentVariable("SessionName").ToUpper.SubString(0,3))
   Case "ICA" 
      bCitrix = True
   Case "RDP"
      bTerminalServer = True
   Case "CON" 
      bPC = True
End Select

I haven't fully tested it out yet, but it looks like it will do what I want. PCs and Terminal Servers reports correctly.

If someone has a way to test this on a Citrix box, it would be much appreciated!

Northeast answered 15/11, 2010 at 21:19 Comment(3)
Works for me. On my citrix server ?environ("SessionName") returns ICA-tcp#56.Ohaus
Notice, that in Windows 2008 TS RemoteApp mode this env.variable is absentNobby
I just ran this on my infrastructure and I get SESSIONNAME=Console, so it seems to not be a reliable method anymore.Hawes
D
2

Based on Helge Klein's revised answer (above) I thought I'd post the VBA code to make this happen to help future VBA users hitting this page. Helge already has the C++ code on his own site. If you find this helpful, please upvote Helge Klein's answer.

Option Explicit

Private Const WTS_CURRENT_SERVER_HANDLE = 0&
Private Const WTS_CURRENT_SESSION As Long = -1

Private Enum WTS_INFO_CLASS
    WTSInitialProgram
    WTSApplicationName
    WTSWorkingDirectory
    WTSOEMId
    WTSSessionId
    WTSUserName
    WTSWinStationName
    WTSDomainName
    WTSConnectState
    WTSClientBuildNumber
    WTSClientName
    WTSClientDirectory
    WTSClientProductId
    WTSClientHardwareId
    WTSClientAddress
    WTSClientDisplay
    WTSClientProtocolType
    WTSIdleTime
    WTSLogonTime
    WTSIncomingBytes
    WTSOutgoingBytes
    WTSIncomingFrames
    WTSOutgoingFrames
    WTSClientInfo
    WTSSessionInfo
    WTSSessionInfoEx
    WTSConfigInfo
    WTSValidationInfo
    WTSSessionAddressV4
    WTSIsRemoteSession
End Enum

Private Declare Function WTSQuerySessionInformation _
    Lib "wtsapi32.dll" Alias "WTSQuerySessionInformationA" ( _
    ByVal hServer As Long, ByVal SessionId As Long, _
    ByVal WtsInfoClass As WTS_INFO_CLASS, _
    ByRef ppBuffer As LongPtr, _
    ByRef pBytesReturned As LongPtr _
    ) As Long

Private Declare Function WFGetActiveProtocol _
    Lib "wfapi.dll" ( _
    ByVal SessionId As Long _
    ) As Long

Private Declare Sub WTSFreeMemory Lib "wtsapi32.dll" ( _
    ByVal pMemory As Long)

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
    Destination As Any, Source As Any, ByVal length As Long)

Public Function SessionType() As String
    Dim ResultCode As Long
    Dim p As LongPtr
    Dim ppBuffer As LongPtr
    Dim pBytesReturned As Long
    Dim ClientProtocolType As Integer
    ResultCode = WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSClientProtocolType, ppBuffer, pBytesReturned)

    If ResultCode = 0 Then
        p = ppBuffer
        CopyMemory ClientProtocolType, ByVal p, pBytesReturned
        WTSFreeMemory ppBuffer
    End If

    Select Case ClientProtocolType
      Case 0:
        On Error Resume Next
        ResultCode = WFGetActiveProtocol(WTS_CURRENT_SESSION)
        If Err.Number = 53 Then
          SessionType = "Console"
        ElseIf Err.Number = 0 Then
          If ResultCode = 1 Then
            SessionType = "Citrix"
          Else
            SessionType = "Console"
          End If
        End If
        Err.Clear
        On Error GoTo 0
      Case 1:
        SessionType = "Citrix"
      Case 2:
        SessionType = "RDP"
      Case Else
        SessionType = "Other (" & ClientProtocolType & ")"
    End Select
End Function

I've tested this on XenApp and XenDesktop.

Diazomethane answered 8/8, 2014 at 15:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.