param ( [switch]$restart = $FALSE, [switch]$norestart = $FALSE, [switch]$verbose = $FALSE ) $retval = $true # # Uninstall any Tricerat Product and cleanup any remaining files # if($verbose) { Write-Host 'Checking for Tricerat products...' } if($verbose) { Write-Host } $applist = Get-WmiObject -class Win32_Product | Where-Object { $_.Name -like "*Simplify Suite*" -or $_.Name -like "*Simplify License*" -or $_.Name -like "*Simplify Scanning*" -or $_.Name -like "*Simplify Cross-Platform Gateway*" -or $_.Name -like "*Simplify Print Server*" -or $_.Name -like "*ScrewDrivers*" } if($applist -eq $null) { if($verbose) { Write-Host 'No Tricerat products to uninstall' } } else { foreach($app in $applist) { if($verbose) { Write-Host 'Uninstalling ' + $app.Name } $errcode = $app.Uninstall().ReturnValue if($errcode -ne 0) { $retval = $false if($verbose) { Write-Host 'Failed to uninstall ' + $app.Name + ' with error code: ' + $errcode } } } # Wait 5 seconds for additional cleanup (services to be fully removed, etc.). sleep -Milliseconds 5000 } # # Delete Services # if($verbose) { Write-Host } if($verbose) { Write-Host 'Checking for Tricerat services...' } if($verbose) { Write-Host } $servicelist = Get-WmiObject Win32_Service -Filter "DisplayName LIKE '%Simplify License%' or DisplayName LIKE '%Simplify Lockdown%' or DisplayName LIKE '%Simplify Notification Service%' or DisplayName LIKE '%Simplify Profiles%' or DisplayName LIKE '%Simplify Stability%' or DisplayName LIKE '%ScrewDrivers%' or DisplayName LIKE '%Simplify Print Server%' or DisplayName LIKE '%Simplify Cross-Platform Gateway%' or DisplayName LIKE 'Print Job Reporting'" if($servicelist -eq $null) { if($verbose) { Write-Host 'No Tricerat services to uninstall' } } else { foreach($service in $servicelist) { if($service.State -ne 'Stopped') { if($verbose) { Write-Host 'Stopping ' + $service.DisplayName } $errcode = $service.StopService().ReturnValue if($errcode -ne 0) { $retval = $false if($verbose) { Write-Host 'Failed to stop service ' + $service.DisplayName + ' with error code: ' + $errcode } } else { # Wait for the service to report that it is stopped. $maxRepeat = 20 $status = "Stopped" do { $count = (Get-Service $service.Name | ? {$_.status -eq $status}).count $maxRepeat-- sleep -Milliseconds 600 } until ($count -eq 0 -or $maxRepeat -eq 0) } } if($verbose) { Write-Host 'Deleting ' + $service.DisplayName } $errcode = $service.delete().ReturnValue if($errcode -ne 0 -And $errcode -ne 16) # Error code 16 indicates that the service has already been marked for deletion. { $retval = $false if($verbose) { Write-Host 'Failed to delete ' + $service.DisplayName ' with error code: ' $errcode } } } } # # Clean Registry # if($verbose) { Write-Host } if($verbose) { Write-Host 'Cleaning up registry keys...' } if($verbose) { Write-Host } # # Define a list of registry keys that we want to delete # $registryList = @('HKCU:\Software\Tricerat' , 'HKCU:\Software\WOW6432Node\Tricerat' , 'HKLM:\Software\Tricerat' , 'HKLM:\Software\WOW6432Node\Tricerat' , 'HKLM:\SYSTEM\CurrentControlSet\Control\Print\Monitors\SD6PICA6' , 'HKLM:\SYSTEM\CurrentControlSet\Control\Print\Monitors\SD6PPDF' , 'HKLM:\SYSTEM\CurrentControlSet\Control\Print\Monitors\SD6PRDP5' , 'HKLM:\SYSTEM\CurrentControlSet\Control\Print\Monitors\SD6PTCP' , 'HKLM:\SYSTEM\CurrentControlSet\Control\Print\Monitors\SD4PICA4' , 'HKLM:\SYSTEM\CurrentControlSet\Control\Print\Monitors\SD4PICA6' , 'HKLM:\SYSTEM\CurrentControlSet\Control\Print\Monitors\SD4PPDF' , 'HKLM:\SYSTEM\CurrentControlSet\Control\Print\Monitors\SD4PRDP5' , 'HKLM:\SYSTEM\CurrentControlSet\Control\Print\Monitors\SD4PTCP' , 'HKLM:\SYSTEM\CurrentControlSet\Control\Print\Environments\Windows NT x86\Drivers\Version - 3\ScrewDriver4' , 'HKLM:\SYSTEM\CurrentControlSet\Control\Print\Environments\Windows NT x86\Drivers\Version - 3\ScrewDriver6' , 'HKLM:\SYSTEM\CurrentControlSet\Control\Print\Environments\Windows x64\Drivers\Version - 3\ScrewDriver4' , 'HKLM:\SYSTEM\CurrentControlSet\Control\Print\Environments\Windows x64\Drivers\Version - 3\ScrewDriver6' ) # # Loop through list. If it exists, remove it # foreach($registryPath in $registryList) { if(Test-Path $registryPath) { if($verbose) { Write-Host 'Removing ' $registryPath } Remove-Item -Path $registryPath -recurse sleep -Milliseconds 500 if(Test-Path $registryPath) { $retval = $false if($verbose) { Write-Host 'Failed to cleanup ' $registryPath } } } } # Remove our entries in AppInit data. if($verbose) { Write-Host 'Cleaning up AppInit registry entry...' } $appInitKey = 'HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Windows' $lockRegex = 'Lock(32|64)\.dll' $stabRegex = 'StabAI(32|64)\.dll' $commaRegex = ',?' # Get the current AppInit_DLLs value. $currentAppInitValue = (Get-ItemProperty -Path $appInitKey -Name AppInit_DLLs).AppInit_DLLs if($verbose) { Write-Host 'Current AppInit registry entry: ' $currentAppInitValue } # Remove the Lockdown and Stability entries, accounting for comma position. if([regex]::match($currentAppInitValue, $lockRegex).Index -eq 0) { $lockRegex = $lockRegex + $commaRegex } else { $lockRegex = $commaRegex + $lockRegex } $newAppInitValue = $currentAppInitValue -replace $lockRegex, "" if([regex]::match($newAppInitValue, $stabRegex).Index -eq 0) { $stabRegex = $stabRegex + $commaRegex } else { $stabRegex = $commaRegex + $stabRegex } $newAppInitValue = $newAppInitValue -replace $stabRegex, "" # Overwrite the AppInit_DLLs value with the entries removed. if($verbose) { Write-Host 'Modified AppInit registry entry: ' $newAppInitValue } Set-ItemProperty -Path $appInitKey -Name AppInit_DLLs -Value $newAppInitValue # Repeat for Wow6432Node if(Test-Path 'HKLM:\Software\WOW6432Node\Microsoft\Windows NT\CurrentVersion\Windows') { $appInitKey = 'HKLM:\Software\WOW6432Node\Microsoft\Windows NT\CurrentVersion\Windows' $lockRegex = 'Lock32\.dll' $stabRegex = 'StabAI32\.dll' # Get the current AppInit_DLLs value. $currentAppInitValue = (Get-ItemProperty -Path $appInitKey -Name AppInit_DLLs).AppInit_DLLs # Remove the Lockdown and Stability entries, accounting for comma position. if([regex]::match($currentAppInitValue, $lockRegex).Index -eq 0) { $lockRegex = $lockRegex + $commaRegex } else { $lockRegex = $commaRegex + $lockRegex } $newAppInitValue = $currentAppInitValue -replace $lockRegex, "" if([regex]::match($newAppInitValue, $stabRegex).Index -eq 0) { $stabRegex = $stabRegex + $commaRegex } else { $stabRegex = $commaRegex + $stabRegex } $newAppInitValue = $newAppInitValue -replace $stabRegex, "" # Overwrite the AppInit_DLLs value with the entries removed. Set-ItemProperty -Path $appinitkey -Name AppInit_DLLs -Value $newAppInitValue } # # Remove Tricerat Directory # if($verbose) { Write-Host } if($verbose) { Write-Host 'Deleting Tricerat program directory...' } if(Test-Path $env:ProgramFiles'\Tricerat') { Remove-Item $env:ProgramFiles'\Tricerat' -recurse -force sleep -Milliseconds 500 if(Test-Path $env:ProgramFiles'\Tricerat') { $retval = $false if($verbose) { Write-Host 'Failed to cleanup Program Files.' } } } if(${Env:ProgramFiles(x86)}) { if($verbose) { Write-Host } if($verbose) { Write-Host 'Deleting Tricerat program directory x86...' } if(Test-Path ${Env:ProgramFiles(x86)}'\Tricerat') { Remove-Item ${Env:ProgramFiles(x86)}'\Tricerat' -recurse -force sleep -Milliseconds 500 if(Test-Path ${Env:ProgramFiles(x86)}'\Tricerat') { $retval = $false if($verbose) { Write-Host 'Failed to cleanup Program Files(x86).' } } } } # # Cleanup Windows Folder # if($verbose) { Write-Host } if($verbose) { Write-Host 'Cleaning up the Windows directory...' } if($verbose) { Write-Host } # Stop the print spooler so we can cleanup files. # -Force stops any dependent services (Citrix Print Manager) as well # without -Force, the service will not stop when there are dependent services if($verbose) { Write-Host 'Stopping the Print Spooler...' } Stop-Service Spooler –Force # Wait for the service to report that it is stopped. $services = "Spooler" $maxRepeat = 20 $status = "Stopped" do { $count = (Get-Service $services | ? {$_.status -eq $status}).count $maxRepeat-- sleep -Milliseconds 600 } until ($count -eq 0 -or $maxRepeat -eq 0) # # Define a list of driver files that we want to delete # $driverList = @( '\System32\spool\drivers\x64\sd4drv.dll' , '\System32\spool\drivers\x64\sd4ui.dll' , '\System32\spool\drivers\x64\3\sd4drv.dll' , '\System32\spool\drivers\x64\3\sd4ui.dll' , '\System32\spool\drivers\W32X86\sd4drv.dll' , '\System32\spool\drivers\W32X86\sd4ui.dll' , '\System32\spool\drivers\W32X86\3\sd4drv.dll' , '\System32\spool\drivers\W32X86\3\sd4ui.dll' , '\System32\spool\drivers\x64\sd6drv.dll' , '\System32\spool\drivers\x64\sd6ui.dll' , '\System32\spool\drivers\x64\3\sd6drv.dll' , '\System32\spool\drivers\x64\3\sd6ui.dll' , '\System32\spool\drivers\W32X86\sd6drv.dll' , '\System32\spool\drivers\W32X86\sd6ui.dll' , '\System32\spool\drivers\W32X86\3\sd6drv.dll' , '\System32\spool\drivers\W32X86\3\sd6ui.dll' ) # # Loop through list. If it exists, remove it # if($verbose) { Write-Host 'Removing Driver and DriverUI files...' } foreach($filePath in $driverList) { if(Test-Path $env:windir$filePath) { if($verbose) { Write-Host 'Removing ' $env:windir$filePath } Remove-Item $env:windir$filePath sleep -Milliseconds 50 if(Test-Path $env:windir$filePath) { $retval = $false if($verbose) { Write-Host 'Failed to cleanup ' $env:windir$filePath } } } } # Start the print spooler when we're done. if($verbose) { Write-Host 'Starting the Print Spooler...' } Start-Service Spooler # # Define a list of files that we want to delete # $fileList = @('\System32\TriceratUserInit.exe' , '\System32\StabAI64.dll' , '\System32\StabAI32.dll' , '\System32\Lock64.dll' , '\System32\Lock32.dll' , '\SysWOW64\StabAI32.dll' , '\SysWOW64\Lock32.dll' ) # # Loop through list. If it exists, remove it # foreach($filePath in $fileList) { if(Test-Path $env:windir$filePath) { if($verbose) { Write-Host 'Removing ' $env:windir$filePath } Remove-Item $env:windir$filePath sleep -Milliseconds 50 if(Test-Path $env:windir$filePath) { $retval = $false if($verbose) { Write-Host 'Failed to cleanup ' $env:windir$filePath } } } } # # Define a list of paths to look for sdica32.dll client plugin # The plugin gets installed to various Citrix folders by install_ica.exe # We do call install_ica.exe -u from the installer, but if its in-use, it can be left behind # This script may not have any better luck but will display a message if -verbose specified # $pathList = @('C:\Program Files\', 'C:\Program Files (x86)\') # # Loop through list. If it exists, remove it # foreach($filePath in $pathList) { $fileList = Get-ChildItem -Path $filePath -Include sdica32.dll -Depth 3 -Name -File -Recurse -ErrorAction SilentlyContinue if ($fileList -eq $null) { if ($verbose) { Write-Host 'No plugin found under' $filePath } } else { foreach($file in $fileList) { $fileSpec = $filePath + $file if(Test-Path $fileSpec) { if($verbose) { Write-Host 'Removing' $fileSpec } Remove-Item $fileSpec -Force sleep -Milliseconds 50 if(Test-Path $fileSpec) { $retval = $false if($verbose) { Write-Host 'Failed to remove' $fileSpec } } } } } } # # Nuke the %ProgramData%\Tricerat folder # $folderSpec=$env:ProgramData + '\Tricerat' if(Test-Path $folderSpec) { if($verbose) { Write-Host 'Removing' $folderSpec 'folder' } Remove-Item $folderSpec -Recurse -Force sleep -Milliseconds 50 if(Test-Path $folderSpec) { $retval = $false if($verbose) { Write-Host 'Failed to remove' $folderSpec } } } # Indicate success or failure before rebooting. if($verbose) { Write-Host } if($retval) { Write-Host 'Cleanup completed successfully.' $retcode = 0 # # Prompt to Restart Server if Successful # if($verbose) { Write-Host } # If neither flag was set, prompt the user. if(!$restart -And !$norestart) { $restartprompt = Read-Host -Prompt 'Server restart required. Restart now? (y/n) ' if($restartprompt -eq 'y' -Or $restartprompt -eq 'Y') { $restart = $TRUE } } # Restart if restart flag was specified or user requested restart. if($restart) { Restart-Computer } } else { Write-Host 'Cleanup failed. Manual cleanup required.' $retcode = -1 } exit $retcode