Access a COM object from a 3rd party dll across threads
Hi!
I've been around here for quite a while now and found a lot of time saving hints. Thanks a lot!
For the problem I stumbled upon 2 days ago I did not find any suitable hint so any help is greatly appreciated.
Roughly speaking, I developed a multithreaded application that converts some data into a predefined format. Now I have to add another input format that is delievered in form a dll packed API.
When I put something like
Code:
myObj->setControl(CLASS_ID_OF_DLL);
somewhere in the main thread everything works fine and I can access the documented methods of the COM Object via dynamicCall(). But the same code in the process() method of another thread will lead to this error message:
Code:
QAxBase::setControl: requested control
{....
} could not be instantiated.
I can't find anything if it is simply not possible to call a COM object from different threads simultaneously or if I'm just to blind to see the solution.
Thanks in advance
Dean
Re: Access a COM object from a 3rd party dll across threads
QAxObject is a QObject subclass and as such cannot be used accross threads.
Re: Access a COM object from a 3rd party dll across threads
Ok, this means I should able to create one individual QAxObject in each thread and access the dll but that results in the error listed above.
The library provides a 3rd party interface for accessing a propriatary file format and is needed by each converter thread that operates on his own file.
Re: Access a COM object from a 3rd party dll across threads
Have the COM objects live in one thread that acts as a server for other threads. You can use signals and slots to communicate between threads, just don't call the COM wrapper directly.
Re: Access a COM object from a 3rd party dll across threads
That would be a nice idea. Unfortunatly this will not work because the processing of one image takes too long for sharing the COM object (it is blocked during the processing because it holds a lock on the current image).
Is it definitely impossible to create two QAxObjects on one COM object at the same time from different threads?
Re: Access a COM object from a 3rd party dll across threads
Quote:
Is it definitely impossible to create two QAxObjects on one COM object at the same time from different threads?
I don't know that. But if the com object is blocked, you won't be able to process more than one request at the same time anyway.
Re: Access a COM object from a 3rd party dll across threads
Well, it's not technically blocked. An ID representing the currently processed file is assigned to the COM object and only released after the processing has finished. Therefore more than one object is needed for parallel processing or otherwise I'm working two times on the same image.
Thank you for your hints so far, but the error from my first post is still persisting.
Re: Access a COM object from a 3rd party dll across threads
Perhaps instead of doing multi threading you could use multi processing and communicate using IPC. This would solve your problem as each object would be in a different process space.
Re: Access a COM object from a 3rd party dll across threads
Till now different importers work together fine using multitasking. I just read through QProcess and IPC roughly but I will give it a try when I'm back in the office tomorrow. Thanks.
Re: Access a COM object from a 3rd party dll across threads
Thanks for your advices so far.
I digged into this thing a little bit further and stumbled across a really strange behaviour. For now I'm able to instantiate my QAxObject in a new thread. But only if accessed it from the main thread before.
The following code works fine but commenting out test() in main() will deliver an error that the control could not be instantiated. I'm getting a little confused now, since I can't find anything about that in the docs. Could someone please lead me in the right direction?
Code:
public:
protected:
void run();
private:
void process();
};
moveToThread(this);
}
void MyThread::process() {
qDebug() << thread() << "accessing";
comObj->setControl("{CLASSID_HERE}");
::CoInitialize(comObj);
}
void MyThread::run() {
process();
}
void test() {
qDebug() << qApp->thread() << "accessing";
comObj->setControl("{CLASSID_HERE}");
CoInitialize(comObj);
QVariantList params = QVariantList();
params.append("D:\\path\\to\\file");
comObj->dynamicCall("openFile(QString)", params);
delete comObj;
}
int main(int argc, char* argv[]) {
test();
MyThread *mt = new MyThread();
mt->start();
return a.exec();
}
Re: Access a COM object from a 3rd party dll across threads
I don't know if this still needs answering, but I stumbled upon this, looking for some answers myself.
The reason your code does not work, when not calling test, is because you are calling CoInitialize in there.
I think you misinterpreted what this function does, as you are passing your com object pointer.
To use COM, you need to initialize it, using CoInitialize. The pointer parameter is a reserved parameter and you should not pass anything in it.
But it still needs to be called, before creating a COM object (so before ->setControl), else it will fail. So I am guessing, the comObj->setControl in your test function, always fails.
However, since you are calling CoInitialize right after that (although with an invalid pointer, you should not pass), it works when you create your thread after that.
Hence, if you do not call test, COM is not intialized yet, so that is why creating your COM object fails in the thread.
So you should do something like this
CoInitialize(0); // So pass 0, instead of some pointer!
However, since you are using COM cross thread, you could also look at CoInitializeEx and pass COINIT_MULTITHREADED as second parameter. Look this up in the MSDN documentation.
Now you can create COM objects, using QAxObject and setControl, which should now work.
When you are done using COM, call CoUninitialize(); to release COM stuff.
And that should work ;)
Re: Access a COM object from a 3rd party dll across threads
Thanks for the tips on CoInitialize(); been chasing that problem for a week.
Re: Access a COM object from a 3rd party dll across threads
Hi,
I'm facing the same problem, but the above codes do not work for me... i have a compile time error "CoInitialize has not been declared" and cannot find any Qt documentation about this. Do you use MSVC compiler, and what includes do you have to access this function?
Thanks!
Re: Access a COM object from a 3rd party dll across threads
It's nothing to do with Qt. CoInitialize() is a deprecated Windows API call, http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx, declared in objbase.h but typically included via windows.h and ole2.h. The ActiveQt server startup code calls the equivalent function on your behalf anyway.
Re: Access a COM object from a 3rd party dll across threads
Quote:
Originally Posted by
ChrisW67
The ActiveQt server startup code calls the equivalent function on your behalf anyway.
So what's wrong if the error remains?
I have the following error after a second thread is started:
Code:
QAxBase: Error calling IDispatch member Version
: Unknown error
Re: Access a COM object from a 3rd party dll across threads
You should call CoInitializeEx in every thread that uses COM. ActiveQt initializes GUI thread for you, but for every extra thread you need to call CoInitializeEx/CoUninitialize,
Another thing, on VS I had to use ::CoInitializeEx(i.e. using global namespace modifier)
Re: Access a COM object from a 3rd party dll across threads
Quote:
Originally Posted by
lanz
You should call CoInitializeEx in every thread that uses COM. ActiveQt initializes GUI thread for you, but for every extra thread you need to call CoInitializeEx/CoUninitialize,
Another thing, on VS I had to use ::CoInitializeEx(i.e. using global namespace modifier)
Okay thanks.
To be sure, when to call CoInitializeEx? I guess in the object constructor before instantiating any COM object.
Re: Access a COM object from a 3rd party dll across threads
No, see http://msdn.microsoft.com/en-us/libr...(v=vs.85).aspx
CoInitializeEx should be called once per thread, say right after it started. And of course before instantiating any COM object.