我试图从Word文档中读取文本和图像并将其关闭。问题是尝试关闭它而不会遇到任何问题或创建多个WINWORD.exe实例。我的问题是,当我在Marshal.FinalReleaseComObject(app);
上调用Word.ApplicationClass
时,Word会触发Windows提供的一般异常(" Word已停止工作")。我已经阅读了How do I properly clean up Excel interop objects?中的许多解决方案并实施了建议,但我仍然遇到了问题。
这是我的代码。我只阅读一个带有一个页面的Word文件(您可能希望跳到" //清理:"发生异常的地方)。
private byte[] GetDocumentText(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.ApplicationClass app = new Word.ApplicationClass();
Word.Documents docs = app.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);
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);
Marshal.FinalReleaseComObject(doc);
Marshal.FinalReleaseComObject(docs);
(app as Word._Application).Quit(x, x, x);
Marshal.FinalReleaseComObject(app); // Word encounters exception here.
doc = null;
docs = null;
app = null;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
try { File.Delete(tmpPath); }
catch { }
return wordDoc;
}
这并不总是在我第一次阅读文件时发生。当我第二次或第三次读它时,我通常会得到错误。
有什么方法可以阻止错误显示?
答案 0 :(得分:1)
看到这次崩溃是相当不寻常的,Word通常知道如何处理这种内存管理的大锤方法。然而,这是一个非常糟糕的做法。最好由Visual Studio团队的this blog post描述。值得一读,“沉默的刺客”部分是最相关的。
调用GC.Collect足以释放所有COM引用,无需其他帮助。但是,如果您运行附带调试器的程序,则无效。 This answer解释了原因。
要使GC.Collect()也在调试器中工作,您需要在单独的方法中移动它,以便调试器无法使引用保持活动状态。这样做最简单:
private byte[] GetDocumentText(byte[] wordBytes, string path) {
var retval = GetDocumentTextImpl(wordBytes, path);
GC.Collect();
GC.WaitForPendingFinalizers();
return retval;
}
private byte[] GetDocumentTextImpl(byte[] wordBytes, string path) {
// etc...
}
将原始代码移动到GetDocumentTextImpl()方法中。只需从代码中删除所有Marshal和GC调用,因为它们完全没必要。而且很危险。
答案 1 :(得分:0)
您可以在调用FinalReleaseComObject之前尝试检查IsObjectValid。
答案 2 :(得分:0)
你根本不应该使用FinalReleaseComObject
,这是一个锤子来释放/删除你肯定知道的RCW 你是唯一的推荐者(在.NET中)。
在这种情况下,您完全减少每个RCW的引用次数doc
,docs
和app
,而不仅仅是来自您的参考文献。
请尝试ReleaseComObject
,但请注意,如果是还有一个.NET枚举器正在使用中,并且附加到您从Word集合中释放的一个对象。
关闭文档,退出Word,将变量设置为null
和GC'应该就足够了。根据编译器的不同,它可能会丢弃堆栈中的变量,并消除将它们设置为null
的代码。