NET 6 COM: An outgoing call cannot be made since the application is dispatching an input-synchronous call

Viewed 25

I have a Win11, NET 6.0, Windows App that calls Word through COM to run a Word macro stored inside of the Word instance. All the relevant code has been running for a year or so. I have been debugging for two days without success, reading up on the exception message that I am suddenly getting, trying random code changes, and so on.

My C# program gets the Word instance and tries to run the macro named "FH3" as shown in the code and log trace below. But the code never gets to run the macro because the code that tries to get the Word instance (WordAppGet) fails at runtime. Here are three lines from my log file that show the execution and exception error message.

MacroRun Running Word macro: 'FH3'
WordAppVarsGet GetActiveObject Word.Application failed.
Exception: An outgoing call cannot be made since the application is dispatching an input-synchronous call. (0x8001010D (RPC_E_CANTCALLOUT_ININPUTSYNCCALL))

The following test case code runs okay in Visual Studio.

  [TestMethod()]
  public void MacroRunTest() {
    var me = MethodBase.GetCurrentMethod()?.Name;
    MacroRunHelper("FH2", me);
  }

  // this code has been running fine for many months
  public static void
    MacroRunHelper(string macroName, string me) {
    var msg1 = $"{me} Running Word macro: '{macroName}'";
    LogMan.SendNormal(msg1);

    var wapp = WordAppGet();
    try {
      wapp.Run(macroName);
    }
    catch (Exception ex) {
      var msg = $"{me} {ex.Message} '{macroName}'";
      LogMan.Send(LogType.Error, msg);
    }
  }

  /// <summary>
  ///   Get and store a reference to the active Word application.
  /// </summary>
  public static Word.Application
    WordAppGet() {
      Word.Application wapp = null;
    try {
      // Marshal2.GetActiveObject is a copy of the Microsoft reference code
      // because it was not included in the MS Interop library when the code was written.
      // And GetActiveObject is still not included as of 2022-09-18.
      //
      // this call fails with the error message
      wapp = Marshal2.GetActiveObject("Word.Application") as Application;
    }
    catch (Exception ex) {
      var msg = $"{me} GetActiveObject Word.Application failed.";
      LogMessage(PError, msg);
      LogMessage(PError, ex.Message);
      throw;
    }

    return wapp;
  }

For what it's worth, the online doc about the error message talks a lot about windows controls and BeginInvoke, but this has nothing to do with any Windows controls as far as I can tell.

I have used this same code pattern to access Word, Excel, Outlook, PowerPoint, on NET Framework for a couple of years, and on NET 5 and 6 for more than a year.

It is a mystery to me why it stopped working for Word. It is a mystery to me why it continues to work in the Visual Studio debugger.

Does anyone know how I can fix it to run again? I wonder if any recent Win11 updates have had any changes that break this kind of code. Thank you.

1 Answers

The source of the problem was far away from the code shown above. I switched my top-level WindowsApp from receiving input commands using sockets to receiving using Windows messages. The WndProc branch called some helper code SYNCHRONOUSLY which called other code, and about 10 or 15 calls down the WordAppGet code was called.

Because WndProc calls were synchronous, the WndProc thread was not allowed to "call out" over COM because it might create a deadlock condition. Using Task.Run(...) in the WndProc message branch put the work on a separate thread that was allowed to call out over COM.

Receiving input commands via sockets does not involve the single-thread STA WndProc thread, so sockets worked for a year or more. I was trying to improve the speed of the code and thought Windows messages would be faster. But I wasted more time debugging the COM issue than sockets execution times would have used in 20 years. Bummer.

FWIW, I did not detect the problem until many weeks after I switched from sockets to messages because the COM use case was not tested after the switch.

Related