iOfficeAI/OfficeCLI · error · InvalidOperationException

Invoke({name}) hr=0x{hr:X8}

Error message

Invoke({name}) hr=0x{hr:X8}

What it means

Thrown when a raw COM IDispatch::Invoke call returns a non-zero HRESULT while driving Microsoft Word (or a WinRT PDF renderer) through hand-rolled P/Invoke delegates in WordPdfBackend. The backend marshals VARIANT arguments into native memory, calls the vtable slot at index 6 (IDispatch::Invoke) via a delegate fetched by VT<F_Invoke>, and surfaces any failure as an InvalidOperationException carrying the member name and the raw HRESULT. This is the lowest-level COM error path in the PDF conversion backend — every DispGet/DispSet/DispMethod call funnels through DispCall, so any Word Automation failure (locked document, bad argument, Word not installed, security dialog blocking) manifests here.

Source

Thrown at src/officecli/Core/WordPdfBackend.cs:228

    {
        int dispId = DispId(d, name);
        IntPtr argArr = args.Length > 0 ? Marshal.AllocHGlobal(VAR_SZ * args.Length) : IntPtr.Zero;
        IntPtr namedArr = isPut ? Marshal.AllocHGlobal(4) : IntPtr.Zero;
        IntPtr dp = Marshal.AllocHGlobal(IntPtr.Size * 2 + 8);
        IntPtr result = Marshal.AllocHGlobal(VAR_SZ);
        Marshal.WriteInt64(result, 0); Marshal.WriteInt64(result, 8, 0); Marshal.WriteInt64(result, 16, 0);
        try
        {
            for (int i = 0; i < args.Length; i++) Wv(argArr + (args.Length - 1 - i) * VAR_SZ, args[i]);
            if (isPut) Marshal.WriteInt32(namedArr, DISPID_PROPERTYPUT);
            Marshal.WriteIntPtr(dp, argArr);
            Marshal.WriteIntPtr(dp, IntPtr.Size, namedArr);
            Marshal.WriteInt32(dp, IntPtr.Size * 2, args.Length);
            Marshal.WriteInt32(dp, IntPtr.Size * 2 + 4, isPut ? 1 : 0);

            var iid = Guid.Empty;
            int hr = VT<F_Invoke>(d, 6)(d, dispId, ref iid, 0, flags, dp, result, IntPtr.Zero, IntPtr.Zero);
            if (hr != 0) throw new InvalidOperationException($"Invoke({name}) hr=0x{hr:X8}");
            return Rv(result);
        }
        finally
        {
            Cv(result); Marshal.FreeHGlobal(result);
            Marshal.FreeHGlobal(dp);
            for (int i = 0; i < args.Length; i++) Cv(argArr + i * VAR_SZ);
            if (argArr != IntPtr.Zero) Marshal.FreeHGlobal(argArr);
            if (namedArr != IntPtr.Zero) Marshal.FreeHGlobal(namedArr);
        }
    }

    internal static void DispSet(IntPtr d, string name, object? v) => DispCall(d, name, 4, [v], true);
    internal static object? DispGet(IntPtr d, string name) => DispCall(d, name, 2, []);
    internal static object? DispMethod(IntPtr d, string name, params object?[] args) => DispCall(d, name, 1, args);

    static byte[] RenderOne(IntPtr doc, uint i, IntPtr drFactory, int timeoutMs)
    {

View on GitHub (pinned to 1ced45e900)

Solutions

  1. Decode the HRESULT in the message: 0x80020009 = DISP_E_EXCEPTION (Word raised its own error — check the document state), 0x80070003 = path not found, 0x80004005 = E_FAIL (Word not properly installed or licensed), 0x80010108 = RPC_E_DISCONNECTED (Word process died).
  2. Verify Microsoft Word is installed and licensed on the machine — the backend instantiates CLSID {000209FF-...} directly; a stub or viewer will fail.
  3. If converting a specific document, open it manually in Word first to check for password prompts, protected view, or macro warnings that block automation.
  4. Ensure the temp directory ($TMP / %TEMP%) is writable and has enough disk space for the intermediate PDF.
  5. If running in a service/container, confirm Word can run in a non-interactive session (set Visible=false is already done; you may need to configure DCOM launch permissions via dcomcnfg for the Word application).

Example fix

// before: Convert with no pre-check, raw COM error surfaces
var pdf = WordPdfBackend.DocxToPdf(docxPath);

// after: Pre-validate the document is not protected before invoking Word
if (IsPasswordProtected(docxPath))
    throw new CliException("Document is password-protected; remove protection before PDF conversion.")
        { Code = "protected_document" };
var pdf = WordPdfBackend.DocxToPdf(docxPath);
Defensive patterns

Strategy: try-catch

Validate before calling

// Before calling DocxToPdf, verify Word is available
try
{
    var clsid = new Guid("000209FF-0000-0000-C000-000000000046");
    var iid = new Guid("00020400-0000-0000-C000-000000000046");
    WordPdfBackend.CoCreateInstance(ref clsid, IntPtr.Zero, 4, ref iid, out var word);
    Marshal.Release(word);
}
catch { /* Word not installed — PDF conversion unavailable */ }

Try / catch

try
{
    var pdf = WordPdfBackend.DocxToPdf(docxPath);
}
catch (InvalidOperationException ex) when (ex.Message.StartsWith("Invoke("))
{
    // Extract and decode the HRESULT from the message
    var hrHex = System.Text.RegularExpressions.Regex.Match(ex.Message, "hr=0x([0-9A-Fa-f]+)");
    if (hrHex.Success)
    {
        var hr = unchecked((int)Convert.ToUInt32(hrHex.Groups[1].Value, 16));
        // DISP_E_EXCEPTION (0x80020009) = Word raised its own error
        // RPC_E_DISCONNECTED (0x80010108) = Word process died
        logger.LogError("Word COM Invoke failed: HRESULT 0x{HR:X8}", hr);
    }
    throw new CliException($"PDF conversion failed: Word Automation error.") { Code = "pdf_conversion_failed" };
}

Prevention

When it happens

Trigger: Calling DocxToPdf or RenderOne when (a) the requested member does not exist or has a different signature on the installed Word version, (b) Word throws a COM error such as DISP_E_EXCEPTION (0x80020009) because the document is password-protected, read-only, corrupt, or contains macros blocked by AutomationSecurity, (c) the IDispatch pointer is stale because Word was killed externally, or (d) an argument VARIANT type does not match what the method expects (e.g. passing a string where an int is required). The 'name' placeholder is the late-bound member invoked (Name, Visible, DisplayAlerts, AutomationSecurity, Documents, Open, SaveAs2, Close, etc.).

Common situations: Running the docx-to-pdf conversion on a server or container where Word is not installed or is a click-to-run stub; converting a password-protected or DRM-locked document; a Windows update changing the Word COM type library; calling the backend on a non-Windows platform (the class is [SupportedOSPlatform("windows")]); a document whose SaveAs2 with format 17 (wdFormatPDF) fails because the target temp path is unwritable or anti-malware blocks the write.

Related errors


AI-assisted analysis of iOfficeAI/OfficeCLI@1ced45e900 (2026-08-13). Data as JSON: /api/errors/ed2973eb4c5e059f. Report an issue: GitHub.