| Abhishek Agrawa...'s profileAbhishek's PowerShell Bl...BlogLists | Help |
|
July 19 Using WindowsInstaller.Installer with PowerShellFinally found some time to update my blog. There are quite a few things I want to write about but just have not found the time lately.
Today, I will talk about PowerShell COM support. As mentioned and discussed elsewhere, powershell provides support for interacting with COM objects. You can do things like:
$a = new-object -com InternetExplorer.Application
$a.visible=$true
$a.Navigate2(http://www.microsoft.com)
new-object -com lets you create a System.__COMObject corresponding to the ProgID "InternetExplorer.Application". PowerShell attempts to get to the TypeInfo for the givem COM object and if avaiable, "adapts" the object so that you can access the properties and methods of the underlying COM object (i.e. the Visible property or the Navigate method) as if they were defined on the top level object. Do a get-member on the object and you will see these members. This is really cool and powerful.
There are times however, when the attempt to get to the TypeInfo does not succeed. When this happens PowerShell is unable to adapt the COM object and you are left with basically a vanilla System.__COMObject which does have useful properties or methods you might have been interested in. One example would be the WindowsInstaller.Installer COM object. We are investigating these cases and looking into additional ways to get to the TypeInfo.
Does it mean you cannot use WindowsInstaller.Installer or these affected COM types with PowerShell? No, you still can. One way is to use tools like tlbimp.exe to generate a RCW wrapper for your COM types. Then you can use this RCW wrapper like any other .Net Type. Many applications (like MS Office) ship with what is known as Primary Interop Assemblies to enable .Net world to talk to their COM types.
A second way is to use reflection against the System.__COMObject. You get to the type by calling gettype() and then use invokemember to invoke the members you want. Off course, this assumes you already know which members you need to get to (You would need to know that anyways if you were doing it in vbscript). Now InvokeMember can be a little verbose at times. To simplify things, I extended the System.__COMObject with ScriptMethods in my Types.ps1xml. These are simple wrappers around InvokeMember. I added the following Methods:
GetProperty // Gets a Property
SetProperty // Sets a Property
InvokeParamProperty // Invokes a paramterized property
InvokeMethod //invokes a method
My.Types.ps1xml looks like the following (Note: I have pulled out the error-handling to make it concise, use it more as a template):
<Types>
<Type> <Name>System.__ComObject</Name> <Members> <ScriptMethod> <Name>GetProperty</Name> <Script> $type = $this.gettype(); $type.invokeMember($args[0],[System.Reflection.BindingFlags]::GetProperty,$null,$this,$null) </Script> </ScriptMethod> <ScriptMethod> <Name>SetProperty</Name> <Script> $type = $this.gettype(); $type.invokeMember($args[0],[System.Reflection.BindingFlags]::GetProperty,$null,$this,@($args[1])) </Script> </ScriptMethod> <ScriptMethod> <Name>InvokeParamProperty</Name> <Script> $type = $this.gettype(); $index = $args.count -1 ; $methodargs=$args[1..$index] $type.invokeMember($args[0],[System.Reflection.BindingFlags]::GetProperty,$null,$this,$methodargs) </Script> </ScriptMethod> <ScriptMethod> <Name>InvokeMethod</Name> <Script> $type = $this.gettype(); $index = $args.count -1 ; $methodargs=$args[1..$index] $type.invokeMember($args[0],[System.Reflection.BindingFlags]::InvokeMethod,$null,$this,$methodargs) </Script> </ScriptMethod> </Members> </Type> </Types> Load this into your powershell session using update-typedata. Now I am all set to use WindowsInstaller.Installer COM type. Scott Hanselman has a VBScript Sample for listing files in MSI installer using WindowsInstaller.Installer. I ported it to powershell using the above described ScriptMethods. The script is listed below.
#ListMSI.ps1
if( $args.count -lt 1)
{ "Usage: listMSI.ps1 <Full Path to MSIFileName>" return; } $msifile = $args[0] $installer = new-object -com WindowsInstaller.Installer $database = $installer.InvokeMethod("OpenDatabase",$msifile,0);
$view = $database.InvokeMethod("OpenView","Select FileName FROM File") $view.InvokeMethod("Execute");
$r = $view.InvokeMethod("Fetch"); $r.InvokeParamProperty("StringData",1); while($r -ne $null)
{ $r = $view.InvokeMethod("Fetch"); if( $r -ne $null) { $r.InvokeParamProperty("StringData",1); } } You would use it like:
> .\ListMSI.ps1 c:\scripts\PowerShell_Setup.msi
HELP.XML|Help.Format.ps1xml
FILESYST.XML|FileSystem.Format.ps1xml DOTNETTY.XML|DotNetTypes.Format.ps1xml CERTIFIC.XML|Certificate.Format.ps1xml MSHCORE.XML|PowerShellCore.format.ps1xml MSHTRACE.XML|PowerShellTrace.format.ps1xml REGISTRY.XML|Registry.format.ps1xml ... PowerShell tries hard to give a consistent, easy and powerful experience. When it cannot, it provides the tools for the user to develop useful workarounds. Comments (26)
Trackbacks (2)The trackback URL for this entry is: http://abhishek225.spaces.live.com/blog/cns!13469C7B7CE6E911!165.trak Weblogs that reference this entry
|
|
|