Our organization has been exploring upgrading our Citrix environment from 6.5 to 7.X. The biggest road blocks we’ve been experiencing? Various nuanced features in Citrix XenApp 6.5 don’t work or are not supported in 7.X.
This brings me to this post and an example of the difficulties we are facing, my exploration of solutions to this problem, and our potential solution.
In Citrix Web Interface 5.1+ you can create a URI with a set of application launch parameters and those parameters would be passed to the application. This is detailed in this Citrix article here (CTX123998). For us, these launches occur with a Site (WI5.4 terminology) or Store (Storefront terminology) that allows anonymous (or unauthenticated) users.
By following that guide and modifying your Web Interface you can change one of your sites so that it accepts launch parameters. You simply enter a specific URI in your browser, and the application would launch with said parameters. An example:
http://bottheory.local/Citrix/XenApp/site/launcher.aspx?CTX_Application=Citrix.MPS.App.XenApp.Notepad&LaunchId=1263894298505&NFuse_AppCommandLine=C:\Windows\WindowsUpdate.log
This allows you to send links around to other people and they can click to automatically launch an application with the parameters specified. If you are really unlucky, your org might document this as an official way to launch a hosted application and this actually gets coded into certain applications. So now you may have tens of local apps utilizing this as an acceptable method to launch a hosted application. For an organization, this may be an acceptable way to launch certain hosted applications since around ~2008 so this “feature”, unfortunately, has built up quite a bit of inertia.
We can’t let this feature break when we move to StoreFront. We track the number of launches from these hosted applications and it’s in the hundreds/thousands per day. This is a critical and well used feature.
So how does this work in Web Interface and what actually happens?

URI substitution and launch process
The modifications that you apply add a new ‘query’ to the URI that is picked up. This ‘query’ is “NFuse_AppCommandLine” and the value it is equal to (“C:\Windows\WindowsUpdate.log” in my example) is passed into the ICA file.
The ICA file, when launched, will pass the parameter to a special string “%**” the is set on the command line of the published application. This token “%**” gets replaced by the parameter specified in the ICA which then generates the launch string. This string is executed and the result is the program launches.
Can we do this with Storefront? Well, first things first, is this even possible with XenApp/XenDesktop 7.X? In order to test this I created an application and specified the special token.
I then generated an ICA file and manually modified the LongCommandLine to add my parameter. I then launched it:
Did it work?

Yes, it worked.
Success! So XenApp/XenDesktop 7.X will substitute the tokens with the LongCommandLine in the ICA file. Excellent. So now we just need Storefront to take the URI and add it to the LongCommandLine. Storefront does not do this out of the box.
However, Citrix offers a couple of possible solutions to this problem (that I explored).
StoreFront WebAPI
StoreFront Store Customization SDK
What are these and how do they work?
StoreFront Web API
This API is billed as:
“Write a new Web UI or integrate StoreFront into your own Web portal”
We are going to need to either modify Storefront or write our own in order to take and parameters in the URI. I decided to write our own. Using the ApiExample.html in the WebAPI I added the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
var getUrlParameter = function getUrlParameter(sParam) { var sPageURL = decodeURIComponent(window.location.search.substring(1)), sURLVariables = sPageURL.split('&'), sParameterName, i; for (i = 0; i < sURLVariables.length; i++) { sParameterName = sURLVariables[i].split('='); if (sParameterName[0] === sParam) { return sParameterName[1] === undefined ? true : sParameterName[1]; } } }; var NFuse_AppCommandLine = getUrlParameter('NFuse_AppCommandLine'); var CTX_Application = getUrlParameter('CTX_Application'); |
This looks into the URI and allows you to get the value of either query string. In my example I am grabbing the values for “CTX_Application” and “NFuse_AppCommandLine”.
I then removed a bunch of the authentication portions from the ApiExample.html (I’ll be using an unauthenticated store). In order to automatically select the specified application I added some javascript to check the resource list and get the launch URL:
1 2 3 4 5 6 7 8 9 10 11 |
function listResourcesSuccess(data) { resourcesData = data.resources; for (var i = 0; i < resourcesData.length; i++) { if (resourcesData[i].name == CTX_Application) { console.log("Name Matches"); console.log(resourcesData[i]); prepareLaunch(resourcesData[i]); } } } |
There is a function within the ApiExample.html file that pulls the ICA file. Could we take this and modify it before returning it with the LongCommandLine addition? We have the NFuse_AppCommandLine now.
It turns out, you can. You can capture the ICA file as a variable and then using javascripts ‘.replace’ method you can modify the line so that it contains your string. But how do you pass the ICA file to the system to launch?
This is how Citrix launches the ICA file in the ApiExample.html:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// To initiate a launch, an ICA file is loaded into a hidden iframe. // The ICA file is returned with content type "application/x-ica", allowing it to be intercepted by the Citrix HDX // browser plug-in in Firefox/Chrome/Safari. For IE, the user may be prompted to open the ICA file. $('#hidden-iframes').append('<iframe id="' + frameId + '" name="' + frameId + '"></iframe>'); if (csrfToken != null) { icaFileUrl = updateQueryString(icaFileUrl, "CsrfToken", csrfToken); } // Web Proxy request to load the ICA file into an iframe // The request is made by adding icaFileUrl = updateQueryString(icaFileUrl, 'launchId', currentTime); $("#" + frameId).attr('src', icaFileUrl); |
It generates a URL then sets it as the src in the iframe. The actual result looks like this:
1 |
<iframe id="launchframe_1493045843944" name="launchframe_1493045843944" src="Resources/LaunchIca/QUhTQ1RYLk5vdGVwYWQtWGVuQXBwNjUgSGVucnk-.ica?CsrfToken=DB1E8E5D042DB7226C2635FCB855BF3C&launchId=1493045843944"></iframe> |
The ICA file is returned in this line:
1 |
src="Resources/LaunchIca/QUhTQ1RYLk5vdGVwYWQtWGVuQXBwNjUgSGVucnk-.ica?CsrfToken=DB1E8E5D042DB7226C2635FCB855BF3C&launchId=1493045843944" |
When we capture the ICA file as a variable the only way that I’ve found you can reference it is via a blob. What does the src path look like when do that?
1 |
src="blob:http://bottheory.local/3f19b9e1-403e-4ab7-b741-a4c77a486b95" |
Ok, this looks great! I can create an ICA file than modify it all through WebAPI and return the ICA file to the browser for execution. Does it work?
Yes and no. 🙁
It works in Chrome and Firefox, but IE doesn’t auto-launch. It prompts to ‘save’ a file. Why? IE doesn’t support opening ‘non-standard’ blobs. MS offers a method called “msSaveOrOpenBlob” which you can use instead, and this method then prompts for opening the blob. This will work for opening the ICA file but now the end user requires an extra step. So this won’t work. It needs to be automatic like its supposed to be for a good experience.
So WebAPI appears to offer part of the solution. We can capture the nFuse_AppCommandLine but we need to get it to LongCommandLine.
At this point I decided to look at the StoreFront Store Customization SDK. It states it has this ability:
Post-Launch ICA file—use this to modify the generated ICA file. For example, use this to change ICA virtual channel parameters and prevent users from accessing their clipboard.
That sounds perfect!
StoreFront Store Customization SDK
The StoreFront store customization SDK bills one of its features as:
The Store Customization SDK allows you to apply custom logic to the process of displaying resources to users and to adjust launch parameters. For example, you can use the SDK to control which apps and desktops are displayed to users, to change ICA virtual channel parameters, or to modify access conditions through XenApp and XenDesktop policy selection.
I underlined the part that is important to me. That’s what I want, exactly! I want to adjust launch parameters.
To start, one of the tasks that I need is to take my nFuse_AppCommandLine and get it passed to the SDK. The only way I’ve found to make this happen is to enable ‘forwardedHeaders‘ in your web.config file.
1 2 3 4 5 6 |
<communication attempts="1" timeout="00:03:00" loopback="On" loopbackPortUsingHttp="80"> <proxy enabled="false" processName="Fiddler" port="8888" /> <forwardedHeaders> <header name="nFuseAppCMDLine" /> </forwardedHeaders> </communication> |
With this, you need to set your POST/GET Header on your request to Storefront to get this parameter passed to the SDK. Here is how I setup my SDK:
Install the “Microsoft Visual Studio Community Edition” on a test StoreFront server.
Download the StoreCustomizationSDK and open the ‘Customization_Launch’ project file:
Right-click ‘Customization_Launch’ and select ‘Properties’.
Modify the Outpath in Build to the “%site%\bin” location. This is NOT the %site%Web, but just the %site%.
Open the LaunchResultModifer.cs
Set a breakpoint somewhere in the file.
Build > Build ‘Customization_Launch’
Ensure the build was successful:
If you get an error message:
1 2 3 4 5 6 7 8 9 |
--------------------------- Microsoft Visual Studio --------------------------- A project with an Output Type of Class Library cannot be started directly. In order to debug this project, add an executable project to this solution which references the library project. Set the executable project as the startup project. --------------------------- OK --------------------------- |
Just ignore it. We’re compiling a library and not an executable and that’s why you get this message.
Now we connect to the debugger to the ‘Citrix Delivery Services Resources’. Select ‘Attach to Process…’
Select the w3wp.exe process whose user name is ‘Citrix Delivery Services Resources’. You may need to select ‘Show processes from all users’ and click ‘ Attach.
Click ‘Attach’.
Browse to your website and click to launch an icon:
Your debugger should pause at the breakpoint:
And you can inspect the values.
At this point I wasn’t interested in trying to re-write the ApiExample.html to get this testing underway, I instead used PowerShell to submit my POST’s and GET’s. Remember, I’m using an unauthenticated store so I could cut down on the requests sent to StoreFront to get my apps. I found a script from Ryan Butler and made some modifications to it. I modified it to remove the parameters since I’m doing testing via hardcoding 🙂
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
$unauthurl = "http://bottheory.local/Citrix/SDKTestSiteWeb/" $appname = "Notepad 2016 - PLB" write-host "Requesting ICA file. Please Wait..." -ForegroundColor Yellow #Gets required tokens $headers = @{ "Accept"='application/xml, text/xml, */*; q=0.01'; "Content-Length"="0"; "X-Citrix-IsUsingHTTPS"="Yes"; "Referer"=$unauthurl; } Invoke-WebRequest -Uri ($unauthurl + "Home/Configuration") -MaximumRedirection 0 -Method POST -Headers $headers -SessionVariable SFSession #Gets resources $headers = @{ "Content-Type"='application/x-www-form-urlencoded; charset=UTF-8'; "Accept"='application/json, text/javascript, */*; q=0.01'; "X-Citrix-IsUsingHTTPS"= "Yes"; "Referer"=$unauthurl; "format"='json&resourceDetails=Default'; } $content = Invoke-WebRequest -Uri ($unauthurl + "Resources/List") -MaximumRedirection 0 -Method POST -Headers $headers -SessionVariable SFSession #Get ICA Launch URL $resources = $content.content | convertfrom-json $resourceurl = $resources.resources|where{$_.name -like $appname} #Launch ICA file, attach customheader (nFuseAppCMDLine) $headers = @{ "Accept"='application/xml, text/xml, */*; q=0.01'; "Content-Length"="0"; "X-Citrix-IsUsingHTTPS"="Yes"; "nFuseAppCMDLine"="C:\Windows\WindowsUpdate.log"; "Referer"=$unauthurl; } $ICA = Invoke-WebRequest -Uri ($unauthurl + $resourceurl.launchurl) -MaximumRedirection 0 -Method GET -Headers $headers -SessionVariable SFSession $ICA.RawContent |
Executing the powershell script and examining a custom variable with breakpoint shows us that our header was successfully passed into the file:
Awesome! So can we do something with this? Citrix has provided a function that does a find/replace of the value you want to modify. To enable it, we need to add it the ICAFile.cs:
Browse to ICAFile.cs
At this point we can use the helper methods within the IcaFile.cs. To do so, just add “using Examples.Helpers;” to the top of the file:
I cleaned out the HDXRouting out of the file, grabbed the nFuseAppCMDLine header, and then returned the result:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
/************************************************************************* * * Copyright (c) 2013-2015 Citrix Systems, Inc. All Rights Reserved. * You may only reproduce, distribute, perform, display, or prepare derivative works of this file pursuant to a valid license from Citrix. * * THIS SAMPLE CODE IS PROVIDED BY CITRIX "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * *************************************************************************/ using Citrix.DeliveryServices.ResourcesCommon.Customization.Contract; using Examples.Helpers; namespace StoreCustomization_Launch { // It is not necessary to implement both ILaunchResultModifier and IHdxRoutingModifier if only customizing // the ICA file generation or only customizing the HDX Routing customization. public class LaunchResultModifier : ILaunchResultModifier { public bool RunExtendedValidation { get { return false; } } public bool ReturnOriginalValueOnFailure { get { return false; } } #region ICA File customization public string Modify(string valueToModify, CustomizationContextData context) { Tracer.TraceInfo("Resource SDK: Launch customization point: ICA file modification"); var icadetails = new IcaFile(valueToModify); //get header we pass in as a variable. var nFuseAppCMDLine = context.HttpContext.Request.Headers["nFuseAppCMDLine"]; //set LongCommandLine to the header variable icadetails.SetPropertyValue(IcaFile.ApplicationSection, "LongCommandLine", nFuseAppCMDLine); // get modified string back from helper breakdown class. string modifiedIcaFile = icadetails.ToString(); //return the result return modifiedIcaFile; } #endregion } } |
The result?
Success! We’ve used the WebAPI and the StoreFront Customization SDK to supply a header, modify the ICA file, and return it with the value needed! The ICA file returned works perfectly! Ok, so I’m thinking this looks pretty good right?! We just need to get the iframe to supply a custom header when the src gets updated.
Except I don’t think it’s possible to tag a custom header when you update a src in an iframe. So then I started thinking maybe I can use a cookie! If I can pass a cookie into the Storefront Customization SDK I could use that instead of the header. Unfortunately, I do not see anyway to query a custom cookie or pass a custom cookie into the SDK. Citrix appears to have (rightfully!) locked down Storefront that this doesn’t seem possible. No custom cookies are passed into the httpcontext (that I can see).
So all this work appears to be for naught (outside of an exercise on how one could setup and customize Storefront).
But what about the Storefront feature “add shortcuts to websites“?
Although doing that will allow to launch an app via a URL, it doesn’t offer any method to pass values into the URL. So it’s a non-starter as well.
Next will be a workaround/solution that appears to work…
Read More