I'm trying to read a group of Word files in a loop. On the first iteration of the loop, there is never a problem. On the 2nd, 3rd, .. nth iteration, I receive the following error attempting to close the document:
The server threw an exception. (exception from hresult: 0x80010105 (rpc_e_serverfault))
My call is as follows:
(doc as Word._Document).Close(Word.WdSaveOptions.wdDoNotSaveChanges, x, x);
(where x is Type.Missing)
Additionally, when only processing one file (i.e. one file in the loop), the error is never raised upon running that loop 2, 3, etc separate times. Something is breaking after the first iteration that is not being fixed in the subsequent iterations. Yet, I seem to reinitialize all of my variables correctly, and am reusing the ApplicationClass object.
I have done some decent research on this error. Aside from learning that we really shouldn't be using COM Interop, I haven't found much. One StackOverflow answer suggested multi-threading was the issue, but that doesn't seem evident in my code; although I'm 90% sure it is a bug. I just haven't been able to find it.
My code is as follows:
I have a class level variable for re-using the application class for each iteration of the loop:
Word.ApplicationClass _WordApp;
The loop runs the following code n times (as many files as there are to read) before it quits the Word application:
Inside Loop:
byte[] wordDocBytes = GetWordDocumentData(att.Data, att.FileName);
pagesToCombine.Add(wordDocBytes);
if (counter == wordFileCount) { QuitWordApplication(); }
else { counter += 1; }
GetWordDocumentData method:
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;
}
QutiWordApplication method:
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);
}
}
Is there any way I can fix or prevent this error? How can I improve how I am managing this document object?
It seems the key was Document.Activate():
doc.Activate();
Which should be run after 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);
I think using ref x, etc... in the Open() command helped also. And I ended up quitting the application every iteration. No crashing now, and the WINWORD.exe process does not multiply. Thank goodness...
Final Code:
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;
}
I think separating the cleanup from the work is important:
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();
}
User contributions licensed under CC BY-SA 3.0