将Word doc / docx文件转换为PDF时,文件系统观察程序停止工作

时间:2016-03-17 16:32:58

标签: windows powershell pdf scripting docx

我有一个Powershell脚本,用于将 .doc / .docx文件自动转换为* .pdf。 该脚本适用于第一个文件。但是如果我在监视文件夹中放入另一个文件,则观察者不会触发事件。

这是完整的脚本。如果我注释掉所有$ doc变量,脚本会多次运行而没有任何问题。我忽视/忽视了什么吗?

$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
}`

1 个答案:

答案 0 :(得分:0)

您的脚本存在一些问题,可以找到更多调试逻辑。

在某些情况下,(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到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;
        }
    }
}