PowerShell - Process multiple word docs (office 2010) - powershell

I am trying to write a PowerShell script to perform steps on multiple Word docs. I have Word 2010 installed on my machine, but I can't seem to get the script to open the docs. Here is the script
$path = "C:\MyPath"
Add-Type -AssemblyName Microsoft.Office.Interop.Word
$wordFiles = Get-ChildItem -Path $path -include *.doc, *.docx -recurse
$objWord = New-Object -ComObject "word.application"
$objWord.visible = $false
foreach($wd in $wordFiles)
{
$doc = $objWord.documents.open($wd.fullname)
#InsertProcessingFunctionsHere
$doc.Save()
$objWord.Documents.Close()
}
$objWord.Quit()
I try and run this, and the error I get back from PowerShell is:
Exception calling "Open" with "1" argument(s): "Command failed"
At C:\Scripts\Process-WordDocs.ps1:10 char:31
+ $doc = $objWord.documents.open <<<< ($wd.fullname)
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ComMethodTargetInvocation
You cannot call a method on a null-valued expression.
At C:\Scripts\Process-WordDocs.ps1:13 char:10
+ $doc.Save <<<< ()
+ CategoryInfo : InvalidOperation: (Save:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Exception calling "Close" with "0" argument(s): "This method or property is not available because a document window is not active."
At C:\Scripts\Process-WordDocs.ps1:14 char:25
+ $objWord.Documents.Close <<<< ()
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ComMethodTargetInvocation
MSDN states that documents.open only requires 1 argument, and the rest are optional. However, a C# example I have seen on the net, showed passing a "ReadOnly: False" parameter to documents.open. Stepping through the script in the ISE Debugger, I can see $wd.fullname is there and points to a valid file, so I am completely unclear why it is not opening. At first, I thought this was because I was using a 64-bit version of the OS (32-bit version of Office), but attempting the script from a 32-bit PowerShell Session resulted in the same error. Anyone have any insight here as to why this may be happening, and how I can fix it? I would prefer all the processing to happen invisible to the user. Any help would be greatly appreciated. Thank you in advance for your time.

I think you want to close the document using $doc.close() instead of $objWord.Documents.Close()

Related

How to call a powershell script to another powershell script with arguments?

I just need to call another script and pass the parameters to it.
I tried doing invoke-expression to access it, i tried using &, and nothing worked
I tried doing the following:
$hostfile = "C:\Users\username\Desktop\csvfile_test.csv"
$outFile = ".\testerFile.xlsx"
& '.\organizer.ps1' "-csvFile $hostfile -outputPath $outFile "
Invoke-Expression 'C:\Users\username\Desktop\organizer.ps1' "$hostfile $outFile"
I receive the following errors:.
with ampersand (&):
PS C:\Users\username\Desktop> C:\Users\username\Desktop\scanner.ps1
Exception calling "ReadLines" with "1" argument(s): "The given path's format is not supported."
At C:\Users\username\Desktop\scanner.ps1:48 char:1
+ [System.IO.File]::ReadLines("$csvFile") | ForEach-Object {
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : NotSupportedException
with invoke-expression:
Exception calling "ReadLines" with "1" argument(s): "The given path's format is not supported."
At C:\Users\username\Desktop\scanner.ps1:48 char:1
+ [System.IO.File]::ReadLines("$csvFile") | ForEach-Object {
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : NotSupportedException
Invoke-Expression : A positional parameter cannot be found that accepts argument 'C:\Users\username\Desktop\csvfile_test.csv .\testerFile.xlsx'.
At C:\Users\username\Desktop\scanner.ps1:69 char:1
+ Invoke-Expression 'C:\Users\username\Desktop\organizer.ps1' "$host ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Invoke-Expression], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.InvokeExpressionCommand
When you write this:
& '.\organizer.ps1' "-csvFile $hostfile -outputPath $outFile "
you are passing a single parameter to the script (one quoted string). That's not what you want.
This is what you need:
$hostfile = "C:\Users\username\Desktop\csvfile_test.csv"
$outFile = ".\testerFile.xlsx"
.\organizer.ps1 -csvFile $hostfile -outputPath $outFile
First, you don't need the & (invocation) operator because your command name (the organizer.ps1 script in the current location, in this example) doesn't contain spaces. (You can add it if you want, but it's unnecessary in this scenario.)
Second, the -csvFile and -outputPath parameters each require a string.

Script works in ISE and not SOME powershell CLI

$wshell = New-Object -ComObject Wscript.Shell
$wshell.Popup("********Please Select the SOURCE Directory********",0,"Directory Selecter 5000",0x1)
Function Get-Folder($initialDirectory)
{
$foldername = New-Object System.Windows.Forms.FolderBrowserDialog
$foldername.rootfolder = "MyComputer"
if($foldername.ShowDialog() -eq "OK")
{
$folder += $foldername.SelectedPath
}
return $folder
}
$a = Get-Folder
$wshellb = New-Object -ComObject Wscript.Shell
$wshellb.Popup("********Please Select the DESTINATION Directory********",0,"Directory Selecter 5000",0x1)
Function Get-Folder($initialDirectory)
{
$foldername = New-Object System.Windows.Forms.FolderBrowserDialog
$foldername.rootfolder = "MyComputer"
if($foldername.ShowDialog() -eq "OK")
{
$folder += $foldername.SelectedPath
}
return $folder
}
$b = Get-Folder
Set-Content -Path "C:\script\scripts\script data.txt" -Value "$a" -Force
Set-Content -Path "C:\script\scripts\script data2.txt" -Value "$b" -Force
So this script works in ISE and if I copy/paste it into a CLI and that's it. If I navigate to the folder in the powershell CLI and run it from there it gives me this error:
New-Object : Cannot find type
[System.Windows.Forms.FolderBrowserDialog]: verify that the assembly
containing this type is loaded. At
C:\script\scripts\pathingworking.ps1:8 char:19
+ $foldername = New-Object System.Windows.Forms.FolderBrowserDialog
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
+ FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand
The property 'rootfolder' cannot be found on this object. Verify that
the property exists and can be set. At
C:\script\scripts\pathingworking.ps1:9 char:5
+ $foldername.rootfolder = "MyComputer"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : PropertyNotFound
You cannot call a method on a null-valued expression. At
C:\script\scripts\pathingworking.ps1:11 char:8
+ if($foldername.ShowDialog() -eq "OK")
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
This error comes back twice, once for each instance of the folder selection window that is supposed to pop up.
I have tried -STA from run dialogue, shortcuts, and even from the CLI and it always gives me that error. I have verified using [System.Threading.Thread]::CurrentThread.GetApartmentState() that the open instance of powershell is STA. I can open a powershell CLI and navigate to the folder where the script is, invoke the script, it will give me the error, then I can copy\paste it in the same exact CLI and it works without issue. I have also right-clicked the file in Explorer and selected "run with powershell" and it also gives me the error. I have elevated the CLI to see if that helped with no success.
I fear this is some small oversight and hopefully someone can give me a hand.
using v1.0
windows 10
Cannot find type [System.Windows.Forms.FolderBrowserDialog] is due to the assembly not being loaded.
load the assembly with
Add-Type -AssemblyName "System.Windows.Forms"
rerun's answer solves the errors, but doesn't do a whole lot of explaining. Designing a script in the ISE (which is what I do almost exclusively) is convenient because it will automatically load types and modules for you. If you plan on running the script from another CLI (Command Line Interpreter) you should make it a habit of explicitly loading needed modules and assemblies early on in the script, so that when those things are called upon later PowerShell will know what you're asking for.
Unfortunately there's no easy way to tell what you will need to do that for when it comes to what types are going to be inherently available, but a little trial and error usually points it out rather quickly, or if you look at your script and see that you are making a new object with System.Kitchen.Pasta, then you probably need to load the assembly for System.Kitchen first, so that it has the Pasta type available when you want it, so you could just include a call to load that assembly near the top of the script with no harm done.
In your specific case, as has already been pointed out, you will need to load the assembly that contains the FolderBrowserDialog box that you want to display. Adding the following as the first line of your script will resolve the errors that you are seeing:
Add-Type -AssemblyName "System.Windows.Forms"

Reflection Assembly Errors in Powershell

I have some Powershell code that takes the Apps running on an IIS server and finds the versions of .NET they are running.
$Apps = Get-WebApplication
foreach($App in $Apps) {
$binLocation = "$($App.physicalPath)\bin"
# get all dlls in bin folder
$dllFolder = Get-Item -Path $binLocation
$dlls = $dllFolder.GetFiles("*.dll")
# analyze dll .net version
$set = New-Object System.Collections.Generic.HashSet[String]
$dlls | ForEach-Object {
$set.Add([Reflection.Assembly]::ReflectionOnlyLoadFrom("$binLocation\$($_.Name)").ImageRuntimeVersion) | Out-Null
}
# print all dll .NET version
$App
$set
}
However when I run this on my server I get 2 types of error;
Exception calling "ReflectionOnlyLoadFrom" with "1" argument(s): "API restriction: The
assembly 'file:///D:\inetpub\wwwroot\OABS_ECRM\bin\WebGrease.dll' has already loaded from a
different location. It cannot be loaded from a new location within the same appdomain."
At line:13 char:74
+ $set.Add([Reflection.Assembly]::ReflectionOnlyLoadFrom ("$binLocation\$($_.Name ...
+ ~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : FileLoadException
and,
Exception calling "ReflectionOnlyLoadFrom" with "1" argument(s): "Could not load file or
assembly 'file:///D:\inetpub\wwwroot\Tablet\bin\epengine.dll' or one of its dependencies.
The module was expected to contain an assembly manifest."
At line:13 char:74
+ $set.Add([Reflection.Assembly]::ReflectionOnlyLoadFrom("$binLocation\$($_.Name ...
+ ~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : BadImageFormatException
I seem to get the .NET versions running on each app from the script but I was wondering what caused these errors and if they can be cleared. I need to solve this in powershell.
Loading multiple DLLs with the same identity into the same AppDomain will give you the first exception.
See my workaround at https://stackoverflow.com/a/62379741/920618.

ACL Error when assigning AD Rights

I have a powershell script that can create multiple Active Directory users in one run. My issue is assigning ACL rights to the home directory. It seems to always work if there is only one user to create. When there are multiple, however, any account after the first may fail or may work. It's a very intermittent issue, although they seem to fail more than work.
Here is the code generating the ACL's:
Function CreateHomeDirectory{
$global:samAccountName = "myaccount"
$global:homeDirectory = "\\path\to\myaccount"
New-Item -Path $global:homeDirectory -Type Directory -Force
$Rights = [System.Security.AccessControl.FileSystemRights]::Read -bor [System.Security.AccessControl.FileSystemRights]::Write -bor [System.Security.AccessControl.FileSystemRights]::Modify -bor [System.Security.AccessControl.FileSystemRights]::FullControl
$Inherit = [System.Security.AccessControl.InheritanceFlags]::ContainerInherit -bor [System.Security.AccessControl.InheritanceFlags]::ObjectInherit
$Propogation = [System.Security.AccessControl.PropagationFlags]::None
$Access = [System.Security.AccessControl.AccessControlType]::Allow
$AccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule($global:samAccountName,$Rights,$Inherit,$Propogation,$Access)
$ACL = Get-Acl $global:homeDirectory
$ACL.AddAccessRule($AccessRule)
$Account = new-object system.security.principal.NTAccount($global:samAccountName)
$ACL.setowner($Account)
$ACL.SetAccessRule($AccessRule)
Set-Acl $global:homeDirectory $ACL
Return
Here are the errors I am currently getting. They seem to change up from time to time, but I'd say these are pretty consistant:
Exception calling "AddAccessRule" with "1" argument(s): "Some or all identity references could not be translated."
At H:\Scripts\Create.ps1:274 char:10
+ $ACL.AddAccessRule($AccessRule)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : IdentityNotMappedException
Exception calling "SetOwner" with "1" argument(s): "Some or all identity references could not be translated."
At H:\Scripts\Create.ps1:276 char:10
+ $ACL.setowner($Account)
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : IdentityNotMappedException
Exception calling "SetAccessRule" with "1" argument(s): "Some or all identity references could not be translated."
At H:\Scripts\Create.ps1:277 char:10
+ $ACL.SetAccessRule($AccessRule)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : IdentityNotMappedException
Spent hours on this and have gotten nowhere. Any suggestions would be appreciated.
According to the error "Some or all identity references could not be translated.", it looks like the user with such still not exist in the Directory (or in the Directory the computer is connected to) when you try to use it.
I would try to first look for the user in the directory before trying to use it. Check the value of $Account before using it.

I need help find the error in powershell script

Trying to get my script to work and need some help here is my code.
#excel
#open ap
$XL = new-object -com "Excel.Application"
$XLbooks = $XL.workbooks
$netci = [system.Globalization.CompareInfo]"en-us"
$wkbk = $XLbooks.PSBase.GetType().Invokemember("Add",[Reflection.BindingFlags]::InvokeMethod,$null,$XLbooks,$null,$newci)
$sheet = $XLbooks.worksheets.item(1)
$sheet.name = "name"
$sheet.cells.item($row,1).formulalocal = "Fred Nurk"
$file = "c\windows\scripts\test.xlsx"
[void]$wkbk.PSBase.GetType().InvokeMember("SaveAs",[Reflection.BindingFlags]::InvokeMethod,$null,$wkbk,$file,$newci)
("Close",[Reflection.BindingFlags]::Invokemedthod,$null,$wkbk,0,$newci)
$XL.quit()
Errors:
Cannot convert the "en-us" value of type "System.String" to type "System.Globalization.CompareInfo".
At C:\scripts\test.ps1:5 char:44
+ $netci = [system.Globalization.CompareInfo] <<<< "en-us"
+ CategoryInfo : NotSpecified: (:) [], RuntimeException
+ FullyQualifiedErrorId : RuntimeException
You cannot call a method on a null-valued expression.
At C:\scripts\test.ps1:7 char:34
+ $sheet = $XLbooks.worksheets.item <<<< (1)
+ CategoryInfo : InvalidOperation: (item:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Property 'name' cannot be found on this object; make sure it exists and is settable.
At C:\scripts\test.ps1:8 char:8
+ $sheet. <<<< name = "name"
+ CategoryInfo : InvalidOperation: (name:String) [], RuntimeException
+ FullyQualifiedErrorId : PropertyNotFound
You cannot call a method on a null-valued expression.
At C:\scripts\test.ps1:9 char:18
+ $sheet.cells.item <<<< ($row,1).formulalocal = "Fred Nurk"
+ CategoryInfo : InvalidOperation: (item:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Exception calling "InvokeMember" with "6" argument(s): "Microsoft Excel cannot access the file 'C:\Users\Jared\Document
s\c\windows\scripts\5ADD7000'. There are several possible reasons:
The file name or path does not exist.
The file is being used by another program.
The workbook you are trying to save has the same name as a currently open workbook."
At C:\scripts\test.ps1:11 char:42
+ [void]$wkbk.PSBase.GetType().InvokeMember <<<< ("SaveAs",[Reflection.BindingFlags]::InvokeMethod,$null,$wkbk,$file,$n
ewci)
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodTargetInvocation
The main issue you need to address is creating the CompareInfo. The first error tells you this line isn't working:
$netci = [system.Globalization.CompareInfo]"en-us"
So what you'll need to do is create the CompareInfo object this way:
$netci = ([system.Globalization.CultureInfo]"en-us").CompareInfo
Though instead of using this crazy way to create a workbook:
$wkbk = $XLbooks.PSBase.GetType().Invokemember("Add",[Reflection.BindingFlags]::InvokeMethod,$null,$XLbooks,$null,$newci)
...try this more sane way instead :D
$wkbk = $XL.workbooks.Add()
If you do it this way, you won't have to worry about creating the CompareInfo object.
The problem that jumps out at me is that $netci = [system.Globalization.CompareInfo]"en-us" is invalid syntax. You're not calling any method on the System.Globalization.CompareInfo class, you're just placing a string after it. PowerShell is interpreting [System.Globalization.CompareInfo] as a typecast operator, and complains that the string "en-us" can't be converted to the data type System.Globalization.CompareInfo - because that data type doesn't exist.
You need to invoke a method that operates on "en-us". You can get a list of methods from MSDN:
http://msdn.microsoft.com/en-us/library/system.globalization.compareinfo.aspx
Assuming that the method you want is GetCompareInfo (seems most likely to me - it returns a CompareInfo object), you'd write that line this way:
$netci = [System.Globalization.CompareInfo]::GetCompareInfo('en-us')
Note, BTW, that you have this variable as $netci when you create it, but as $newci in the rest of the script.
I haven't looked too deeply at the other errors, but they're probably a cascade effect from the failure to create $newci properly, so I suspect that if you fix this, the other errors will go away.