1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > COM组件开发实践(七)---多线程ActiveX控件和自动调整ActiveX控件大小(上)

COM组件开发实践(七)---多线程ActiveX控件和自动调整ActiveX控件大小(上)

时间:2018-12-17 02:50:52

相关推荐

COM组件开发实践(七)---多线程ActiveX控件和自动调整ActiveX控件大小(上)

声明:本文代码基于CodeProject的文章《A Complete ActiveX Web Control Tutorial》修改而来,因此同样遵循Code Project Open License (CPOL)。

最近遇到两个需求:1)在ActiveX控件中使用工作线程来完成底层的硬件设备扫描任务,并在工作线程中根据操作结果回调外部web页面的JavaScript函数;2)能根据控件任务的不同自动调整控件大小。但在查阅了大量资料后,发现网上讨论ActiveX中多线程开发的文章基本没有,最后在csdn论坛里遇到一个高手帮忙后,摸索了几天才解决这两个问题,本文的目的就在于记录下我解决这两个问题的过程,也希望能帮助到以后有同样需求的朋友。

简单抽象下第一个任务的模型:在AcitveX控件中开启一个工作线程去执行特点任务后,然后根据工作线程的执行结果中去通知外部的web页面的JavaScript。在进入到多线程之前,先来介绍下ActiveX中调用外部web页面的JavaScript函数的两种方式。

ActiveX中调用JavaScript

第一种方式是使用事件,这是最简单方法。在“类视图”中,右键CMyActiveXCtrl,选择“添加事件”,这种方式就不赘述了。

第二种方式是利用IWebBrowser2和IHTMLDocument2这两个COM组件来访问包含ActiveX控件的外部Web页面上的所有元素。具体实现步骤如下:

1,在CMyActiveXCtrl类中加入两个变量:

public:

IWebBrowser2*pWebBrowser;//IE浏览器

IHTMLDocument2*pHTMLDocument;//包含此控件的web页面

2,重载OnSetClientSite函数。

voidCMyActiveXCtrl::OnSetClientSite()

{

HRESULThr=S_OK;

IServiceProvider*isp,*isp2=NULL;

if(!m_pClientSite)

{

COMRELEASE(pWebBrowser);

}

else

{

hr=m_pClientSite->QueryInterface(IID_IServiceProvider,reinterpret_cast<void**>(&isp));

if(FAILED(hr))

{

hr=S_OK;

gotocleanup;

}

hr=isp->QueryService(SID_STopLevelBrowser,IID_IServiceProvider,reinterpret_cast<void**>(&isp2));

if(FAILED(hr))

{

hr=S_OK;

gotocleanup;

}

hr=isp2->QueryService(SID_SWebBrowserApp,IID_IWebBrowser2,reinterpret_cast<void**>(&pWebBrowser));//查询IE浏览器接口

if(FAILED(hr))

{

hr=S_OK;

gotocleanup;

}

hr=pWebBrowser->get_Document((IDispatch**)&pHTMLDocument);//查询Web页面接口

if(FAILED(hr))

{

hr=S_OK;

gotocleanup;

}

cleanup:

//Freeresources.

COMRELEASE(isp);

COMRELEASE(isp2);

}

}

3,控件在加载后会调用OnSetClientSite函数的,因此就会查询到对应包含控件的Web页面,有了这个页面后,就可以使用下述函数来调用Web页面中的JavaScript函数了。下述代码来自CodeGuru 的文章《JavaScript Calls from C++》,感兴趣的话可以细读。

boolCMyActiveXCtrl::GetJScript(CComPtr<IDispatch>&spDisp)

{

CHECK_POINTER(pHTMLDocument);

HRESULThr=pHTMLDocument->get_Script(&spDisp);

ATLASSERT(SUCCEEDED(hr));

returnSUCCEEDED(hr);

}

boolCMyActiveXCtrl::GetJScripts(CComPtr<IHTMLElementCollection>&spColl)

{

CHECK_POINTER(pHTMLDocument);

HRESULThr=pHTMLDocument->get_scripts(&spColl);

ATLASSERT(SUCCEEDED(hr));

returnSUCCEEDED(hr);

}

boolCMyActiveXCtrl::CallJScript(constCStringstrFunc,CComVariant*pVarResult)

{

CStringArrayparamArray;

returnCallJScript(strFunc,paramArray,pVarResult);

}

boolCMyActiveXCtrl::CallJScript(constCStringstrFunc,constCStringstrArg1,CComVariant*pVarResult)

{

CStringArrayparamArray;

paramArray.Add(strArg1);

returnCallJScript(strFunc,paramArray,pVarResult);

}

boolCMyActiveXCtrl::CallJScript(constCStringstrFunc,constCStringstrArg1,constCStringstrArg2,CComVariant*pVarResult)

{

CStringArrayparamArray;

paramArray.Add(strArg1);

paramArray.Add(strArg2);

returnCallJScript(strFunc,paramArray,pVarResult);

}

boolCMyActiveXCtrl::CallJScript(constCStringstrFunc,constCStringstrArg1,constCStringstrArg2,constCStringstrArg3,CComVariant*pVarResult)

{

CStringArrayparamArray;

paramArray.Add(strArg1);

paramArray.Add(strArg2);

paramArray.Add(strArg3);

returnCallJScript(strFunc,paramArray,pVarResult);

}

boolCMyActiveXCtrl::CallJScript(constCStringstrFunc,constCStringArray&paramArray,CComVariant*pVarResult)

{

CComPtr<IDispatch>spScript;

if(!GetJScript(spScript))

{

//ShowError("CannotGetScript");

returnfalse;

}

CComBSTRbstrMember(strFunc);

DISPIDdispid=NULL;

HRESULThr=spScript->GetIDsOfNames(IID_NULL,&bstrMember,1,

LOCALE_SYSTEM_DEFAULT,&dispid);

if(FAILED(hr))

{

//ShowError(GetSystemErrorMessage(hr));

returnfalse;

}

constintarraySize=paramArray.GetSize();

DISPPARAMSdispparams;

memset(&dispparams,0,sizeofdispparams);

dispparams.cArgs=arraySize;

dispparams.rgvarg=newVARIANT[dispparams.cArgs];

for(inti=0;i<arraySize;i++)

{

CComBSTRbstr=paramArray.GetAt(arraySize-1-i);//backreading

bstr.CopyTo(&dispparams.rgvarg[i].bstrVal);

dispparams.rgvarg[i].vt=VT_BSTR;

}

amedArgs=0;

EXCEPINFOexcepInfo;

memset(&excepInfo,0,sizeofexcepInfo);

CComVariantvaResult;

UINTnArgErr=(UINT)-1;//initializetoinvalidarg

hr=spScript->Invoke(dispid,IID_NULL,0,

DISPATCH_METHOD,&dispparams,&vaResult,&excepInfo,&nArgErr);

delete[]dispparams.rgvarg;

if(FAILED(hr))

{

//ShowError(GetSystemErrorMessage(hr));

returnfalse;

}

if(pVarResult)

{

*pVarResult=vaResult;

}

returntrue;

}

4,现在就可以来测试上述两种调用JavaScript函数的方式了,为了简单起见,就在原文代码的基础上修改了下。

voidCMyActiveXCtrl::LoadParameter(void)

{

AFX_MANAGE_STATE(AfxGetStaticModuleState());

m_OutputParameter=m_InputParameter;

//Fireaneventtonotifywebpage

FireParameterLoaded();

CStringstrOnLoaded("OnLoaded");

this->CallJScript(strOnLoaded);

}

并且在web页面中加入了一个测试用的JavaScript函数

functionOnLoaded()

{

alert("phinecos");

}

多线程ActiveX控件

有了上面调用JavaScript函数的基础,现在就来为控件加入工作线程,然后在线程中根据任务执行结果来通知外部Web页面做出应有的响应。

我的第一个思路就是在主线程中设置回调函数,等创建子线程时,让子线程保存主线程的指针,然后在线程执行过程中根据执行的结果去回调主线程的回调函数。这种思路看上去很不错,就先按这步走。

首先创建一个回调函数接口,指明主线程应有的回调函数

classICallBack

{

public:

virtualvoidOnSuccesful()=0;//操作成功

virtualvoidOnFailed()=0;//操作失败

};

然后让CMyActiveXCtrl控件类继承自这个虚基类,实现这些回调函数接口

class CMyActiveXCtrl : public COleControl,public ICallBack

线程基类

为了处理线程方便,本文使用了CodeProject上《TrafficWatcher》这篇文章中的一个CThread类,稍作修改得到下面的CMyThread类,就是在其中加入了ICallBack* pCallBack这个主线程的回调函数接口。

classCMyThread

{

public:

CMyThread()

{

m_pThreadFunction=CMyThread::EntryPoint;

m_runthread=FALSE;

}

virtual~CMyThread()

{

if(m_hThread)

Stop(true);//threadstillrunning,soforcethethreadtostop!

}

DWORDStart(DWORDdwCreationFlags=0)

{

m_runthread=true;

m_hThread=CreateThread(NULL,0,m_pThreadFunction,this,dwCreationFlags,&m_dwTID);

m_dwExitCode=(DWORD)-1;

returnGetLastError();

}

/**//**

*Stopsthethread.

*

*@parambForceKilliftrue,theThreadiskilledimmediately

*/

DWORDStop(boolbForceKill=false)

{

if(m_hThread)

{

//尝试"温柔地"结束线程

if(m_runthread==TRUE)

m_runthread=FALSE;//first,trytostopthethreadnice

GetExitCodeThread(m_hThread,&m_dwExitCode);

if(m_dwExitCode==STILL_ACTIVE&&bForceKill)

{//强制杀死线程

TerminateThread(m_hThread,DWORD(-1));

m_hThread=NULL;

}

}

returnm_dwExitCode;

}

/**//**

*Stopsthethread.firsttellthethreadtostopitselfandwaitforthethreadtostopitself.

*iftimeoutoccursandthethreadhasn'tstoppedyet,thenthethreadiskilled.

*@paramtimeoutmillisecondstowaitforthethreadtostopitself

*/

DWORDStop(WORDtimeout)

{

Stop(false);

WaitForSingleObject(m_hThread,timeout);//等待一段时间

returnStop(true);

}

/**//**

*suspendsthethread.i.e.thethreadishaltedbutnotkilled.TostartasuspendedthreadcallResume().

*/

DWORDSuspend()

{//挂起线程

returnSuspendThread(m_hThread);

}

/**//**

*resumesthethread.thismethodstartsacreatedandsuspendedthreadagain.

*/

DWORDResume()

{//恢复线程

returnResumeThread(m_hThread);

}

/**//**

*setsthepriorityofthethread.

*@paramprioritythepriority.seeSetThreadPriority()inwindowssdkforpossiblevalues.

*@returntrueifsuccessful

*/

BOOLSetPriority(intpriority)

{//设置线程优先级

returnSetThreadPriority(m_hThread,priority);

}

/**//**

*getsthecurrentpriorityvalueofthethread.

*@returnthecurrentpriorityvalue

*/

intGetPriority()

{//获取线程优先级

returnGetThreadPriority(m_hThread);

}

voidSetICallBack(ICallBack*pCallBack)

{

this->pCallBack=pCallBack;

}

protected:

/**

*子类应该重写此方法,这个方法是实际的工作线程函数

*/

virtualDWORDThreadMethod()=0;

private:

/**//**

*DONToverridethismethod.

*

*thismethodisthe"function"usedwhencreatingthethread.itisstaticsothatway

*apointertoitisavailableinsidetheclass.thismethodcallsthenthevirtual

*methodoftheparentclass.

*/

staticDWORDWINAPIEntryPoint(LPVOIDpArg)

{

CMyThread*pParent=reinterpret_cast<CMyThread*>(pArg);

pParent->ThreadMethod();//多态性,调用子类的实际工作函数

return0;

}

private:

HANDLEm_hThread;//线程句柄

DWORDm_dwTID;//线程ID

LPVOIDm_pParent;//thispointeroftheparentCThreadobject

DWORDm_dwExitCode;//线程退出码

protected:

LPTHREAD_START_ROUTINEm_pThreadFunction;//工作线程指针

BOOLm_runthread;//线程是否继续运行的标志

ICallBack*pCallBack;//主线程的回调函数接口

};

具体的工作线程子类

具体的工作线程子类只需要从CMyThread继承下去,重载ThreadMethod方法即可,为了简单起见,下面就只模拟了操作设备成功的情况,当然可以根据实际应用记入具体操作代码。

classCMyTaskThread:publicCMyThread

DWORDCMyTaskThread::ThreadMethod()

{

while(m_runthread)

{

this->pCallBack->OnSuccesful();//模拟操作成功,回调主线程

Sleep(5000);//休息会再模拟

}

return0;

}

回调函数

按照最明显的思路,结合第一部分的知识,很显然回调函数应该是下面这样,选择事件或直接调用外部的JavaScript函数来通知外部web页面响应。

voidCMyActiveXCtrl::OnSuccesful()

{//操作成功

//FireParameterLoaded();

CStringstrOnLoaded("OnLoaded");

this->CallJScript(strOnLoaded);

}

但不幸的是,这样做根本无效,外部的Web页面无法收到响应,就这个问题折腾了好几天,思路上看好像没什么错呀,怎么就回调不了呢?。。。

那么正确的做法应该是怎样的呢?限于本文篇幅,将在下一篇中给出解答,并放出完整源代码。

作者:phinecos(洞庭散人)

出处:/

本文版权归作者和博客园共有,欢迎转载,但请保留此段声明,并在文章页面明显位置给出原文连接。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。