i am trying to put different header and footer dynamically on each page by dividing into sections
and making linktoprevios false but it gives this error message
Property 'LinkToPrevious' cannot be found on this object; make sure it exists and is settable.
At D:\work\scripts_done\abcd\config.ps1:19 char:36
+ $Doc.LastSection.HeadersFooters. <<<< LinkToPrevious = $false;
+ CategoryInfo : InvalidOperation: (LinkToPrevious:String) [], RuntimeException
+ FullyQualifiedErrorId : PropertyNotFound
Whats wrong in this ...... my documents takes only 26 line in a page i m trying to inserting header footer on each page
$target_dir="D:\ABCD"
$dir="$target_dir"
$val=ls $dir
$filename="D:\ABCD\er.txt"
$filedata = (get-content $filename)
$Word = New-Object -ComObject Word.Application;
$Word.Visible = $true;
$Doc = $Word.Documents.Add();
$selection.Font.Name="Courier New"
$selection.Font.Size=11
$selection.Font.Spacing=0.5
$selection=$word.Selection
$count=0
foreach ( $line in $filedata ){
if ( $count -eq 26 ){
$Doc.LastSection.HeadersFooters.Header.Add("abcd $count");
$Doc.AddSection();
$Doc.LastSection.HeadersFooters.LinkToPrevious = $false;
$count=0;
}
$count++;
}
AFAICR the LinkToPrevious is not for the section, but for the individual headers/footers. But the HeadersFooters collection is an oddity anyway. I'm thinking in VBA here, not PowerShell, but I'm not sure even your line
$Doc.LastSection.HeadersFooters.Header.Add("abcd $count");
will work. Assuming $Doc.LastSection gives you a Word Section object, I think you need something more like the following, suitably adjusted for PowerShell
$Doc.LastSection.Headers(wdHeaderFooterPrimary).Range.Text = "abcd $count"
$Doc.AddSection()
$Doc.LastSection.Headers(wdHeaderFooterPrimary).LinkToPrevious = $false;
Related
I'm trying to read excel files into Powershell, open, password protect them and write them back. I can do it individually but within a loop the script fails:
#working individually
$f = ("C:my\path\Out Files\1234dv.xlsx")
$outfile = $f.FullName + "out"
$xlNormal = -4143
$xl = new-object -comobject excel.application
$xl.Visible = $True
$xl.DisplayAlerts = $False
$wb = $xl.Workbooks.Open($f)
$a = $wb.SaveAs("C:my\path\Out Files\test.xls",$xlNormal,"test")
$a = $xl.Quit()
$a = Release-Ref($ws)
$a = Release-Ref($wb)
$a = Release-Ref($xl)
#not working in loop, error after
function Release-Ref ($ref) {
([System.Runtime.InteropServices.Marshal]::ReleaseComObject(
[System.__ComObject]$ref) -gt 0)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
}
foreach ($f in Get-ChildItem "C:\my\path\Out Files"){
$ff = $f
$outfile = $f.FullName + "out"
$xlNormal = -4143
$xl = new-object -comobject excel.application
$xl.Visible = $True
$xl.DisplayAlerts = $False
$wb = $xl.Workbooks.Open($ff)
$a = $wb.SaveAs("C:\my\path\Out Files\test.xls",$xlNormal,"test")
$a = $xl.Quit()
$a = Release-Ref($ws)
$a = Release-Ref($wb)
$a = Release-Ref($xl)
}
Sorry, we couldn't find 1234dv.xlsx. Is it possible it was moved,
renamed or deleted? At line:16 char:5
+ $wb = $xl.Workbooks.Open($ff)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], COMException
+ FullyQualifiedErrorId : System.Runtime.InteropServices.COMException COM object that has been
separated from its underlying RCW cannot be used. At line:17 char:5
+ $a = $wb.SaveAs("C:\my\path ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], InvalidComObjectException
+ FullyQualifiedErrorId : System.Runtime.InteropServices.InvalidComObjectException
That error repeats for all four test files I'm working with.
I'm not really familiar with Powershell so I relied on MS docs, and I couldn't password protect the files in python so thought this would be easier. I know this doesn't address the password yet either but trying to get the loop to work first. Any help would be greatly appreciated. Thank you.
You should use
$wb = $xl.Workbooks.Open($ff.FullName)
To give Excel the full file path. Otherwise, $ff is a FileInfo object where a string (path) is required
Slightly off topic for your question , but not for your intent :
From a security perspective using .xls passwords is not security, it is merely an annoyance.
If you need security, then i suggest you use something like Azure Information protection that allows you to encrypt , and share the file securely only with those that need access.
You still need to create your xls or .xlsx files (or any other file for that matter) then you can the powershell simply loop over them :
PS C:\>foreach ($file in (Get-ChildItem -Path \\server1\Docs -Recurse -Force |
where {!$_.PSIsContainer} |
Where-Object {$_.Extension -eq ".xls"})) {
Protect-RMSFile -File $file.PSPath -InPlace -DoNotPersistEncryptionKey All -TemplateID "e6ee2481-26b9-45e5-b34a-f744eacd53b0" -OwnerEmail "IT#Contoso.com"
}
https://learn.microsoft.com/en-us/powershell/module/azureinformationprotection/protect-rmsfile?view=azureipps
I am in the process of assigning a footer to hundreds of word documents with their current filepath. Here is my code, which does the job:
I plan to have $Word.Visible set to false, but it isn't for now for debugging purposes.
This gets all the word docs in a directory, adds footer with their file path, then saves and closes.
I am trying to handle a case like this:
I just want to skip this, or possibly force open and continue. Not sure the best way to go about this, however, and am seeking some help.
Thanks,
Elijah
Set-ExecutionPolicy bypass;
$path = 'somepath';
$documents = Get-ChildItem -Path $path *.docx -Recurse -Force
$filepaths = foreach ($document in $documents) {$document.fullname}
$Word = New-Object -ComObject Word.application;
$Word.Visible = $true;
foreach ($filepath in $filepaths){
$Doc = $Word.Documents.OpenNoRepairDialog($filepath);
$Doc.Unprotect();
$Selection = $Word.Selection;
$Doc.ActiveWindow.ActivePane.View.SeekView = 4;
$Selection.ParagraphFormat.Alignment = 1;
$Selection.TypeText($filepath);
$Doc.Save();
$Doc.Close();
}
$Word.Quit();
Edit1:
I've made an edit where it adds the dynamic field object for the file path, rather than just typing in the file path, that way if you happen to move the file, the file path can be updated to the new path. You will have to press F9 while selecting the footer in word, but this is the best you can do without making a macro and saving the file as a .docm.
Here is the amended code:
$documents = Get-ChildItem -path *docx -recurse -force
$filepaths = foreach($document in $documents){$document.FullName}
Set-Variable -Name wdFieldFileName -Value 29 -Option constant -Force -ErrorAction SilentlyContinue
$word = New-Object -ComObject Word.Application
#$word.Visible = $true
foreach($filepath in $filepaths){
$doc = $word.Documents.Open($filepath)
$sections = $doc.Sections
$item1 = $sections.Item(1)
$footer = $item1.Footers.Item(1)
$range = $footer.Range
$doc.Fields.Add($range, $wdFieldFileName, '\p')
$doc.Save()
$doc.Close()
}
$word.Quit()
I am still running into the error window when trying to open corrupted or document "in need of repair" as diagnosed by word.
Passing in multiple arguments to the Open() method does not yield results as expected. Here is an example:
Exception calling "Open" with "16" argument(s): "Type mismatch. (Exception from HRESULT: 0x80020005 (DISP_E_TYPEMISMATCH))"
At line:1 char:1
+ $doc = $word.Documents.Open($filepath, $False, $False, $False, $null, ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ComMethodTargetInvocation
I get following error:
Cannot index into a null array.
At C:\tmp\Folder\excel\output\net45\test.ps1:14 char:1
+ $Data = $Reader.AsDataSet().Tables[0].Rows
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : NullArray
# Zero based index. The second row has index 1.
$StartRow = 2
# Input File
$InputFileName = "C:\tmp\Folder\excel\output\net20\test.xlsx"
# Output File
$OutputFileName = "C:\tmp\Folder\excel\output\net20\SomeFile.csv"
# Path to Excel.dll is saved (downloaded from http://exceldatareader.codeplex.com/)
$DllPath = "C:\tmp\Folder\excel\output\net45\Excel.4.5.dll"
[void]([Reflection.Assembly]::LoadFrom($DllPath))
$Stream = New-Object IO.FileStream($InputFileName, "Open", "Read")
$Reader = [Excel.ExcelReaderFactory]::CreateBinaryReader($Stream)
$Data = $Reader.AsDataSet().Tables[0].Rows
# Read the column names. Order should be preserved
$Columns = $Data[$StartRow].ItemArray
# Sort the remaining data into an object using the specified columns
$Data[$($StartRow + 1)..$($Data.Count - 1)] | % {
# Create an object
$Output = New-Object Object
# Read each column
for ($i = 0; $i -lt $Columns.Count; $i++) {
$Output | Add-Member NoteProperty $Columns[$i] $_.ItemArray[$i]
}
# Leave it in the output pipeline
$Output
} | Export-CSV $OutputFileName -NoType
You're calling the binary method (.xls) and using an Open XML format file (.xlsx). Try using [Excel.ExcelReaderFactory]::CreateOpenXmlReader($Stream) instead.
This works for me:
$DllPath = 'C:\Excel.DataReader.45\Excel.4.5.dll';
$FilePath = 'C:\Students.xlsx';
$FileMode = [System.IO.FileMode]::Open;
$FileAccess = [System.IO.FileAccess]::Read;
Add-Type -Path $DllPath;
$FileStream = New-Object -TypeName System.IO.FileStream $FilePath, $FileMode, $FileAccess;
$ExcelDataReader = [Excel.ExcelReaderFactory]::CreateOpenXmlReader($FileStream);
$ExcelDataReader.IsFirstRowAsColumnNames = $true;
$ExcelDataSet = $ExcelDataReader.AsDataSet();
$ExcelDataReader.Dispose();
$FileStream.Close();
$FileStream.Dispose();
$ExcelDataSet.Tables | Format-Table -AutoSize
If you're still having trouble, you might consider using the Microsoft.ACE.OLEDB.12.0 provider, which you install separately from Office. There's some doc here.
I've read this "Convert XLS to CSV on command line" and this "convert-xlsx-file-to-csv-using-batch" before in a similar doubt I have. Try too see if it helps.
I am trying to write a powershell script that kicks off two workflows.
One needs to be kicked off only once while the other needs to be run on every item in the list. However I am being plagued by an error and I don't know how to get rid of it. When I add a 4 argument I get the same error. I am running the script as an spshelladmin
Error
Exception calling "StartWorkflow" with "3" argument(s): ""
At C:\cert.ps1:26 char: 29
+ $em = $manager.StartWorkFlow <<<< ($items[0],$emails,$data)
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
Code
$web = Get-SPWeb -Identity "http://portal.com/sites/it"
$manager = $web.Site.WorkFlowManager
$list = $web.Lists["Certificate Tracking"]
$assoc = $list.WorkflowAssociations.GetAssociationByName("Certificate Notification","en-US")
$assoc.AllowAsyncManualStart = $true
$assoc.AllowManual = $true
$emails = $list.WorkflowAssociations.GetAssociationByName("Status Update","en-US")
$emails.AllowAsyncManualStart = $true
$emails.AllowManual = $true
$view = $list.Views["All Items"] #All Items
$items = $list.GetItems($view)
$data = $assoc.AssociationData
$emData = $emails.AssociationData
$count = 0
foreach ($item in $items) {
$wf = $manager.StartWorkFlow($item,$assoc,$data)
}
$em = $manager.StartWorkFlow($items[0],$emails,$emData,$true)
$web.Dispose()
I actually discovered that a specific item I was targeting already had a workflow running on it, I did not place proper checks for the status (check out the comments on this code http://paulryan.com.au/2014/cancel-all-workflows/), very misleading error, I thought I had wrong overload with method call on StartWorkFlow ($item, $association, $data, $true), in SP 2013 there are 4 parameters not sure why the thread title says three... Anyways hope that helps someone.
I posted this script the other day in an effort to discover a good way to change file extensions when "saving as." I had the problem licked, but as of this morning, the script will not run without errors. Here's the error message I'm getting:
Processing : C:\users\xxx\Desktop\ht\Automatic_Post-Call_Survey.htm
Exception calling "SaveAs" with "16" argument(s): "This is not a valid file name.
Try one or more of the following:
* Check the path to make sure it was typed correctly.
* Select a file from the list of files and folders."
At C:\users\xxx\Desktop\hd.ps1:11 char:20
+ $opendoc.saveas <<<< ([ref]"$docpath\$doc.FullName.doc", [ref]$saveFormat);
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
if "16" is the error code, that represents an inability to delete the directory...but it doesn't appear as if I'm asking for that at all--unless there's some default parameter in place somewhere. I'm pretty much baffled.anyone have any other ideas I can try out?
$docpath = "c:\users\xxx\desktop\do"
$htmPath = "c:\users\xxx\desktop\ht"
$srcfiles = Get-ChildItem $htmPath -filter "*.htm*"
$saveFormat = [Enum]::Parse([Microsoft.Office.Interop.Word.WdSaveFormat], "wdFormatDocument");
$word = new-object -comobject word.application
$word.Visible = $False
$filename = ($_.fullname).substring(0,($_.FullName).lastindexOf("."))
function saveas-document {
$opendoc = $word.documents.open($doc.FullName);
$opendoc.saveas([ref]"$docpath\$filename", [ref]$saveFormat);
$opendoc.close();
}
ForEach ($doc in $srcfiles) {
Write-Host "Processing :" $doc.FullName
saveas-document
$doc = $null
}
$word.quit();
this should do what do you need, but is not the best design :)
$docpath = "c:\users\xxx\desktop\do"
$htmPath = "c:\users\xxx\desktop\ht"
$srcfiles = Get-ChildItem $htmPath -filter "*.htm*"
$saveFormat = [Enum]::Parse([Microsoft.Office.Interop.Word.WdSaveFormat], "wdFormatDocument");
$global:word = new-object -comobject word.application
$word.Visible = $False
#$filename = ($_.fullname).substring(0,($_.FullName).lastindexOf("."))
function saveas-document ($docs) {
$opendoc = $word.documents.open($docs);
$savepath = $docs -replace [regex]::escape($htmPath),"$docpath"
$savepath = $savepath -replace '\.html*', '.doc'
$opendoc.saveas([ref]"$savepath", [ref]$saveFormat);
$opendoc.close();
}
ForEach ($doc in $srcfiles) {
Write-Host "Processing :" $doc.FullName
saveas-document -doc $doc.FullName
$doc = $null
}
$word.quit();