How to connect Powershell script to an already opened document? - powershell

I have a Powershell script that performs some modifications with a MSWord document. In the beginning of the script PS opens the document:
$word = New-Object -ComObject Word.Application
$desktop_path = [Environment]::GetFolderPath("Desktop")
$doc = $word.Documents.Open("$desktop_path" + "blabla.docx")
But as the requirements changed, I now need to run this PS script within an already opened document. Is there any options to force PowerShell find an opened document (by the name for example) and "connect" to it?
FYI: The sequense I want to obtain is: I open the file, launch some macros, call from VBA my PSScript (and here I need PS to be able to "fetch" an opened doc), launch other macros.
Thank you a lot in advance!

This should the same as for Excel:
$word = [Runtime.Interopservices.Marshal]::GetActiveObject('Word.Application')
Note the same-user limitation. I would recommend doing the whole thing programmatically via PowerShell though, as suggested by Guenther.
If word is not running, is running under a different user, or is running as admin (and PowerShell is not running as admin) you will get an error:
Exception calling "GetActiveObject" with "1" argument(s): "Operation unavailable (Exception from HRESULT: 0x800401E3 (MK_E_UNAVAILABLE))"
At line:1 char:1
+ $word = [Runtime.Interopservices.Marshal]::GetActiveObject('Word.Appl ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : COMException
As advised, check that PowerShell and Word are running under the same user. You can so this by going to Task Manager > Details, and checking the user name column for WINWORD.EXE and powershell.exe
To get a specific document based on name:
$doc = $word.Documents | Where-Object {$_.Name -eq "Document2"}
You can check how many documents are open by looking at the count:
$word.Documents.Count

Related

How to create a MSI extractor with Powershell? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 months ago.
Improve this question
A few months back I made a simple MSI extractor script, I improved it some today by adding a popup dialog using WScript.Shell, however I also wanted to improve on the first part of the script, the extractor. Originaly users would have to go in and manually edit the input file (.msi) and the output directory. I used the old friend google to see what the file popup syntax was. Tried to encorperate it into the script, with no luck.
Current code:
msiexec /n /a $FileBrowser /qb TARGETDIR=$Out # This uses the built in Windows tool to extract the MSI
$FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property #{
InitialDirectory = [Environment]::GetFolderPath('Desktop')
Filter = 'Windows Packages (*.msi)|*.msi'
}
$Out = $FileBrowser.ShowDialog()
$Shell = New-Object -ComObject "WScript.Shell"
$Button = $Shell.Popup("Once you install the MSI using this PowerShell script, please add any programs that run from a shell (i.e. CMD, PowerShell) be added to Path.
To add a program to path, search for Control Panel in Windows Search, and open it. Once in Control Panel,
select User Accounts, then User Accounts again. On the side bar, select Change my Enviorment Variables.
Select the Path variable, and then Edit. Select a unfilled box, and type the path to the program (for most, it can be just the root folder, some may need to be bin) and then Ok, and Ok again.
You WILL need to restart any open shells.", 0, "Thank you for using MSI-Extractor", 0)
Any help is greatly apprecitaed,
James
I tried adding vars, with no luck.
EDIT: I got some outside help, and I somewhat fixed it.
#Use Windows Forms to open a file select dialog
$FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property #{
InitialDirectory = [Environment]::GetFolderPath('Desktop')
Filter = 'Windows Packages (*.msi)|*.msi'
}
$Out = $FileBrowser.ShowDialog() #Display the dialog
#Select output directory
$FolderBrowser = New-Object System.Windows.Forms.FolderBrowserDialog -Property #{
Description = 'Output'
}
$Out = $FolderBrowser.ShowDialog() #Display the dialog
$FolderBrowser.SelectedPath #Variable stuff
msiexec /a $FileBrowser.FileName /qb TARGETDIR=$($FolderBrowser.SelectedPath) # This uses the built in Windows tool to extract the MSI
#A helpful message
$Shell = New-Object -ComObject "WScript.Shell"
$Button = $Shell.Popup("Once you install the MSI using this PowerShell script, please add any programs that run from a shell (i.e. CMD, PowerShell) be added to Path.
To add a program to path, search for Control Panel in Windows Search, and open it. Once in Control Panel,
select User Accounts, then User Accounts again. On the side bar, select Change my Enviorment Variables.
Select the Path variable, and then Edit. Select a unfilled box, and type the path to the program (for most, it can be just the root folder, some may need to be bin) and then Ok, and Ok again.
You WILL need to restart any open shells.", 0, "Thank you for using MSI-Extractor", 0)
Running this in Windows Powershell ISE works, but in Powershell directly it errors out
New-Object : Cannot find type [System.Windows.Forms.OpenFileDialog]: verify that the assembly containing this type is
loaded.
At C:\Users\693982\Downloads\MSI-extractor.ps1:4 char:16
+ ... leBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
+ FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand
You cannot call a method on a null-valued expression.
At C:\Users\693982\Downloads\MSI-extractor.ps1:9 char:1
+ $Out = $FileBrowser.ShowDialog() #Display the dialog
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
New-Object : Cannot find type [System.Windows.Forms.FolderBrowserDialog]: verify that the assembly containing this
type is loaded.
At C:\Users\693982\Downloads\MSI-extractor.ps1:13 char:18
+ ... erBrowser = New-Object System.Windows.Forms.FolderBrowserDialog -Prop ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
+ FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand
You cannot call a method on a null-valued expression.
At C:\Users\693982\Downloads\MSI-extractor.ps1:17 char:1
+ $Out = $FolderBrowser.ShowDialog() #Display the dialog
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Running this in Windows Powershell ISE works, but in Powershell directly it errors out
The reason this works in the ISE is that the ISE will autoload needed libraries/modules. The Powershell consoles (powershell.exe/pwsh.exe) will not.
You need to put stuff like this at the top of your code for the shells to load for use in GUI-based scripts.
Add-Type -AssemblyName System.Drawing,
PresentationCore,
PresentationFramework,
System.Windows.Forms,
microsoft.VisualBasic
[System.Windows.Forms.Application]::EnableVisualStyles()
Point of Note:
You don't need all of those for what you are doing, but I put them here for your awareness/research of them. Sure, you can still use this old COM...
New-Object -ComObject "WScript.Shell"
... for popups; however, you should be using the modern .Net namespaces above.
Example details:
[enum]::GetNames([System.Windows.MessageBoxImage])
# Results
<#
None
Hand
Error
Stop
Question
Exclamation
Warning
Asterisk
Information
#>
[System.Windows.MessageBox]::Show('Do you want to proceed?', 'Confirm', 'YesNoCancel','Error')
Example message boxes using .Net.
[System.Windows.MessageBox]::Show(
"
General Info
`n Some other Info
`n Username
`n Password,
", 'Dialog Title'
)
[System.Windows.Forms.MessageBox]::Show(
"
General Info
`n Some other Info
`n Username
`n Password
", 'Dialog Title'
)
Here's a refactor of your popup message for readability.
[System.Windows.MessageBox]::Show(
"1. Once you install the MSI using this PowerShell script,
please add any programs that run from a shell (i.e. CMD, PowerShell)
be added to Path.
`n2. To add a program to path,
`t->search for Control Panel in Windows Search, and open it.
`n3. Once in Control Panel,
`t->select User Accounts,
`t->then User Accounts again.
`n4. On the side bar, select Change my Enviorment Variables.
`tSelect the Path variable, and then Edit.
`t`Select a unfilled box, and type the path to the program
`t->for most, it can be just the root folder,
`t->some may need to be bin
`t->and then Ok, and Ok again.
`n5. You WILL need to restart any open shells.", "Thank you for using MSI-Extractor"
)

The start-process cmdlet of powershell will fail with -PassThru argument when trying to launch Edge browser

In my test script by powershell, I'd start an Edge browser and get it's main process's ID. Simply like this:
$edge = Start-Process microsoft-edge: www.ted.com -PassThru
but instead of able to get the process id from $edge.Id, I've got error message like this:
Start-Process : This command cannot be run completely because the system cannot find all the information required.
At line:1 char:7
+ $edge=Start-Process microsoft-edge: www.ted.com -PassThru
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [Start-Process], InvalidOperationException
+ FullyQualifiedErrorId : InvalidOperationException,Microsoft.PowerShell.Commands.StartProcessCommand
Is there any simple and straightforward way to really doing this without enumerate and check the process list?
Thanks!
I try to test your script and I am getting a similar error as yours.
Based on my search result, passthru is not one of the common parameters, and it does not exist everywhere.
Reference:
Use the PowerShell Passthru Parameter and Get Back Objects
I think it is not available for the MS Edge browser and that's why the script gives an error.
I try to search for an alternative command for PassThru but I did not get any helpful information about it.
In this situation, it is better to enumerate and check the process list

Word as COM object has no attributes/ability to set attributes

I'm sorry that this is quite alien to me, but I had a previously working script that functioned like the following:
$wd = new-object -comobject word.application
but when I print the COM object it has empty/blank values for all attributes.
PS C:\Users\XXX> $wd
Application :
Creator :
Parent :
Name :
Documents :
Windows :
ActiveDocument :
ActiveWindow :
Selection :
And it cannot set any or perform any functions
PS C:\Users\XXX> $wd.Visible = $true
Exception setting "Visible": "Unable to cast COM object of type 'Microsoft.Office.Interop.Word.ApplicationClass' to interface type 'Microsoft.Office.Interop.Word._Application'. This
operation failed because the QueryInterface call on the COM component for the interface with IID '{00020970-0000-0000-C000-000000000046}' failed due to the following error: Error loading
type library/DLL. (Exception from HRESULT: 0x80029C4A (TYPE_E_CANTLOADLIBRARY))."
At line:1 char:1
+ $wd.Visible = $true
+ ~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], SetValueInvocationException
+ FullyQualifiedErrorId : ExceptionWhenSetting
I tried browsing related questions but was unable to see if the mistakes are exactly identical. I've run all Windows updates and did a repair installation of Office. (Running Windows 10 and Office 2013)
Sometimes Word/Excel/PPT COM Objects are acting wildly. Thus using these is not recommended for automation purposes by Microsoft itself (sorry but couldn't find the source). But whatever personally I use those all the time :).
System Restore or closing and repoening the Powershell Session actually fixes those problems for me.
Unfortunately I couldn't figured out a programmatic way to handle this yet. So looking forward for further answers. (+1)

PowerShell and Onenote

I would like to read and write on OneNote pages using PowerShell scripts. I have been trying different scripts I found, but none of them even run on my machine. I am wondering if I have to enable something, or get some kind of library.
When trying to run some scripts I found, I get this error:
Unable to find type [Microsoft.Office.InterOp.OneNote.HierarchyScope].
At line:3 char:27
+ ... erarchy("", [Microsoft.Office.InterOp.OneNote.HierarchyScope]::hsPage ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (Microsoft.Offic....HierarchyScope:TypeName) [], RuntimeException
+ FullyQualifiedErrorId : TypeNotFound
Could someone point me in the right direction? Or is it even possible?
PS Version: 5.1.17134.407
OneNote 2013
Agreed with Theo on this one.
How are you trying to run them, consolehost, ISE, VSCode, other editor?
Did you set your ExecutionPolicy so that you could run them at all.
Is OneNote on your machine and activated or are you trying to hit a remote box?
However, there is a provider you can use to assist you here...
OneNotePowerShellProvider
# Example Scripts
Name
----
ConvertTo-Object.ps1
Create-OneNoteDocumentation.ps1
Export-PsOn.ps1
Get-OneNoteApplication.ps1
Get-OneNoteDigest.ps1
Get-OneNoteText.ps1
Get-ProviderTests.ps1
Import-FilesToOneNote.ps1
Robocopy-Items.ps1
Set-OneNoteDebug.ps1
Start-Tests.ps1
… as well as this article...
Read and Write Content from OneNote with PowerShell
# Examples
# get a table of all notebooks
$OneNote = New-Object -ComObject OneNote.Application
[xml]$Hierarchy = ""
$OneNote.GetHierarchy("", [Microsoft.Office.InterOp.OneNote.HierarchyScope]::hsPages, [ref]$Hierarchy)
$Hierarchy.Notebooks.Notebook | Format-Table Name, path, isUnread, isCurrentlyViewed

Cannot run same script twice in same PowerShell session

I'm writing a PowerShell script that will open an application, run a process, then close it, and then open another application, run a process, and close it. (Ultimately more than two, but I'm starting small.)
I can run the following script, with no issues:
# Project Config
$projectName = "c:\temp\test3.egp"
$projectName2= "c:\temp\test4.egp"
# Start Enterprise Guide
$app = New-Object -ComObject SASEGObjectModel.Application.7.1
#Open the Enterprise Guide Project
$projectObject = $app.Open($projectName, "")
# Save the Enterprise Guide Project
$projectObject.Save()
# Close the Enterprise Guide Project
$projectObject.Close()
# Open the second project
$projectObject = $app.Open($projectName2, "")
# Save the second project
$projectObject.Save()
#Close the second project
$projectObject.Close()
# Quit Enterprise Guide
$app.Quit()
PS C:\temp> ./test.ps1
PS C:\temp>
However, if I run it a second time at that above prompt, without exiting PowerShell, I get errors.
PS C:\temp> ./test.ps1
Exception calling "Open" with "2" argument(s): "Path is not a directory 'C:': The filename, directory name, or volume label syntax is incorrect."
At C:\temp\test.ps1:9 char:27
+ $projectObject = $app.Open <<<< ($projectName, "")
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ComMethodTargetInvocation
... more errors related to $projectObject not having a value, and then a second copy of each of these for the second attempt ...
PS C:\temp>
It works fine if I exit from Powershell, and reopen Powershell, and run it again.
Restrictions that are documented primarily include only one Project being able to be opened at one time; however, clearly $projectObject.Close() works fine, because it does successfully run two in one script. It's only when I've left the script and run it again that it's a problem.
Now, if I remove $app.Quit(), it then allows me to run it a second time in the same session; but I do want to quit the app (to ensure that any following run of the script isn't impacted by anything I just did; this is a programming environment, so things like setting session macro variables and such could have bad impacts). I also don't entirely understand why the line $app = New-Object ... doesn't create a new instance of the application (and so why the prior run's $app quit is relevant)?
Is there something in PowerShell I am doing wrong here? Or is this solely an issue with the API I'm using that I'll have to talk to SAS (the vendor)?
Inside your original PS session create a new PS session for each project: New-PSSession
More details and examples can be found on the Microsoft docs site here.