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,选择添加事件,这种方式就不赘述了。

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

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

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> public :
IWebBrowser2
* pWebBrowser; // IE浏览器
IHTMLDocument2 * pHTMLDocument; // 包含此控件的web页面

2重载OnSetClientSite函数。

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> void CMyActiveXCtrl::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;
goto cleanup;
}
hr
= isp -> QueryService(SID_STopLevelBrowser,IID_IServiceProvider,reinterpret_cast < void **> ( & isp2));
if (FAILED(hr))
{
hr
= S_OK;
goto cleanup;
}
hr
= isp2 -> QueryService(SID_SWebBrowserApp,IID_IWebBrowser2,reinterpret_cast < void **> ( & pWebBrowser)); // 查询IE浏览器接口
if (FAILED(hr))
{
hr
= S_OK;
goto cleanup;
}
hr
= pWebBrowser -> get_Document((IDispatch ** ) & pHTMLDocument); // 查询Web页面接口
if (FAILED(hr))
{
hr
= S_OK;
goto cleanup;
}
cleanup:
// Freeresources.
COMRELEASE(isp);
COMRELEASE(isp2);
}
}

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

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> bool CMyActiveXCtrl::GetJScript(CComPtr < IDispatch >& spDisp)
{
CHECK_POINTER(pHTMLDocument);
HRESULThr
= pHTMLDocument -> get_Script( & spDisp);
ATLASSERT(SUCCEEDED(hr));
return SUCCEEDED(hr);
}

bool CMyActiveXCtrl::GetJScripts(CComPtr < IHTMLElementCollection >& spColl)
{
CHECK_POINTER(pHTMLDocument);
HRESULThr
= pHTMLDocument -> get_scripts( & spColl);
ATLASSERT(SUCCEEDED(hr));
return SUCCEEDED(hr);
}

bool CMyActiveXCtrl::CallJScript( const CStringstrFunc,CComVariant * pVarResult)
{
CStringArrayparamArray;
return CallJScript(strFunc,paramArray,pVarResult);
}

bool CMyActiveXCtrl::CallJScript( const CStringstrFunc, const CStringstrArg1,CComVariant * pVarResult)
{
CStringArrayparamArray;
paramArray.Add(strArg1);
return CallJScript(strFunc,paramArray,pVarResult);
}

bool CMyActiveXCtrl::CallJScript( const CStringstrFunc, const CStringstrArg1, const CStringstrArg2,CComVariant * pVarResult)
{
CStringArrayparamArray;
paramArray.Add(strArg1);
paramArray.Add(strArg2);
return CallJScript(strFunc,paramArray,pVarResult);
}

bool CMyActiveXCtrl::CallJScript( const CStringstrFunc, const CStringstrArg1, const CStringstrArg2, const CStringstrArg3,CComVariant * pVarResult)
{
CStringArrayparamArray;
paramArray.Add(strArg1);
paramArray.Add(strArg2);
paramArray.Add(strArg3);
return CallJScript(strFunc,paramArray,pVarResult);
}

bool CMyActiveXCtrl::CallJScript( const CStringstrFunc, const CStringArray & paramArray,CComVariant * pVarResult)
{
CComPtr
< IDispatch > spScript;
if ( ! GetJScript(spScript))
{
// ShowError("CannotGetScript");
return false ;
}
CComBSTRbstrMember(strFunc);
DISPIDdispid
= NULL;
HRESULThr
= spScript -> GetIDsOfNames(IID_NULL, & bstrMember, 1 ,
LOCALE_SYSTEM_DEFAULT,
& dispid);
if (FAILED(hr))
{
// ShowError(GetSystemErrorMessage(hr));
return false ;
}
const int arraySize = paramArray.GetSize();
DISPPARAMSdispparams;
memset(
& dispparams, 0 , sizeof dispparams);
dispparams.cArgs
= arraySize;
dispparams.rgvarg
= new VARIANT[dispparams.cArgs];
for ( int i = 0 ;i < arraySize;i ++ )
{
CComBSTRbstr
= paramArray.GetAt(arraySize - 1 - i); // backreading
bstr.CopyTo( & dispparams.rgvarg[i].bstrVal);
dispparams.rgvarg[i].vt
= VT_BSTR;
}
dispparams.cNamedArgs
= 0 ;
EXCEPINFOexcepInfo;
memset(
& excepInfo, 0 , sizeof excepInfo);
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));
return false ;
}
if (pVarResult)
{
* pVarResult = vaResult;
}
return true ;
}

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

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> void CMyActiveXCtrl::LoadParameter( void )
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
m_OutputParameter
= m_InputParameter;
// Fireaneventtonotifywebpage
FireParameterLoaded();
CStringstrOnLoaded(
" OnLoaded " );
this -> CallJScript(strOnLoaded);
}

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

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> function OnLoaded()
{
alert(
" phinecos " );
}

多线程ActiveX控件

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

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

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

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> class ICallBack
{
public :
virtual void OnSuccesful() = 0 ; // 操作成功
virtual void OnFailed() = 0 ; // 操作失败
};

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

class CMyActiveXCtrl : public COleControl,public ICallBack

线程基类

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

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> class CMyThread
{
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 ;
return GetLastError();
}
/**//* *
*Stopsthethread.
*
*@parambForceKilliftrue,theThreadiskilledimmediately
*/
DWORDStop(
bool bForceKill = 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;
}
}
return m_dwExitCode;
}
/**//* *
*Stopsthethread.firsttellthethreadtostopitselfandwaitforthethreadtostopitself.
*iftimeoutoccursandthethreadhasn'tstoppedyet,thenthethreadiskilled.
*@paramtimeoutmillisecondstowaitforthethreadtostopitself
*/
DWORDStop(WORDtimeout)
{
Stop(
false );
WaitForSingleObject(m_hThread,timeout);
// 等待一段时间
return Stop( true );
}
/**//* *
*suspendsthethread.i.e.thethreadishaltedbutnotkilled.TostartasuspendedthreadcallResume().
*/
DWORDSuspend()
{
// 挂起线程
return SuspendThread(m_hThread);
}
/**//* *
*resumesthethread.thismethodstartsacreatedandsuspendedthreadagain.
*/
DWORDResume()
{
// 恢复线程
return ResumeThread(m_hThread);
}
/**//* *
*setsthepriorityofthethread.
*@paramprioritythepriority.seeSetThreadPriority()inwindowssdkforpossiblevalues.
*@returntrueifsuccessful
*/
BOOLSetPriority(
int priority)
{
// 设置线程优先级
return SetThreadPriority(m_hThread,priority);
}
/**//* *
*getsthecurrentpriorityvalueofthethread.
*@returnthecurrentpriorityvalue
*/
int GetPriority()
{
// 获取线程优先级
return GetThreadPriority(m_hThread);
}
void SetICallBack(ICallBack * pCallBack)
{
this -> pCallBack = pCallBack;
}
protected :
/* *
*子类应该重写此方法,这个方法是实际的工作线程函数
*/
virtual DWORDThreadMethod() = 0 ;
private :

/**//* *
*DONToverridethismethod.
*
*thismethodisthe"function"usedwhencreatingthethread.itisstaticsothatway
*apointertoitisavailableinsidetheclass.thismethodcallsthenthevirtual
*methodoftheparentclass.
*/
static DWORDWINAPIEntryPoint(LPVOIDpArg)
{
CMyThread
* pParent = reinterpret_cast < CMyThread *> (pArg);
pParent
-> ThreadMethod(); // 多态性,调用子类的实际工作函数
return 0 ;
}
private :
HANDLEm_hThread;
// 线程句柄
DWORDm_dwTID; // 线程ID
LPVOIDm_pParent; // thispointeroftheparentCThreadobject
DWORDm_dwExitCode; // 线程退出码
protected :
LPTHREAD_START_ROUTINEm_pThreadFunction;
// 工作线程指针
BOOLm_runthread; // 线程是否继续运行的标志
ICallBack * pCallBack; // 主线程的回调函数接口
};

具体的工作线程子类

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

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> class CMyTaskThread: public CMyThread

DWORDCMyTaskThread::ThreadMethod()
{
while (m_runthread)
{
this -> pCallBack -> OnSuccesful(); // 模拟操作成功,回调主线程
Sleep( 5000 ); // 休息会再模拟
}
return 0 ;
}

回调函数

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

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> void CMyActiveXCtrl::OnSuccesful()
{
// 操作成功
// FireParameterLoaded();
CStringstrOnLoaded( " OnLoaded " );
this -> CallJScript(strOnLoaded);

}

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

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

作者:phinecos(洞庭散人)
出处:http://phinecos.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但请保留此段声明,并在文章页面明显位置给出原文连接。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值