Why COM object methods are not visible/accessible in powershell? - powershell

As described here, COM objects can be created using New-Object -ComObject ProgID.
With the same steps we can create COM object with ProgID wscAPI.WSCProductList.1. But we see no methods are available whereas there are few as per docs here. Why these methods are not shown/accessible in powershell?
PS C:\>$Wsc = New-Object -ComObject wscAPI.WSCProductList.1
PS C:\>$Wsc | Get-Member
TypeName: System.__ComObject
Name MemberType Definition
---- ---------- ----------
CreateObjRef Method System.Runtime.Remoting.ObjRef CreateObjRef(type requestedType)
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetLifetimeService Method System.Object GetLifetimeService()
GetType Method type GetType()
InitializeLifetimeService Method System.Object InitializeLifetimeService()
ToString Method string ToString()
PS: There is a sample code to access these methods using C++ here.
OS: Microsoft Windows 10 Enterprise

Well, by design, PowerShell is not a COM Object Browser. COM objects are only loaded when instantiated and called.
PowerShell is .Net based, and .Net is part is the Operating system and loaded when the OS starts. This is the reason what you get the .Net namespaces in IntelliSense In PowerShell without extra work. Though even with that one needs to call it correctly. For example,
[System.Net.Dns]::
… or if you want if you don’t what to type all that, you can put this…
Using Namespace System.Net
at the very top of your profile / your script or the first thing you’d put in an interactive session, then you could just due
[Dns]::
About Using It allows you to indicate which namespaces are used
in the session.
COM would not be available to you in this manner. As for your pointer the C++ code. You can use external code segments/blocks/functions in PowerShell to supplement what you may be trying to do and this is what the Add-type cmdlet is used for.
Examples ---
Using CSharp (C#) code in Powershell scripts
As noted online:
from within PS run help add-type -full and look for the F# example
(example 7 on my system). Assuming you have a C++ compiler that
follows the "CodeDom" model, you can mirror the F# example.
Also, PowerShell can call unmanaged code using P/Invoke (this is a .NET concept), search the web for the use case.
So, based on even what you are showing, you are talking beyond this?
$wscAPI = New-Object -ComObject wscAPI.WSCProductList.1
$wscAPI.CreateObjRef(
... so, you want to be able to say to have IntelliSense/tab completion for when you start typing
New-Object -ComObject #[tab if on the command line or IntelliSense popup in an editor]
Yet there are modules/scripts, etc. via the Microsoft powershellgallery.com, the demonstrate how you could write your own object browser. See the resources below:
PowerShell Object Browser
This is a PowerShell application that displays objects in an explorer
type application written in PowerShell. The PowerShell Object Browser
displays the properties and child objects/collections for any
PowerShell / .Net framework object. For an explanation on how to use
this a
Download: PSObjectBrowser.ps1
There are several WMI, CIM, .Net examples/tools that show how to do
object browsing, but again, that .Net. Just search for them as well.
For Example:
PowerShell Object Browser
This is a PowerShell application that displays objects in an explorer
type application written in PowerShell. The PowerShell Object Browser
displays the properties and child objects/collections for any
PowerShell / .Net framework object. For an explanation on how to use
this a
Download: PSObjectBrowser.ps1
Weekend Scripter: The WMI Explorer Tool
Weekend Scripter: Announcing WMI Explorer 2.0
PowerShell ISE Add-on - CIM Explorer
PowerShell ISE add-on for navigating CIM(WMI) namespaces/definitions
and creating PowerShell script examples through CIM cmdlets
Free WMI Explorer, WMI Tools & Powershell GUI Builder
Coretech WMI and PowerShell Browser
· A powerful WMI tool that allows you to connect to and work with WMI
locally and remotely. With the tool, you can browse WMI namespaces,
classes, methods, properties and class instances. Besides being a WMI
browser the Coretech WMI and PowerShell Explorer allows also to export
query results and namespace methods.
These could give you ideas regarding how you might design one for COM. Yet, you'd have to know all the possible COM classes (registered or not) you'd like to instantiate. So, you could do something like this to find them ...
Get-ChildItem -Path 'HKLM:\Software\Classes' -ErrorAction SilentlyContinue |
Where-Object {$PSItem.PSChildName -match '^\w+\.\w+$' -and
(Get-ItemProperty "$($PSItem.PSPath)\CLSID" -ErrorAction SilentlyContinue)} |
Format-Table PSChildName -AutoSize
# Results
<#
Sample output from the COM lookup.
PSChildName
-----------
...
Access.Application
...
AcroPDF.PDF
...
AudioVBScript.1
...
CDO.Message
...
Cmiv2.CmiFactory
...
Excel.Application
...
InternetExplorer.Application
...
Outlook.Application
...
Paint.Picture
...
#>
One, of course, can modify the regex to look up whatever string(s) they choose. See the Regular Expression resource for detailed guidance on regex use.
RegExLib.com Regular Expression Cheat Sheet (.NET)
Regular-Expressions.Info Tutorials
So, with that block for say this effort...
New-Object -ComObject #[tab if on the command line or IntelliSense popup in an editor]
... as long as you load this at startup, and give them a populated variable name, that would be a quick and dirty way to do this. I've done similar things not only for this but for the .Net classes as well...
Function Get-AllCimClassesAsVariables
{
# Set variables for CimClasses
ForEach($Item in ((Get-CimClass -ClassName *).CimClassName))
{Set-Variable -Name $Item -Value $Item}
}
Point of note here:
I don't use this approach anymore, because it is cumbersome, has a heavy startup load because it would have to populate this list on every PowerShell session.
I have full Visual Studio on my workstation (one can use the paid-for or free version), as well, so I use it for PowerShell development stuff as well to reference and browse COM resources.
Prior to that, I used OleView.exe from the Windows / MSOffice SDKs. There was also in the Windows Resource kits that were free to download. I still have that in a cloud location for my easy access, pinned to my taskbar when I just need that vs spinning up Visual Studio or when I am in a location where I don’t have Visual Studio at hand.
To do what you are after, it would be prudent just to use OleVIew.exe as part of your PowerShell toolset to look up and use the COM info, find the ones you'd regularly use and put those into a module you regularly load via your profile or you'll need to gin up whatever thingy you need for browsing the similar to what tools are shown for WMI/CIM/.Net ones above.

Related

Poweshell, Namespaces, and Variable Declaration

I'm making some Powershell routines that automate tasks for Windows 10, Firewall, Services, and Task Scheduler.
Windows 10 Pro x64
Powershell 7.1.2 x64
I declare variables using their object declaration exposed by their namespaces, this works quite well for functions operating on Windows Services. As follows:
using namespace System
using namespace System.ServiceProcess # ServiceController exposure
Calling the System.ServiceProcess objects/library, exposes ServiceController object, so that I can declare variable or function returns as such.
[ServiceController] $WmplayerNetSrv = (Get-Service -Name "WMPNetworkSvc" -ErrorAction Stop)
This allows me to know exactly what a variable is, and enables a better intellisense list when referencing the variable.
All is well until I construct functions for the Windows 10 Firewall.
using namespace System
using namespace Microsoft.Management.Infrastructure #.CimInstance exposure
using namespace Microsoft.Management.Infrastructure.CimInstance#root/standardcimv2 # /MSFT_NetFirewallRule exposure
I know variables in Powershell need not be declared, but I really want to do so.
I've tried:
using namespace Microsoft.Management.Infrastructure.CimInstance#root/standardcimv2
[MSFT_NetFirewallRule] $WinFwRule = (Get-NetFirewallRule -DisplayName "RULE_NAME" -ErrorAction Stop)
Doesn't work, complains it can't find, MSFT_NetFirewallRule, type.
using namespace Microsoft.Management.Infrastructure.CimInstance
[#root/standardcimv2/MSFT_NetFirewallRule] $WinFwRule = (Get-NetFirewallRule -DisplayName "RULE_NAME" -ErrorAction Stop)
Doesn't work, complains about the #, and the /, I'm sure the type wouldn't work too.
[Microsoft.Management.Infrastructure.CimInstance#root/standardcimv2/MSFT_NetFirewallRule] $WinFwRule = (Get-NetFirewallRule -DisplayName "RULE_NAME" -ErrorAction Stop)
Doesn't work, complains it can't find, MSFT_NetFirewallRule, type.
So my questions are, how can I declare a variable as, [MSFT_NetFirewallRule]? Is this possible? I know I can fall back on [object[]], or not declare it.
Thanks and regards,
-njc
Your confusion is probably because you're mixing up CIM classes with dotnet classes (i.e. types in the dotnet type system).
MSFT_NetFirewallRule is a CIM class (see MSFT_NetFirewallRule) and not a type from the dotnet type system, and as far as PowerShell is concerned your firewall rule is just a dotnet object of type CimInstance with a string property called CimClass that happens to have the value Root/StandardCimv2:MSFT_NetFirewallRule.
Your code should work if you do this:
[CimInstance] $WinFwRule = (Get-NetFirewallRule -DisplayName "RULE_NAME" -ErrorAction Stop)
but that's about as specific as you can be with your types.

Get installed software product context using powershell

I can easily get all installed software products on a machine using
Get-WmiObject -Class Win32_Product
Now I'd like to also fetch the Product Context. How can I access this information for every installed product using PowerShell.
In VB I did that by using the WindowsInstaller COM-Object and then querying the information. In essence this:
Set Com = CreateObject('WindowsInstaller.Installer')
Set Products = Com.ProductsEx(vbNullString,"S-1-1-0",7)
For Each P in Products
context = P.Context
Which I dont not manage to replicate in PowerShell
I realize this question is a bit stale, but I disagree with what seems to be the prevailing notion that working with Windows Installer in PowerShell is somehow a "pain" and more complicated than working with it in VBScript (this post is just one of many).
I have found that VBScript Windows Installer code translates quite literally to PowerShell, which means there are numerous examples of VBScript Windows Installer scripts that can be adapted to PowerShell and used to learn how to work with Windows Installer in PowerShell.
For this specific question of install context, the PowerShell code is quite similar to the VB code the OP gave.
# code must be run with admin rights to use "S-1-1-0" SID
enum InstallContext {
FirstVisible = 0 # product visible to the current user
None = 0 # Invalid context for a product
UserManaged = 1 # user managed install context
UserUnmanaged = 2 # user non-managed context
Machine = 4 # per-machine context
All = 7 # All contexts. OR of all valid values
AllUserManaged = 8 # all user-managed contexts
}
$Installer = New-Object -ComObject WindowsInstaller.Installer
foreach ($P in $Installer.ProductsEx("", "S-1-1-0", 7)) {
[InstallContext]$P.Context()
}
NOTE: I used Enums (about Enum - PowerShell | Microsoft Docs) with PowerShell here since tagMSIINSTALLCONTEXT is an enum in the msi.h file.
It's a pain to use that com object in powershell. I would use vbscript instead and save the text output to a powershell variable, or find an msi powershell module. That com object doesn't have a "type library" or support "IDispatch". The Windows Powershell in Action appendix for 2nd edition goes into it, but even there it's not pretty. That vbscript code has errors.

Where to find documentation about add_<eventname>(<scriptblock>) like add_click, add_shown using powershell?

I'm creating windows form.
I'm looking for documentation about this code :
$bouton.Add_click($boxtext)
$form.Add_Shown({$form.Activate(); $textbox.Focus()})
and if it exists other code using $form.Add_
I use Windows Powershell ISE but i haven't completion for this
Go through the below links:
How-to-build-a-form
Creating_GUIs_in_Windows_PowerShell_1.0_with_WinForms
The accepted answer to this question is lacking info that those new to PowerShell and Windows forms and controls may find useful.
You can find detailed information and documentation for events in Windows forms and controls through both PowerShell code and online webpages.
This script:
Provides info for the ListView control.
Creates, and opens, ListView_Add_Events.txt which list all "Add_" methods for adding events for the ListView control.
Opens a GridView showing all events for ListView and their definitions.
### 3 ways for making System.Windows.Forms available to the script ###
## Only works in PowerShell 5.1 ##
# using assembly System.Windows.Forms
## Works in PowerShell 5.1 and 7.x ##
Add-Type -Assembly System.Windows.Forms
## Works in PowerShell 5.1 and 7.x ##
# [void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$ListView = [Windows.Forms.ListView]#{
}
$ListView | Get-Member -Force | Where-Object { $_.Name.StartsWith('Add_','CurrentCultureIgnoreCase')} | Out-File ListView_Add_Events.txt
.\ListView_Add_Events.txt
$ListView | Get-Member -MemberType Event | Select-Object -Property Name, Definition | Out-GridView
Further more, Microsoft does provide detailed documentation for the ListView control - though the focus is on C# and not PowerShell. To start with, we want documentation that matches our .NET version that we are using. According to this page:
Windows PowerShell 5.1 is built on top of the .NET Framework v4.5
Doing a little searching I was able to find the events for .NET Framework 4.5.2 version of ListView in this page.
Once you find the online documentation, you can easily find documentation for other controls, or even the events for Forms, using then navigation panel on left side of the webpage, and you can also select the desired version of .NET using the Version selector near top left of the page.
One final note: I found this Answer useful for adding/creating events.

InternetExplorer.Application com object and windows 2012 in powershell

I am trying to access the document of an internet explorer com object with windows 2012. The code works great in windows 2008 but as soon as I try to run it on windows 2012 (fresh install, tried on more than one server), the same code stops working. In other words, $ie.document.documentHtml returns as null.
Below is the code:
$ie = new-object -com "InternetExplorer.Application"
$ie.navigate2("http://www.example.com/")
while($ie.busy) {start-sleep 1}
$ie.document.documentHtml.innerhtml
Has the interexplorer com object changed in windows 2012? and if yes, how do I do I retrieve the document contents in windows 2012?
Thanks in advance
edit: Added a bounty to sweeten things up. Invoke-WebRequest is nice but it works only on windows 2012 but I need to use internet explorer and have it work both on windows 2008 and windows 2012. I have read somewhere that installing microsoft office solves the issue. It is not an option either.
edit2: as I need to remotely invoke the script on multiple windows server (both 2008 and 2012), I would prefer not to copy files manually
It's a know bug: http://connect.microsoft.com/PowerShell/feedback/details/764756/powershell-v3-internetexplorer-application-issue
An extract from the workaround:
So, here's a workaround:
Copy Microsoft.html.dll from a location where it is installed (eg: from C:\Program Files(x86)\Microsoft.NET\Primary Interop Assemblies to your script's location (can be a network drive)
Use the Load-Assembly.ps1 script (code provided below and at: http://sdrv.ms/U6j7Wn) to load the assembly types in memory
eg: .\Load-Assembly.ps1 -Path .\microsoft.mshtml.dll
Then proceed as usual to create the IE object etc. Warning: when dealing with the write() and writeln() methods use the backward compatible methods: IHTMLDocument2_write() and IHTMLDocument2_writeln().
$ie.document.documentHtml.innerhtml
The bigger question is how this ever could have worked. The Document property returns a reference to the IHTMLDocument interface, it does not have a "documentHtml" property. It is never that clear what you might get back when you use late binding as was done in this code. There is an old documentHtml property supported by the DHTML Editing control, that has been firmly put to the pasture. Admittedly rather a wild guess.
Anyhoo, correct syntax is to use, say, the body property:
$ie = new-object -com "InternetExplorer.Application"
$ie.navigate2("http://www.example.com/")
while($ie.busy) {start-sleep 1}
$txt = $ie.document.body.innerhtml
Write-Output $txt
If you still have problems, Powershell does treat null references rather undiagnosably, then try running this C# code on the machine. Ought to give you a better message:
using System;
class Program {
static void Main(string[] args) {
try {
var comType = Type.GetTypeFromProgID("InternetExplorer.Application");
dynamic browser = Activator.CreateInstance(comType);
browser.Navigate2("http://example.com");
while (browser.Busy) System.Threading.Thread.Sleep(1);
dynamic doc = browser.Document;
Console.WriteLine(doc.Body.InnerHtml);
}
catch (Exception ex) {
Console.WriteLine(ex.ToString());
}
Console.ReadLine();
}
}
As far as I can tell, on Windows Server 2012 to get the full html of a page:
$ie.document.documentElement.outerhtml
There is also an innerhtml property on the documentElement, which strips off the root <html> element.
Of course, if all you want to do is get the raw markup, consider using Invoke-WebRequest:
$doc = Invoke-WebRequest 'http://www.example.com'
$doc.Content
Get any PC with Office installed and copy Microsoft.mshtml.dll to your script location.
c:\program files (x86)\Microsoft.net\primary interop assemblies\Microsoft.mshtml.dll
add-Type -Path Microsoft.mshtml.dll
Script works.

How do I get a list of all tfs alert subscriptions via powershell (get .Net assembly via PowerShell?)

I'm trying to move my tools to powershell, can this be done in PowerShell? the bit I'm really interested in is:
IEventService es = tfs.GetService(typeof(IEventService)) as IEventService;
List<Subscription> ls = es.GetAllEventSubscriptions().ToList();
Edit: what I really want to do might be using a .NET assembly from powershell and this might then be a duplicate of Using .NET library from PowerShell
Here is a TFS API in PowerShell function that I found on a blog long ago that will get you started. I've posted it to a GitHub Gist. Basically you ensure you've loaded up the TFS assemblies into the AppDomain and then you can add any TFS Service Interfaces you want to the object and just operate on them just as you would in any c# application, etc.
https://gist.github.com/3288447
Once you have the TFS object returned from the method in the Gist above, you can operate on the loaded services like so:
#use work item service
$tfs = get-tfs $env:TFSSERVERURL -silent
$project = $tfs.wit.Projects | ?{ $_.Name -eq $projectName}
#todo - replace with text for query or file read -- this is deprecated
$query = $project.StoredQueries | ?{ $_.Name -eq 'Active Bugs' }
$queryText = $query.QueryText.Replace("#project","'$projectName'")
$results = $tfs.wit.Query($queryText)
#do something with the results...
In your request above you can just alter the get-tfs method to add your service interface to the set loaded and then operate on the .NET methods much like I do in the example above.