Monday, October 1, 2012

PowerShell Application for Deleting ThinApp Cache

User self-service is a win-win. Users are empowered to solve their own problems, and admins are freed up to do bigger things (which sometimes includes fixing the core issue behind the problem you are self-servicing). That is exactly the scenario a cohort and I found ourselves in. We inherited a VMWare View VDI environment where all of the applications were packaged in ThinApp. ThinApp is great is some scenarios, but it can also be problematic. The users of the VDI environment were constantly experiencing application problems that required them to exit the application and delete the ThinApp cache from their AppData directory. This was frustrating for them, and time consuming for us as we tried to help them resolve their issues while also trying to stabilize the environment. In order to free up our time and help the users achieve quicker resolution, I turned to PowerShell to create a GUI for users to delete their own ThinApp cache. The script uses PowerShell to see what items are in a user's ThinApp cache. It then generates a GUI that let's them select the application that is giving them problems. The script then kills the application and deletes the cache. We gave them an icon to execute the script app.

This is what the GUI looks like when a user launches it:
Below is the script that creates this window and deletes the cache. It relies on the .NET System.Windows.Forms to create the application window.


#$ErrorActionPreference = "silentlycontinue"

# Gets environment variables
$username = get-content env:username
$appdata = get-content env:AppData
$ScriptPath = Split-Path -Path $MyInvocation.MyCommand.Path -Paren

$ThinAppCache = $appdata + "\Thinstall"
$ThinAppUnreg = $ThinAppCache + "\UnRegister"

# Finds all entries in the ThinApp Cache
if (test-path $ThinAppCache) {
    $CacheItems=@()
get-childitem $ThinAppCache | select-object Name | % { 
        if ($_.Name -ne "Unregister") { $CacheItems = $CacheItems + $_.Name }
    }
}

# Creates Form
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") 
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") 
[System.Windows.Forms.Application]::EnableVisualStyles()

$objForm = New-Object System.Windows.Forms.Form 
$objForm.Text = "ThinApp Cache Deleter" 
$objForm.Size = New-Object System.Drawing.Size(320,100)
$objForm.MaximumSize = New-Object System.Drawing.Size(320,800)
$objForm.AutoSize = $true
$objForm.StartPosition = "CenterScreen"
$objForm.Icon = new-object System.Drawing.Icon("$ScriptPath\delete-thinappcache.ico")
$objForm.WindowState = [System.Windows.Forms.FormWindowState]"Normal";

#Sets Enter and Escape Keys to Close Form
$objForm.KeyPreview = $True
$objForm.Add_KeyDown({if ($_.KeyCode -eq "Enter") {$result=$objListBox.SelectedItem;$objForm.Close()}})
$objForm.Add_KeyDown({if ($_.KeyCode -eq "Escape") {$result="Cancel";$objForm.Close()}})

$objTableLayout = new-object "System.Windows.Forms.TableLayoutPanel" 
$objTableLayout.AutoSize = $true 
$objTableLayout.width = 320
$objTableLayout.AutoSizeMode = "GrowAndShrink"
$objTableLayout.Dock = "Fill" 
$objTableLayout.CellBorderStyle = "none"
$objTableLayout.RowCount = 3
$objTableLayout.ColumnCount = 2
[void]$objTableLayout.ColumnStyles.Add((new-object System.Windows.Forms.ColumnStyle([System.Windows.Forms.SizeType]::Percent, 50)))
[void]$objTableLayout.ColumnStyles.Add((new-object System.Windows.Forms.ColumnStyle([System.Windows.Forms.SizeType]::Percent, 50)))
$objForm.Controls.Add($objTableLayout)

$objLabel = New-Object System.Windows.Forms.Label
$objLabel.MaximumSize = New-Object System.Drawing.Size(320,600)
$objLabel.AutoSize = $true
$objLabel.Text = "Please select ThinApp Cache to delete."
$objLabel.Dock = "Fill"
$objTableLayout.Controls.Add($objLabel)
$objTableLayout.setColumnSpan($objLabel,$objTableLayout.ColumnCount)

$objListBox = New-Object System.Windows.Forms.ListBox 
$objListBox.Dock = "Fill"
$objListBox.Height = 80
$CacheItems | % { 
    $item = $_
    [void] $objListBox.Items.Add($item) 
    }
$objListBox.SelectedIndex = 0
$objTableLayout.Controls.Add($objListBox)
$objTableLayout.setColumnSpan($objListBox,$objTableLayout.ColumnCount)

$DeleteButton = New-Object System.Windows.Forms.Button
$DeleteButton.Dock = "Fill"
$DeleteButton.Text = "Delete"
$DeleteButton.Add_Click({$result=$objListBox.SelectedItem;$objForm.Close()})
$objTableLayout.Controls.Add($DeleteButton)

$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Dock = "Fill"
$CancelButton.Text = "Cancel"
$CancelButton.Add_Click({$result="Cancel";$objForm.Close()})
$objTableLayout.Controls.Add($CancelButton)

$objForm.Topmost = $True


$objForm.Add_Shown({$objForm.Activate();$objForm.WindowState = [System.Windows.Forms.FormWindowState]"Normal"})

[void] $objForm.ShowDialog()

$result

#Acts on form result
if ($result -ne "Cancel") {
    # Find and kill all app exe's using the vbs files in the apps UnRegister folder entry
    get-childitem $ThinAppUnreg | % { if ($_.Name -like "$result*") { $AppUnreg = $_.Name } }
    $AppUnregPath = $ThinAppUnreg + "\" + $AppUnreg

    get-childitem $AppUnregPath | where-object {$_.Name -like "*.exe.vbs"} | select-object Name | % {
        $_.Name
    $ProcessName = $_.Name -replace ".exe.vbs"
        $ProcessName = $ProcessName.Substring(1)
        write-host Process: $ProcessName
        $process = get-process $ProcessName
    stop-process -inputobject $process -force
        while ($process.HasExited -eq $false) {
            start-sleep -m 10
        }
    }
    
    $AppCache = $ThinAppCache + "\" + $result
    $i=0
    while ((test-path $AppCache) -and ($i -ne 10)) {
        start-sleep -m 100
        remove-item $AppCache -recurse -force
        $i++
    }
    
    if (test-path $AppCache) {
        $Msg = "Error deleteting ThinApp Cache for " + $result + ". Please log out and back in and try to delete the cache again."
        [System.Windows.Forms.MessageBox]::Show($Msg , "Error" , 0, "Error")
    } else {
        $Msg = "ThinApp Cache deleted for " + $result + "!"
        [System.Windows.Forms.MessageBox]::Show($Msg , "Success" , 0, "Exclamation")
    }
}


No comments:

Post a Comment