Use Office Interop under IIS

Errors related to this post:

  • COMException (0x80010001): Call was rejected by callee.
  • COMException (0x8001010A): The message filter indicated that the application is busy.
  • COMException (0x800A1735): The requested member of the collection does not exist.
  • COMException (0x800A03EC): Cannot access the file xxx.
  • Maybe more.

My recent project requires the ability to convert PowerPoint and Word files into images. Our first solution is to use LibreOffice (PowerPoint/Word to PDF) and ImageMagick (PDF to images), but soon we found LibreOffice sometimes could be unreasonably slow, and soffice would hang if there were multiple instances of it at the same time.

Then I realize we could try Office Interop API, after all it's official.

It went kind of smoothly when I started to write a command line tool on my own computer (using C#). The code was actually like what you would get searching related keywords:

// Here's the code of exporting a Word file into images.
// As it's longer (comparing to PowerPoint version).

var app = new Word.Application();

var document = app.Documents.Open(inputFile);
var window = document.ActiveWindow;
var pane = window.ActivePane;
var pages = pane.Pages;

for (var i = 1; i <= pages.Count; i++) {
    try {
        var page = pages[i];

        using (var stream = new MemoryStream((byte[])(page.EnhMetaFileBits))) {
            var image = System.Drawing.Image.FromStream(stream);
            image.Save(String.Format("{0}.png", i));
        }
    } catch (COMException e) {
        Console.WriteLine(e);
    }
}

app.Quit();

Then I found it exported only the first page of the Word file. After some efforts, I found adding app.Visible = true; before open documents solved this issue (which is NOT part of the solution).

Later I integrated this tool with our Node.js server happily and was hoping to enjoy a break.

Wait, Node.js? You are not even saying ASP.NET? Actually the main issue I met (and probably you met as you are reading this post) is not about a specific language, but about permission and identity.

But from this line on, let's go to the complete solution directly.

  1. I am using LocalSystem as application pool identity, and I haven't tested other identities.
  2. Create folder Desktop under C:\Windows\System32\config\systemprofile\, and if you are running on a 64-bit Windows, you might also need to create one under C:\Windows\SysWOW64\config\systemprofile\.
  3. Open related Office apps and dismiss all prompts. Use Office apps as default apps, or you might need step 4.
  4. (Optional if everything works fine) Download PSTools on your server and use it to start Word/PowerPoint with LocalSystem identity and ensure you dismissed all prompts (e.g. default program prompt).
  5. Besides the identity issue we addressed, we still have the app.Visible = true; to refine. Change how you get a page as below (and we don't need app.Visible = true; any more):
Word.Page page;

while (true) {
    try {
        // Load this page manually.
        window.Selection.GoTo(Word.WdGoToItem.wdGoToPage, Type.Missing, i, Type.Missing);
        page = pages[i];

        break;
    } catch (Exception e) {
        // If not error "The requested member of the collection does not exist."
        if ((uint)e.HResult != 0x800A1735) {
            throw;
        }

        Thread.Sleep(500);
    }
}

Now you should have a go. ;)