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
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();
}
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...
CoMarshalInterThreadInterfaceInStream
GlobalInterfaceTable (GIT)
CoCreateFreeThreadedMarshaler
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.
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
Related
In a multi-threaded c++ project (working on Windows 11 and Visual Studio 2019), I open an IStream COM object in a dedicated thread, let's naming it Thread A, with the following code:
...
::CComPtr<IStream> pStreamThreadA;
HRESULT hr = pShellItem->BindToHandler(nullptr, BHID_Stream, IID_IStream, (void**)&pStreamThreadA);
...
Once opened, I want to read its content in a second thread, let's naming it Thread B. As it's a COM object, I'm aware that it's hazardous to try to use it in the Thread B while it was created in the Thread A. For that reason I Marshall it with the following code:
In Thread A:
...
const HRESULT hr = ::CoMarshalInterThreadInterfaceInStream(IID_IStream, pStreamThreadA, &m_pMarshall);
...
In Thread B:
...
::CComPtr<IStream> pStreamThreadB;
const HRESULT hr = ::CoGetInterfaceAndReleaseStream(m_pMarshall, IID_IStream, (void**)&pStreamThreadB);
m_pMarshall.Release();
...
But this doesn't work. When I try to seek in my stream in the Thread B, e.g. with the following code:
...
const LARGE_INTEGER zero = {0};
ULARGE_INTEGER pos;
const HRESULT hr = pStreamThreadB->Seek(zero, ::STREAM_SEEK_CUR, &pos);
...
the code execution get stuck in the Seek() function and never returns. For that reason I assume that the stream pointer became invalid (i.e dangling).
On the other hand, and paradoxically, if I bypass the Marshaling code above and I use the stream pointer directly between the threads, barely respecting the usual lock mechanisms, all work fine. But I'm aware that this is very unsafe and not recommended.
So can someone explain me what I'm doing wrong in the above code, and why my Marshaled stream lead to an invalid pointer?
I have a custom interface defined in a type library and implemented in my code. I have not created my own proxy/stub. I have successfully marshaled the interface pointer from my main thread (STA) to a background thread (STA), in my case using IGlobalInterfaceTable.
From that background thread, I want to make an asynchronous call to the object on the UI thread. I have not implemented ICallFactory in my object. I see that the standard proxy does indeed implement ICallFactory (i.e, I can successfully QI on the background thread for ICallFactory). But the CreateCall for my custom interface fails with HRESULT 0x80040150 (Could not read key from registry).
Do I need to create my own proxy that explicitly implements ICallFactory in order to do this?
Here's my IDL:
[
object,
uuid(92303FE7-A79D-47DD-923F-62062105C00E),
async_uuid(2880C40C-9965-4544-AE39-DF08056E8CB6),
nonextensible,
pointer_default(unique),
oleautomation
]
interface IFoo: IUnknown
{
HRESULT Foo([in] long a, [in] long b);
}
[
uuid(D58B0A31-A2D5-4BFB-8702-3B710320493B)
]
coclass Foo
{
[default] interface IFoo;
};
And here's the background thread proc in my unit test:
static DWORD WINAPI threadproc(LPVOID lpParameter)
{
// get arguments
DWORD cookie = *(DWORD*)lpParameter;
// initialize COM
Assert::AreEqual(S_OK, CoInitializeEx(NULL, COINIT_APARTMENTTHREADED));
{
// get global interface table
IGlobalInterfaceTablePtr globalInterfaceTable;
Assert::AreEqual(S_OK, globalInterfaceTable.CreateInstance(CLSID_StdGlobalInterfaceTable));
// get object
MyLib::IFooPtr object;
Assert::AreEqual(S_OK, globalInterfaceTable->GetInterfaceFromGlobal(cookie, MyLib::IID_IFoo, (LPVOID*)&object));
// get async call factory
ICallFactoryPtr callFactory;
Assert::AreEqual(S_OK, object->QueryInterface(&callFactory));
//
// Everything is fine up until the CreateCall call below,
// which fails with HRESULT 0x80040150
//
// create async call object
IUnknownPtr callObject;
Assert::AreEqual(S_OK, callFactory->CreateCall(MyLib::IID_AsyncIFoo, NULL, IID_IUnknown, &callObject));
}
// uninitialize COM
CoUninitialize();
// success
return 0;
}
As far as I know, the universal marshaller does not work with Async COM. You need to build the (MIDL-generated) proxy (although, IIRC, you can merge the stubs if you're building a DLL).
You also need to register the proxy DLL (and build it with the -DREGISTER_PROXY_DLL define). Async COM needs more registry keys defined in order to make it work.
I have some event handles and I add them to a list. I want to know if I can store these handles, close the local ones and still use the stored ones later.
Example:
std::map<std::string, HANDLE> Events;
DWORD OpenSingleEvent(std::string EventName, bool InheritHandle, DWORD dwDesiredAccess, DWORD dwMilliseconds)
{
Handle hEvent = OpenEvent(dwDesiredAccess, InheritHandle, EventName.c_str()); //Local Handle.
if (hEvent)
{
DeleteSingleEvent(EventName); //Delete the correct/old handle in the map.
Events.insert(EventName, hEvent); //Add this new handle to the map.
DWORD Result = WaitForSingleObject(hEvent, dwMilliseconds);
CloseHandle(hEvent); //Close THIS handle. Not the one in my Map.
return Result;
}
CloseHandle(hEvent); //Close this handle.
return WAIT_FAILED;
}
Will the above work? If not, is there another way to do this? It's for shared memory communication so I cannot duplicate handles since I only have the client PID not the Server's.
Also can someone explain what InheritHandle does? The function I use is OpenEvent and it has that parameter but I'm not sure what it does.
A HANDLE is simply a void *, it's a token which actually represents an object in kernel space. Calling CloseHandle actually deallocates the kernel object so the short answer to your question is no, you can't keep a list of them and then close all the local ones. All you'll have is a list of void* which don't represent anything.
What you can do is use DuplicateHandle which actually creates another kernel object on your behalf. However... why not just close the handles when you've finished with the entry in the list?
The MSDN documentation for the CCmdTarget::OnFinalRelease method is pretty brief:
Called by the framework when the last OLE reference to or from the
object is released.
I have created a sub-class of CCmdTarget
class CMyEventHandler : public CCmdTarget { ... }
I'm trying to figure out under what conditions the OnFinalRelease method will be called. I have some code that looks something like this:
CMyEventHandler* myEventHandler = new CMyEventHandler();
LPUNKNOWN pUnk = myEventHandler->GetIDispatch(FALSE);
AfxConnectionAdvise(myEventSource, DIID_IMyEventInterface, pUnk, FALSE, myCookie);
// Application continues...events arrive...eventually the event sink is shutdown
LPUNKNOWN pUnk = myEventHandler->GetIDispatch(FALSE);
AfxConnectionUnadvise(myEventSource, DIID_IMyEventInterface, pUnk, FALSE, myCookie);
Using this code, I observe that the OnFinalRelease method is never called. This means I have a memory leak. So I modified the wrap-up code as follows:
LPUNKNOWN pUnk = myEventHandler->GetIDispatch(FALSE);
AfxConnectionUnadvise(myEventSource, DIID_IMyEventInterface, pUnk, FALSE, myCookie);
delete myEventHandler;
myEventHandler = NULL;
This section of code is triggered off periodically throughout the day. What I notice now is that, while the destructor for the wrapped up instance of myEventHandler is called as expected, the OnFinalRelease function is getting called now! What's worse, it is being called not on the instance that has been wrapped up, but instead on a newly created instance of CMyEventHandler! Thinking that this might be due to a reference counting issue, I modified my wire-up and wrap-up code:
CMyEventHandler* myEventHandler = new CMyEventHandler();
LPUNKNOWN pUnk = myEventHandler->GetIDispatch(TRUE);
AfxConnectionAdvise(myEventSource, DIID_IMyEventInterface, pUnk, TRUE, myCookie);
pUnk->Release();
// Application continues...events arrive...eventually the event sink is shutdown
LPUNKNOWN pUnk = myEventHandler->GetIDispatch(TRUE);
AfxConnectionUnadvise(myEventSource, DIID_IMyEventInterface, pUnk, TRUE, myCookie);
pUnk->Release();
delete myEventHandler;
myEventHandler = NULL;
I let this run all day and now observe that OnFinalRelease is never called. The destructor for the wrapped up instance is called as I would expect, but I'm left feeling uneasy as I clearly don't understand the circumstances under which OnFinalRelease is called. Is OnFinalRelease called on some delay, or is there a way to force it to fire? What will trigger OnFinalRelease to be called?
If it matters, the event source is a .NET assembly exposing events via COM interop.
With COM you should always use the CoCreateInstance() AddRef() and Release() paradigm to manage lifetime of your objects, and let COM do the destruction of your objects based on reference counts. Avoid new and delete because using them breaks this paradigm and causes interesting side effects. You probably have a bug in the management of the reference counts.
The way to debug why the reference counts are not being managed correctly is to override CCmdTarget::InternalRelease() copy the source from oleunk.cpp and put some trace output or break points.
DWORD CMyEventHandler::InternalRelease()
{
ASSERT(GetInterfaceMap() != NULL);
if (m_dwRef == 0)
return 0;
LONG lResult = InterlockedDecrement(&m_dwRef);
if (lResult == 0)
{
AFX_MANAGE_STATE(m_pModuleState);
OnFinalRelease();
}
return lResult;
}
There are lots of times when passing IDispatch interfaces that code will bump reference counts and you have to decrement the reference count using Release(). Pay attention to where your code may be passing this interface because there is aconvention in COM that when Interfaces are passed using [in] or [out] where the caller or callee has to release the interface.
When the reference count issue is corrected you shoudl see the objects OnFinalRelease code being called and the object destoryed by hte MFC framework:
For CCmdTarget the destruction should happen as a result of the final
release in the parent class CWnd:
void CWnd::OnFinalRelease()
{
if (m_hWnd != NULL)
DestroyWindow(); // will call PostNcDestroy
else
PostNcDestroy();
}
FYI: Passing interfaces across threads without marshalling the interface pointers is another common reason to get errors in COM.
It doesn't appear that you ever call myEventHandler->Release(). Therefore, the last reference is never released, and OnFinalRelease is never called.
I am writing a Win32 C++ DLL that uses the COM to query WMI. How can I programmatically determine if COM has already been initialized? Thanks.
Mark Ransom is right
the straightforward, clean and simple solution is to require COM initialization by the caller.
Ugly hack
You can try your first call - likely CoCreateInstance, and if it returns CO_E_NOTINITIALIZED, run CoInitialize yourself (and don't forget to uninit in that case)
However, it is still problematic to "inject" a CoInitialize into a caller thread from a DLL. So there's a
Clean Solution
Let the DLL create a worker thread (which means the DLL needs Init and Teardown calls), CoInitializeEx in this thread yourself, and move all the COM calls to that separate thread.
The easiest way is not to bother, just make it a requirement of anybody using your DLL that they initialize COM first. Otherwise you run the risk of messing up their own initialization if they perform it after yours.
On the other hand if your flags to CoInitializeEx match those of the application, you should be fine. From the CoInitializeEx documentation:
Multiple calls to CoInitializeEx by
the same thread are allowed as long as
they pass the same concurrency flag,
but subsequent valid calls return
S_FALSE.
It follows #peterchen clean solution as I coded it for a thread-safe COM logger component that I wanted to wrap:
IComLoggerPtr _logger;
_bstr_t _name;
HANDLE _thread;
HANDLE _completed;
Logger::Logger(_bstr_t name)
{
_name = name;
_completed = ::CreateEvent(NULL, false, false, NULL);
if (_completed == NULL)
::AtlThrowLastWin32();
// Launch the thread for COM interation
DWORD threadId;
_thread = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(this->threadRun),
(LPVOID)this, 0, &threadId);
// Wait object initialization
HRESULT hr = ::WaitForSingleObject(_completed, INFINITE);
if (FAILED(hr))
AtlThrow(hr);
}
Logger::~Logger()
{
::SetEvent(_completed);
CloseHandle(_thread);
CloseHandle(_completed);
}
DWORD WINAPI Logger::threadRun(LPVOID opaque)
{
Logger *obj = (Logger *)opaque;
// Init Free-Threaded COM subsystem
HRESULT hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
if (FAILED(hr))
::AtlThrow(hr);
hr = obj->_logger.CreateInstance(__uuidof(ComLogger));
if (FAILED(hr))
::AtlThrow(hr);
obj->_logger->Init(obj->_name);
// Initialization completed
bool success = ::SetEvent(obj->_completed);
if (!success)
::AtlThrowLastWin32();
// Wait release event
hr = ::WaitForSingleObject(obj->_completed, INFINITE);
if (FAILED(hr))
AtlThrow(hr);
obj->_logger.Release();
// Release COM subsystem
::CoUninitialize();
}
HRESULT Logger::Log(_bstr_t description)
{
return _logger->Log(description);
}
CoInitializeEx\CoUninitialize should only be called by threads (not by Dll-calls).
BTW ,you should not Use CoInitializeEx\CoUninitialize in DllMain !