0

I have a COM object implemented in a STA EXE (calls CoInitialize(NULL)), let's call it CMyObject which implements IControl. Pseudo code is like this:

IControl
{
 Start();
 Stop();
};

CMyClass : IControl
{
 public:
  Start()
  {
   //create an ISomething in the main thread
   mSomething = CoCreate(ISomething);
   //this thread will make a call through ISomething
   mThread = std::thread(ThreadMain, this);
  }
  Stop()
  {
   kill(mThread);
   mSomething->Release();
  }

  UseSomething() //called from another thread
  {
   mSomething->DoStuff();
  }
 private:
  std::thread mThread;
  ISomething *mSomething;
};

void ThreadMain(CMyClass *o)
{
 for(;;)
 {
  if(<some condition>)
   o->UseSomething();
 }
}

My test code basically follows this pattern and works without problems (so far) but reading MSDN on STA suggests I need to:

  • Call CoInitialize/CoUninitialze in ThreadMain
  • Use marshalling to call the interface from the worker thread

This question (How to access COM objects from different apartment models?) also suggests marshalling is required and advocates use of the GIT.

However apartment models is one part of COM I've never really managed to grok and I wanted to check this is necessary in my situation - especially since it is currently working nicely without errors being thrown - I don't want to add code just "in case it's needed, I can't tell".

If it makes any difference, the COM ISomething object in question is only called by the worker thread, not by the main thread. In my specific case, only one CMyObject will ever exist at any time.

Community
  • 1
  • 1
Mr. Boy
  • 60,845
  • 93
  • 320
  • 589
  • I would expect the calls from the thread function to fail with `CoIntialize has not been called`. Are they succeeding? – lcs Nov 10 '15 at 19:41
  • Yes - the interface `ISomething` in my real code is implemented by a C# DLL (which emits a TLB I `#import` in my C++ code), and its methods are being called successfully - The C# code is performing logging, etc. – Mr. Boy Nov 10 '15 at 19:44
  • 2
    You are violating COM threading model rules. Basically, you are not really doing COM - you have a direct pointer to an object, you pass that pointer to another thread, and you call methods from that thread, on an object that didn't promise to be thread-safe. COM runtime is not in the picture to stop you. You may or may not get away with it, depending on what exactly the object you call is doing. – Igor Tandetnik Nov 11 '15 at 14:43
  • Thanks @IgorTandetnik. If the COM object is _only_ called by the worker thread would it technically be safe? Is it _possible_ to say under what circumstances it is/isn't safe? Or just fundamentally dodgy and I should just use GIT? – Mr. Boy Nov 11 '15 at 15:01
  • The object has apparently declared itself to be apartment-threaded. As such, it is legally allowed to be thread-affine - that is, only work properly when called from a thread that created it. The most common reason for thread affinity is displaying UI (or creating a hidden window, or otherwise relying on the host running a message pump), but there may be other reasons (e.g. an object may create other COM objects that are, in turn, thread-affine). If your object doesn't do anything like that, then, while still technically a violation, you would probably get away with it. – Igor Tandetnik Nov 11 '15 at 15:10
  • Come to think of it, you only use the object on the worker thread - so why not also create it there? You do need to have this thread initialize COM, but you wouldn't need marshaling if the thread itself creates and later releases the object. This way, you'd stay fully within the rules. – Igor Tandetnik Nov 11 '15 at 15:12
  • 1
    @IgorTandetnik I did consider that but it looked a bit clunky - basically a C program in my ThreadMain! Could be an option though. I think your comments are quitea coherent answer by the way, if you fancied combining them into one. – Mr. Boy Nov 11 '15 at 15:14
  • 2
    Realize that, if you create an object on an STA thread and then marshal it to another thread, what that other thread gets is a proxy. Every call on that proxy is marshaled back to the original thread, executed there, and the results sent back to the worker (remember, the object you created may be thread-affine, so COM must ensure it is only ever called on the thread that created it). In this scenario, the "worker" doesn't actually do any work - it just sits there waiting for the main thread to complete the out-of-apartment COM call. That doesn't sound like what you had in mind. – Igor Tandetnik Nov 11 '15 at 15:18
  • 2
    By the way, [this is the best description](https://support.microsoft.com/en-us/kb/150777) of COM threading model I've ever seen. – Igor Tandetnik Nov 11 '15 at 15:20

0 Answers0