1 May 2016

I always love when I’m asked at work to do the maximum imaginable result with the least amount of resources. It’s like being asked to move a mountain and handed a single rope. While not impossible (you could do it in many trips with the rope tied to chunks), it does make you wonder, while you’re laboring, how on Earth you got yourself into this.

Thankfully I was ready for a challenge. Recent projects were the same ol’, same ol’, and I was bored. When I was asked to take the reigns on vetting an app virtualization product to use I was pretty stoked. I spent a few weeks on one product that has a great Cloud capability but we eventually settled on Microsoft’s App-V for various reasons. I was then asked to figure out how to publish apps without the need of a management server and database. 😐

Management doesn’t want the overhead of maintaining those two additional servers…. OK. I took it as a challenge and set to figuring it out. One goal I made for myself was to figure out a way to do it in Powershell that was generic. We use PVS with out Citrix environment but the thought of having to open up an image every time we add or change an App-V package wasn’t very appealing. After all, the goal is the least amount of maintenance and hassle. Mind you, integration for App-V packages is now built in with newer versions of XenApp but we aren’t there yet. So, the initial foray was quite easy and successful. To publish an individual app it only takes 3 lines of Powershell code. However, that’s a specific application and I wanted to go generic. That’s where the fun began.

My inital plan was to write a script that lived in the PVS image and would accept parameters. I’d use the published app in the XenApp console to pass those parameters depending on what app was published. My two parameters were the name of the App-V package and the path to the EXE that needed to be launched. The script was capable of taking the name of the package and building the DFS path to where the packages are stored. The folder names are the same as the package names for ease of use. The script would then add the package to the local disk and then publish it globally. The path to the EXE is, well, to launch the correct app. This didn’t work out so well because the EXE’s for a package are burried deep in the folder structure for App-V and the Windows command line can only accept a command that is 255 characters long or less.

I then dug into the cmdlets for App-V looking for a way to identify executables based off a package. Nothing in the way of EXE’s. I then tried creating shortcuts in a folder at the root of C inside the package’s XML configuration. That failed and it appears an App-V package wont write a shortcut in most directions. It has to be either the public desktop, start menu, or inside the packages folder structure. I figured if I can’t do it by manually editing the XML file, then perhaps I can do it by adding it in the package itself. Nope. It writes the code to the XML file to create the shortcut just like I had manually, but it never actually creates it. I’m assuming this probably isn’t a bug, since virtualized apps are supposed to be contained and writing shortcuts anywhere on the computer would be a mess. It’s probably just an undocumented feature.

After a little bit of feeling frustrated I got the sudden inspiration to return to the XML file. Why? Because the XML file lists every single EXE that was packaged! I didn’t need to pass an entire path because I could just read it from the XML file. So that’s what I did. My second parameter in the Citrix published app was now just the EXE name I needed to launch. The publishing script in the PVS image now, after adding and publishing the package, will read in the XML configuration file and search it for the EXE name being passed to it. Once it finds it, it takes the path for it in the XML, does a little string modification in the way of parsing, replacing, and concatenating, and voila, I have the entire path to the program I need to launch.

It took me about a day and a half to finish the entire script. I added a couple more things like popping up an error message if it encountered a problem adding and publishing the App-V package. Also, a loading screen that cleverly waits for the app to launch and show up before disappearing so the end-user doesn’t think nothing is happening. Script is below. I also published it as my first script to poshcode.org.

The location for the Citrix published app looks something like this: powershell.exe -file C:\Scripts\AppVAddAndPublish.ps1 “AppName” “Exename.exe”
(don’t forget to set-executionpolicy in the PVS image or override it here)

###################################################################
##  Description:  Generic script for publishing and launching App-V
##  packages in Citrix published app is calls the script and passes
##  it the name of the package and the name of the EXE to launch.  
##  App-V packages should be saved in a DFS structure that uses the
##  name of the app as the folder.
##
##  This script is provided as is and may be freely used and 
##  distributed so long as proper credit is maintained.
##
##  Written by: thegeek@thecuriousgeek.org
##  Website:  http://thecuriousgeek.org
##  Date Modified:  05-01-2016
###################################################################


param(
	[string]$appName,
	[string]$exeName
)

# Window message to the user so they don't think nothing is happening
Write-Host "`n`n                        Your application is loading..."

Import-Module AppvClient
# Try and get package to see if it's loaded
$package = Get-AppVClientPackage -Name $appName

# If the package isn't already loaded, load it
If ( !$package ) {
	Try {
		# Add and publish package.  Make sure to update with your own path
		$package = Add-AppVClientPackage \\PathToApp-VPackageStorage\$appName\$appName.appv
		Publish-AppVClientPackage $appName -Global | Out-Null
		$didComplete = $true
	} Catch {
		# If it didn't complete, popup an error message to the user with details
		$errorMessage = "Error Item: $_.Exception.ItemName `n`nError Message: $_.Exception.Message"
		$didComplete = $false
		$wshell = New-Object -ComObject Wscript.Shell
		$wshell.Popup($errorMessage,0,"Error loading application",0x0)
	}
} else {
	# Package is already loaded
	$didComplete = $true
}

# In order to know when the app has launched, we'll check the number of window titles
# This is the count before we try to start the app
$windowTitleCount = (get-process | where {$_.MainWindowTitle}).Count

If ($didComplete) { 
	# Get the local package directory as set in App-V Config, used for building path to EXE
	# Powershell can't expand regular environment variables so have to use CMD to echo it back
	$cmdExpand = "cmd /c echo $( (Get-AppVClientConfiguration | where {$_.Name -eq 'PackageInstallationRoot'}).Value )"
	$packageRoot = Invoke-Expression $cmdExpand
	$packagePath = "$packageRoot\$($package.PackageId)\$($package.VersionId)\Root\VFS"
	# Pull in deployment config because it contains the path to all executables.  Make sure to update with your own path
	[XML]$packageConfig = Get-Content "PathToApp-VPackageStorage\$appName\$($appName)_DeploymentConfig.xml"
	$packageApps = $packageConfig.DeploymentConfiguration.UserConfiguration.Applications.Application.Id
	# Loop through each app/exe listed in the config and find the one that contains the $appExe passed to the script
	ForEach ($app in $packageApps) {
		If ($app -match $exeName) {
			$appPath = $app
			break
		}
	}
	# Get rid of the special characters App-V uses to reference variables in it's application path so we can create the real path
	$appPath = $appPath -replace "(\[|\{|\}|\])",""  #previous regex  [^0-9a-zA-Z\\\s\.]
	$fullPath = "$packagePath\$appPath"
	Start $fullPath
	
	# Check number of window titles again and keep looping until the count is greater than the starting one we took earlier
	$currentTitleCount = (get-process | where {$_.MainWindowTitle}).Count
	While ($windowTitleCount -ge $currentTitleCount) {
		$currentTitleCount = (get-process | where {$_.MainWindowTitle}).Count
	}
}

Of course, now we’re upgrading to 7.8 for Citrix so all of this work is probably going to be useless! It was fun and challenging anyway 🙂


There are no comments.



You must be logged in to post a comment.