-
-
Save KGHague/2c562ee88492c1c0c0eac1b3ae0fecd8 to your computer and use it in GitHub Desktop.
function ConvertTo-PackedGuid { | |
<# | |
.SYNOPSIS | |
https://gist.github.com/MyITGuy/d3e039c5ec7865edefc157fcd625a20a | |
Converts a GUID string into a packed globally unique identifier (GUID) string. | |
.DESCRIPTION | |
Takes a GUID string and breaks it into 6 parts. It then loops through the first five parts and reversing the order. It loops through the sixth part and reversing the order of every 2 characters. It then joins the parts back together and returns a packed GUID string. | |
.EXAMPLE | |
ConvertTo-PackedGuid -Guid '{7C6F0282-3DCD-4A80-95AC-BB298E821C44}' | |
The output of this example would be: 2820F6C7DCD308A459CABB92E828C144 | |
.PARAMETER Guid | |
A globally unique identifier (GUID). | |
#> | |
[CmdletBinding()] | |
[OutputType([System.String])] | |
param ( | |
[Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, Mandatory)] | |
[ValidateScript( { [System.Guid]::Parse($_) -is [System.Guid] })] | |
[System.Guid]$Guid | |
) | |
process { | |
Write-Verbose "Guid: $($Guid)" | |
$GuidString = $Guid.ToString('N') | |
Write-Verbose "GuidString: $($GuidString)" | |
$Indexes = [ordered]@{ | |
0 = 8 | |
8 = 4 | |
12 = 4 | |
16 = 2 | |
18 = 2 | |
20 = 12 | |
} | |
$PackedGuid = '' | |
foreach ($index in $Indexes.GetEnumerator()) { | |
$Substring = $GuidString.Substring($index.Key, $index.Value) | |
Write-Verbose "Substring: $($Substring)" | |
switch ($index.Key) { | |
20 { | |
$parts = $Substring -split '(.{2})' | Where-Object { $_ } | |
foreach ($part In $parts) { | |
$part = $part -split '(.{1})' | |
Write-Verbose "Part: $($part)" | |
[System.Array]::Reverse($part) | |
Write-Verbose "Reversed: $($part)" | |
$PackedGuid += $part -join '' | |
} | |
} | |
default { | |
$part = $Substring.ToCharArray() | |
Write-Verbose "Part: $($part)" | |
[System.Array]::Reverse($part) | |
Write-Verbose "Reversed: $($part)" | |
$PackedGuid += $part -join '' | |
} | |
} | |
} | |
[System.Guid]::Parse($PackedGuid).ToString('N').ToUpper() | |
} | |
} | |
# Create an instance of the WindowsInstaller.Installer object | |
$installer = New-Object -ComObject WindowsInstaller.Installer | |
# Get the list of installed programs and find the VMware Tools entry | |
$vmwareTools = Get-WmiObject -Query "SELECT * FROM Win32_Product WHERE Name = 'VMware Tools'" | |
if ($vmwareTools) { | |
# Extract the identifying number (GUID) | |
[guid]$guid = $vmwareTools.IdentifyingNumber | |
# Convert the guid to a packed guid | |
[string]$packedguid = ConvertTo-PackedGuid $guid | |
# Get the LocalPackage path from the registry | |
$localPackage = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\$packedguid\InstallProperties" | Select-Object -ExpandProperty LocalPackage | |
if ($localPackage) { | |
Write-Output "VMware Tools MSI path: $localPackage" | |
# Open the MSI database in read-write mode | |
$database = $installer.GetType().InvokeMember("OpenDatabase", "InvokeMethod", $null, $installer, @("$localPackage", 2)) | |
# Remove the VM_LogStart and VM_CheckRequirements rows in the CustomAction table | |
# VM_CheckRequirements added as recommended by @DanAvni | |
$query = "DELETE FROM CustomAction WHERE Action='VM_LogStart' OR Action='VM_CheckRequirements'" | |
$view = $database.GetType().InvokeMember("OpenView", "InvokeMethod", $null, $database, @($query)) | |
$view.GetType().InvokeMember("Execute", "InvokeMethod", $null, $view, $null) | |
$view.GetType().InvokeMember("Close", "InvokeMethod", $null, $view, $null) | |
[System.Runtime.InteropServices.Marshal]::FinalReleaseComObject($view) | |
# Commit the changes and close the database | |
$database.GetType().InvokeMember("Commit", "InvokeMethod", $null, $database, $null) | |
[System.Runtime.InteropServices.Marshal]::FinalReleaseComObject($database) | |
# Uninstall VMware Tools | |
Start-Process msiexec.exe -ArgumentList "/x `"$localPackage`" /qn" -Wait | |
} else { | |
Write-Output "LocalPackage path not found in the registry." | |
} | |
} else { | |
Write-Output "VMware Tools is not installed." | |
} |
does this script remove the monitor driver and vmxnet3?
Thanks for this! Much cleaner uninstall than the brute-force cleanup method.
does this script remove the monitor driver and vmxnet3?
It does not. It only does as much as what VMware's own uninstall does, which is a bit less than desired. This script could probably be combined with some of the brute-force methods to clean up some of the various drivers and non-present devices left behind.
does this script remove the monitor driver and vmxnet3?
@aadnehovda I threw this into my own clean-up script I'm using:
Get-PnpDevice -Status "UNKNOWN" -Class "Processor" | ForEach-Object {pnputil /remove-device $_.InstanceId}
Get-PnpDevice -Status "UNKNOWN" -Class "System" | ForEach-Object {pnputil /remove-device $_.InstanceId
Get-PnpDevice -Status "UNKNOWN" -Class "USB" | ForEach-Object {pnputil /remove-device $_.InstanceId}
Get-PnpDevice -Status "UNKNOWN" -Class "Volume" | ForEach-Object {pnputil /remove-device $_.InstanceId}
Get-PnpDevice -Status "UNKNOWN" -Class "SCSIAdapter" | ForEach-Object {pnputil /remove-device $_.InstanceId}
Get-PnpDevice -Status "UNKNOWN" -Class "Ports" | ForEach-Object {pnputil /remove-device $_.InstanceId}
Get-PnpDevice -Status "UNKNOWN" -Class "HDC" | ForEach-Object {pnputil /remove-device $_.InstanceId}
Get-PnpDevice -Status "UNKNOWN" -Class "FloppyDisk" | ForEach-Object {pnputil /remove-device $_.InstanceId}
Get-PnpDevice -Status "UNKNOWN" -Class "FDC" | ForEach-Object {pnputil /remove-device $_.InstanceId}
Get-PnpDevice -Status "UNKNOWN" -Class "HIDClass" | ForEach-Object {pnputil /remove-device $_.InstanceId}
Get-PnpDevice -Status "UNKNOWN" -Class "Battery" | ForEach-Object {pnputil /remove-device $_.InstanceId}
Get-PnpDevice -Status "UNKNOWN" | Where-Object {$_.FriendlyName -like "VMware*"} | ForEach-Object {pnputil /remove-device $_.InstanceId}
For newer versions of VMware tools, you need to remove an additional custom action "VM_CheckRequirements". Only when both are removed from the MSI file I am able to run it
For newer versions of VMware tools, you need to remove an additional custom action "VM_CheckRequirements". Only when both are removed from the MSI file I am able to run it
@DanAvni How did you update the script to make the change you recommended?
EDIT - I added the following lines:
# Remove the VM_CheckRequirements row in the CustomAction table
$query = "DELETE FROM CustomAction WHERE Action='VM_CheckRequirements'"
$view = $database.GetType().InvokeMember("OpenView", "InvokeMethod", $null, $database, @($query))
$view.GetType().InvokeMember("Execute", "InvokeMethod", $null, $view, $null)
$view.GetType().InvokeMember("Close", "InvokeMethod", $null, $view, $null
[System.Runtime.InteropServices.Marshal]::FinalReleaseComObject($view)
Just below line 90 in the script and it ran successfully removing VMware Tools from the VMs. I also issued the following commands to in an elevated command line to confirm the services were removed.
sc delete VMTools
sc delete vmmouse
sc delete VMMEMCTL
sc delete VMX_svga
sc delete "VMware Physical Disk Helper Service"
sc delete vmhgfs
sc delete vmci
sc delete vmaudio
sc delete vmusbmouse
sc delete vmvss
A few of the VMs still had the VMMEMCTL installed after running the initial script. After that, the only other item that had to be manually removed was the VMware Tools folder located at C:\ProgramData\VMware.
@gilbreen , Change the query line to be
$query = "DELETE FROM CustomAction WHERE Action='VM_LogStart' OR Action='VM_CheckRequirements'"
@DanAvni Thanks!
I've updated the VM_CheckRequirements deletion as suggested by @DanAvni
Fixed type mismatch error in OpenDatabase method by putting quotes around "$localPackage"