Let’s see what WMI can tell us about COM objects on the system.
Get a list of all WMI classes with the word “COM” in them (doing a case sensitive match to avoid entries like “computer”).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
PS> gwmi -list | ?{ $_.Name -cmatch "COM" } NameSpace: ROOT\cimv2 Name Methods Properties ---- ------- ---------- MSFT_WMI_GenericNonCOMEvent {} {ProcessId, PropertyNames, PropertyValues, ProviderName...} Win32_COMApplication {} {Caption, Description, InstallDate, Name...} Win32_DCOMApplication {} {AppID, Caption, Description, InstallDate...} Win32_COMClass {} {Caption, Description, InstallDate, Name...} Win32_ClassicCOMClass {} {Caption, ComponentId, Description, InstallDate...} Win32_COMSetting {} {Caption, Description, SettingID} Win32_ClassicCOMClassSetting {} {AppID, AutoConvertToClsid, AutoTreatAsClsid, Caption...} Win32_DCOMApplicationSetting {GetLaunchSecurit... {AppID, AuthenticationLevel, Caption, CustomSurrogate...} Win32_ClassicCOMClassSettings {} {Element, Setting} Win32_COMApplicationSettings {} {Element, Setting} Win32_DCOMApplicationAccessAllow... {} {Element, Setting} Win32_COMApplicationClasses {} {GroupComponent, PartComponent} Win32_ClassicCOMApplicationClasses {} {GroupComponent, PartComponent} Win32_DCOMApplicationLaunchAllow... {} {Element, Setting} |
Win32_COMApplication
gives me about 386 results. It includes the AppID
of the application associated with the COM object.
I am not a programmer so I won’t be going further into AppID
s and such, but it’s worth knowing about this and related COM terminology so here’s a quick rundown:
- All COM objects have a
CLSID
which is basically a 128-bit hexadecimal Globally Unique IDentifier (GUID) for the COM object. This way COM objects can be referred to independent of their installation path. The CLSID is unique across network computers too (relevant, when used with DCOM).WMI objects refer to this as
ComponentID
. They can also be found in the registry underHKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
. For example, theCLSID
for WordPad is{73FDDC80-AEA9-101A-98A7-00AA00374959}
CLSID
s are not easy to remember, and sometimes COM objects with differentCLSID
s can be used interchangeably (for instance: different versions of Internet Explorer will have differentCLSID
s but you need some way of referring to the Internet Explorer COM object such that whatever version is installed on the system is used). For this reason you haveProgID
s (Programmatic IDentifier).ProgID
s can be found in the registry underHKEY_LOCAL_MACHINE\SOFTWARE\Classes
. The format of a ProgID is<Program>.<Component>.<Version>
, separated by periods and with no spaces. For example, theProgID
for WordPad isWordPad.Document.1
.PowerShell’s
New-Object
cmdlet usesProgID
when creating new objects that refer to COM objects soProgID
s are relevant to us.12345678910PS> help New-Object -Parameter ComObject-ComObject <String>Specifies the programmatic identifier (ProgID) of the COM object.Required? truePosition? 1Default value NoneAccept pipeline input? falseAccept wildcard characters? false- While not relevant to the current topic, there’s also an
AppID
which is yet another 128-bit hexadecimal GUID. You can read more about it here.
Now let’s get a list of how many objects are returned by each WMI class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
PS> gwmi -list | ?{ $_.Name -cmatch "COM" } | ft Name,@{Name="Number of objects";Expr={(gwmi $_.Name | Measure-Object -Line).Lines}} -AutoSize Name Number of objects ---- ----------------- MSFT_WMI_GenericNonCOMEvent 0 Win32_COMApplication 397 Win32_DCOMApplication 397 Win32_COMClass 5721 Win32_ClassicCOMClass 5721 Win32_COMSetting 6118 Win32_ClassicCOMClassSetting 5721 Win32_DCOMApplicationSetting 397 Win32_ClassicCOMClassSettings 5721 Win32_COMApplicationSettings 397 Win32_DCOMApplicationAccessAllowedSetting 526 Win32_COMApplicationClasses 579 Win32_ClassicCOMApplicationClasses 579 Win32_DCOMApplicationLaunchAllowedSetting 491 |
The biggest results are from the Win32_COMSetting
class. Let’s compare the results of this with the Win32_ClassicCOMClassSetting
class (which has the second largest number of results) to see what’s different. Since the ProgID
property is what’s relevant to PowerShell, let’s filter the results to objects where ProgID
is not $null
.
1 2 3 4 5 |
# store the results from each class in a variable; filter out ones where ProgID is $null PS> $COMClassicSet = gwmi Win32_ClassicCOMClassSetting | ?{ $_.ProgId -ne $null } | Sort-Object PS> $COMSet = gwmi Win32_COMSetting | ?{ $_.ProgId -ne $null } | Sort-Object PS> Compare-Object $COMSet $COMClassicSet PS> |
There’s no difference. So in terms of ProgID
both classes contain the same number of objects. Now let’s recreate the above table to only consider results where ProgID
is not $null
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
PS> gwmi -list | ?{ $_.Name -cmatch "COM" } | ft Name,@{Name="Number of objects";Expr={(gwmi $_.Name | ?{ $_.ProgID -ne $null } | Measure-Object -Line).Lines}} -AutoSize Name Number of objects ---- ----------------- MSFT_WMI_GenericNonCOMEvent 0 Win32_COMApplication 0 Win32_DCOMApplication 0 Win32_COMClass 0 Win32_ClassicCOMClass 0 Win32_COMSetting 1613 Win32_ClassicCOMClassSetting 1613 Win32_DCOMApplicationSetting 0 Win32_ClassicCOMClassSettings 0 Win32_COMApplicationSettings 0 Win32_DCOMApplicationAccessAllowedSetting 0 Win32_COMApplicationClasses 0 Win32_ClassicCOMApplicationClasses 0 Win32_DCOMApplicationLaunchAllowedSetting 0 |
The interesting thing to note is that all the other classes have 0 objects now. In fact, we can see that the number of objects returned by the Win32_COMSetting
class is equal to the number returned by Win32_ClassicCOMClassSetting
plus Win32_COMApplication
or Win32_DCOMApplication
or Win32_DCOMApplicationSetting
or Win32_COMApplicationSettings
. Something to investigate later?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
PS C:\Users\rakhesh\Code> gwmi -list | ?{ $_.Name -cmatch "COM" } | ft Name,@{Name="Number of objects";Expr={(gwmi $_.Name | Measure-Object -Line).Lines}} -AutoSize Name Number of objects ---- ----------------- MSFT_WMI_GenericNonCOMEvent 0 Win32_COMApplication 397 Win32_DCOMApplication 397 Win32_COMClass 5721 Win32_ClassicCOMClass 5721 Win32_COMSetting 6118 Win32_ClassicCOMClassSetting 5721 Win32_DCOMApplicationSetting 397 Win32_ClassicCOMClassSettings 5721 Win32_COMApplicationSettings 397 Win32_DCOMApplicationAccessAllowedSetting 526 Win32_COMApplicationClasses 579 Win32_ClassicCOMApplicationClasses 579 Win32_DCOMApplicationLaunchAllowedSetting 491 |
So, the best way to get COM objects via WMI & PowerShell is to use the Win32_COMSetting
class. And to get the ProgID
or any application (such as Internet Explorer in the previous post) something along the following lines will do:
1 2 3 4 5 6 7 8 9 |
PS> gwmi Win32_COMSetting | ?{ $_.ProgId -match "Internet" } | ft ProgId,Caption ProgId Caption ------ ------- InternetExplorer.Application.1 Internet Explorer(Ver 1.0) Internet.HHCtrl.1 HHCtrl Object Internet.HHCtrl.1 HHCtrl Object Internet.HHCtrl.1 HHCtrl Object InternetShortcut Internet Shortcut |
If there’s only one version, the version number can be skipped in ProgID
which is why InternetExplorer.Application
too works.
Update: In fact, only the Win32_COMSetting
and Win32_ClassicCOMClassSetting
classes contain ProgID
in their output. So these are the only two classes one can use. And since the only difference them is that Win32_COMSetting
contains objects without any ProgID
(of no use in our case!) it’s ok to use either class.
1 2 3 4 5 6 7 8 9 |
PS> gwmi -list | ?{ $_.Name -cmatch "^Win32.*COM" } | ?{ @(gwmi $_.Name | gm -MemberType Property | select -Unique -Expand Name).Contains("ProgId") } NameSpace: ROOT\cimv2 Name Methods Properties ---- ------- ---------- Win32_COMSetting {} {Caption, Description, SettingID} Win32_ClassicCOMClassSetting {} {AppID, AutoConvertToClsid, AutoTreatAsClsid, Caption...} |
Update2: Turns out WMI is not a good way to get the list of COM objects. A better approach is to query the registry. See my next post.