12

I have a COM server (C++/STA (MFC based app)) and a COM client (C#/MTA). The COM server must live in an STA, since it's an MFC app (I have no choice in this matter). The client issues a call to the server, and the server issues a callback to the client. That's where the error happens (RPC_E_CANTCALLOUT_ININPUTSYNCCALL). I'm guessing if the server had been an MTA, this problem would never have arised, but sadly, the documentation for MFC explicitly denies initializing the apartment as an MTA.

Any ideas on how to work around this problem?

I have been toying with the idea of letting the server object (the object I expose through the running object table) live in an apartment of its own (MTA). Would this be a good idea, or is there something simpler to try first?

UPDATE

The server object is just a thin interface point to certain functions within the application. Most of the time it just reads and writes to memory locations, but there are instances where it generates window messages to various windows within the application. The server object itself is not the entire application.

Jörgen Sigvardsson
  • 4,839
  • 3
  • 28
  • 51
  • It is a direct consequence of violating the apartment threading requirements. Pretty unclear how it came about, but if the server requires a single threaded apartment then you'd better create the RCW on the C# side on an STA thread. – Hans Passant Jul 07 '11 at 12:27
  • The *app* in which the server lives, requires an STA. The server object itself does not require an STA. Some calls to the server will generate window messages being sent to various windows inside the application. – Jörgen Sigvardsson Jul 07 '11 at 12:33
  • What do you mean by creating the RCW on the C# side? – Jörgen Sigvardsson Jul 07 '11 at 12:33

3 Answers3

20

RPC_E_CANTCALLOUT_ININPUTSYNCCALL means that you attempted to make a marshalled COM call from within the handler for a windows message sent via SendMessage. This is to help avoid certain deadlock situations. You have a number of options, which boil down to "avoid COM calls in a SendMessage handler":

  • You could use PostMessage to queue up a message to yourself, and in that posted message handler, invoke the COM callback.
  • You could use asynchronous DCOM, and avoid blocking on the result of the call from within the message handler.
  • You could marshal the callback interface, then invoke it from a thread pool work item. Since this is independent from the main application's message loop, it won't be in a SendMessage call, and it could even be in a MTA.
  • You could forgo the MFC COM support, and invoke CoRegisterClassObject directly from another thread. This means any calls into your server COM object will be invoked from the COM thread pool (or, if you use a STA thread, from that thread), not from the MFC UI thread, so you'll need to use Windows messages to communicate across threads; but if you need to make synchronous calls back to the client it may be the best approach.
zett42
  • 25,437
  • 3
  • 35
  • 72
bdonlan
  • 224,562
  • 31
  • 268
  • 324
  • I ended up using point 1, as it would mean the simplest way to deal with the issue at hand, as I don't have many callbacks (three as it stands, and I don't see that figure changing anytime soon). This automation code I'm working on is not prioritized work, and I can't spend too much time and effort on it. Especially when it's an "in house" thing I'm working on. Your points however are very informative, and I will keep these in mind if I ever have to deal with this sort of thing again in production code. Thank you! – Jörgen Sigvardsson Jul 07 '11 at 20:52
  • 1
    In WPF I just wrapped my code around `new Thread(new ThreadStart(() => {` and `})).Start();` and voila! – ioan Oct 24 '16 at 03:24
  • @ioanb7 you da man or woman..not sure! This prevented the error the OP mentioned in WinForms as well. – Michael Z. Apr 09 '17 at 16:16
2

No matter how much I twist and turn, I cannot remove myself of the STA context within the app when I'm being called from the client. It doesn't matter if I host the server object in an MTA, I still have to obey the laws of COM. The STA is a really nasty "correctional facility" in this case. I'm doing hard time...

This has led me to a rather ugly path, but it works. Instead of using COM to communicate back to the client, I'm hand rolling my own communications path to the MTA that hosts the server object and the callback references. I'm basically creating my own marshalling code by setting up a call queue (STL container with parameters to send), which the MTA thread picks up and delivers to the client. The response is then returned to the code that's responding to the initial call. Synchronization is done using Win32 event objects.

Luckily, there aren't many callbacks I have to cover, and the nature of the mechanism is static, and will only be used for my own purposes (will not be run in a production environment).

Wheew... Sometimes I wonder what life would've been if I chose to become a carpenter instead.

Jörgen Sigvardsson
  • 4,839
  • 3
  • 28
  • 51
0

You can create a timer when you get the message and then do your COM calls and further processing in your TimerProc/WinProc under WM_TIMER. This works for me.

Meow
  • 178
  • 2
  • 7