查看完整版本: [-- 子线程中调用COM函数失败[已解决] --]

QTCN开发网 -> Qt基础编程 -> 子线程中调用COM函数失败[已解决] [打印本页] 登录 -> 注册 -> 回复主题 -> 发表主题

sbtree 2010-08-11 00:17

子线程中调用COM函数失败[已解决]

这个问题困扰了我很久了,一直没有弄清楚原因
环境:Win Vista + VS2008 + QT4.5.2
利用QAxObject封装了一个第三方COM组件,在主线程使用没问题,每一个函数都能被正确调用,问题是我把这个封装的组件放在一个继承自QThread的子类中以后,COM中的所有函数在run函数中都无法被成功调用了,提示
"First-chance exception at 0x77ae42eb in myapp.exe: 0x800401F0: CoInitialize was not called."
从意思上看,应该是组件没有被初始化。事实上在我的QThread子类的构造函数中已经成功地创建了该对象,当然,QThread子类的实例是在主线程中创建的,不知道这个会不会对COM的实例有影响。也曾尝试在QThread子类的run函数中创建由QAxObject封装的COM组件的实例,但问题仍然得不到解决。
之前似曾在哪里看到过,似乎因为权限的原因,好像COM组件要在自身的线程内创建,并调用CoInitialize(NULL, MULI_THREAD)(希望参数是正确的)之类的函数,但是对于QAxObject来讲,好像这些调用都已经封装好了,在我的QAxObject子类的构造函数中主要是调用了setControl(QString)函数载入COM的(见文档)。不知道对于子线程还需要做什么?一

dbzhang800 2010-08-11 07:37
我对COM不了解(接近一窍不通),谈下自己的认识,仅供参考

1. 使用 COM 的过程:
   a.  CoInitialize(NULL)
   b.  CoCreateInstance(....)
   c.  CoUnInitialize()

2. 当使用 QAppplicaton 时:
  a 和 c 由 QApplication 自动完成了 (似乎是因为它本身需要用到COM)
 b 由 setControl 或 QAxObject 的构造函数完成(这个不用说)

3. 如果你使用的 QCoreApplication,必须自己调用 a 和 c

4. 在次线程内的话,按照
http://www.vckbase.com/document/viewdoc/?id=1183
所说,应该需要你自己调用 a 和 c

sbtree 2010-08-11 15:59
引用第1楼dbzhang800于2010-08-11 07:37发表的  :
我对COM不了解(接近一窍不通),谈下自己的认识,仅供参考
1. 使用 COM 的过程:
   a.  CoInitialize(NULL)
   b.  CoCreateInstance(....)
.......

多谢回复,你给的连接的确是一篇好文章。
张老师(请允许我这么称呼),你太谦虚了,向你学习

sbtree 2010-08-11 19:34
解决方法:
之前的QAxObject子类我是通过dumpcpp工具生成的,默认的构造函数和析构函数都没有显示的调用CoInitilize和CoUninitialize函数,可能是Qt默认为该类会在主线程中使用的原因吧(猜测)。
解决的方法很简单,就是分别在构造函数和析构函数中加上他们,这样,在子线程中就可以创建并使用了。

sbtree 2010-08-11 19:46
个人理解的一点补充
COM是线程独立的,也就是说CoInitialize和CoUnitialize应该在每线程中称对出现一次

dbzhang800 2010-08-11 19:51
引用第3楼sbtree于2010-08-11 19:34发表的  :
解决方法:
之前的QAxObject子类我是通过dumpcpp工具生成的,默认的构造函数和析构函数都没有显示的调用CoInitilize和CoUninitialize函数,可能是Qt默认为该类会在主线程中使用的原因吧(猜测)。
解决的方法很简单,就是分别在构造函数和析构函数中加上他们,这样,在子线程中就可以创建并使用了。

如果线程中只有一个 QAxObject,这样肯定没问题,但不清楚CoInitilize 和 CoUninitialize 能不能反复或混合调用,比如先CoInitilize两次,再Un两次。如果不能这么做,你这样弄是有隐患的。

你可以win32或Com相关的论坛咨询一下

sbtree 2010-08-12 15:07
引用第5楼dbzhang800于2010-08-11 19:51发表的  :
如果线程中只有一个 QAxObject,这样肯定没问题,但不清楚CoInitilize 和 CoUninitialize 能不能反复或混合调用,比如先CoInitilize两次,再Un两次。如果不能这么做,你这样弄是有隐患的。
你可以win32或Com相关的论坛咨询一下


我继续努力研究,不过就我现在的了解,COM环境的初始化似乎采用了引用计数的方法,所以只要初始化和反初始化过程在每线程中成对调用就好

dbzhang800 2010-08-12 15:15
引用第6楼sbtree于2010-08-12 15:07发表的  :
我继续努力研究,不过就我现在的了解,COM环境的初始化似乎采用了引用计数的方法,所以只要初始化和反初始化过程在每线程中成对调用就好


每个线程都这么做没问题(线程开始时Co...结束时CoUn...),问题是你把它们放到了 QAxObject的构造和析构函数中,如果同一个线程中有多个你改造过的QAxObject ,会发生什么状况?

sbtree 2010-08-12 17:11
引用第7楼dbzhang800于2010-08-12 15:15发表的  :
每个线程都这么做没问题(线程开始时Co...结束时CoUn...),问题是你把它们放到了 QAxObject的构造和析构函数中,如果同一个线程中有多个你改造过的QAxObject ,会发生什么状况?


不错,这的确不是一个好方法,其实我真正的做法是在run函数的开始处CoIn...,然后在线程finished()信号的响应槽中CoUni...的,之前的方法是我在假定应用程序中只有唯一的子线程和唯一的COM对象实例的情况下的一个临时方法,当然这对于真正多线程应用程序来讲的确是不可取的。多谢张老师指点!

还有一点值得一提,finished和terminated信号看起来很相似,原始的意义是在run函数自然结束或者调用exit致使线程结束的时候发射finished信号,而terminated信号是在线程外部被强制终止的时候发射,所以当初把两个信号都跟同一个槽连接起来了,结果经常出现两次的槽调用。后来看了一下源码,发现finished信号无论线程如何结束都回发射,而terminated信号只是在线程被强制终止的时候才发射,希望我的理解是正确的。

sbtree 2010-08-12 17:14
目前对COM还是在探索中,对于深入理解COM还相去甚远,不当之处,还请多指点

sbtree 2010-08-12 22:25
突然想起一个问题,如果我在finished信号所连接的槽中调用CoUni,这意味着线程已经不再存在(这里不是指QThread对象本身,因为这时候对象还存在,但线程已经结束,当然也就不存在了),那我调用CoUni岂不是错误?因为这时候的调用已经是在主线程(槽所在的对象的生存空间)中了,如何才能让CoUni函数能在线程即将结束之前被调用呢?如果有一个aboutToQuit的信号就好了

nashinianqin 2011-06-14 09:48
CoInitialize和CoUnitialize 你们是怎么调用的啊,为什么我没法调用?????


查看完整版本: [-- 子线程中调用COM函数失败[已解决] --] [-- top --]



Powered by phpwind v8.7 Code ©2003-2011 phpwind
Gzip disabled