我有一个Powershell脚本,用于自动将.doc / .docx文件转换为* .pdf。 该脚本运行良好的第一个文件。 但是如果我把另一个文件放在监视文件夹中,监视器不会触发一个事件。
这是完整的脚本。 如果我注释掉所有$ docvariables,脚本就会多次运行而不会有任何问题。 我忽略了/忽略了什么?
$watcher = New-Object System.IO.FileSystemWatcher $watcher.Path = "$Env:DropboxRoot" $watcher.Filter = "*.doc*" $watcher.IncludeSubdirectories = $true $watcher.EnableRaisingEvents = $true Add-type -AssemblyName Microsoft.Office.Interop.Word $action = { $name = (get-item $Event.SourceEventArgs.FullPath).BaseName ### DON'T PROCESS WORD BACKUP FILES (START WITH A TILDE ~) if(!($name.startsWith("~"))){ write-host Triggered event from $Event.SourceEventArgs.FullPath $inputFilePath = $Event.SourceEventArgs.FullPath $parentPath = (get-item $inputFilePath).Directory $filename = (get-item $inputFilePath).BaseName $pdfDir = "$parentPath\PDF" if(!(Test-Path -Path $pdfDir)){ New-Item -ItemType directory -Path $pdfDir } ###Execute PDF generate script write-host Create word object $word = New-Object -ComObject "Word.Application" ######define the parameters###### write-host Define parameters $wdExportFormat =[Microsoft.Office.Interop.Word.WdExportFormat]::wdExportFormatPDF $OpenAfterExport = $false $wdExportOptimizeFor = [Microsoft.Office.Interop.Word.WdExportOptimizeFor]::wdExportOptimizeForOnScreen $wdExportItem = [Microsoft.Office.Interop.Word.WdExportItem]::wdExportDocumentContent $IncludeDocProps = $true $KeepIRM = $false #Don't export Inormation Rights Management informations $wdExportCreateBookmarks = [Microsoft.Office.Interop.Word.WdExportCreateBookmarks]::wdExportCreateWordBookmarks #Keep bookmarks $DocStructureTags = $true #Add additional data for screenreaders $BitmapMissingFonts = $true $UseISO19005_1 = $true #Export as PDF/A $outputFilePath = $pdfDir + "\" + $filename + ".pdf" $doc = $word.Documents.Open($inputFilePath) $doc.ExportAsFixedFormat($OutputFilePath,$wdExportFormat,$OpenAfterExport,` $wdExportOptimizeFor,$wdExportRange,$wdStartPage,$wdEndPage,$wdExportItem,$IncludeDocProps,` $KeepIRM,$wdExportCreateBookmarks,$DocStructureTags,$BitmapMissingFonts,$UseISO19005_1) $doc.Close() $word.Quit() [void][System.Runtime.InteropServices.Marshal]::ReleaseComObject($doc) [void][System.Runtime.InteropServices.Marshal]::ReleaseComObject($word) [GC]::Collect() [GC]::WaitForPendingFinalizers() } } $created = Register-ObjectEvent $watcher -EventName "Created" -Action $action $renamed = Register-ObjectEvent $watcher -EventName "Renamed" -Action $action while($true) { sleep 5 }`
您的脚本有一些问题,更多的调试逻辑可以找到。
在某些情况下, (Get-Item System.Management.Automation.PSEventArgs.SourceEventArgs.FullPath)
返回null。 由于未知的原因,这似乎发生一次每个文件被转换。 也许它与“〜Temp”文件有关。
随后, if(!($name.startsWith("~")
会抛出异常。
当你使用$inputFilePath = $Event.SourceEventArgs.FullPath
,你的变量是一个FileInfo,你真的想要传递一个字符串到$word.Documents.Open($inputFilePath)
。
最后,有时BaseName
是空的。 不知道为什么,但代码可以测试,或使用其他手段剖析FullPath获取名称和路径部分。
所有这一切说,一旦你得到这个工作,我个人的经验是调用Word上的COM对象来做PowerShell中的这种转换是不可靠的(Word挂起,〜Temp文件被留下,你必须从任务管理器杀死Word,在PowerShell中的COM调用永远不会返回) 。 我的测试显示,调用C#控制台应用程序进行转换要可靠得多。 你可以完全用C#编写这个目录观察器和转换器,并完成相同的任务。
假设您仍想将两者,一个PowerShell观察器和一个C#Word to PDF转换器相结合,下面是我提出的一个解决方案。 该脚本运行大约一分钟,以便您可以在ISE或控制台中进行测试。 从控制台按一个键退出。 在退出之前,通过注销在ISE中测试的相当有用的事件,脚本干净地退出。 根据您打算如何运行该脚本来相应地更改。
PowerShell观察者
$watcher = New-Object System.IO.FileSystemWatcher $watcher.Path = "d:\test\docconvert\src" $watcher.Filter = "*.doc*" $watcher.IncludeSubdirectories = $true $watcher.EnableRaisingEvents = $true # copy this somehwere appropriate # perhaps in same directory as your script # put on a read-only share, etc. $wordToPdf = 'd:\test\docconvert\WordToPdf\WordToPdf\bin\Debug\WordToPdf.exe' $action = { try { Write-Host "Enter action @ $(Get-Date)" $fullPathObject = (Get-Item $Event.SourceEventArgs.FullPath) if (!($fullPathObject)) { Write-Host "(Get-Item $Event.SourceEventArgs.FullPath) returned null." return } $fullPath = ($fullPathObject).ToString() Write-Host "Triggered event from $fullPath" $fileName = Split-Path $FullPath -Leaf if ($fileName -and ($fileName.StartsWith("~"))) { Write-Host "Skipping temp file" return } # put pdf in same dir as the file # can be changed, but a lot easier to test this way $pdfDir = Split-Path $FullPath -Parent $baseName = [System.IO.Path]::GetFileNameWithoutExtension($fileName) $outputFilePath = Join-Path $pdfDir $($baseName + ".pdf") Write-Host "outputFilePath is: '$outputFilePath'" # call c# WordToPdf to do conversion because # it is way more reliable than similar calls # from PowerShell & $wordToPdf $fullPath $outputFilePath if ($LASTEXITCODE -ne 0) { Write-Host "Conversion result: FAIL" } else { Write-Host "Conversion result: OK" } } catch { Write-Host "Exception from ACTION:`n$($_ | Select *)" } finally { Write-Host "Exit action @ $(Get-Date)" } } $created = Register-ObjectEvent $watcher -EventName "Created" -Action $action $renamed = Register-ObjectEvent $watcher -EventName "Renamed" -Action $action $count = 12 while($count--) { Write-Output "run/sleep ($count)..." sleep 5 # will exit from console, not ISE if ([console]::KeyAvailable) { $key = [console]::ReadKey() break } } $created | % {Unregister-Event $_.Name} $renamed | % {Unregister-Event $_.Name}
C#WordToPdf转换器
为参数添加适当的错误检查…
添加引用到COM Microsoft.Office.Interop.Word
using System; using Microsoft.Office.Interop.Word; namespace WordToPdf { class Program { static int Main(string[] args) { Console.WriteLine($"Converting: {args[0]} to {args[1]}"); var conversion = new DocumentConversion(); bool result = conversion.WordToPdf(args[0], args[1]); if (result) { return 0; } else { return 1; } } } public class DocumentConversion { private Microsoft.Office.Interop.Word.Application Word; private object Unknown = Type.Missing; private object True = true; private object False = false; public bool WordToPdf(object Source, object Target) { bool ret = true; if (Word == null) Word = new Microsoft.Office.Interop.Word.Application(); try { Word.Visible = false; Word.Documents.Open(ref Source, ref Unknown, ref True, ref Unknown, ref Unknown, ref Unknown, ref Unknown, ref Unknown, ref Unknown, ref Unknown, ref Unknown, ref Unknown, ref Unknown, ref Unknown, ref Unknown, ref Unknown); Word.Application.Visible = false; Word.WindowState = WdWindowState.wdWindowStateMinimize; #if false object saveFormat = Microsoft.Office.Interop.Word.WdSaveFormat.wdFormatPDF; Word.ActiveDocument.SaveAs(ref Target, ref saveFormat, ref Unknown, ref Unknown, ref Unknown, ref Unknown, ref Unknown, ref Unknown, ref Unknown, ref Unknown, ref Unknown, ref Unknown, ref Unknown, ref Unknown, ref Unknown, ref Unknown); #else Word.ActiveDocument.ExportAsFixedFormat( (string)Target, WdExportFormat.wdExportFormatPDF, false, WdExportOptimizeFor.wdExportOptimizeForOnScreen, WdExportRange.wdExportAllDocument, 0, 0, WdExportItem.wdExportDocumentContent, true, false, WdExportCreateBookmarks.wdExportCreateWordBookmarks, true, true, true); #endif } catch (Exception e) { Console.WriteLine(e.Message); ret = false; } finally { if (Word != null) { // close the application Word.Quit(ref Unknown, ref Unknown, ref Unknown); } } return ret; } } }