Introduction
There is a Gostdown project on GitLab which is dedicated to styling .docx files in accordance with national standards for documentation. The pipeline of getting a final version of a .docx document is:
One puts all needed info into .md files.
Pandoc generates single simple .docx file.
PowerShell script styles it in a preset way.
Problem
The problem occures on step 3: script works fine locally but can't handle .docx file as COM-object if I run it via gitlab-runner on the same machine.
The message I get on GitLab is:
Possible reason
This message pops up on every call of anything related to this variable:
$word = New-Object -ComObject Word.Application
It seems PowerShell doesn't work with COM if it runs with gitlab-runner but I know it did work on another machines (on project's author machine at least) without any super special settings.
Script itself:
param (
[Parameter(Mandatory=$true)][string]$template,
[Parameter(Mandatory=$true)][string[]]$md,
[string]$docx,
[string]$pdf,
[switch]$embedfonts,
[switch]$counters
)
if ([string]::IsNullOrEmpty($docx) -and [string]::IsNullOrEmpty($pdf))
{
Write-error "-docx or -pdf must be specified"
exit 113
}
$exe = "pandoc.exe"
$template = [System.IO.Path]::GetFullPath($template)
$md = $md | % { [System.IO.Path]::GetFullPath($_ )}
$is_docx_temporary = $False
if ([string]::IsNullOrEmpty($docx))
{
$docx = [System.IO.Path]::GetTempFilename() + ".docx"
$is_docx_temporary = $True
}
else
{
$docx = [System.IO.Path]::GetFullPath($docx)
}
if (-not [string]::IsNullOrEmpty($pdf))
{
$pdf = [System.IO.Path]::GetFullPath($pdf)
}
$tempdocx = [System.IO.Path]::GetTempFilename() + ".docx"
write-host "Executing Pandoc..."
&$exe $md -o $tempdocx --lua-filter ./linebreaks.lua --filter pandoc-crossref --filter pandoc-citeproc --reference-doc $template
if ($LASTEXITCODE -ne 0)
{
Write-error "Pandoc execution has failed"
exit 111
}
$word = New-Object -ComObject Word.Application
$curdir = Split-Path $MyInvocation.MyCommand.Path
Set-Location -Path $curdir
$word.ScreenUpdating = $False
$doc = $word.Documents.Open($template)
$doc.Activate()
$selection = $word.Selection
# Save under a new name as soon as possible to prevent auto-saving
# (and polluting) the template
write-host "Saving..."
$doc.SaveAs([ref]$docx)
if (-not $?)
{
$doc.Close()
$word.Quit()
exit 112
}
write-host "Inserting main text..."
if ($selection.Find.Execute("%MAINTEXT%^13", $True, $True, $False, $False, $False, $True, `
[Microsoft.Office.Interop.Word.wdFindWrap]::wdFindContinue, $False, "", `
[Microsoft.Office.Interop.Word.wdReplace]::wdReplaceNone))
{
$start = $Selection.Range.Start
$Selection.InsertFile($tempdocx)
$end = $Selection.Range.End
$inserted_tables = $doc.Range([ref]$start, [ref]$end).Tables
# Check if there is anything after the main text
$selection.WholeStory()
$totalend = $Selection.Range.End
# If there is nothing after the main text, remove the extra CR which
# mystically appears out of nowhere in that case
if ($end -ge ($totalend - 1))
{
$selection.Collapse([Microsoft.Office.Interop.Word.wdCollapseDirection]::wdCollapseEnd) | out-null
$selection.MoveLeft([Microsoft.Office.Interop.Word.wdUnits]::wdCharacter, 1, `
[Microsoft.Office.Interop.Word.wdMovementType]::wdExtend) | out-null
$selection.Delete() | out-null
}
}
write-host "Searching styles..."
foreach ($style in $doc.Styles)
{
switch ($style.NameLocal)
{
'TableStyleContributors' {$TableStyleContributors = $style; break}
'TableStyleAbbreviations' {$TableStyleAbbreviations = $style; break}
'TableStyleGost' {$TableStyleGost = $style; break}
'TableStyleGostNoHeader' {$TableStyleGostNoHeader = $style; break}
'UnnumberedHeading1' {$UnnumberedHeading1 = $style; break}
'UnnumberedHeading1NoTOC' {$UnnumberedHeading1NoTOC = $style; break}
'UnnumberedHeading2' {$UnnumberedHeading2 = $style; break}
}
}
$BodyText = [Microsoft.Office.Interop.Word.wdBuiltinStyle]::wdStyleBodyText
$Heading1 = [Microsoft.Office.Interop.Word.wdBuiltinStyle]::wdStyleHeading1
$Heading2 = [Microsoft.Office.Interop.Word.wdBuiltinStyle]::wdStyleHeading2
$Heading3 = [Microsoft.Office.Interop.Word.wdBuiltinStyle]::wdStyleHeading3
$bullets = [char]0x2014,[char]0xB0,[char]0x2014,[char]0xB0
$numberposition = 0,0.75,1.75,3
$textposition = 0.85,1.75,3,3.5
$tabposition = 1,1.75,3,3.5
$format_nested = "%1)","%1.%2)","%1.%2.%3)","%1.%2.%3.%4)"
$format_headers = "%1","%1.%2","%1.%2.%3","%1.%2.%3.%4"
$format_single = "%1)","%2)","%3)","%4)"
write-host "Handling list templates..."
foreach ($tt in $doc.ListTemplates)
{
for ($il = 1; $il -le $tt.ListLevels.Count -and $il -le 4; $il++)
{
$level = $tt.ListLevels.Item($il)
$bullet = ($level.NumberStyle -eq [Microsoft.Office.Interop.Word.wdListNumberStyle]::wdListNumberStyleBullet)
$arabic = ($level.NumberStyle -eq [Microsoft.Office.Interop.Word.wdListNumberStyle]::wdListNumberStyleArabic)
$roman = ($level.NumberStyle -eq [Microsoft.Office.Interop.Word.wdListNumberStyle]::wdListNumberStyleLowercaseRoman)
if ($bullet)
{
if ($level.NumberFormat -ne " ")
{
$level.NumberFormat = $bullets[$il - 1] + ""
}
$level.NumberPosition = $word.CentimetersToPoints($numberposition[$il - 1])
$level.Alignment = [Microsoft.Office.Interop.Word.wdListLevelAlignment]::wdListLevelAlignLeft
$level.TextPosition = $word.CentimetersToPoints($textposition[$il - 1])
$level.TabPosition = $word.CentimetersToPoints($tabposition[$il - 1])
$level.ResetOnHigher = $il - 1
$level.StartAt = 1
$level.Font.Size = 12
$level.Font.Name = "PT Serif"
if ($il % 2 -eq 0)
{
$level.Font.Position = -4
}
$level.LinkedStyle = ""
$level.TrailingCharacter = [Microsoft.Office.Interop.Word.wdTrailingCharacter]::wdTrailingTab
}
if (($arabic -and ($level.NumberFormat -ne $format_headers[$il - 1])) -or $roman)
{
if ($level.NumberFormat -ne " " )
{
if ($arabic)
{
$level.NumberFormat = $format_nested[$il - 1]
}
if ($roman)
{
$level.NumberStyle = [Microsoft.Office.Interop.Word.wdListNumberStyle]::wdListNumberStyleArabic;
$level.NumberFormat = $format_single[$il - 1]
}
}
$level.NumberPosition = $word.CentimetersToPoints($numberposition[$il - 1])
$level.Alignment = [Microsoft.Office.Interop.Word.wdListLevelAlignment]::wdListLevelAlignLeft
$level.TextPosition = $word.CentimetersToPoints($textposition[$il - 1])
$level.TabPosition = $word.CentimetersToPoints($tabposition[$il - 1])
$level.ResetOnHigher = $il - 1
$level.StartAt = 1
$level.Font.Size = 12
$level.Font.Name = "PT Serif"
$level.LinkedStyle = ""
$level.TrailingCharacter = [Microsoft.Office.Interop.Word.wdTrailingCharacter]::wdTrailingTab
}
}
}
# Disable grammar checking (it takes time and spews out error messages)
$doc.GrammarChecked = $True
$ntables = 0
write-host "Handling tables..."
# Loop over other tables
for ($t = 1; $t -le $inserted_tables.Count; $t++)
{
$table = $inserted_tables.Item($t)
if ($table.Cell(1, 1).Range.Style.NameLocal -eq "ContributorsTable")
{
$table.Select()
$selection.ClearParagraphAllFormatting()
$pf = $selection.paragraphFormat
$pf.LeftIndent = 0
$pf.RightIndent = 0
$pf.SpaceBefore = 0
$pf.SpaceBeforeAuto = $False
$pf.SpaceAfter = 0
$pf.SpaceAfterAuto = $False
$table.Style = $TableStyleContributors
foreach ($row in $table.Rows)
{
# Row height can not be set in table style
$row.Height = $word.CentimetersToPoints(1.4)
# Alignment and line spacing are set in table style, but are not applied (low priority?)
# So we set them explicitly
$row.Select()
$pf = $selection.paragraphFormat
$pf.Alignment = [Microsoft.Office.Interop.Word.wdParagraphAlignment]::wdAlignParagraphLeft
$pf.LineSpacingRule = [Microsoft.Office.Interop.Word.wdLineSpacing]::wdLineSpaceSingle
}
continue
}
if ($table.Cell(1, 1).Range.Style.NameLocal -eq "AbbreviationsTable")
{
$table.Style = $TableStyleAbbreviations
$table.Select()
$pf = $selection.paragraphFormat
$selection.ClearParagraphAllFormatting()
$pf.LeftIndent = 0
$pf.RightIndent = 0
$pf.SpaceBefore = 0
$pf.SpaceBeforeAuto = $False
$pf.SpaceAfter = 0
$pf.SpaceAfterAuto = $False
continue
}
# This is to fix the widths of the columns
$table.AllowAutoFit = $False
# Numbered equations are 2-column tables without titles and borders, and with equations in both columns
if ([string]::IsNullOrEmpty($table.Title) `
-and (-not $table.Rows.Item(1).Cells.Borders.Item([Microsoft.Office.Interop.Word.wdBorderType]::wdBorderBottom).Visible) `
-and ($table.Columns.Count -eq 2) `
-and ($table.Cell(1,1).Range.OMaths.Count -eq 1) `
-and ($table.Cell(1,2).Range.OMaths.Count -eq 1))
{
# There can be multiple equations (rows) in one table
foreach ($row in $table.Rows)
{
# After removing the equation, the text contents remains
if ($row.Cells.Item(2).Range.OMaths.Count -ne 0)
{
$row.Cells.Item(2).Range.OMaths.Item(1).Remove()
}
$row.Cells.Item(2).VerticalAlignment = [Microsoft.Office.Interop.Word.wdCellVerticalAlignment]::wdCellAlignVerticalCenter;
$row.Cells.Item(2).Select()
$selection.ClearParagraphAllFormatting()
$pf = $selection.paragraphFormat
$pf.LeftIndent = $word.CentimetersToPoints(0)
$pf.RightIndent = $word.CentimetersToPoints(0)
$pf.Alignment = [Microsoft.Office.Interop.Word.wdParagraphAlignment]::wdAlignParagraphRight
$pf.SpaceBefore = 0
$pf.SpaceBeforeAuto = $False
$pf.SpaceAfter = 0
$pf.SpaceAfterAuto = $False
$pf.LineSpacingRule = [Microsoft.Office.Interop.Word.wdLineSpacing]::wdLineSpaceSingle
$pf.CharacterUnitLeftIndent = 0
$pf.CharacterUnitRightIndent = 0
$pf.LineUnitBefore = 0
$pf.LineUnitAfter = 0
}
}
else # Ordinary tables
{
# Count only tables with title (and number)
if (-not [string]::IsNullOrEmpty($table.Title))
{
$ntables = $ntables + 1
}
$table.Select()
$pf = $selection.paragraphFormat
$pf.LineSpacingRule = [Microsoft.Office.Interop.Word.wdLineSpacing]::wdLineSpaceSingle
# If the first row has line under it, then it is a table with a header row
if ($table.Rows.Item(1).Cells.Borders.Item([Microsoft.Office.Interop.Word.wdBorderType]::wdBorderBottom).Visible)
{
$table.Style = $TableStyleGost
}
else # table without header row
{
$table.Style = $TableStyleGostNoHeader
}
# Fix alignment of display-math objects so they math the aligment of text in the cells
foreach ($row in $table.Rows)
{
foreach ($cell in $row.Cells)
{
foreach ($math in $cell.Range.OMaths)
{
if ($math.Type -eq [Microsoft.Office.Interop.Word.wdOMathType]::wdOMathDisplay)
{
$al = $cell.Range.ParagraphFormat.Alignment
if ($al -eq [Microsoft.Office.Interop.Word.wdParagraphAlignment]::wdAlignParagraphRight)
{
$math.Justification = [Microsoft.Office.Interop.Word.wdOMathJc]::wdOMathJcRight;
}
elseif ($al -eq [Microsoft.Office.Interop.Word.wdParagraphAlignment]::wdAlignParagraphLeft)
{
$math.Justification = [Microsoft.Office.Interop.Word.wdOMathJc]::wdOMathJcLeft;
}
elseif ($al -eq [Microsoft.Office.Interop.Word.wdParagraphAlignment]::wdAlignParagraphCenter)
{
$math.Justification = [Microsoft.Office.Interop.Word.wdOMathJc]::wdOMathJcCenter;
}
}
}
}
}
}
}
write-host "Updating paragraph styles..."
$heading1namelocal = $doc.styles.Item([Microsoft.Office.Interop.Word.wdBuiltinStyle]::wdStyleHeading1).NameLocal
$nchapters = 0
$nfigures = 0
$nreferences = 0
$nappendices = 0
foreach ($par in $doc.Paragraphs)
{
$namelocal = $par.Range.CharacterStyle.NameLocal
if ($namelocal -eq "UnnumberedHeadingOne")
{
$par.Style = $UnnumberedHeading1
}
elseif ($namelocal -eq "AppendixHeadingOne")
{
$par.Style = $UnnumberedHeading1
$nappendices = $nappendices + 1
}
elseif ($namelocal -eq "UnnumberedHeadingOneNoTOC")
{
$par.Style = $UnnumberedHeading1NoTOC
}
elseif ($namelocal -eq "UnnumberedHeadingTwo")
{
$par.Style = $UnnumberedHeading2
}
else
{
$namelocal = $par.Style.NameLocal
# Make source core paragraphs smaller
if ($namelocal -eq "Source Code")
{
$par.Range.Font.Size = 10.5
}
# No special style for first paragraph to avoid unwanted space
# between first and second paragraphs
elseif ($namelocal -eq "First Paragraph")
{
$par.Style = $BodyText
}
elseif ($namelocal -eq $heading1namelocal)
{
$nchapters = $nchapters + 1
}
elseif ($namelocal -eq "Captioned Figure")
{
$nfigures = $nfigures + 1
}
elseif ($namelocal -eq "ReferenceItem")
{
$nreferences = $nreferences + 1
}
}
}
if ($counters)
{
write-host "Inserting number of chapters, figures, and tables..."
$selection.HomeKey([Microsoft.Office.Interop.Word.wdUnits]::wdStory) | out-null
$selection.Find.Execute("%NCHAPTERS%", $True, $True, $False, $False, $False, $True, `
[Microsoft.Office.Interop.Word.wdFindWrap]::wdFindContinue, $False, $nchapters + "", `
[Microsoft.Office.Interop.Word.wdReplace]::wdReplaceOne) | out-null
$selection.HomeKey([Microsoft.Office.Interop.Word.wdUnits]::wdStory) | out-null
$selection.Find.Execute("%NFIGURES%", $True, $True, $False, $False, $False, $True, `
[Microsoft.Office.Interop.Word.wdFindWrap]::wdFindContinue, $False, $nfigures + "", `
[Microsoft.Office.Interop.Word.wdReplace]::wdReplaceOne) | out-null
$selection.HomeKey([Microsoft.Office.Interop.Word.wdUnits]::wdStory) | out-null
$selection.Find.Execute("%NTABLES%", $True, $True, $False, $False, $False, $True, `
[Microsoft.Office.Interop.Word.wdFindWrap]::wdFindContinue, $False, $ntables + "", `
[Microsoft.Office.Interop.Word.wdReplace]::wdReplaceOne) | out-null
$selection.HomeKey([Microsoft.Office.Interop.Word.wdUnits]::wdStory) | out-null
$selection.Find.Execute("%NREFERENCES%", $True, $True, $False, $False, $False, $True, `
[Microsoft.Office.Interop.Word.wdFindWrap]::wdFindContinue, $False, $nreferences + "", `
[Microsoft.Office.Interop.Word.wdReplace]::wdReplaceOne) | out-null
$selection.HomeKey([Microsoft.Office.Interop.Word.wdUnits]::wdStory) | out-null
$selection.Find.Execute("%NAPPENDICES%", $True, $True, $False, $False, $False, $True, `
[Microsoft.Office.Interop.Word.wdFindWrap]::wdFindContinue, $False, $nappendices + "", `
[Microsoft.Office.Interop.Word.wdReplace]::wdReplaceOne) | out-null
}
write-host "Increasing math font size..."
foreach ($math in $doc.OMaths)
{
# Size equations up a bit to match Paratype font size
$math.Range.Font.Size = 12.5
}
write-host "Handling INCLUDEs..."
$selection.HomeKey([Microsoft.Office.Interop.Word.wdUnits]::wdStory) | out-null
while ($selection.Find.Execute("%INCLUDE(*)%^13", $True, $True, $True, $False, $False, $True, `
[Microsoft.Office.Interop.Word.wdFindWrap]::wdFindContinue, $False, "", `
[Microsoft.Office.Interop.Word.wdReplace]::wdReplaceNone))
{
if ($selection.Text -match '%INCLUDE\((.*)\)%')
{
$filename = $matches[1]
$start = $Selection.Range.Start
$Selection.InsertFile([System.IO.Path]::GetFullPath($filename))
if (!$?)
{
break
}
$end = $Selection.Range.End
# Check if there is anything after the inserted documnt
$selection.WholeStory()
$totalend = $Selection.Range.End
# If there is nothing after the inserted documnt, remove the extra CR which
# mystically appears out of nowhere in that case
if ($end -ge ($totalend - 1))
{
$selection.Collapse([Microsoft.Office.Interop.Word.wdCollapseDirection]::wdCollapseEnd) | out-null
$selection.MoveLeft([Microsoft.Office.Interop.Word.wdUnits]::wdCharacter, 1, `
[Microsoft.Office.Interop.Word.wdMovementType]::wdExtend) | out-null
$selection.Delete() | out-null
}
}
}
$selection.HomeKey([Microsoft.Office.Interop.Word.wdUnits]::wdStory) | out-null
write-host "Inserting ToC..."
if ($selection.Find.Execute("%TOC%^13", $True, $True, $False, $False, $False, $True, `
[Microsoft.Office.Interop.Word.wdFindWrap]::wdFindContinue, $False, "", `
[Microsoft.Office.Interop.Word.wdReplace]::wdReplaceNone))
{
$doc.TablesOfContents.Add($selection.Range, $False, 9, 9, $False, "", $True, $True, "", $True) | out-null
# Manually add level 1,2,3 headers to ToC
$toc = $doc.TablesOfContents.Item(1)
$toc.UseHeadingStyles = $True
$toc.HeadingStyles.Add($UnnumberedHeading1, 1) | out-null
$toc.HeadingStyles.Add($UnnumberedHeading2, 2) | out-null
$toc.HeadingStyles.Add($Heading1, 1) | out-null
$toc.HeadingStyles.Add($Heading2, 2) | out-null
$toc.HeadingStyles.Add($Heading3, 3) | out-null
$toc.Update() | out-null
}
write-host "Inserting number of pages..."
# Seemingly is not needed but who knows
$doc.Repaginate()
# Inserting "section pages" field gets the number of pages wrong, and no way has
# been found to remedy that other than manual update in Word.
# So here is another way to get the number of pages in the section
if ($doc.Sections.Count -gt 1) # two-section template?
{
$npages = $doc.Sections.Item(2).Range.Information([Microsoft.Office.Interop.Word.wdInformation]::wdActiveEndPageNumber) - `
$doc.Sections.Item(1).Range.Information([Microsoft.Office.Interop.Word.wdInformation]::wdActiveEndPageNumber)
}
else
{
$npages = $doc.Sections.Item(1).Range.Information([Microsoft.Office.Interop.Word.wdInformation]::wdNumberOfPagesInDocument)
}
$selection.HomeKey([Microsoft.Office.Interop.Word.wdUnits]::wdStory) | out-null
$selection.Find.Execute("%NPAGES%", $True, $True, $False, $False, $False, $True, `
[Microsoft.Office.Interop.Word.wdFindWrap]::wdFindContinue, $False, $npages + "", `
[Microsoft.Office.Interop.Word.wdReplace]::wdReplaceOne) | out-null
if ($embedfonts)
{
# Embed fonts (for users who do not have Paratype fonts installed).
# This costs a few MB in file size
$word.ActiveDocument.EmbedTrueTypeFonts = $True
$word.ActiveDocument.DoNotEmbedSystemFonts = $True
$word.ActiveDocument.SaveSubsetFonts = $True
}
if (-not $is_docx_temporary)
{
write-host "Saving docx..."
$doc.Save()
}
if (-not [string]::IsNullOrEmpty($pdf))
{
write-host "Saving PDF..."
$doc.SaveAs2([ref]$pdf, [ref][Microsoft.Office.Interop.Word.wdSaveFormat]::wdFormatPDF)
}
$doc.Close()
$word.Quit()
write-host "Removing temporary files..."
Remove-item -path $tempdocx
if ($is_docx_temporary)
{
Remove-item -path $docx
}
I tried to turn on "Allow service to interact with desktop" in services.msc but it didn't help.
I am new to this forum so please excuse if this is not the right channel.
I am looking for some help in searching the strings which are in Excel Column Header "$Col4 / $Col6 / $Col7 / $Col19 / $Col22 / $Col27" and if found, then delete the entire column.
The concern is if I am changing the line as per below then it goes into loop but reads only Row 1 of the sheet. But if I keep as per the below code then because it reads all the $col value as Row 1, it gives the result as "Nothing found".
if (($sheet.cells.Item($row,$column).text -eq $Col4) -or ($sheet.cells.Item($row,$column).text -eq $Col6) -or ($sheet.cells.Item($row,$column).text -eq $Col7) -or ($sheet.cells.Item($row,$column).text -eq $Col19) -or ($sheet.cells.Item($row,$column).text -eq $Col22) -or ($sheet.cells.Item($row,$column).text -eq $Col27)) {
$column++
$Col4 = 'Col4'
$Col6 = 'Col6'
$Col7 = 'Col7'
$Col19 = 'Col19'
$Col22 = 'Col22'
$Col27 = 'Col27'
$Results = "C:\Temp\Results.xls"
$excel = New-Object -Com Excel.Application -Property #{Visible = $false}
# open Excel file
$workbook = $excel.Workbooks.Open($Results)
$sheet = $workbook.ActiveSheet
$row = 1
$column = 1
$found = $false
# Search for /TESTOut in A1
$info = $sheet.cells.Item($column, $row).Text
# Write-Host $info
if($info -eq "/Test"){
$sheet.Cells.Item(1, 1).EntireRow.Delete() # Delete the first row
}
$WorksheetRange = $sheet.UsedRange
$RowCount = $WorksheetRange.Rows.Count
$ColumnCount = $WorksheetRange.Columns.Count
While ($row -ne $RowCount) {
If ($sheet.cells.Item($row,$column).text -eq $Col4) {
$column++
If ($sheet.cells.Item($row,$column).text -eq $Col6) {
$column++
If ($sheet.cells.Item($row,$column).text -eq $Col7) {
$column++
If ($sheet.cells.Item($row,$column).text -eq $Col19) {
$column++
If ($sheet.cells.Item($row,$column).text -eq $Col22) {
$column++
If ($sheet.cells.Item($row,$column).text -eq $Col27) {
Write-Host -ForegroundColor Green "Found match at Row: " $row # Replace this line to delete the Column
$found =$true
}
}
}
}
}
}
Else {
$row++
$column = 1
}
}
If (!($found)) {
Write-Host -ForegroundColor Red "Nothing found" # This is for test purpose only. Do Nothing.
}
$workbook.Close($true) # Close workbook and save changes
$excel.Quit() # Quit Excel
[Runtime.Interopservices.Marshal]::ReleaseComObject($excel) # Release COM
This could be done with a simple counting loop running backwards:
# columns to delete
$deleteThese = 'Col4', 'Col6', 'Col7', 'Col19', 'Col22', 'Col27'
$file = 'C:\Temp\Results.xls'
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $false
# open the Excel file
$workbook = $excel.Workbooks.Open($file)
$sheet = $workbook.ActiveSheet
# get the number of columns in the sheet
$colMax = $sheet.UsedRange.Columns.Count
# loop through the columns to test if the column header is found in the $deleteThese array
# do the loop BACKWARDS, otherwise the indices will change on every deletion.
for ($col = $colMax; $col -ge 1; $col--) {
$header = $sheet.Cells.Item(1, $col).Value2
if ($deleteThese -contains $header) {
$null = $sheet.Columns($col).EntireColumn.Delete()
}
}
# save and exit
$workbook.Close($true)
$excel.Quit()
# clean up the COM objects used
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($sheet)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($workbook)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
I'm new with powershell and i would like to use a loop to ping several Printers on my network.
My problem is : once i'm in the loop of pinging , i can't go out of the loop ...
I tried several things from google but without success ( start-stop , Timer ) . Does anybody have any idea?
Here is the code :
$BtnStartPingClicked = {
if ($LblFileSelectPing.Text -eq "*.txt") {
Add-Type -AssemblyName PresentationCore,PresentationFramework
$ButtonType = [System.Windows.MessageBoxButton]::OK
$MessageIcon = [System.Windows.MessageBoxImage]::Error
$MessageBody = "Please select a list of printer first"
$MessageTitle = "Error"
$Result = [System.Windows.MessageBox]::Show($MessageBody,$MessageTitle,$ButtonType,$MessageIcon)
Write-Host "Your choice is $Result"
}
else {
do {
$IPList = Get-Content ($LblFileSelectPing.Text)
$snmp = New-Object -ComObject olePrn.OleSNMP
$ping = New-Object System.Net.NetworkInformation.Ping
$i = 11
$j = 1
foreach ($Printer in $IPList) {
try {
$result = $ping.Send($Printer)
} catch {
$result = $null
}
if ($result.Status -eq 'Success') {
$((Get-Variable -name ("GBMachine"+$j+"Ping")).value).Visible = $True
$j++
test-Connection -ComputerName $Printer -Count 1 -Quiet
$printerip = $result.Address.ToString()
# OPEN SNMP CONNECTION TO PRINTER
$snmp.open($Printer, 'public', 2, 3000)
# MODEL
try {
$model = $snmp.Get('.1.3.6.1.2.1.25.3.2.1.3.1')
} catch {
$model = $null
}
# Serial
try {
$serial = $snmp.Get('.1.3.6.1.4.1.1602.1.2.1.8.1.3.1.1').toupper()
} catch {
$Dns = $null
}
# progress
$TBMonitoringPing.SelectionColor = "green"
$TBMonitoringPing.AppendText("$Printer is Pinging")
$TBMonitoringPing.AppendText("`n")
$mac = (arp -a $Printer | Select-String '([0-9a-f]{2}-){5}[0-9a-f]{2}').Matches.Value
# OPEN SNMP CONNECTION TO PRINTER
$((Get-Variable -name ('LblMach' + $i)).value).Text = "IP : $Printerip"
$i++
$((Get-Variable -name ('LblMach' + $i)).value).Text = "Model : $Model"
$i++
$((Get-Variable -name ('LblMach' + $i)).value).Text = "MAC : $mac"
$i++
$((Get-Variable -name ('LblMach' + $i)).value).Text = "Serial : $serial"
$TBAnswerMachine.AppendText("$Model")
$TBAnswerMachine.AppendText("`n")
$TBAnswerMachine.AppendText("$Printer - $Serial")
$TBAnswerMachine.AppendText("`n")
$TBAnswerMachine.AppendText("$Mac")
$TBAnswerMachine.AppendText("`n")
$TBAnswerMachine.AppendText("`n")
Get-Content ($LblFileSelectPing.Text) | Where-Object {$_ -notmatch $Printer} | Set-Content ("C:\_canonsoftware\out.txt")
$i = $i+7
$snmp.Close()
Start-Sleep -milliseconds 1000 # Take a breather!
}
else {
$TBMonitoringPing.selectioncolor = "red"
$TBMonitoringPing.AppendText("$Printer not pinging")
$TBMonitoringPing.AppendText("`n")
Start-Sleep -milliseconds 1000 # Take a breather!
}
}
$LblFileSelectPing.Text = "C:\_canonsoftware\out.txt"
} until($infinity)
}
}
thanks for your answers...
1 - part of my object are indeed not declared in the code because they are in my other PS1 file....
2 - I do a do until infinity because i don't want to stop the code before i decide it...
3 - I didn't explain my problem correctly ( excuse my poor english ) ... i would like to be able to go out of the loop do until at the moment i click on a stop button ... but apprently the windows doens't respond while in the loop ... i have to stop the script with powershell ... which is annoying because i'd like to make an executable with it ... and not have to go out of my program ...
thank you for your ideas
I have a script that helps a user find if a file hash exists in a folder. After the user has entered the hash I determine what type of hash it is and if it is not supported or if the user missed a letter it will return to asking for a hash. For ease of use I want to be able to pre-fill out what the user had type in the previously so they do not need to start over.
while (1)
{
$hashToFind = Read-Host -Prompt "Enter hash to find or type 'file' for multiple hashes"
# Check if user wants to use text file
if ($hashToFind -eq "file" )
{
Write-Host "Be aware program will only support one has type at a time. Type is determined by the first hash in the file." -ForegroundColor Yellow
Start-Sleep -Seconds 3
$hashPath = New-Object system.windows.forms.openfiledialog
$hashPath.InitialDirectory = “c:\”
$hashPath.MultiSelect = $false
if($hashPath.showdialog() -ne "OK")
{
echo "No file was selected. Exiting program."
Return
}
$hashToFind = Get-Content $hashPath.filename
}
# Changes string to array
if ( $hashToFind.GetTypeCode() -eq "String")
{
$hashToFind+= " a"
$hashToFind = $hashToFind.Split(" ")
}
if ($hashToFind[0].Length -eq 40){$hashType = "SHA1"; break}
elseif ($hashToFind[0].Length -eq 64){$hashType = "SHA256"; break}
elseif ($hashToFind[0].Length -eq 96){$hashType = "SHA384"; break}
elseif ($hashToFind[0].Length -eq 128){$hashType = "SHA512"; break}
elseif ($hashToFind[0].Length -eq 32){$hashType = "MD5"; break}
else {echo "Hash length is not of supported hash type."}
}
I am newer to PowerShell so if there are any other comments they are welcome!
From Super User:
[System.Windows.Forms.SendKeys]::SendWait("yes")
Read-Host "Your answer"
I have came up with solution like this:
while (1)
{
$hashToFind = Read-Host -Prompt "Enter hash to find or type 'file' for multiple hashes. Enter 'R' for reply input"
if ($hashToFind -eq 'R' -and $PreviousInput)
{
$handle = (Get-Process -Id $PID).MainWindowHandle
$code = {
param($handle,$PreviousInput)
Add-Type #"
using System;
using System.Runtime.InteropServices;
public class Tricks {
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetForegroundWindow(IntPtr hWnd);
}
"#
[void][Tricks]::SetForegroundWindow($handle)
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.SendKeys]::SendWait($PreviousInput)
}
$ps = [PowerShell]::Create()
[void]$ps.AddScript($code).AddArgument($handle).AddArgument($PreviousInput)
[void]$ps.BeginInvoke()
}
$PreviousInput = $hashToFind
# Check if user wants to use text file
if ($hashToFind -eq "file" )
{
$PreviousInput = $null
Write-Host "Be aware program will only support one has type at a time. Type is determined by the first hash in the file." -ForegroundColor Yellow
Start-Sleep -Seconds 3
$hashPath = New-Object system.windows.forms.openfiledialog
$hashPath.InitialDirectory = “c:\”
$hashPath.MultiSelect = $false
if($hashPath.showdialog() -ne "OK")
{
echo "No file was selected. Exiting program."
Return
}
$hashToFind = Get-Content $hashPath.filename
}
# Changes string to array
if ( $hashToFind.GetTypeCode() -eq "String")
{
$hashToFind+= " a"
$hashToFind = $hashToFind.Split(" ")
}
if ($hashToFind[0].Length -eq 40){$hashType = "SHA1"; break}
elseif ($hashToFind[0].Length -eq 64){$hashType = "SHA256"; break}
elseif ($hashToFind[0].Length -eq 96){$hashType = "SHA384"; break}
elseif ($hashToFind[0].Length -eq 128){$hashType = "SHA512"; break}
elseif ($hashToFind[0].Length -eq 32){$hashType = "MD5"; break}
else {echo "Hash length is not of supported hash type."}
}