描述:
一个COM组件,导出的一个函数Add需要长时间执行,如下所示:
STDMETHODIMP CSimpObj::Add(short param1, short param2, long *plResult)
{
if(!plResult)return E_POINTER;
*plResult = param2 + param1;
for(int i=0;i<30000;i++)
{
Sleep(1);
MSG msg;
while(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
DispatchMessage (&msg);
}
}
Fire_CalculateComplete( *plResult );
return S_OK;
}
网页中的代码如下:
<script type="text/javascript" for="simpobj" event="CalculateComplete(lResult)" language="javascript">
alert("计算结果是:" + lResult);
</script>
将该组件嵌入网页执行时,导致网页上的其它控件响应不灵敏(但是可以响应)。另外更严重的是,在该函数执行时网页无法跳转,在地址栏输入地址然后回车、使用javascript的location.href="www.baidu.com"都不行,要直到alert("计算结果是:" + lResult);代码执行完此对话框关闭之后就唰的一下跳转了。
必须在COM组件中使用工作线程吗?有没有其它的解决方法?谢谢!
解决方案1:
你通过GIT,做了marshal了,获得指针是proxy/stub,会对变量做线程的同步...
解决方案2: 没说要你把控件改成有窗口的控件,创建一个隐藏窗口即可,无窗口控件也能创建窗口。
工作线程是能触发事件,但不能直接调用Fire_XXX,也不能把接口指针作为线程函数的参数传递进来,因为指针需要列集。无论工作线程使用哪种方式进行成功的触发事件,核心都是隐藏窗口在工作,还不如自己直接创建隐藏窗口。
IE内部有保护,当代码执行到ActiveX或者BHO中时,很多操作都被限制或者进入延迟执行队列,必须返回到IE中才能执行队列缓存的操作,不能导航就是一个例子,例如脚本执行window.open、location.navigate等操作都属于延迟操作。
在网页控件中使用多线程要非常小心,因为任何页面导航的发生将导致控件的生命期结束。一个组件的方法都应该尽快返回,如果操作耗时,那么应该使操作进入异步模式,使用工作线程或者使用定时器都可以选择,个人倾向于使用定时器,创建一个隐藏窗口处理定时器消息,一旦操作完成,可以直接调用Fire_***触发事件,如果操作还未完成而页面发生了导航,那么ActiveX控件会被释放,此时应该马上销毁窗口,停止定时器任务。
这种情况工作就应该交给另一个线程了,本身就是计算,自然要放到另一个,而不是当前的UI线程等来阻塞UI...
解决方案5:就用线程加链接点实现就可以,除了你线程应该没有其他合适的方法,你就是想要计算和界面响应的并行工作,在整个Windows平台做并行都是靠多线程支持的,即使是其他方法我估计本质也是Multithread。呵呵
解决方案6:在这种情况下,不知道还能不能收到 OnBeforeNavigate2 ,如果可以的话可以让消息循环跳出。不过最好还是工作线程来解决。
解决方案7:你为什么不把这段代码丢到工作线程中执行呢?