Smooth Roaming + Responsive Design

In this post, we look at adding session reconnection event handling to our responsive design prototype using the Citrix XenApp Mobile Application SDK. Be sure to check out the video of the prototype.

In a previous article, we looked at using responsive design with enterprise applications by utilizing the Citrix XenApp Mobile Application SDK. Jonathan Chin left an interesting comment about hooking up an event handler to apply the responsive design to a reconnection event.  So, I decided to investigate doing just that and, as it turns out, it isn’t that hard.  

In the previous version, the responsive design code only kicked in on session initiation (i.e. logon) – meaning if you started a session on a fat client and then reconnected to the session on a mobile device, then the fat client display would show up on the mobile device. We don’t want that. So, by catching the reconnection event, we can re-style the app at any point (session initiation or reconnection). Start the application on a fat client and then reconnect via a mobile device and the mobile style will kick in (and vice versa).

Here’s the bit of code to take care of it (if you don’t care about the code and just want to skip to the download, just follow this link):

 

[DllImport("WtsApi32.dll")]
private static extern bool WTSRegisterSessionNotification(IntPtr hWnd, [MarshalAs(UnmanagedType.U4)]int dwFlags);

[DllImport("WtsApi32.dll")]
private static extern bool WTSUnRegisterSessionNotification(IntPtr hWnd);

private const int NOTIFY_FOR_THIS_SESSION = 0;
private const int WM_WTSSESSION_CHANGE = 0x2b1;
private const int WTS_REMOTE_CONNECT = 0x3;
private const int WTS_REMOTE_DISCONNECT = 0x4;
private bool registered = false;

protected override void OnHandleDestroyed(EventArgs e)
{
    // unregister the handle before it gets destroyed
    if(registered)
        WTSUnRegisterSessionNotification(this.Handle);

    base.OnHandleDestroyed(e);
}

protected override void OnHandleCreated(EventArgs e)
{
    base.OnHandleCreated(e);
    registered = WTSRegisterSessionNotification(Handle, NOTIFY_FOR_THIS_SESSION);
}

protected override void WndProc(ref Message m)
{
    if (m.Msg == WM_WTSSESSION_CHANGE)
    {
        if (m.WParam.ToInt32() == WTS_REMOTE_CONNECT)
        {
            // The session is in the reconnect state, so style the app for the new session
            setAppStyle();
        }

        if (m.WParam.ToInt32() == WTS_REMOTE_DISCONNECT)
        {
            // The session is in the disconnect state, so close the CMP if it is open
            if (this.cmp != null && this.cmp.IsChannelOpen())
            {
                try
                {
                    this.cmp.CloseChannel();
                }
                catch {}
            }
        }
    }
    base.WndProc(ref m);
}

 

Try It Out Yourself

Want to try it out yourself?  I have made the program and the source available so you can beat it up and come up with some more ideas.  You can download it here.

 

Citrix XenApp 6 PowerShell SDK: Getting a List of Applications with C#

This post will show you how to use the Citrix XenApp 6 PowerShell SDK to obtain a list of applications from your XenApp 6 farm. We’ll look at how to do this with using the PowerShell Runspace and how to do this using the Citrix XenApp 6 wrapper assembly.

This post will show you how to use the Citrix XenApp 6 PowerShell SDK to obtain a list of applications from your XenApp 6 farm. We’ll look at how to do this with using the PowerShell Runspace and how to do this using the Citrix XenApp 6 wrapper assembly. The examples used in this post will be using an ASP.NET website, but the code can be reused in a Windows application, Console application, web service, etc.

Note Note: Be sure to read the getting started post for information about adding the correct references to your project.

Using the PowerShell Runspace

I added a Web Form to my project named RunSpaceFactory.aspx. Here is what it looks like:

using System.Management.Automation;
using System.Management.Automation.Runspaces;

namespace WebApplication1
{
    public partial class RunSpaceFactory : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            Runspace rs = RunspaceFactory.CreateRunspace();
            rs.Open();

            PowerShell ps = PowerShell.Create();
            ps.Runspace = rs;

            PSSnapInException ex;
            rs.RunspaceConfiguration.AddPSSnapIn("Citrix.XenApp.Commands", out ex);

            ps.AddCommand("Get-XAApplication");

            // You can add a search string like this:
            // ps.AddCommand("Get-XAApplication").AddParameter("BrowserName", "n*");

            foreach (PSObject app in ps.Invoke())
            {
                Response.Write(app.Properties["DisplayName"].Value);
                Response.Write("");
            }

            rs.Close();
        }
    }
}

Lines 10 – 14 are standard PowerShell things you would do to work with PowerShell in any C# application.

Line 17 adds the Citrix PowerShell SnapIn to the Runspace so we can execute the Citrix commands.

Lines 19 – 28 lists all the applications in the XenApp 6 farm.

Note line 21 & 22. If line 22 was uncommented, this excerpt would list all the applications that start with the letter ‘n’ by adding a parameter to the Get-XAApplication command. You can use any matching pattern here like ‘%o*’ which would get all applications whose second letter is ‘o’.

Using the Citrix XenApp 6 Wrapper Assembly

When using the Citrix XenApp 6 wrapper assembly, you first need to add references to the appropriate DLLs to your project. The DLLs can be found in %ProgramFiles%\Citrix\XenApp Server SDK\bin

AddXenApp6Reference

After adding the references, here is the code to accomplish the same task from above:

using System.Management.Automation;
using System.Management.Automation.Runspaces;

using Citrix.Management.Automation;
using Citrix.XenApp.Sdk;
using Citrix.XenApp.Commands;

namespace WebApplication1
{
    public partial class ListApplications : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            GetXAApplicationByName apps = new GetXAApplicationByName();
            apps.BrowserName = new string[] {"*"};

            foreach (PSObject _app in CitrixRunspaceFactory.DefaultRunspace.ExecuteCommand(apps))
            {
                XAApplication app = (XAApplication)_app.BaseObject;

                Response.Write(app.BrowserName);
                Response.Write("");
            }
        }
    }
}

As you can see, there is quite a bit less code here.

Line 14 sets up all your Runspace stuff and adds the appropriate command.

Line 15 adds a parameter to the command. This is an interesting line because the BrowserName property expects an array of strings. I’m just passing one string here, but you could pass several to match. For example, string[] {“n*”, “%o*”} would find all apps that either started with the letter ‘n’ or the second letter was ‘o’.

We loop through the results in lines 18 – 24. Note that I cast the PSObject (generic PowerShell object) to a XAApplication object. This helps with the type safety and IntelliSense. To be fair, you can do the exact same thing in the PowerShell Runspace example above if you wanted.

The Results

Here is what the Citrix Delivery Services Console looks like concerning published applications:

Citrix Delivery Services Console Applications

Here is the output from both examples:

WebAppPS

What’s Next?

The next examples will show you how to publish and application as well as how to manage sessions. You can also get a jump start by downloading the example code below:

Getting Started with the Citrix XenApp PowerShell SDK and C#

In XenApp 6, MFCOM is out and PowerShell is in. This post is the first in a series to help you understand how to develop appliations that utilize the Citrix XenApp 6 PowerShell SDK and Microsoft C#.

At BriForum 2010, Brandon Shell and I presented a session on how to make the transition from MFCOM to PowerShell.  At the end of the session, I presented several examples on how to use the XenApp PowerShell SDK in C#.  I wanted to share some of the details on those examples in a few blog posts.  This first blog post will detail what is needed to get started developing applications that leverage the Citrix XenApp 6 PowerShell SDK.

What you need

To get started, you need to set up a development environment.  You really only need two things:

Actually, there isn’t anything set in stone that says you *have* to use Visual Studio, but it will make your life a whole lot easier.  I recommend installing Visual Studio on a XenApp server because remoting in PowerShell is less than desirable for these examples.

Creating your first project

After you install Visual Studio and the Citrix XenApp 6 PowerShell SDK, you are ready to get started with your first project.  The examples I will be showing will be web projects.  So, launch Visual Studio and select  File > New > Project…

Visual Studio New Project

VSNewProject

Add a Reference to System.Management.Automation

After you create your Project, you need to add a reference to System.Management.Automation.dll in order to do stuff with PowerShell in your project.  If you go looking for this DLL in the file system, you will find it at %ProgramFiles%\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0 <- DO NOT USE THIS FILE.  The version of System.Management.Automation you want is in the GAC.

Normally, if you want to add a reference to an assembly that lives in the GAC to your project, you can just right-click your project in Visual Studio, select Add Reference and browse for the name.  But, for some reason, this assembly isn’t like the others.  You actually have to close your project in Visual Studio and manually add a reference to System.Management.Automation by editing the .csproj file.  For example, if your project name was WebApplication1, you would need to edit WebApplication1.csproj (you can use Notepad to edit this file if you like since this file is just XML) and manually add:

<Reference Include=”System.Management.Automation” />

 

Once you manually add this reference to your project file, you will want to add a couple of using statement to your code files that will deal with PowerShell:

using System.Management.Automation;
using System.Management.Automation.Runspaces;

 

To Wrap or not to Wrap

There are basically two ways to use the Citrix XenApp 6 PoweShell SDK in a C# project:

  1. Use “traditional” PowerShell Runspace (uses common PowerShell Runspaces)
  2. Use the XenApp 6 wrapper assemply (wraps each command and paramter in a Class for safe typing)

There are pros and cons for both:

“Traditional” PowerShell Runspace pros:

  • Easy to convert existing PowerShell scripts to C#.
  • You can log all the PowerShell commands and save before running – kind or like Microsoft Exchange 2007 does.

“Traditional” PowerShell Runspace cons:

  • No type safety – meaning that commands and parameters are specified as strings – which means you can mess up by typing the wrong string making troubleshooting difficult.
  • No IntelliSense for XenApp commands/odjects in Visual Studio.

XenApp 6 Wrapper pros:

  • Type safety.  Every command and parameter is wrapped in a class so there is no chance of misspelling a string parameter or passing an incorrect parameter.
  • Enables IntelliSense in Visual Studio.

XenApp 6 Wrapper cons:

  • Does not translate well to PowerShell commands.  For instance, the PowerShell command to get an application is Get-XAApplication.  The wrapper assembly’s command is GetXAApplicationByName().

I like using the wrapper assembly personally, but in the end it is all the same.

What’s Next?

I’ll be writing a few more posts on concrete examples of using the XenApp 6 PowerShell SDK.  Stay tuned…

How to Save Web Interface Usernames in a Cookie

In the article titled “How to Enable AutoComplete for Web Interface Logon”, I explained how to enable the AutoComplete functionality of web browsers in order to save usernames and/or passwords for Citrix Web Interface. As the article explained, there are a lot of moving parts to the solution such as web browser settings, Protected Storage, JavaScript workarounds, etc. In this article, I will explain how to accomplish something similar by using cookies to remember the last username entered for Web Interface.

In the article titled “How to Enable AutoComplete for Web Interface Logon”, I explained how to enable the AutoComplete functionality of web browsers in order to save usernames and/or passwords for Citrix Web Interface. As the article explained, there are a lot of moving parts to the solution such as web browser settings, Protected Storage, JavaScript workarounds, etc. In this article, I will explain how to accomplish something similar by using cookies to remember the last username entered for Web Interface.

How it Works (if you do not really care how it works, you can just download the modification at the end of this post)

There are really only two parts to this solution. Part 1 – store the username in a cookie. Part 2 – get the username from the cookie and populate the “user name” field at logon. There is a little Web Interface SDK work to obtain the username, but I will explain what is going on.

 

Part 1 – Storing the Username in a Cookie

There are two steps to accomplish this part.

First, we need to get the username after it is validated by Web Interface. An ideal place to get the authenticated username is applistView.ascx. applistView.ascx is the user control in Web Interface that is responsible for displaying a list of applications a user has access to. So, we know if we got to applistView.ascx in the Web Interface logon/application enumeration process that a username should be available.

Second, we need to store the username in a cookie (this is pretty easy in comparison to getting the username).

Here is the code:

Modify applistView.ascx

Find the following text (around line 10):

<!–#include file=”../serverscripts/include.aspxf”–>

Paste the following right after this line

<%

//----------------------------------------------------------------
// WI mod
//----------------------------------------------------------------

string username = String.Empty;

// Get username
com.citrix.authentication.tokens.AccessToken accessToken = com.citrix.wi.pageutils.Authentication.authGetPrimaryAccessToken(wiContext.getWebAbstraction());
if(accessToken != null)
{
if(accessToken is com.citrix.authentication.tokens.GuestToken)
{
// Guest
username = wiContext.getString("Guest");
}
else
{
username = accessToken.getUserIdentity();
}
}

HttpCookie userCookie = Request.Cookies.Get("WI_username");

if(userCookie == null)
{
// Cookie doesn't exist, so create it
userCookie = new HttpCookie("WI_username");
}
else if(userCookie.Value != username)
{
// Need to update the cookie value
Response.Cookies.Set(Request.Cookies.Get("WI_username"));
}

userCookie.Value = username;
userCookie.Expires = DateTime.Now.AddDays(100);
Response.Cookies.Add(userCookie);

//----------------------------------------------------------------
// End WI mod
//----------------------------------------------------------------

%>

Code explanation:

First, we need to get an AccessToken (see line 10 above). In the Web Interface object model, an AccessToken “…encapsulates information that may be used for authorization and authentication when a Subject requires access to a resource.” What that means in layman’s terms is an AcessToken basically holds your username, password, domain, identity, etc. (you can actually use the PasswordBasedToken Interface to get the password of a user if you wanted to as described in this article).

After we get the AccessToken, we can test to see if this is an anonymous user or not (an anonymous user would have a GuestToken) on lines 12-23 above. If the token is not a GuestToken, then we know we have an authenticated user. There are currently 3 methods you can use to get the user details from the AccessToken:

  1. getAccountIdentity() – gets an AccountIdentiy object.
  2. getShortUserName() – returns the username as a string. Example – if your user identity was domain\jasonco, getShortUserName() returns “jasonco”.
  3. getUserIdentity() – returns the entire logon identity as a string. Example: domain\username or user@domain (if using UPN). I chose getUserIdentity() in the code above.

The rest of the code is pretty straight forward – it just creates or updates the cookie named WI_username. You could actually name this cookie anything you wanted.

Part 2 – Retrieving/Populating the Username from the Cookie

This part is quite a bit easier than the first part. All we need to do is to get the cookie, if any, from the previous code and populate the username field on the log on screen. The logon screen code can be found in loginMainForm.inc.

Here is the code:

Modify loginMainForm.inc

Find the following text (around line 106):

<td colspan="2">
    <input type='text' name='<%=Constants.ID_USER%>' id='<%=Constants.ID_USER%>'
        class='loginEntries<%=viewControl.getExplicitDisabled()?" loginEntriesDisabled":""%>'
        maxlength='<%=Constants.LOGIN_ENTRY_MAX_LENGTH%>' <%=viewControl.getExplicitDisabledStr()%>
        tabindex='<%=Constants.TAB_INDEX_FORM%>'

Paste the following right after this text:

<%
//----------------------------------------------------------------
//       WI mod
//----------------------------------------------------------------

HttpCookie userCookie = Request.Cookies.Get("WI_username");

if(userCookie != null)
{
%>
    value='<%=userCookie.Value %>'
<%

}

//----------------------------------------------------------------
//       End WI mod
//----------------------------------------------------------------
%>

Code explanation:

The <input… tag above is the field where you fill in your username in loginMainForm.inc. The code inserted above sets the value of the input tag to whatever was stored in the WI_username cookie (if there was anything stored in the cookie at all).

If you want to implement this in your environment and do not feel like copying and pasting all this code, you can download the modified files below:

download Modification for Web Interface 5.1