views:

902

answers:

3

Note:

  • Using raw Win32 CreateTheard() API
  • No MFC
  • An interface is simply a pointer to a vtable

Question:

  • How to pass an interface pointer to a thread?

Illustration:

IS8Simulation *pis8 = NULL;

...

CoCreateInstance(
                 clsid,
                 NULL,
                 CLSCTX_LOCAL_SERVER,
                 __uuidof(IS8Simulation),
                 (void **) &pis8);



...

hThread = CreateThread(
                NULL,
          0,
          SecondaryThread,
          //interface pointer pis8
          0,
          &dwGenericThreadID);

...

DWORD WINAPI SecondaryThread(LPVOID iValue)
{
    //using iValue accordingly
    //E.g.: iValue->Open

Regards

+6  A: 

As was stated below, passing a COM interface pointer between threads in not safe.

Assuming you know what you are doing:

hThread = CreateThread(
                NULL,
                        0,
                        SecondaryThread,
                        (LPVOID) pis8
                        0,
                        &dwGenericThreadID);

DWORD WINAPI SecondaryThread(LPVOID iValue)
{
   ((IS8Simulation*) iValue)->Open();
}

Thread safe version:

void MainThread()
{
    IStream* psis8;
    HRESULT res = CoMarshalInterThreadInterfaceInStream (IID_IS8SIMULATION, pis8, &psis8);
    if (FAILED(res))
         return;
    hThread = CreateThread(
                NULL,
                0,
                SecondaryThread,
                (LPVOID) psis8
                0,
                &dwGenericThreadID
          );
}

DWORD WINAPI SecondaryThread(LPVOID iValue)
{
   IS8Simulation* pis8;
   HRESULT res = CoGetInterfaceAndReleaseStream((IStream*) iValue, IID_IS8SIMULATION, &pis8);
   if (FAILED(res))
      return (DWORD) res;
   pis8->Open();
}
Quassnoi
@Quassnoi: Thanks!
Aaron
-1: As Martin said, this solution is incorrect for COM interfaces, unless the COM object is free-threaded. For other threading models, COM runtime needs to create a proxy to ensure the object is called on a permitted thread (e.g. the creating one for apartment threaded)
peterchen
This needs Interface Marshalling.
Vinay
+4  A: 

If the interface in your question is a COM interface, the approach given by Quassnoi might not be sufficient. You have to pay attention to the threading-model of the COM object in use. If the secondary thread will join a separate COM apartment from the one that your COM object was created in, and if that object is not apartment-agile, you'll need to marshal that interface pointer so that the secondary thread gets a proxy, and not a direct pointer to the object.

A COM object is normally made apartment-agile by using a special implementation of IMarshal. The simplest approach is to aggregate the Free Threaded Marshaler.

Some useful links...

Update: About the Free-threaded Marshaler...

It's clear from comments on this topic that some people would recommend that you never touch the FTM. While "Effective COM" is an excellent book, I think some of its recommendations are open to interpretation. Item 33 says "Beware the FTM"; it does not say "Never use the FTM". Very wisely it advises caution particularly when your apartment-agile object holds references to other objects, because they might not be apartment-agile. So really the advice is: think carefully when building apartment-agile objects, whether or not they use the FTM to achieve their agility. If you're sure you can build an apartment-agile object, I see no reason why you wouldn't use the FTM to achieve that.

Martin
FTM has dangerous aspects (see Effective COM, item 33, "Beware the Free-Threaded Marshaler")
Jason S
The GIT is very useful though.
Jason S
Yeah, never use FTM...
Matt Davison
+1  A: 

You basically need to do the following:

  • CoMashalInterThreadInterfaceInStream ==> you get an IStream interface.
  • pass that IStream to the thread, e.g. as Quassnoi said.
  • in SecondaryThread, call CoGetInterfaceAndReleaseStream to get the interface (or a proxy to it, if necessary).

Do not release the IStream interface unless creating the thread fails, and don't exit the thread until yu have called CoGetInterfaceAndReleaseStream.

COM runtime will create the proxy for you automatically. The proxy ensures that e.g. an apartment-threaded COM component is called on the thread that created it. However, this also requires that:

  • The interface is IDispatch, or proxy/stub components are registered for the interface
  • the threadthat created the component has a message loop and processes messages
peterchen
Where can I find more information about a "message loop" is that similar to Win32 API: WaitForSingleObject() ?
Aaron