InternetExplorer.Application com object and windows 2012 in powershell - 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.

Related

Why COM object methods are not visible/accessible in 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.

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.

Download file from website using SendKeys in Powershell

I'm trying to download an file from a particular website by clicking on the file icon. Website login works but i'm hoping to use keystroke "TAB" to navigate to the excel file and finally key "Enter" to download. Ran the code but resulted in the Powershell text of "FALSE". Any advice is appreciated! Thanks.
Reference: Table screenshot
$url = "https://abcdefg.com"
$username="test#gmail.com"
$password="TestPW"
$ie = New-Object -com internetexplorer.application;
$ie.visible = $true;
$ie.navigate($url);
while ($ie.Busy -eq $true)
{
Start-Sleep -Milliseconds 1000;
}
$ie.Document.getElementById("txtEmail").value = $username
$ie.Document.getElementByID("txtPassword").value=$password
$ie.Document.getElementById("Login").Click();
Start-Sleep -Milliseconds 10000
$obj = new-object -com WScript.Shell
$obj.AppActivate('Internet Explorer')
$obj.SendKeys('{TAB}')
$obj.SendKeys('{TAB}')
$obj.SendKeys('{TAB}')
$obj.SendKeys('{TAB}')
$obj.SendKeys('{Enter}')
Why are you doing that vs using web scraping to find the link you are trying to hit, and use the link URL directly?
Your post is really a duplicate of this Q&A.
Use PowerShell to automate website login and file download
SendKeys could work, but they are very hinky and on different systems may not function as you'd expect. There are better tools dedicated to do this, AutoIT, Selenium, WASP
--- That WASP tool still works, but has not been updated in a long while.
Using PowerShell 2.0 With Selenium to Automate Internet Explorer, Firefox, and Chrome
Internet Explorer
Next you want to obtain the Internet Explorer driver from this site. I
recommend version 2.41 because “as of 15 April 2014, IE 6 is no longer
supported”. This must reside in your current PATH so in your script
you may want to modify your PATH to ensure the executable
(IEDriverServer.exe) can be found there. If you’re wondering whether
to get the 32-bit or the 64-bit version, start with the 32-bit even if
you’ve got a 64-bit Windows.
At this point you’ll want to quickly instantiate Internet Explorer and
navigate somewhere. Great. Let’s do it.
# Load the Selenium .Net library
Add-Type -Path "C:\selenium\WebDriver.dll" # put your DLL on a local hard drive!
# Set the PATH to ensure IEDriverServer.exe can found
$env:PATH += ";N:\selenium"
# Instantiate Internet Explorer
$ie_object = New-Object "OpenQA.Selenium.IE.InternetExplorerDriver"
# Great! Now we have an Internet Explorer window appear. We can navigate to a new URL:
$ie_object.Navigate().GoToURL( "http://www.bbc.co.uk/languages" )
# This worked! The call won’t return until the page download is complete.
# Next let’s click on a link from the link text:
$link = $ie_object.FindElementByLinkText( "Spanish" )
$link.Click()
# display current URL
$ie_object.Url
Selenium Tutorial: All You Need To Know About Selenium WebDriver
Update for the OP
As for...
However the file does not have a redirected URL
Then you need to look deeper at the site, to find the anchor to the file that you can force click on.
Example:
# Scrape a web page with PowerShell
$w = Invoke-WebRequest -Uri 'https://www.reddit.com/r/PowerShell'
$w | Get-Member
$w.AllElements
$w.AllElements.Count
$w.Links.Count
$w.Links
$w.Forms
$w.Forms.Fields
$w.Forms[0]
$w.Forms[0].Fields
$w.RawContent
$w.ParsedHtml
once you find tag names, or the like, you need to parse that to get stuff out of it.
$w.AllElements | Where-Object -Property 'TagName' -EQ 'P' | Select-Object -Property 'InnerText'
For tables you have to dig more.
Extracting Tables from PowerShell’s Invoke-WebRequest

PSImageTools cannot load Wia.ImageFile

I have installed the PowerShellPack on a Server 2008 R2 machine, but am unable to use the PSImageTools module. All image operations yield the error New-Object : Cannot load COM type Wia.ImageFile. I have tried installing the same package on my Windows 7 laptop, and there it works.
I figure this must be because of missing Dll:s on the server, but even copying the wia*.dll files to System32 from the laptop (there are no corresponding files already in place) helps. It is not possible to register the dlls (regsvr32 wiaaut.dll, for instance), that is replied to with "The file failed to load".
So, has anybody successfully used Wia on Windows Server 2008? We're going to run a scheduled powershell script to update the outlook image of our users, so it needs to be able to run on the server.
I get the same error on Windows Server 2008 x64. Tried both x64 and x86 version of PowerShell v2.
As an alternative you might be able to use System.Drawing.Image. Depends on what your script needs to do. Perhaps post some example code.
http://msdn.microsoft.com/en-us/library/system.drawing.image.aspx
Add-Type -AssemblyName System.Drawing
$image = [System.Drawing.Image]::FromFile("C:\pic.bmp")
EDIT: Here's how you can change an image's dimensions:
$pixWidth = 90
$pixHeight = 90
$image2 = New-Object System.Drawing.Bitmap -ArgumentList $image, $pixWidth, $pixHeight
$image2.Save('C:\new_pic.bmp', [System.Drawing.Imaging.ImageFormat]::Bmp)
Found this article after quite a bit of searching:
http://kb.winzip.com/kb/entry/207/
Basically, you need to enable and set to Automatic the "Windows Image Acquisition" service in Windows 2003, or in Windows 2008, install the Desktop Experience feature first, then enable the "Windows Image Acquisition" service and set to Automatic.

Open url from script on Windows Server 2008

I want to write a script that loads a url (eg. http://google.com) automatically. But I don't want to install any 3rd party libraries or programs to the server. what's the easiest way to do this?
I just my options are batch script, vb script or powershell right?
FYI from PowerShell, if you want to retrieve the contents of the URL you can do this;
$page = (new-object net.webclient).DownloadString("http://www.bing.com")
$page # writes the contents to the console
If you just want to open it in a browser:
Start-Process http://www.bing.com
Or using the start alias
start http://www.bing.com
Start-Process is new in PowerShell 2.0.
The beauty of Powershell is it has so many ways to do something.
This is my Powershell 2.0 example code - consisting of a Pause function to allow the site to open. It uses Internet Explorer as the browser. In this case - IE is a better browser than the others because it integrates with Powershell through a verbose API.
$url = "http://www.google.com/"
$ie = new-object -com "InternetExplorer.Application"
$ie.Navigate($url)
There are many different functions attached to this object. I recommend loading up the Powershell command line, typing in the above code, and checking what other functions this object has. Type $ie. and pressing TAB iterates through all the methods of this library.
The more I learn of Powershell, the more exciting it becomes. There is nothing it cannot do on Windows.
you can use vbscript
url="http://somewhere.com"
Set objHTTP = CreateObject( "WinHttp.WinHttpRequest.5.1" )
objHTTP.Open "GET", url, False
objHTTP.Send
wscript.Echo objHTTP.ResponseText
objFile.Close