1

我正在尝试循环读取一组 Word 文件。在循环的第一次迭代中,从来没有问题。在第 2 次、第 3 次、第 n 次迭代中,我在尝试关闭文档时收到以下错误:

The server threw an exception. (exception from hresult: 0x80010105 (rpc_e_serverfault))

我的电话如下:

(doc as Word._Document).Close(Word.WdSaveOptions.wdDoNotSaveChanges, x, x);(其中 x 是 Type.Missing)

此外,当只处理一个文件(即循环中的一个文件)时,在单独运行循环 2、3 等时永远不会引发错误。第一次迭代后出现了一些问题,在后续迭代中没有得到修复。然而,我似乎正确地重新初始化了所有变量,并且正在重用 ApplicationClass 对象。

我对这个错误做了一些体面的研究。除了了解到我们真的不应该使用 COM Interop 之外,我还没有发现太多。一个 StackOverflow 答案表明多线程是问题所在,但这在我的代码中似乎并不明显;虽然我 90% 确定这是一个错误。我只是没能找到它。

我的代码如下:我有一个类级变量,用于为循环的每次迭代重新使用应用程序类: Word.ApplicationClass _WordApp;

循环在退出 Word 应用程序之前运行以下代码 n 次(与要读取的文件一样多):

内循环:

        byte[] wordDocBytes = GetWordDocumentData(att.Data, att.FileName);
        pagesToCombine.Add(wordDocBytes);
        if (counter == wordFileCount) { QuitWordApplication(); }
        else { counter += 1; }

GetWordDocumentData 方法:

    private byte[] GetWordDocumentData(byte[] wordBytes, string path)
    {
        // Save bytes to word file in temp dir, open, copy info. Then delete the temp file after.

        object x = Type.Missing;
        string ext = Path.GetExtension(path).ToLower();
        string tmpPath = Path.ChangeExtension(Path.GetTempFileName(), ext);
        File.WriteAllBytes(tmpPath, wordBytes);

        // Open temp file with Excel Interop:
        Word.Documents docs = null;
        if (_WordApp == null)
        {
            _WordApp = new Word.ApplicationClass();
        }

        try
        {
            docs = _WordApp.Documents;
        }
        catch (COMException cx)
        {
            _WordApp = new Word.ApplicationClass();
            docs = _WordApp.Documents;
        }
        Word.Document doc = docs.Open(tmpPath, x, x, x, x, x, x, x, x, x, x, x, x, x, x);

        doc.ActiveWindow.Selection.WholeStory();
        doc.ActiveWindow.Selection.Copy();
        IDataObject data = Clipboard.GetDataObject();
        string documentText = data.GetData(DataFormats.Text).ToString();

        // Add text to pages.
        byte[] wordDoc = null;
        using (MemoryStream myMemoryStream = new MemoryStream())
        {
            Document myDocument = new Document();
            PdfWriter myPDFWriter = PdfWriter.GetInstance(myDocument, myMemoryStream); // REQUIRED.
            PdfPTable table = new PdfPTable(1);
            myDocument.Open();

            // Create a font that will accept unicode characters.
            BaseFont bfArial = BaseFont.CreateFont(@"C:\Windows\Fonts\Arial.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
            Font arial = new Font(bfArial, 12);

            // If Hebrew character found, change page direction of documentText.
            PdfPCell page = new PdfPCell(new Paragraph(documentText, arial)) { Colspan = 1 };
            Match rgx = Regex.Match(documentText, @"\p{IsArabic}|\p{IsHebrew}");
            if (rgx.Success) page.RunDirection = PdfWriter.RUN_DIRECTION_RTL;

            table.AddCell(page);

            // Add image to document (Not in order with text...)
            foreach (Word.InlineShape ils in doc.InlineShapes)
            {
                if (ils != null && ils.Type == Word.WdInlineShapeType.wdInlineShapePicture)
                {
                    PdfPCell imageCell = new PdfPCell();
                    ils.Select();
                    doc.ActiveWindow.Selection.Copy();
                    System.Drawing.Image img = Clipboard.GetImage();
                    byte[] imgb = null;
                    using (MemoryStream ms = new MemoryStream())
                    {
                        img.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); // Null reference exception - SOMETIMES.
                        imgb = ms.ToArray();
                    }

                    Image wordPic = Image.GetInstance(imgb);
                    imageCell.AddElement(wordPic);
                    table.AddCell(imageCell);
                }
            }

            myDocument.Add(table);
            myDocument.Close();
            myPDFWriter.Close();
            wordDoc = myMemoryStream.ToArray();
        }

        // Cleanup:
        Clipboard.Clear();
        (doc as Word._Document).Close(Word.WdSaveOptions.wdDoNotSaveChanges, x, x); // "The server generated an exception." - SOMETIMES.
        try { File.Delete(tmpPath); }
        catch { }

        return wordDoc;
    }

QutiWord应用方法:

    private void QuitWordApplication()
    {
        try
        {
            (_WordApp as Word._Application).Quit(Type.Missing, Type.Missing, Type.Missing);
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message + ex.StackTrace);
        }
    }

有什么办法可以修复或防止此错误?如何改进管理此文档对象的方式?

4

1 回答 1

0

看来关键是 Document.Activate():

doc.Activate();

应该在 docs.Open(...) 之后运行:

Word.Document doc = docs.Open(ref fpath, ref x, ref readOnly, ref x, ref x, ref x, ref x, ref x, ref x, ref x, ref x, ref visible, ref x, ref x, ref x, ref x);

我认为在 Open() 命令中使用 ref x 等也有帮助。我最终退出了每次迭代的应用程序。现在没有崩溃,WINWORD.exe 进程也不会成倍增加。谢天谢地...

最终代码:

    private byte[] GetWordDocumentData(byte[] wordBytes, string path) //
    {
        // Save bytes to word file in temp dir, open, copy info. Then delete the temp file after.

        object x = System.Reflection.Missing.Value;
        string ext = Path.GetExtension(path).ToLower();
        string tmpPath = Path.ChangeExtension(Path.GetTempFileName(), ext);
        File.WriteAllBytes(tmpPath, wordBytes);

        // Open temp file with Excel Interop:
        Word.Documents docs = null;
        Word.ApplicationClass app = new Word.ApplicationClass();

        try
        {
            docs = app.Documents;
        }
        catch
        {
            app = new Word.ApplicationClass();
            docs = app.Documents;
        }

        object fpath = tmpPath;
        object visible = false;
        object readOnly = false;
        Word.Document doc = docs.Open(ref fpath, ref x, ref readOnly, ref x, ref x, ref x, ref x, ref x, ref x, ref x, ref x, ref visible, ref x, ref x, ref x, ref x);
        doc.Activate(); //New
        doc.ActiveWindow.Selection.WholeStory();
        doc.ActiveWindow.Selection.Copy();
        IDataObject data = Clipboard.GetDataObject();
        string documentText = data.GetData(DataFormats.Text).ToString();

        // Add text to pages.
        byte[] wordDoc = null;
        using (MemoryStream myMemoryStream = new MemoryStream())
        {
            Document myDocument = new Document();
            PdfWriter myPDFWriter = PdfWriter.GetInstance(myDocument, myMemoryStream); // REQUIRED.
            PdfPTable table = new PdfPTable(1);
            myDocument.Open();

            // Create a font that will accept unicode characters.
            BaseFont bfArial = BaseFont.CreateFont(@"C:\Windows\Fonts\Arial.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
            Font arial = new Font(bfArial, 12);

            // If Hebrew character found, change page direction of documentText.
            PdfPCell page = new PdfPCell(new Paragraph(documentText, arial)) { Colspan = 1 };
            Match rgx = Regex.Match(documentText, @"\p{IsArabic}|\p{IsHebrew}");
            if (rgx.Success) page.RunDirection = PdfWriter.RUN_DIRECTION_RTL;

            table.AddCell(page);

            // Add image to document (Not in order with text...)
            foreach (Word.InlineShape ils in doc.InlineShapes)
            {
                if (ils != null && ils.Type == Word.WdInlineShapeType.wdInlineShapePicture)
                {
                    PdfPCell imageCell = new PdfPCell();
                    ils.Select();
                    doc.ActiveWindow.Selection.Copy();
                    System.Drawing.Image img = Clipboard.GetImage();
                    byte[] imgb = null;
                    using (MemoryStream ms = new MemoryStream())
                    {
                        img.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
                        imgb = ms.ToArray();
                    }

                    Image wordPic = Image.GetInstance(imgb);
                    imageCell.AddElement(wordPic);
                    table.AddCell(imageCell);
                }
            }

            myDocument.Add(table);
            myDocument.Close();
            myPDFWriter.Close();
            wordDoc = myMemoryStream.ToArray();
        }

        Cleanup(x, tmpPath, app, doc);

        return wordDoc;
    }

我认为将清理工作与工作分开很重要:

    private static void Cleanup(object x, string tmpPath, Word.ApplicationClass app, Word.Document doc)
    {
        Clipboard.Clear();
        object Save = false;
        (doc as Word._Document).Close(ref Save, ref x, ref x);
        doc = null;
        (app as Word._Application).Quit();
        app = null;
        try { File.Delete(tmpPath); }
        catch { }
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }
于 2013-10-21T21:19:01.100 回答