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 AppIDs and such, but it’s worth knowing about this and related COM terminology so here’s a quick rundown:
- All COM objects have a
CLSIDwhich 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, theCLSIDfor WordPad is{73FDDC80-AEA9-101A-98A7-00AA00374959} CLSIDs are not easy to remember, and sometimes COM objects with differentCLSIDs can be used interchangeably (for instance: different versions of Internet Explorer will have differentCLSIDs 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 haveProgIDs (Programmatic IDentifier).ProgIDs 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, theProgIDfor WordPad isWordPad.Document.1.PowerShell’s
New-Objectcmdlet usesProgIDwhen creating new objects that refer to COM objects soProgIDs 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
AppIDwhich 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.
