Cisco Finesse Thick Client Screen Pop Gadget

Introduction

As customers are forced to upgrade Cisco Agent Desktop (CAD) to the web-based Cisco Finesse Desktop, some are left with screen pop integrations to old thick client applications (software installed natively on the user’s PC as opposed to a web app).  Since we are unlikely to force an upgrade of the customer’s legacy business application to a web client with easy-to-use APIs, we are left to integrate Cisco Finesse with the thick client.  Ugly but necessary.  The following write-up provides an overview of one way to do this.  We’re going to assume you already know basics of developing a Finesse gadget.  If not, checkout Cisco’s Finesse DevNet site and ScreenPop Sample Gadget which the below gadget is based on.

Overview

First, let’s explain the problem we are trying to solve and provide a high level overview of our solution.  In our scenario, a customer is calling into their financial institution, providing their account number to an IVR, then speaking to a customer service agent about their account.  The customer service agent will receive the customer’s account number and be able to view it in their Finesse desktop (screenshot below).

The piece that we are trying to solve here is how to push the customer’s account number from the Finesse desktop to the thick client application that holds all of the customer account information.  In this case, the thick client application is a TN3270 terminal emulator (screenshot below).  When a customer service agent is about to receive a call, we want their TN3270 terminal to pop up as the active window on the desktop and pull up the caller’s account without having to manually type in the account number.

This is pretty easy with CAD, so why is this difficult with Finesse?  Well, Finesse is a web application running in your web browser, no different than Google or Facebook, and due to security concerns, web applications are very limited in their ability to interact with software installed on your PC.  Imagine if a random website could type things into your email app or start opening and closing arbitrary windows on your desktop — not good.

To overcome this limitation, we’re going to use something called a Custom URI Scheme, which allows us to register a custom URI format – like “screenpop:12345” – that automatically launches a registered application when opened in a web browser.

At a high level, our Finesse gadget will do the following to automate the push of the customer’s account number from Finesse client to TN3270 terminal:

  1. The Finesse gadget will trigger a request to a custom Windows URI scheme when the agent state goes to RESERVED (the state right before receiving a call).
  2. The custom Windows URI scheme triggers the execution of a custom C# application located on the agent’s Windows desktop.
  3. The C# executable will then send keystrokes (of caller’s account number) to the client’s TN3270 terminal to pull up the customer’s account.

Now, let’s dive into the details …

Finesse Gadget

In our Finesse gadget, we are going to add code to an existing sample gadget to track the agent’s calls (dialogs in Finesse API speak) and state.  In the code snippet below we are checking agent state and setting the local variable acdCall to true if state is RESERVED.

handleUserChange = function(userevent) {

var currentState = user.getState();
clientLogs.log("*** The current agent state is: " + currentState);

if (currentState == 'RESERVED') {
acdCall = true;
}
};

Now we will take a look at the caller’s information provided by the IVR in callVariable1, which holds our account number (line 16 in code snippet below).  We confirm the account number is available (line 19) and a valid 9-digit number (line 25), and if so, we call our sendPop function (line 27).

handleNewDialog = function(dialog) {
    clientLogs.log("handleNewDialog");
    // get the call variable data from the dialog
    // dialog.getMediaProperties() returns an array of properties
    var callInfo = new Array();
    callInfo = dialog.getMediaProperties();
    numDialogs++;
    clientLogs.log("handleNewDialog: Number of dialogs is " + numDialogs.toString());

    if (acdCall && numDialogs == 1) {
        // Prevent the initial pop from occurring again
        // if user initiates a transfer/conference but cancels it
        if (alertCall == false) {
            alertCall = true;
            // Caller's accountNumber
            accountNumber = callInfo["callVariable1"];
            clientLogs.log("handleNewDialog:cv1=" + accountNumber);
            
            if (accountNumber == null) {
                // Add a handler for subsequent dialog events
                // where the call data will have been updated
                dialog.addHandler('change', _handleDialogChange);
            } else {
                // If the accountNumber is not 9 digits, abort the pop
                if (accountNumber.length == 9) {
                    // render the html in the gadget
                    sendPop(accountNumber);
                } else {
                    clientLogs.log("handleNewDialog:accountNumber not length 9");
                }
            }
        }
    }
},

In our sendPop function (code below), we will create and click a hyperlink that passes our accountNumber variable to a Windows URI scheme.  This request, if you manually type it into a web browser, would look like screenpop:123456789 (where 123456789 is the account number).  In standard browsers, this will do nothing useful which is why we have to setup a custom Windows URI scheme to tell the Windows OS how to handle our request.

function sendPop(accountNumber) {
    var screenpop = 'screenpop:';
    var popURL = screenpop.concat(accountNumber);
    var alertingLink= document.getElementById('alertLink');
    alertingLink.href = popURL;
    alertingLink.click();
}

Worth noting, when the browser sends the initial sendPop request to Windows, you’ll most likely get a pop-up asking you to confirm the action and allow the execution of the local executable.  The screenshots below show the pop-up from both Chrome and Firefox.  Make sure you click the “Remember my choice…” option so the agent doesn’t have to deal with this every time they receive a call.

       

 Windows Custom URI Scheme

To create a Windows URI scheme we modify the registry as shown in this Microsoft document: https://msdn.microsoft.com/en-us/library/aa767914.aspx.  The URI scheme allows us to take our sendPop request (screenpop:123456789) and pass it to a registered Windows application.  In our example, we are going to pass our account number to a C# executable (ScreenPop.exe) that we have placed on the C: drive.  The %1 parameter allows us to pass the account number variable.  The screenshot below shows the ScreenPop URI scheme configured in the agent PC registry.  Notice the “command” registry key has a string value with the location of the custom executable (C:\ScreenPop.exe).

Windows Executable

The custom URI scheme can trigger the execution of any local Windows application, but in our example, we need a way to send our account number to our old-school terminal emulator.  In order to do this, we coded up a way to send keystrokes in a small C# script.

The first thing we need to do within the C# code is find which window on the desktop is the terminal emulator and pop it up as the active window.  The tricky part here is finding the application window class name in order to execute the FindWindow function.  There’s no easy way to find this information.  What we ended up doing is running a free Windows tool called System Explorer.  In the screenshot below, you can see the class name for all active windows.  Sometimes this value makes sense and other times it’s something you’d never guess.  For example, Notepad++ has a class name of Notepad++ (makes sense), but SoapUI has a window class name of SunAwtFrame (never would have guessed).

Once we have the window class name, you can see in the code below that we run the FindWindow function (line 2) to grab our terminal emulator window.  Next we run ShowWindow (line 11) to maximize the window (in case it’s minimized).  Then we run SetForegroundWindow (line 13) to actually pop the window to the front of the desktop and set it as the active window.  Once the terminal is active, we can start sending keystrokes.  The tricky part with keystrokes is the timing.  You have to put some delay between each keystroke or the terminal will not receive them all.  In the example below, we are deleting any previous value in the active field, then we send the account number (line 34) that we’ve been passing along this entire time and finally hit the enter key (line 36) to pull up the caller’s account information.

// Get a handle to the iSeries terminal application.
IntPtr iHandle = FindWindow("SunAwtFrame", windowname);

// Verify iSeries terminal is a running process.
if (iHandle == IntPtr.Zero)
{
    MessageBox.Show("The terminal is not running.");
}
else
{
    ShowWindow(iHandle, 9);
    Thread.Sleep(200);
    SetForegroundWindow(iHandle);
    Thread.Sleep(200);
    SendKeys.SendWait("{DELETE}");
    Thread.Sleep(5);
    SendKeys.SendWait("{DELETE}");
    Thread.Sleep(5);
    SendKeys.SendWait("{DELETE}");
    Thread.Sleep(5);
    SendKeys.SendWait("{DELETE}");
    Thread.Sleep(5);
    SendKeys.SendWait("{DELETE}");
    Thread.Sleep(5);
    SendKeys.SendWait("{DELETE}");
    Thread.Sleep(5);
    SendKeys.SendWait("{DELETE}");
    Thread.Sleep(5);
    SendKeys.SendWait("{DELETE}");
    Thread.Sleep(5);
    SendKeys.SendWait("{DELETE}");
    Thread.Sleep(5);
    //Send the following keystrokes for an Account Number value
    SendKeys.SendWait(accountNumber);
    Thread.Sleep(5);
    SendKeys.SendWait("{ENTER}");
}

The first time you launch your custom executable on Windows, you’ll most likely get an Unknown Publisher warning (Windows 10 screenshot below).  There’s a few way to get around this…

  1. Sign your executable with a code signing certificate (recommended).
  2. Modify the Attachment Manager Group Policy to allow execution of .exe file types.  Not recommended as it allows ALL .exe files to execute which significantly lowers your security posture.
  3. Uncheck the “Always ask before opening this file” checkbox (shown in screenshot below).  Also not recommended as you have to do this on every agent PC.

And voila!…we have our solution for those old legacy thick clients as well as flexibility to perform many other functions on the Windows desktop.