多线程activex控件回调javascript方法的问题

xiaoyisnail 2007-02-12 08:10:03
我写了一个控件,利用wininet模拟http client,由于效率问题,打算利用多线程来提高效率,由于是通过javascript来调用activex接口的所以要等线程完成回调js方法来讲http response返回给js,但Invoke方法执行了但从页面上看却没有执行js的方法,我后来试着把Invoke方法移到了重写的SetSite接口里,js方法就执行了,请问高手这是为什么,难道只能在覆写的com接口里才能调用javascript方法?
...全文
1559 20 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
zgcbj 2012-03-30
  • 打赏
  • 举报
回复
学习了
My_lolo 2008-12-25
  • 打赏
  • 举报
回复
太深入了,以后多学习学习,请多指点指点
大龄代码农民 2008-07-21
  • 打赏
  • 举报
回复
学习了很多东西 谢谢
xiaoyisnail 2007-02-18
  • 打赏
  • 举报
回复
谢谢,过年忘了来给分,谢谢两位了,我的问题解决了
seasol 2007-02-14
  • 打赏
  • 举报
回复
一种简单的方法:
1、将CComPtr<IStream> pStream;改为IStream* pStream即可;因为CoGetInterfaceAndReleaseStream会自动释放接口;

比较好的方法:
2、
在SetSite中,只获取并保存CComPtr<IWebBrowser2> 指针 即可。
因为一个Web窗口,CComPtr<IWebBrowser2> 指针是不变的,而IHTMLDocument2指针是会变化的(比如刷新页面时,比如Navigate到其他URL时。)
所以,动态取得m_spDoc和pStream,就可以及时地释放。

另外,pStream 可以放入线程参数ThreadParam进行传递。只要方案定下来了,没有实现不了的,试试看吧。


xiaoyisnail 2007-02-14
  • 打赏
  • 举报
回复
现在只有pStream是控件的成员变量,但如果不把pStream作为成员变量的话我就无法把pStream传给线程方法了,因为我是在SetSite里获得IHTMLDocument2的,但线程是在另一个控件暴露的接口中启动的
现在我的线程函数是这样的:
void PostThread(void *pParam)
{
::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
ThreadParam *param = (ThreadParam*)pParam;
//这里调用控件接口来回调JS方法
::CoUninitialize();

return;
}
现在能正常的回调页面中的js方法,一切都正常,但当我关闭页面时就报错了
调用堆栈为:
> TaleV.dll!ATL::CComPtrBase<IStream>::~CComPtrBase<IStream>()
TaleV.dll!ATL::CComPtr<IStream>::~CComPtr<IStream>()
TaleV.dll!CXIPost::~CXIPost()
vs2005显示错误行为:
~CComPtrBase() throw()
{
if (p)
-> p->Release();
}
从上面看是页面关闭,控件析构时在释放CComPtr<IStream> pStream时出错了,但COM的CComPtr智能指针应该不需要我手动释放的吧(不是很懂),我网上查了些其他的资料,看到有人说去掉::CoUninitialize();,但我试着做了还是不行,也试过在CoInitializeEx和CoUninitialize()之间用花括号把代码括起来,可也不行,我实在没办法了,还请jiangsheng和seasol再帮下忙,谢谢了
蒋晟 2007-02-13
  • 打赏
  • 举报
回复
为什么要用CoMarshalInterface的原因可以看http://vcfaq.mvps.org/com/1.htm
蒋晟 2007-02-13
  • 打赏
  • 举报
回复
在你对IOleObject::Close的实现里面释放资源
xiaoyisnail 2007-02-13
  • 打赏
  • 举报
回复
jiangsheng的文章看了,但似乎对我的问题帮助不大,seasol的话可能就是问题所在,但我没理解你说的"在两个线程之间进行调度"的具体意思,惭愧
seasol 2007-02-13
  • 打赏
  • 举报
回复

建议你m_spDoc和pStream都不要作为控件的成员变量,什么时候用,什么时候获取就行了。
这样就能及时地释放接口。
seasol 2007-02-13
  • 打赏
  • 举报
回复

线程中不能直接调用m_spDoc。你需要在两个线程之间进行调度。
参考:CoMarshalInterface/CoUnmarshalInterface。不麻烦,试着做就行了。
蒋晟 2007-02-13
  • 打赏
  • 举报
回复
http://vcfaq.mvps.org/com/11.htm
xiaoyisnail 2007-02-13
  • 打赏
  • 举报
回复
终于搞定了,原因还是在于我对com的线程模型不太了解,错误的使用了CoInitializeEx(NULL, COINIT_MULTITHREADED);改成CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);后就行了

不过现在有另一个问提,现在CComPtr<IStream> pStream还是作为控件的成员变量的,我不知道如何去释放它,在关闭浏览器的时候报错了,位置是ATL::CComPtr<IStream>::~CComPtr<IStream>(),应该是pStream没有合理的释放吧,不知道这怎么解决
xiaoyisnail 2007-02-13
  • 打赏
  • 举报
回复
dispid为-1是DISPID_UNKNOWN,说明没找到我到的东西,是不是我获得js脚本函数的方法错了?
xiaoyisnail 2007-02-13
  • 打赏
  • 举报
回复
to seasol:
按照你说的改了一下,还是不行
CComPtr<IHTMLDocument2> spDoc;
::CoGetInterfaceAndReleaseStream(pStream, IID_IHTMLDocument2, (void**)&spDoc);
CComPtr<IDispatch> spDisp;
m_spDoc->get_Script(&spDisp);
跟了一下发现上面的语句执行完后spDisp为NULL
把上面两行改写为:
IDispatch* spDisp = NULL;
m_spDoc->QueryInterface(IID_IDispatch, (void**)&spDisp);
重新跟了一下,发现spDisp得到了,但跟到以下两行时
CComBSTR bstrMember("test");
DISPID dispid;
spDisp->GetIDsOfNames(IID_NULL,&bstrMember,1,LOCALE_SYSTEM_DEFAULT,&dispid);
发现dispid的值为-1,应该不对吧

由于_beginthread不是在SetSite里调的,所以pStream如果不作为控件的成员变量的话就无法传给线程了,所以pStream我还是写成成员变量的
seasol 2007-02-13
  • 打赏
  • 举报
回复

关键的错误:
不能直接定义接口指针的指针,如IStream** m_pStream ,或CComPtr<IStream> * m_pStream;这没有为返回的接口指针预留空间。


////////////
你可以这样:
CComPtr<IStream> pStream;
::CoMarshalInterThreadInterfaceInStream(IID_IHTMLDocument2, m_spDoc, &pStream);


pStream不必作为控件的成员变量,而是通过线程参数传递过去。

在ThreadStart里加入:
CComPtr<IHTMLDocument2> spDoc;
::CoGetInterfaceAndReleaseStream(pStream, IID_IHTMLDocument2, (void**)&spDoc);
xiaoyisnail 2007-02-13
  • 打赏
  • 举报
回复
上面的m_pStream是新添的控件的成员变量
xiaoyisnail 2007-02-13
  • 打赏
  • 举报
回复
我现在知道使用CoMarshalInterface/CoUnmarshalInterface的原因了,但是改了代码后还是不行,不知道哪里不对,由于我是第一次接触COM,对COM的线程模型更是只之甚少,所以希望高手帮忙了
修改的代码:
SetSite中加入:
CComPtr<IHTMLDocument2> m_spDoc; //m_spDoc不再作为控件的成员变量
pBro->get_Document((IDispatch**)&m_spDoc);
::CoMarshalInterThreadInterfaceInStream(IID_IHTMLDocument2, m_spDoc, m_pStream);

在ThreadStart里加入:
CComPtr<IHTMLDocument2> spDoc;
::CoGetInterfaceAndReleaseStream(*m_pStream, IID_IHTMLDocument2, (void**)&spDoc);
CComPtr<IDispatch> spDisp;
m_spDoc->get_Script(&spDisp);
...以下不变
xiaoyisnail 2007-02-12
  • 打赏
  • 举报
回复
问题就是ThreadStart里调用Invoke从结果看并没有调用javascript函数,debug的结果是ThreadStart里的代码都执行了
如果把ThreadStart里的那段代码直接放在SetSite里就能调用js方法,所以我在怀疑是不是不能像我这样在新开的线程里调js方法
xiaoyisnail 2007-02-12
  • 打赏
  • 举报
回复
代码的大致框架如下:
STDMETHODIMP AXClass::SetSite(IUnknown *pUnkSite)
{
CComPtr<IServiceProvider> pSP;
HRESULT hr = pUnkSite->QueryInterface(&pSP);
CComPtr<IWebBrowser2> pBro;
pSP->QueryService(IID_IWebBrowserApp, IID_IWebBrowser2, (LPVOID*)&pBro);
pBro->get_Document((IDispatch**)&m_spDoc);//m_spDoc是控件的成员变量
}

STDMETHODIMP AXClass::SendRequest(name,...)//name由js传入,是请求完毕后要调用的js方法
{
...
_beginthread(ThreadStart, ...); //这里新建一个线程,ThreadStart是线程入口函数
...
}

void ThreadStart(name, ...)
{
//发送请求并得到返回数据responseData
...
CComPtr<IDispatch> spDisp;
m_spDoc->get_Script(&spDisp);

CComBSTR bstrMember(name);
DISPID dispid;
spDisp->GetIDsOfNames(IID_NULL,&bstrMember,1,LOCALE_SYSTEM_DEFAULT,&dispid);
DISPPARAMS dispparams;
memset(&dispparams, 0, sizeof dispparams);
dispparams.cArgs = 1;
dispparams.rgvarg = new VARIANT[dispparams.cArgs];
CComBSTR bstr = responseData; //responseData是http响应数据
bstr.CopyTo(&dispparams.rgvarg[0].bstrVal);
dispparams.rgvarg[i].vt = VT_BSTR;
dispparams.cNamedArgs = 0;
EXCEPINFO excepInfo;
memset(&excepInfo, 0, sizeof excepInfo);
CComVariant vaResult;
UINT nArgErr = (UINT)-1;
spDisp->Invoke(dispid,IID_NULL,0,DISPATCH_METHOD,&dispparams,&vaResult,&excepInfo,&nArgErr);
//线程结束
}

3,248

社区成员

发帖
与我相关
我的任务
社区描述
ATL,Active Template Library活动(动态)模板库,是一种微软程序库,支持利用C++语言编写ASP代码以及其它ActiveX程序。
社区管理员
  • ATL/ActiveX/COM社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧