Script doesn't work but copy/paste it into PowerShell does

Please help. My first PowerShell script. It watches for file(s) added to a specific folder, opens the file(s) in MS Word, saves them in a different format and moves everything to other locations. The script works perfectly within ISE and VS Code but it doesn’t work in PowerShell (4.0, 5.1 and 7.2). However, if I copy the script and paste it into PowerShell it works fine. I’m out of ideas. Anecdotally many have had similar issues–works in an IDE, doesn’t work in PowerShell–but none of the solutions work for me. Not surprising, the issues were dissimilar to mine and each issue seemed to have its own unique solution.

Charles,
Welcome to the forum. :wave:t4:

It will be nearly impossible to advice anything helpful without seeing your actual code. So please share it (formatted as code. please).

Do you try to run the script from a scheduled task or something like this? The MSFT office apps are well known for beeing very picky when running from a non interactive session.

Thank you for the welcome Olaf, glad to be here.

The script does not run as a scheduled task. The original script was just the $action block, I wasn’t initially concerned with a watch folder, and it worked fine in PowerShell. Would that dismiss the thought that MS Word is acting up in this case or does introduciion of the System.IO.FileSystemWatcher change the game?

$homePath = "$HOME\Documents\PowerShell_Home"
$sourcePath = "$homePath\Source"
$destinationPath = "$homePath\Destination"
$processedPath = "$homePath\Processed"
$word = New-Object -ComObject word.Application
$format = [Microsoft.Office.Interop.Word.WdSaveFormat]::wdFormatXMLDocument
$filewatcher = New-Object System.IO.FileSystemWatcher
$filewatcher.Path = "$sourcePath"
$filewatcher.EnableRaisingEvents = $true
$filewatcher.Filter = "*.rtf"

$action = {
  Get-ChildItem -Path $sourcePath *.rtf | ForEach-Object {
    $rtfFile = $word.Documents.Open($_.FullName)
    $docxFile = "$($_.DirectoryName)\$($_.BaseName).docx"
    $rtfFile.SaveAs([ref]$docxFile, [ref]$format)
    $rtfFile.Close()
  }
  Move-Item -Force $sourcePath\*.docx -Destination $destinationPath
  Move-Item -Force $sourcePath\*.rtf -Destination $processedPath
}

$handler = . {
  Register-ObjectEvent -InputObject $FileWatcher -EventName Created -Action $action -SourceIdentifier FSCreate
}

Write-Host ""
Write-Host "Watching for changes to $sourcePath"
try {
  do {
    Wait-Event -Timeout 1
    Write-Host "." -NoNewline
  } while ($true)
}
finally {
  Unregister-Event -SourceIdentifier FSCreate
  $handler | Remove-Job
  $filewatcher.EnableRaisingEvents = $false
  $filewatcher.Dispose()
  "Event Handler disabled"
}
$word.Quit()

Wait … what? … are you saying you tested the script without the FileSystemWatcher part? And now since you added the FileSystemWatcher the script does not work anymore? :thinking:

How do you actually run this script? I used to think that the functionality the FileSystemWatcher class offers only makes sense for a script running automatically … mostly as scheduled task.

I’ve never used PowerShell before, this is completely new territory. What I was originally asked to do was simply write a script to do the conversion. Works as advertised in PowerShell:

$homePath = "$HOME\Documents\PowerShell_Home"
$sourcePath = "$homePath\Source"
$destinationPath = "$homePath\Destination"
$processedPath = "$homePath\Processed"
$word = New-Object -ComObject word.Application

Get-ChildItem -Path $sourcePath *.rtf | ForEach-Object {
  $rtfFile = $word.Documents.Open($_.FullName)
  $docFile = "$($_.DirectoryName)\$($_.BaseName).doc"
  $rtfFile.SaveAs([ref]$docFile, [ref]0)
  $rtfFile.Close()
}
Move-Item $sourcePath\*.doc -Destination $destinationPath
Move-Item $sourcePath\*.rtf -Destination $processedPath
$word.Quit()

I suggested to my colleague that it run as a scheduled task or maybe it could continually watch for .rtf file additions to a folder.

Since I posted my code I stripped the conversion code out to test. No MS Word object, just a simple Move-Item. Same deal. Works in VS Code, doesn’t work in PowerShell. What am I missing here because such a bare bones FileSystemWatcher object should work.

$homePath = "$HOME\Documents\PowerShell_Home"
$sourcePath = "$homePath\Source"
$destinationPath = "$homePath\Destination"
$processedPath = "$homePath\Processed"

$filewatcher = New-Object System.IO.FileSystemWatcher
$filewatcher.Path = "$sourcePath"
$filewatcher.EnableRaisingEvents = $true
$filewatcher.Filter = "*.rtf"

$action = {
  Get-ChildItem -Path $sourcePath *.rtf | ForEach-Object {
  }
  Move-Item -Force $sourcePath\*.rtf -Destination $processedPath
}

$handler = . {
  Register-ObjectEvent -InputObject $FileWatcher -EventName Created -Action $action -SourceIdentifier FSCreate
}

Write-Host "`nWatching for changes to $sourcePath"

try {
  do {
    Wait-Event -Timeout 1
    Write-Host "." -NoNewline
  } while ($true)
}
finally {
  Unregister-Event -SourceIdentifier FSCreate
  $handler | Remove-Job
  $filewatcher.EnableRaisingEvents = $false
  $filewatcher.Dispose()
  "Event Handler disabled"
}

But if you wrote that code yourself it looks like you have experiences in other languages, don’t you? :wink:

To be honest - I like to follow the KISS principle and so far I’ve never had a business case where I needed to use the FileSystemWatcher approach. And if you don’t want to run this solution autmatically via a scheduled task I cannot see any advantage in using the FileSystemWatcher approach.

But anyway … since you want to convert simple RTF files I wonder if you really need to use the Word ComObject approach. LibreOffice for example is able to convert RTF files to DOC or DOCX files (and many other formats) via command line. :wink:

& 'C:\Program Files\LibreOffice\program\soffice.exe' --% --headless --convert-to docx *.rtf

That would convert all RTF files in the current directory to DOCX format.

Then you should NOT use MSFT Word. It probably will not work. Or at least not reliably.

If it’s not crucial for the process I’d try to avoid this level of complexity. … again … KISS principle. :wink:

Since I cannot see your screen or cannot read your mind - I don’t know. :wink: How exactly do you run this script? You may add some logging to your script. Or - if you run it with an open console - some verbose output.

This may help as well:

Yup Olaf, I have had experience with quite a few programming languages during my career.

There is no obligation to use FileSystemWatcher, a scheduled task is still a route I may go but to this PowerShell noobie it seemed like FileSystemWatcher was a clever and rather simple approach; comparable to droplets in MacOS. Scheduled tasks will be OK with my colleague who is developing this automation. I don’t know his plans in implementing his automation, I think design of it is still a work in progress. All I know is the final destination of .docx files (why he wanted to use .doc format is beyond me :man_shrugging:) is an Oracle database.

I like LibreOffice. But nj.gov is determined to be a Microsoft shop. And if LibreOffice were allowed, in he past I’ve found it only nearly identical to Word’’s .doc/.docx format—and for that matter Excel documents too. Not sure how much progress has been made in that. :confused:

So. I love the challenge of solving elusive problems, I’ll look at powershell.one, thanks for that, before I must settle on a scheduled task.

Again … Microsoft Office apps are not supported and do not work in non-interactive sessions!!! :point_up_2:t4: :point_up_2:t4:

Thanks for all the advice Olaf. :bowing_man: I will give LibreOffice a try. :crossed_fingers: