Contact

Subscribe via Email

Subscribe via RSS

Categories

Creative Commons Attribution 4.0 International License
© Rakhesh Sasidharan

How to get a list of COM objects from the registry

Today I was looking for the COM ProgID for Excel on my machine and my previous approach of using WMI didn’t work.

That’s not correct as I can create a new COM object by referring to Excel.Application.

Since ProgIDs are found in the registry under HKEY_LOCAL_MACHINE\SOFTWARE\Classes I figured that would be a good place to get an exhaustive list from. Under this key there are three types of subkeys:

  1. Subkeys for each file extension registered with the system, each of which has a (Default) entry with the ProgID and also a subkey with the ProgID. Not all these ProgIDs seem to be valid though (based on my testing), so it’s best to go by the other set of subkeys detailed below.
  2. Subkeys for each ProgID – usually of both ProgID formats <Program>.<Component>.<Version> and <Program>.<Component> – again, not all valid, but a good test of validity (based on my testing) being whether they contain a subkey called CLSID.
  3. Subkeys such as “Unknown”, “Undecided”, etc and also some that look like ProgIDs but are not in the correct format (for instance: they don’t contain the <Component>.<Version> bit, or contain spaces and underscores (not allowed)

So a sensible way to filter out ProgIDs seems to be to check for subkeys of the format <Program>.<Component>.<Version> (the <Version> being optional) and to further filter out those which have a CLSID subkey.

Thus we have the following:

And to search for a specific application, one can do:

How to get a list of COM objects via WMI & PowerShell

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”).

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:

  1. 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 under HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID. For example, the CLSID for WordPad is {73FDDC80-AEA9-101A-98A7-00AA00374959}

  2. CLSIDs are not easy to remember, and sometimes COM objects with different CLSIDs can be used interchangeably (for instance: different versions of Internet Explorer will have different CLSIDs 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 have ProgIDs (Programmatic IDentifier).

    ProgIDs can be found in the registry under HKEY_LOCAL_MACHINE\SOFTWARE\Classes. The format of a ProgID is <Program>.<Component>.<Version>, separated by periods and with no spaces. For example, the ProgID for WordPad is WordPad.Document.1.

    PowerShell’s New-Object cmdlet uses ProgID when creating new objects that refer to COM objects so ProgIDs are relevant to us.

  3. 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.

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.

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.

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?

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:

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.

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.

Get a list of links in a web page (part 2)

A continuation to my previous post on getting links.

Internet Explorer is an application with a rich COM interface. And PowerShell can work with COM objects. Thus you can do the following:

The Document property is very powerful and lets you see a lot of details of the page. It has a subproperty Link that gives all the link elements in the page (it has nearly 800 properties, methods, and events!). The output is as objects, and since we are only interested in the actual link href elements we can select that property.

If you are PowerShell v3 things are even easier. There’s a cmdlet called Invoke-WebRequest who is your friend.

To get an object representing the website do:

To get all the links in that website:

And to just get a list of the href elements:

Like the System.Net.Webclient class Invoke-WebRequest has parameters to specify proxy, headers, encoding, etc.

Using Get-ACL to view and modify Access Control Lists (part 1)

The Get-ACL cmdlet can be used to view and modify Access Control Lists in PowerShell. It works with any object you have a PSProvider for – be it files, folders, registry keys, Active Directory objects, and so on.

Viewing and setting the Owner

To find the owner of an object there are couple of ways. The easy way is to use the Owner property:

But there’s a GetOwner() method too. This takes as input the type of output you want. The input must be of class System.Security.Principal.IdentityReference. There are two such class: System.Security.Principal.NTAccount gives you the user account while System.Security.Principal.SecurityIdentifier gives you the SID. These two classes are worth remembering when you want to convert between a user account and SID or vice versa.

You can set the owner too using the SetOwner() method.

Simply doing a SetOwner() won’t do the trick though because what Get-Acl returns is an ACL object and so all we are really doing is setting the ownership info in that object but not applying it back to the file or folder whose ACL it is.

The correct way would be thus:

Setting the owner – gotchas

For reasons I don’t know, the following doesn’t work:

It could be because of this:

When you set the owner directly, the object is empty. It could be because the SetOwner method doesn’t return anything so $acl is assigned nothing. But when you set $acl to the ACL and then set its owner, it works.

Another gotcha is that Set-Acl cannot be set to give ownership to another user. It can only be used to take ownership. Have a look at this:

I couldn’t find much but this Hey, Scripting Guy! post briefly mentions the following:

… although you can take ownership of a file using Windows PowerShell, we don’t believe that you can give ownership of a file to someone else. To transfer ownership to another user you’ll need to use the Windows Resource Kit utility Subinacl.exe.

Yet another gotcha is that built-in groups like “Users” and “Administrators” need to be referred to differently:

I discovered this looking at this page of well known SIDs, finding the SID of the built-in “Users” group, and translating it via PowerShell:

SID: S-1-5-32-545
Name: Users
Description: A built-in group. After the initial installation of the operating system, the only member is the Authenticated Users group. When a computer joins a domain, the Domain Users group is added to the Users group on the computer.

In fact, the error Exception calling "SetOwner" with "1" argument(s): "Some or all identity references could not be translated." is a good way of checking if a user account exists.

More later!

Get a list of links in a web page (part 1)

Using the System.Net.Webclient class and using old-fashioned regexp to cull out links:

The System.Net.Webclient class

The system.net.webclient class can be used to deal with web pages.

To download and display pages this class has couple of methods:

  • DownloadData downloads the page and displays it as an array of bytes.
  • DownloadString downloads the page and displays it as one long string.
  • DownloadFile downloads the page and saves it to a file name you specify.

The class also has properties you can set to be used while downloading a page. For instance:

  • QueryString to specify pairs of query parameters and their values. For example: to do a Google search for the word “rakhesh” one can fetch the page http://www.google.com/search?q=rakhesh. This q=rakhesh is a query string, with q being a parameter and rakhesh being a value to the parameter. To do the same via the system.net.webclient class one would do the following:

  • Headers to specify pairs of headers that can be set when requesting the web page:

  • Credentials to specify credentials for accessing the web page:

  • ResponseHeaders to view the headers received in response.

There are other properties and methods too, the above are what I had a chance to look at today.

Scope in PowerShell

PowerShell variables and functions have scope. The “scope” is what defines the visibility of that variable or function.

For instance, consider the following script:

If you were to run this script, here’s the output:

As you can see, the variable x was set as 2 initially but when the function blah set it to 5 the change didn’t really affect it. This is because of scopes. What happens is that the script variable x belongs to a different scope from the function variable x and although both have the same name they are in fact two different beings. In a way it’s like the variables are in two different boxes – each script/ function has a box full of variables, and whatever change they make to the variables in their box doesn’t affect the variables in other boxes. This is the case even if a function is within a script or another function – the child-function has a box of variables independent of its parent.

Now, the concept of scopes only matter when you are making changes to a variable. If you were just reading the value of a variable then scope doesn’t matter much. Consider the following script:

Here even though the function blah has a box of its own variables, when we refer to variable x within it since the variable is not present in its box it looks up to the box of its parent and reads the value from there. Hence the output of the script is:

But suppose we had made a change to variable x within the function (as in the first case), then a new variable x is created within the scope of that function and whatever change we make is limited to that variable.

There are four types of scopes:

  1. Local scope: the box you have, the current scope basically of the script or function you are in.
  2. Global scope: the big box that’s out there, the scope containing all variables that are in your particular PowerShell session (be it a PowerShell window or ISE)
  3. Script scope: another big box, but not as big as the global scope and not as limited as the local scope, this is the scope containing all variables that are in your particular PowerShell script session. The script scope is like a global scope for scripts.
  4. Private scope: a secret box you have, containing variables you don’t want your children to see. Remember I said earlier that if a variable is not present in a function it looks to the parent scope and reads it from there? If you have any variables you don’t want such children functions to see, define them in a private scope.

Consider the following script:

This is just to illustrate the first three scopes mentioned above. Here’s the output from the script:

The variable x was set as 2 initially. Function blah changed it to 5, but that only took effect in the local scope of the function and so x was still 2. Next, function blah2 changed x to 5 in the global scope (by addressing the variable as $global:x), but that too didn’t change the value of x as the change was made in the global scope and not the script scope. Finally, function blah3 changed x to 5 in the script scope (by addressing it as $script:x) and that effected the change.

From this example one can also see the need for a script scope and a global scope. Function blah3 changed variable x globally within the script, but any variable x existing outside the script and in the PowerShell session was unaffected. Similarly function blah2 changed variable x within the PowerShell session of the script, but the variable x within the script itself was unaffected (of course, if the script didn’t have a variable x then any references to x will pick up the variable from the global scope). If we didn’t have separate scopes for scripts and the PowerShell session, all scripts would mess about with the global scope variables.

Note though: The global scope and script scope are separate only if the script is run and not dot-sourced. If a script is dot-sourced, the script scope becomes the global scope.

And lastly, private scopes. Consider this script:

And here’s the output:

Since y was defined with a private scope in the script, function blah can’t read its value.

Scopes can also be referred to by a number. 0 is the local scope, 1 is the parent scope, 2 is the grandparent scope, and so on. This is useful with the Get-Variable cmdlet, for instance. Consider the following script:

Its output is thus:

As you can see, there are only two scopes – 0 is the local scope (the script scope in this case), 1 is the global scope. If I had defined a function within the script, then scope 0 would be the local scope, 1 would be the script scope, and 2 would be the global scope.

Accessing scopes by number is also useful with cmdlets such as Set-Variable. For instance, the following sets a variable in the parent scope of the calling script:

That’s all for now. It’s worth reiterating that within a script you have a global scope and a script scope, and the two are separate. Setting a variable in the global scope from a script does not affect the variable in the script scope.

Write-Host and Write-Output

Good to remember:

  1. Write-Host converts the object to a string representation and then outputs it to the host (console). There’s two things to keep in mind here: (1) the output bypasses any pipelines etc and is sent directly to the host; (2) the object is converted to a string representation, using the toString() method (which is present in all classes), and so the string representation may not necessarily be what you expect.

    Case in hand:

  2. Write-Output is better in the sense that (1) it only passes the output to the next step of the pipeline (or to the host if we are at the end of the pipeline) and (2) no conversion happens, the object is output and formatted as it is.

    To give an example with the previously created table:

    This is same as if you were to output the object directly:

    As previously mentioned the formatting of such output depends the ps1xml defintions for that object.

PowerShell.exe command line switches

The powershell.exe command has many useful switches.

Two especially useful ones are the -Version and -ExecutionPolicy switches. The former lets you start PowerShell as a different version – useful you want to test your script on older versions of PowerShell – and the latter lets you set the execution policy for the current session – useful when you are telling users to run a script via the -File switch and want to allow execution for that session so they don’t get any prompts. This is equivalent to running the following cmdlet from within PowerShell:

You can run a PowerShell cmdlet (or scriptblock) via the -command parameter. Couple of things to remember:

  1. From within a command prompt window: If the argument to -command is a cmdlet it is executed and the result passed to command prompt. But if the argument is a script block then it is only typed as it is, no execution happens.
  2. From within a PowerShell window: The argument to -command can be a cmdlet or a script block. Both work.
  3. From within a PowerShell window, if you launch powershell.exe via start-process same rules as the command prompt window case apply. Also, bear in mind as a new process is started it terminates immediately after the cmdlet/ script block is executed. If you want the window to not exit, specify the -noexit switch as an argument to powershell.exe. Something like this:

    The -noexit switch must be placed before the -command parameter else it has no effect.

  4. In all cases, when a script block is passed to -command if it is enclosed in double quotes (as a string) and the script block is prefixed with the & operator (the call operator) the script block will get executed. Like thus:

Converting text to capital case, lower case, and title case

The previously blogged System.Globalization.CultureInfo class has some useful methods for converting text to upper case, lower case, and title case.

These methods can be used thus:

Good to know!

Get all users in an OU where specified account does not have any rights

The following code gets a list of all users in an OU (specified by the variable $OU) on which the account (specified by $account) does not have any rights. There’s probably better ways to do this (and also check for specific rights) but I wanted something quick and dirty and this is what I came up with today:

Playing with Get-Culture and System.Globalization.CultureInfo

The Get-Culture cmdlet shows you the current system “culture”. Culture is the stuff that defines the date formatting, number formatting, UI language, etc of your computer. Usually you set it in the Control Panel under Language/ Regional Settings, or when installing the OS.

The output is of the .NET type System.Globalization.CultureInfo and has some useful static methods.

Using the Get-Culture cmdlet I can check out the date-time format on any system:

Similarly I can check the number format, get the friendly name of the culture, etc.

I am more interested in the class itself: System.Globalization.CultureInfo. Let’s examine its members again:

Particularly useful is the ability to examine another culture and then use the methods of that culture object to learn more about it:

More later!

Naming PowerShell custom objects and setting their default display properties

Learnt a couple of things today. Not in depth, but now I am aware of these features and will explore them in depth someday.

I knew how to create custom objects in PowerShell and I have always tried to return output from my functions/ scripts as custom objects. I was also aware that you can set the default display properties so the output is neater.

Say I create a new object like this:

Notice when I output the object all its properties are output. Usually I may not want that. I may want that only the name property is output and the rest are silent, only shown if asked for.

It’s possible to define the default properties you are interested in. This link gives you more details, the tl;dr summary of which is as follows:

  1. All objects contain a member object called PSStandardMembers which defines the default properties of the object.
  2. The PSStandardMembers object a member object called DefaultDisplayPropertySet. This object contains a property called ReferencedPropertyNames which lists the default displayed properties of the object.
  3. Apart from DefaultDisplayPropertySet you have DefaultKeyPropertySet and DefaultDisplayProperty objects too. I am not sure what DefaultDisplayProperty does but DefaultKeyPropertySet is used when sorting and grouping objects.

To set the PSStandardMembers property of an object one does the following:

Notice now only the properties we specified are shown.

As an aside, and purely because I spent some time trying to figure this out, here’s how DefaultKeyPropertySet influences sorting:

(Thanks to this post which made me realize what DefaultKeyPropertySet does).

Back to DefaultDisplayPropertySet – the problem is that it doesn’t work in PowerShell v2. It’s a bug with PowerShell v2 and this Stack Overflow post gives a workaround which involves creating a temporary ps1xml file for the custom objects and defining its default properties.

I haven’t explored ps1xml files much but the gist of the matter is (1) they are what PowerShell uses to format object output and (2) you can create custom ps1xml files for your custom objects. The Stack Overflow post gives a function that takes an object and an array of properties and sets these properties as the default for that object. It’s a neat function and works as expected, but for a catch …

The catch is that since all custom objects have the same name you can’t set different default properties for different objects. Unless you give a name for the custom object, of course, which differentiates each type of custom object from the other. So how do you go about naming custom objects?

First up, how do you get the current name of an object? From my reflection post we know the following works:

To fiddle with the type name you have to use some hidden members of every object. (This was another new thing learnt today. Didn’t know objects had hidden members too). The way to see these is via Get-Member -Force cmdlet. Have a look at the help for the -Force parameter:

From the help file its clear PSTypeNames is what we are interested in.

The members Clear and Add seem to be what we want:

Instead of clearing the existing types, one can Insert the new type to the top of the list too:

Gotta love it when things fall into place and you have a language that makes it easy to do all these things!

My thanks to this and this post for pointing me towards PSTypeNames.

PowerShell RunAs elevated

Learnt today that I can start a new process within PowerShell with elevated access (i.e. you get a UAC prompt that gives you more rights) using the Verb switch to the Start-Process cmdlet.

WMI queries

A useful link to refer to later: WMI Query language examples.

PowerShell can access WMI via Get-WMIObject. And while it’s easier to just pull all WMI objects and then filter, it’s faster if you do the filtering while pulling itself.

Case in hand: getting a list of installed drivers. The Win32_PnpSignedDriver WMI class is what you need to tap into for this. Like thus:

This gives a bucket-load of output. If you want to filter out just a particular driver, the easy way is this:

The better way, though, is to use WMI queries and filter while running WMI itself. Like this:

Both return the same number of results:

But the latter is faster.

The difference isn’t much in the example above, but when you run this across the network for multiple computers it adds up quickly.

So when in doubt filter at the WMI level. And to make filter queries like the above, the link at the start of this post helps. It gives you the keywords you can use. Like, for instance, to do wildcard matches use the like keyword as above. And like * is the wildcard symbol usually, for WMI queries % is the wildcard symbol. Similarly, to do exact matches use the = keyword; to do relational matches (greater than, less than) use the < and > keywords; and so on.

Special shout-out to this blog post where I first came across the like keyword.