Use InsertAdjacentHtml by Powershell with IE COM - powershell

I am trying to use InsertAdjacentHtml with IE COM by Powershell but my code fails what can be the reason ?
$oIE = new-object -ComObject InternetExplorer.Application
$oIE.visible=$True
$oIE.navigate2("http://www.quirksmode.org/dom/core/getElementsByName.html")
While ($ie.Busy) {
Sleep 2
}
$doc = $oIE.Document
$btns = $doc.getElementsByTagName("input")
$btns.insertAdjacentHTML('afterend', '<div id="secondDiv">Second</div>');
$oIE.visible=$True
The comand line shows Invalid Operation error

I ran your script both line by line and by saving it as a PowerShell script, but got different results - both were errors.
When running line by line, I get this error:
Method invocation failed because [System.__ComObject] does not contain a method named 'insertAdjacentHTML'.
At line:1 char:1
+ $btns.insertAdjacentHTML('afterend', '<div id="secondDiv">Second</div>');
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (insertAdjacentHTML:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
Altough the method 'insertAdjacentHTML' is listed with Get-Member, it cannot be used.
So, I wanted to find a reason for that or another way to achieve the same thing. I ended up with this conclusion: PowerShell might not be the best tool to achieve what you are trying for OR you should use objects that have more reliable methods.
When I run an altered script, line by line, it sort of works:
$oIE = new-object -ComObject InternetExplorer.Application
$oIE.visible=$True
$oIE.navigate2("http://www.quirksmode.org/dom/core/getElementsByName.html")
$doc = $oIE.Document
$btn = $doc.all.item("test", 1)
$btn.insertAdjacentHTML('afterend', '<div id="secondDiv">Second</div>')
$oIE.visible=$True
This is the HTML produced:
<div id="test">
<p name="test">This is a paragraph with name="test"</p>
<ppk name="test">This is a ppk tag with name="test"</ppk>
<p><input name="test"><div id="secondDiv">Second</div>This is an input with name="test"</p>
<p><img name="test">This is an image with name="test"</p>
</div>
To make things even stranger, this only works in a normal PowerShell console, it fails when using PowerShell ISE.
EDIT: Try it with a ForEach loop, it just might work. It occurred to me that you cannot run a method on an array of objects unless you call it within a loop AND another thing, that the page the script was navigating to might have issues.
So, this works:
$oIE = new-object -ComObject InternetExplorer.Application
$oIE.visible = $True
$oIE.navigate2("https://stackoverflow.com/questions/28650033/use-insertadjacenthtml-by-powershell-with-ie-com/")
Start-Sleep -Milliseconds 3333
$doc = $oIE.Document
$btns = $doc.getElementsByName("_id_")
$btns | ForEach-Object { $_.insertAdjacentHTML('afterend', '<div id="secondDiv">Second</div>') }
Thank you for the question, this is nice to know.

Related

You cannot call a method on a null-valued expression in powershell with excel

I have started learning PowerShell with excel and getting null-valued expression error
#open excel application
$x1 = New-Object -comobject excel.application
#open excel to show the result in realtime
$x1.Visible = $true
#open the already existing excel to edit
$test = $x1.Workbooks.Open("C:\Users\tushar.v\OneDrive - HCL Technologies Ltd\Documents\test.xlsx")
#to open a specific worksheet
$test2 = $test.worksheets.Item(1).Activate
$test2.Cells.Item(1,1) = "alphatext"
Error :
You cannot call a method on a null-valued expression.
At line:9 char:1
+ $test2.Cells.Item(1,1) = "alphatext"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Also , I am not getting the output in the excel
First of all, I would advise to use better variable names, so in a larger script it is clear what every variable contains. $test and $test2 are not really descriptive..
Then for what you have tried:
The worksheet's Activate() method does not return an object referencing the activated worksheet as you might think, so you need to first get the worksheet object in a variable and use that to perform the Activate() method.
Try:
# create excel application
$excel = New-Object -comobject excel.application
# open excel to show the result in realtime
$excel.Visible = $true
# open the already existing excel to edit
$workbook = $excel.Workbooks.Open("D:\Test\blah.xlsx")
# get a reference to the first worksheet in the file
$worksheet = $workbook.WorkSheets.Item(1)
# make this worksheet active
$worksheet.Activate()
# and add content to the cell
$worksheet.Cells.Item(1,1) = "alphatext"
Finally, creating COM objects consumes memory, so after your work is done, you need to tell Windows that it can clean up those objects:
# Don't forget to quit Excel when done (after saving perhaps?) and to remove the created COM objects from memory:
$excel.Quit()
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($worksheet)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($workbook)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()

Basic IE Automation Issues - PowerShell

Apologies, I'm incredibly new to PowerShell (And scripting in general), and I'm having a problem with the very basics of IE Automation that I can't quite get my head round.
What I want to do is have a script that automatically logs onto a webpage, and then inputs data into a form. But I can't seem to input data into the text input fields on the login page. I've been scouring the internet left right and centre, but haven't yet found the answer, though I imagine it will be an obvious one.
Here is my script so far:
$ie = new-object -ComObject InternetExplorer.Application;
$requestUri = "www.testurl.com"
$ie.visible = $true
$ie.navigate($requestUri)
while ($ie.ReadyState -ne 4)
{
start-sleep -Seconds 1;
}
$doc = $ie.Document
$doc.GetElementById("ppm_login_username") = $userName
$userName.value = "UserName"
However, whenever I run the script, I get the error
The property 'value' cannot be found on this object. Verify that the property exists and can be set.
+ $userName.value = "UserName"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : PropertyNotFound
I have no experience with a lot of this, so again, apologies for using incorrect terminology.
Using the DOM explorer, the input field has the following line of code:
<INPUT id=ppm_login_username maxLength=240 size=40 name=userName>
So I'm confident that I'm getting the correct object, but it doesn't seem to accept the 'value' method when trying to pass data through to it.
Value does show up as a property of the object though, so I can't understand why it doesn't pass through.
Any help and time you can offer is greatly appreciated!
You've mixed it up. It should be $variable = value. Try:
$doc = $ie.Document
#Set $username to reference of "ppm_logon_username"-input node
$userName = $doc.GetElementById("ppm_login_username")
$userName.value = "UserName"
Where is the variable $userName defined?
It needs to have the property "value".
Usually you could add it like this:
$Username | Add-Member -MemberType NoteProperty -Value "UserName" -Name value
but I am not sure whether this is possible for a variable of the type string.
I am not sure why you are trying to set $userName.value = "UserName" anyway.
Additionally it might be better to just use Invoke-WebRequest / Invoke-RestMethod, instead of trying to automate IE :)
https://technet.microsoft.com/de-de/library/hh849901.aspx
https://technet.microsoft.com/en-us/library/hh849971(v=wps.620).aspx

powershell outlook automation namespace

I'm working with automating the opening of a public folder by EntryID from IE with javascript and activex and ran across some errors. To debug I've re-wrote it as a power shell script.
$eid = "HEX EntryID FOR PUBLIC FOLDER";
$o = new-object -com outlook.application;
$ns = $o.GetNamespace("MAPI");
#$ns #if this line is commented, error
$f = $ns.GetFolderFromID($eid)
$f.Display();
If I shut outlook down completely, and then run the script I get the following error
Exception calling "GetFolderFromID" with "2" argument(s): "The messaging interface has returned an unknown error. If the problem persists, restart Outlook."
At G:\scripts\outlook.ps1:5 char:25
+ $f = $ns.GetFolderFromID <<<< ($eid)
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
If I uncomment the $ns line, everything works fine, even if its removed again. That is, until I close out of Outlook completely, almost as if the $ns com object isn't being truly initialized until I output it to the console.
I'd like to know:
why calling $ns fixes the problem
why powershell thinks I'm passing 2 arguments
is there a way to implement this workaround
in javascript
The GetFolderFromID() requires two arguments: EntryID and StoreID of folder wanted.
This code gives no error and show outlook with PublicFolder selected:
$o = new-object -com outlook.application;
$ns = $o.GetNamespace("MAPI");
$cp = $ns.Folders # FolderClass ComObject containing all Outlook folders, usually first is the PublicFolder
$f = $ns.GetFolderFromID( $cp.GetFirst().EntryID ,$cp.GetFirst().StoreID )
$f.Display();
Using your code I can't do it, $ns line commented or not.
You can get the public folders store programatically:
$ol = New-Object -ComObject Outlook.Application
$pf = $ol.GetNamespace("MAPI").Folders | Where-Object {$_.FolderPath -like "\\Public Folders*"}
$pf.Display()

Powershell - SaveAs function when file already exists

I'm trying to run some code that looks for all .doc & .docx files in a directory & sub-directories and then converts each one to PDF format.
The code below works only if there are no instances of the pdf in these directories i.e. it only works first time. Every subsequent time it fails with:
Exception calling "SaveAs" with "2" argument(s): "Command failed"
At C:\convert\convertword.ps1:12 char:13
+ $doc.saveas <<<< ($path, $wdFormatPDF)
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ComMethodTargetInvocation
When I delete the previously created PDFs and re-run the PS it works fine. Therefore I can only assume there is a switch or parameter that I'm missing from my SaveAs function which somehow forces the overwrite?
$wdFormatPDF = 17
$word = New-Object -ComObject word.application
$word.visible = $false
$folderpath = "c:\convert\*"
$fileTypes = "*.docx","*doc"
Get-ChildItem -path $folderpath -recurse -include $fileTypes |
foreach-object `
{
$path = ($_.fullname).substring(0,($_.FullName).lastindexOf("."))
"Converting $path to pdf ..."
$doc = $word.documents.open($_.fullname)
$doc.saveas($path, $wdFormatPDF)
$doc.close()
}
$word.Quit()
Ok I finally think I've tracked down the problem. It's the Windows Explorer Preview Pane which is locking the file. I had show preview pane turned on the directory where the files were being created and converted, this must have been creating a file lock on the pdf's therefore the script cannot save the new pdf. I turned off preview pane in my Windows Explorer and the script now works repeatedly! Therefore nothing wrong with the Powershell Scripting but thanks for all the input guys. Here's a link to the closest MS KB article that I could find on the subject http://support.microsoft.com/kb/942146
try this:
$word.displayalerts = $false
$doc.saveas($path, $wdFormatPDF) # with Word2010 I've to use $doc.saveas([ref]$path, [ref]$wdFormatPDF)
$word.displayalerts = $true
No error is raised, but I'm using Word2010 I can't test it with other versions
There's no flag to overwrite according to the documentation for SaveAs and SaveAs2. So you could just remove it before saving with something like this:
Remove-Item -Path $path -Force -ErrorAction SilentlyContinue
$doc.saveas ($path, $wdFormatPDF)

HTA writing to a <span> from a text file

I am trying to write data from a text file to a in an HTA.
I'm running a powershell script inside of the HTA, using VBscript for the input buttons
Get-TSSession -computername ismeta | where { $_.username -eq 'amis5235'} | format-table windowstationname,username,state,sessionid | out-file C:\windows\temp\PSTerminalServices.txt
I'm going to be using a for each loop for about 60 servers
Then I was hoping to write the output to a within the HTA, kind of like a streamer in VB or stacking a string the VBscript, something like:
strHTML = strHTML & "Running Process = " & objProcess.Name & " PID = " & objProcess.ProcessID & " Description = " & objProcess.Description & "<br>"
but it seems there should be a simpler way to do this.
I think this minimal HTA will solve your problem. It runs a command line and reads the output stream, one line every 1/10 second, then pushes the results into a textarea. You may want to alter your Powershell script to return the process details to STDOUT, but it will probably work.
<script language="Javascript">
var E, LineWriteTimerID
function execWithStatus(cmdLine){//Can't run minimized with Exec. Can't capture StdOut/StdErr with Run.
E = new ActiveXObject("WScript.Shell").Exec(cmdLine);
LineWriteTimerID = window.setInterval("writeOutLine()",100);//pause for 100ms
E.StdIn.Close();//must close input to complete a ps command
}
function writeOutLine(){
if(E.StdOut.AtEndOfStream) window.clearTimeout(LineWriteTimerID);
if(!E.StdErr.AtEndOfStream) txtResults.value += "ERROR: " + E.StdErr.ReadAll() + "\n";
if(!E.StdOut.AtEndOfStream) txtResults.value += E.StdOut.ReadLine() + "\n";
}
</script>
<textarea id=txtCmd style="width:90%" rows=1>
powershell.exe -noninteractive -command ls c:\windows\system32\drivers\etc\</textarea>
<button onclick="execWithStatus(txtCmd.value)">Run</button>
<br><textarea id=txtResults style="width:100%" rows=20></textarea>
Save this code as an .HTA file, change the contents of the txtCmd textarea to be your command line, and give it a try. Good Luck!
Ok Here is the way I use.
On the theorical point of view it consist in building an interface with Windows Forms and then put PowerSell code behind event.
On technical point of view two solutions :
1) Use visual studio free edition to build interface in C# and then a conversion tool to create the associate PowerShell source (french article here)
2) you can download freely (you just need to register) Sapiens PrimalFormsCE.exe (Community Edition)
This tool allow you create a form and then to generate Powershell associete code.
You can also build forms from crash here is a peace of sample code :
Add-Type -AssemblyName system.Windows.Forms
# Create the form
$form = New-Object Windows.Forms.Form
$form.Text = "Test Saisie"
$form.Size = New-Object System.Drawing.Size(250,154)
# Create EntryFiel
$TB_Saisie = New-Object System.Windows.Forms.TextBox
$TB_Saisie.Location = New-Object System.Drawing.Point(50,31)
$TB_Saisie.Size = New-Object System.Drawing.Size(150,32)
# Create "Ok" Button
$PB_Ok = New-Object System.Windows.Forms.Button
$PB_Ok.Text = "Ok"
$PB_Ok.Location = New-Object System.Drawing.Point(50,62)
$PB_Ok.Size = New-Object System.Drawing.Size(50,32)
$PB_Ok.DialogResult = [System.Windows.Forms.DialogResult]::OK
# Create "Cancel" Button
$PB_Cancel = New-Object System.Windows.Forms.Button
$PB_Cancel.Text = "Cancel"
$PB_Cancel.Location = New-Object System.Drawing.Point(150,62)
$PB_Cancel.Size = New-Object System.Drawing.Size(50,32)
$PB_Cancel.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
# Add controls to the form
$form.Controls.Add($PB_Ok)
$form.Controls.Add($PB_Cancel)
$form.Controls.Add($TB_Saisie)
# Message loop
$Res = $form.ShowDialog()
If ($Res -eq [System.Windows.Forms.DialogResult]::OK)
{
Write-Host ("Accepted : {0}" -f $TB_Saisie.Text)
}
else
{
Write-Host "Cancel"
}