Resetting WMI repository - dos and dont's

Today's topic:

Resetting WMI Repository

dos and dont's

Some people like Windows Management Instrumentation (WMI) because of its power - some just hate it. The ones who hate it often claim that the WMI repository is not really reliable and often gets corrupt / unusable.

Even though this may happen - it's not a usual scenario. Anyhow - if you face a corrupt WMI repository - you need to repair it.
The basic steps would be

  • set WinMgmt service state to Disabled
  • stop WinMgmt service
  • walk through all dlls in %windir%\system32\wbem and re-register those
  • call /regserver method on WMI private server executable (wmiprvse.exe)
  • call /resetrepository method on WinMgmt service executable to  reset the WMI repository to the initial state and restore MOF files that contain the #pragma autorecover statement
  • set WinMgmt service state to Auto
  • start WinMgmt service
  • walk through all relevant Windows Management Object Files (.mof) in %windir%\system32\wbem and all Windows Management Language Files (.mfl) in language subfolders of %windir%\system32\wbem and re-register them by mofcomp.exe calls.
  • restart machine

There are various articles out there that describe how to do it - some even provide automation code. Unfortunately most of these disregard the fact that the purpose of some MOF files is to uninstall the WMI namespace / class defined in the MOF - they just run through all MOF files and register those.
Saying - if you mofcomp all MOF files found in the wbem folder - you may end up with an even more unusable WMI repository - just because you uninstalled necessary WMI data ...

How you detect MOFs / MFLs that are intended to uninstall WMI namespaces / classes:

Some MOFs / MFLs are very kind to the admins and have 'uninstall' or 'remove' as part of their name - but this does not detect all classes that uninstall WMI data. To reliably identify the MOFs / MFLs to be registered by mofcomp -> do the following:

  • skip MOFs / MFLs that have 'autorecover' as part of their name
  • skip MOFs / MFLs that have 'uninstall' or 'remove' as part of their name
  • skip MOFs / MFLs that contain the #pragma autorecover statement (already handled by winmgmt /resetrepository)
  • skip MOFs / MFLs that do not contain the #pragma autorecover statement but either #pragma deleteinstance or #pragma deleteclass statement(s)

To bring this all together in some PoSh code:

[code lang="powershell"]
function DisableService([System.ServiceProcess.ServiceController]$svc)
{ Set-Service -Name $svc.Name -StartupType Disabled }

function EnableServiceAuto([System.ServiceProcess.ServiceController]$svc)
{ Set-Service -Name $svc.Name -StartupType Automatic }

function StopService([System.ServiceProcess.ServiceController]$svc)
{
[string]$dep = ([string]::Empty)

foreach ($depsvc in $svc.DependentServices)
{ $dep += $depsvc.DisplayName + ", " }

Write-Host "Stopping $($svc.DisplayName) and its dependent services ($dep)"

$svc.Stop()

$svc.WaitForStatus([System.ServiceProcess.ServiceControllerStatus]::Stopped)

Write-Host "Stopped $($svc.DisplayName)"
}

function StartService([System.ServiceProcess.ServiceController]$svc, [bool]$handleDependentServices)
{
if ($handleDependentServices)
{ Write-Host "Starting $($svc.DisplayName) and its dependent services" }

else
{ Write-Host "Starting $($svc.DisplayName)" }

if (!$svc.Status -ne [System.ServiceProcess.ServiceControllerStatus]::Running)
{
try
{
$svc.Start()

$svc.WaitForStatus([System.ServiceProcess.ServiceControllerStatus]::Running)
}

catch { }
}

Write-Host "Started $($svc.DisplayName)"

if ($handleDependentServices)
{
[System.ServiceProcess.ServiceController]$depsvc = $null;

foreach ($depsvc in $svc.DependentServices)
{
if ($depsvc.StartType -eq [System.ServiceProcess.ServiceStartMode]::Automatic)
{ StartService $depsvc $handleDependentServices }
}
}
}

function RegSvr32([string]$path)
{
Write-Host "Registering $path"

regsvr32.exe $path /s
}

function RegisterMof([System.IO.FileSystemInfo]$item)
{
[bool]$register = $true

Write-Host "Inspecting: $($item.FullName)"

if ($item.Name.ToLowerInvariant().Contains('uninstall'))
{
$register = $false
Write-Host "Skipping - uninstall file: $($item.FullName)"
}

elseif ($item.Name.ToLowerInvariant().Contains('remove'))
{
$register = $false
Write-Host "Skipping - remove file: $($item.FullName)"
}

else
{
$txt = Get-Content $item.FullName

if ($txt.Contains('#pragma autorecover'))
{
$register = $false
Write-Host "Skipping - autorecover: $($item.FullName)"
}

elseif ($txt.Contains('#pragma deleteinstance'))
{
$register = $false
Write-Host "Skipping - deleteinstance: $($item.FullName)"
}

elseif ($txt.Contains('#pragma deleteclass'))
{
$register = $false
Write-Host "Skipping - deleteclass: $($item.FullName)"
}
}

if ($register)
{
Write-Host "Registering $($item.FullName)"
mofcomp $item.FullName
}
}

function HandleFSO([System.IO.FileSystemInfo]$item, [string]$targetExt)
{
if ($item.Extension -ne [string]::Empty)
{
if ($targetExt -eq 'dll')
{
if ($item.Extension.ToLowerInvariant() -eq '.dll')
{ RegSvr32 $item.FullName }
}

elseif ($targetExt -eq 'mof')
{
if (($item.Extension.ToLowerInvariant() -eq '.mof') -or ($item.Extension.ToLowerInvariant() -eq '.mfl'))
{ RegisterMof $item }
}
}
}

# get Winmgmt service
[System.ServiceProcess.ServiceController]$wmisvc = Get-Service 'winmgmt'

# disable winmgmt service
DisableService $wmisvc

# stop winmgmt service
StopService $wmisvc

# get wbem folder
[string]$wbempath = [Environment]::ExpandEnvironmentVariables("%windir%\system32\wbem")

[System.IO.FileSystemInfo[]]$itemlist = Get-ChildItem $wbempath -Recurse | Where-Object { $_.FullName.Contains('AutoRecover') -ne $true}

[System.IO.FileSystemInfo]$item = $null

# walk dlls
foreach ($item in $itemlist)
{ HandleFSO $item 'dll' }

# call /regserver method on WMI private server executable
wmiprvse /regserver

# call /resetrepository method on WinMgmt service executable
winmgmt /resetrepository

# enable winmgmt service
EnableServiceAuto $wmisvc

# start winmgmt service
StartService $wmisvc $true

# walk MOF / MFLs
foreach ($item in $itemlist)
{ HandleFSO $item 'mof' }

 

Kudos to my dear colleague Constantin - one of the WMI haters :- )

Have fun resetting...

4/6/2019: Updated PoSh code to only start dependent services if start mode of those is set to 'Automatic'

Michael
Have keyboard. Will travel.

Comments

  • Anonymous
    April 02, 2019
    So. First off, Beautiful code! Thanks for building this! Secondly, I have a problem. When I run this, it seems to hang on "Starting Network Connectivity ASsistant and it's dependent services." Any ideas??
    • Anonymous
      April 05, 2019
      I'm guessing it should be relatively safe to modify this section...# start winmgmt serviceStartService $wmisvc $trueto# start winmgmt serviceStartService $wmisvc $falseIt appears it attempts to ensure all dependent services of WMI are running before proceeding. However, Network Connectivity Assistant doesn't stay running. It's a manual process that will stop itself if it doesn't have anything to do. So the script appears to be waiting on a service to stay in a running state that never will.
      • Anonymous
        April 06, 2019
        Hi guys,thx for error feedback - very welcome.Changed code above to only start dependency services if start mode of the service is set to 'Automatic': foreach ($depsvc in $svc.DependentServices) { if ($depsvc.StartType -eq [System.ServiceProcess.ServiceStartMode]::Automatic) { StartService $depsvc $handleDependentServices } }
  • Anonymous
    May 25, 2019
    The comment has been removed