ICA connections using PowerShell – Part 4

It has been a while since I last posted something here. And in my previous post in the ICA-PowerShell series, I mentioned doing another post about simulating keyboard and mouse. To sum up, part 1 was about the ICO Object basics, part 2 was about controlling the ICA session appearance and in part 3 I talked about using the ICO Object’s events.

Enabling Simulation API

The ability to control the mouse and keyboard in an ICA session is not enabled by default. For this to work, you will need to enable the Simulation API. This is done by adding a registry key to the machine you will be using:

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Citrix\ICA Client\CCM]
"AllowSimulationAPI"=dword:00000001

The “CCM” subkey doesn’t exist by default, so you should create it. If you’re using a 32bit platform for testing, the registry key should look like this:

[HKEY_LOCAL_MACHINE\SOFTWARE\Citrix\ICA Client\CCM]
"AllowSimulationAPI"=dword:00000001

Controlling the session

Let’s start off with the basic code we used in the previous posts. You have to publish Notepad for this code to work.

[System.Reflection.Assembly]::LoadFile("C:\Program Files (x86)\Citrix\ICA Client\WfIcaLib.dll")
$ICA = New-Object WFICALib.ICAClientClass
$ICA.HttpBrowserAddress = "XASRV001"
$ICA.Username = "TestUser01"
$ICA.SetProp("Password","MyUsersPassword")
$ICA.Domain = "LAB"
$ICA.Application = "#Notepad"
$ICA.Launch = $true
$ICA.OutputMode = [WFICALib.OutputMode]::OutputModeNormal
$ICA.DesiredHRes = 1024
$ICA.DesiredVRes = 768
$ICA.DesiredColor = [WFICALib.ICAColorDepth]::Color16bit
$ICA.Connect()

Once connected, your $ICA object should have a “Session” property, this property has two properties called “Keyboard” and “Mouse”. These properties are the ones which we will be focusing on.

Waiting for ICA Logon

Since we only want to control the session when it’s active, we will have to wait for the session to be logged on. In the previous post, I talked about the available events. In this case we want to wait for the OnLogon event. I’ve talked about the “Register-ObjectEvent” CmdLet to register a certain event, but I’ll introduce a new CmdLet called “Wait-Event”. This CmdLet allows you to wait for a certain event to be triggered. We’ll be using the Register-ObjectEvent in a different way now:

Register-ObjectEvent -InputObject $ICA -EventName OnLogon -SourceIdentifier ICA_OnLogon

You probably have noticed that I didn’t define any script block, defining which action should be taken when the event is triggered. This is because I will be using this event just as a trigger to continue the script once it is triggered. After the calling the $ICA.Connect() method, we want to wait for the session to be logged on.

$ICA.Connect()
$Trigger = Wait-Event  -SourceIdentifier ICA_OnLogon -TimeOut 30
if ($Trigger -ne $null)
{
 # Logon Event triggered
}

Using the “-SourceIdentifier” parameter, we can refer to the registered event (using the same name). This last line of code will wait for the OnLogon event to be triggered and will timeout after 30 seconds. If the variable $Trigger is not $null, we know that the OnLogon event has triggered successfully.

Controlling the keyboard

Once the sessions has logged on, we can start using the Session property, which contains the Keyboard property (this is a WFICALib.KeyboardClass object). There are a few events and methods available, but we will focusing on the SendKeyDown() and SendKeyUp() methods. Both of these methods use an integer as parameter, this integer is the key code that will be sent to the session. For example, the character “a” is integer “65”.

You can use the Keys enumeration which is available in the System.Windows.Forms .Net namespace. This makes the code a bit more “readable”. By default, the System.Windows.Forms namespace is not loaded in PowerShell, this can be done as follows:

[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

After executing this command, you can use the Keys enumeration as follows:

$ICA.Session.Keyboard.SendKeyDown([int][System.Windows.Forms.Keys]::A)

This will send the “a” key to the connected ICA session. So, now we have the basics, let the fun begin! 🙂 The following code will start notepad and type some text (make sure you have a published notepad instance):

[System.Reflection.Assembly]::LoadFile("C:\Program Files (x86)\Citrix\ICA Client\WfIcaLib.dll")
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$ICA = New-Object WFICALib.ICAClientClass
$ICA.HttpBrowserAddress = "XASRV001"
$ICA.Username = "tmpuser1"
$ICA.SetProp("Password","Password!")
$ICA.Domain = "LAB"
$ICA.Application = "#Notepad"
$ICA.Launch = $true
$ICA.OutputMode = [WFICALib.OutputMode]::OutputModeNormal
$ICA.DesiredHRes = 1024
$ICA.DesiredVRes = 768
$ICA.DesiredColor = [WFICALib.ICAColorDepth]::Color16bit
$ICA.TWIMode = $true
Register-ObjectEvent -InputObject $ICA -EventName OnLogon -SourceIdentifier ICA_OnLogon
$ICA.Connect()
$Trigger = Wait-Event -SourceIdentifier ICA_OnLogon -Timeout 30
If ($Trigger -ne $null)
{
    Start-Sleep -Seconds 5
    $ICA.Session.Keyboard.SendKeyDown([int][System.Windows.Forms.Keys]::A)
    $ICA.Session.Keyboard.SendKeyDown([int][System.Windows.Forms.Keys]::W)
    $ICA.Session.Keyboard.SendKeyDown([int][System.Windows.Forms.Keys]::E)
    $ICA.Session.Keyboard.SendKeyDown([int][System.Windows.Forms.Keys]::S)
    $ICA.Session.Keyboard.SendKeyDown([int][System.Windows.Forms.Keys]::O)
    $ICA.Session.Keyboard.SendKeyDown([int][System.Windows.Forms.Keys]::M)
    $ICA.Session.Keyboard.SendKeyDown([int][System.Windows.Forms.Keys]::E)
}

Awesome! 🙂 This gives you a basic idea how to control the keyboard in an ICA session.

Controlling the mouse

Controlling the mouse in an ICA session is almost the same as controlling the keyboard. Except, the mouse methods are in the $ICA.Session.Mouse property. The most important methods are SendMouseMove(), SendMouseDown() and SendMouseUp().

And example of clicking left mouse button at a specific coordinate (x = 400, y = 400) is as follows:

$ICA.Session.Mouse.SendMouseDown(1,0,400,400)

Since you don’t really know what will be where in the session’s screen (screen resolution could vary, application window can differ in size and location), my advice is to always use keyboard shortcuts and only fall back to mouse clicks when necessary.

Hope this series helped you out with the Citrix ICO SDK. The code is in PowerShell, but can of course be ported to C#, VB or whatever language you prefer.

14 thoughts to “ICA connections using PowerShell – Part 4”

  1. Very cool! I was not aware of these capabilities of the ICA client before reading you post. I am getting some inconsistent results though. It seems sometimes the script waits for the connection to be build, and sometimes it immediately closes. Also, the key entry bit seems flaky. Any ideas what the cause of that could be?

    Great read though!

    1. Hi Johan,

      thanks for the reply.
      I never experienced these inconsistencies. Did you try adding an event handler to see wat happens when you connect/logon?

      regards,
      Floris

  2. Nice guide, but I’ve been trying do the same thing with Wfica.ocx, cause I haven’t this dll file. I registred this ocx object, but still can’t use it. (new-object -comObject Wfica.ocx). I have an error – cannot load com object type

    1. Hi Marczano,

      which version of the ICA Client did you install? The DLL file should be named WficaLib.dll, located in C:\Program Files(x86)\Citrix\ICA Client.

      1. Maybe if it is possible to work with WficaLib.dll on my ICA Client version, I’ll be glad if u could email me this file. From what I see, It is unavailable to download.

  3. Hi Floris, I am trying to adapt your script as a way of monitoring my citrix servers. I successfully logon but when I try to use the method $ICA.logoff nothing happens. Any help would be great

    1. Hi Dave,

      my testing environment is down, so I can’t check what is going on with the logoff. You could try to register the Logoff events (OnLogoffFailed, OnLogoffSessions, OnLogoffSessionsFailed) as described in part 3.

      This could give you some more information. Let me know if you need some more help.

      kind regards,
      Floris

  4. I tried using the ScalingMode property and it doesn’t seem to be changeable from ScalingModeDisabled. I set it using $ica.scalingmode=[wficalib.scalingmode]::ScalingModePercent and then set $ica.scalepercent to 90.

    When I connected the session it was still a default-sized sessioin, not 90% of the screen size and when I looked at the scalingmode property it was still set to ScalingModeDisabled.

    I am using the Windows ICA Client Version 12.1.1.1

    Thanks,
    Joel

  5. Just an FYI, I found that the events don’t seem to work with powershell v3 but you can get around it by having it launch in version 2 mode (-version 2.0)

  6. Hi Floris,

    Great post!,

    How would get the name of the application or server currently running.

    I was able to use EnumerateCCMSessions and retrieve SessionID’s.


    [System.Reflection.Assembly]::LoadFile("C:\Program Files\Citrix\ICA Client\WfIcaLib.dll")
    $ICO = New-Object WFICALib.ICAClientClass
    $ICO.OutputMode = [WFICALib.OutputMode]::OutputModeNormal
    $EnumHandle = $ICO.EnumerateCCMSessions()
    $NumSessions = $ICO.GetEnumNameCount($EnumHandle );
    "Number of live CCM sessions are:" + $NumSessions.tostring()
    for( $index = 0; $index -lt $NumSessions ;$index++)
    {

    "`n##############################################"
    $sessionid = $ICO.GetEnumNameByIndex($EnumHandle, $index)
    "Sessionid $index is $sessionid"
    $ICO.StartMonitoringCCMSession($sessionid,$true)
    $sessionCount = $ICO.GetSessionCount();
    "Current Session Count is $sessionCount"

    $ICOPropCount = $ICO.GetPropCount();
    for( $i = 0; $i -lt $ICOPropCount ;$i++)
    {
    $ICO.GetPropNameByIndex($i) + " : " + $ICO.GetPropValueByIndex($i)
    }
    "isConnected before StopMonitoringCCMSession is " + $ICO.isConnected()
    $SessionHandle = $ICO.GetSessionHandle()
    $ICO.StopMonitoringCCMSession($sessionid);
    "isConnected after StopMonitoringCCMSession is " + $ICO.isConnected()
    "##############################################"
    }
    $ICO.CloseEnumHandle($EnumHandle)

    output

    GAC Version Location
    --- ------- --------
    False v2.0.50727 C:\Program Files\Citrix\ICA Client\WfIcaLib.dll
    Number of live CCM sessions are:2

    ##############################################
    Sessionid 0 is 2546889957
    Current Session Count is 1
    Clientname : SSMCXDWIN7051
    ClientPath : C:\Program Files\Citrix\ICA Client\
    ClientVersion : 12.3.0.8
    Connected : TRUE
    NotificationReason : 0
    OutputMode : 1
    Version : 2.8
    isConnected before StopMonitoringCCMSession is True
    isConnected after StopMonitoringCCMSession is False
    ##############################################

    ##############################################
    Sessionid 1 is 1060737381
    Current Session Count is 1
    Clientname : SSMCXDWIN7051
    ClientPath : C:\Program Files\Citrix\ICA Client\
    ClientVersion : 12.3.0.8
    Connected : TRUE
    NotificationReason : 0
    OutputMode : 1
    Version : 2.8
    isConnected before StopMonitoringCCMSession is True
    isConnected after StopMonitoringCCMSession is False
    ##############################################
    0

Leave a Reply

Your email address will not be published. Required fields are marked *

Complete the following sum: * Time limit is exhausted. Please reload CAPTCHA.

This site uses Akismet to reduce spam. Learn how your comment data is processed.