本文代码基于上篇《ATL COM开发入门(一)(JS调用ActiveX/COM组件)》。
完成的需求:后台ATL代码回调前台js函数,后台ATL启动定时器完成前台计数。截图如下:
分析:完成此需求有两个难点,一是如何回调前台js代码?二是ATL是无窗口的如何使用定时器(MFC一般的定时器SetTimer、OnTimer都是CWnd的成员)?
MSDN提供了ATL直接获取前台IHTMLDocument2对象的方法,但是写得很不够详细,详见《获得ActiveX控件所在网页的对象模型》,不过笔者没有调试成功,有兴趣的读者可以深入研究下。这里笔者提供另外一种更加简便的方式:前台传入浏览器窗口对象的IDispatch指针(js中this便是),然后通过它就可以在ATL中任意获取网页元素,进行Invoke调用。
定时器采用Win32 API的SetTimer,在回调函数中回调前台js函数,显示计数。
一、ATLDemo.idl 中添加一个新的接口:BeginTiming函数体:
STDMETHODIMP CAtlDemoIf::BeginTiming(IDispatch* pIDispatch)
{
gIDispatch = pIDispatch;
UINT nRet = SetTimer(NULL, // handle to main window
ID_TIMER, // 定时器标识
1000, // 1 秒间隔
(TIMERPROC)TimerProc); // 回调函数
return S_OK;
}
TimerProc回调函数:
IDispatch* gIDispatch = NULL;
#define ID_TIMER 1
VOID CALLBACK TimerProc(
HWND hwnd, // 定时器消息的窗口句柄
UINT message, // WM_TIMER 消息
INT_PTR idTimer, // 定时器标志
DWORD dwTime) // 当前系统启动计时
{
if (!gIDispatch)
return;
CComQIPtr<IHTMLWindow2> spHtmlWin;
CComQIPtr<IHTMLDocument2> spHtmlDoc2;
gIDispatch->QueryInterface(IID_IHTMLWindow2, (void **)&spHtmlWin);
spHtmlWin->get_document(&spHtmlDoc2);
DISPPARAMS param = {0};
VARIANT vtRet;
CallJSFunction(spHtmlDoc2, _T("TimingCallbackFunc"), param, &vtRet, NULL, NULL);
}
CallJSFunction:
HRESULT CallJSFunction(IHTMLDocument2* pDoc2,
CString strFunctionName,
DISPPARAMS dispParams,
VARIANT* varResult,
EXCEPINFO* exceptInfo,
UINT* nArgErr )
{
IDispatch *pDispScript = NULL;
HRESULT hResult;
hResult = pDoc2->get_Script(&pDispScript);
if(FAILED(hResult))
{
return S_FALSE;
}
DISPID dispid;
CComBSTR objbstrValue = strFunctionName;
BSTR bstrValue = objbstrValue.Copy();
OLECHAR *pszFunct = bstrValue ;
hResult = pDispScript->GetIDsOfNames(IID_NULL,
&pszFunct,
1,
LOCALE_SYSTEM_DEFAULT,
&dispid);
if (FAILED(hResult))
{
pDispScript->Release();
return hResult;
}
varResult->vt = VT_VARIANT;
hResult = pDispScript->Invoke(dispid,
IID_NULL, LOCALE_USER_DEFAULT,
DISPATCH_METHOD,
&dispParams,
varResult,
exceptInfo,
nArgErr);
pDispScript->Release();
return hResult;
}
二、前台代码:
<HTML>
<HEAD>
<TITLE>COM接口测试页</TITLE>
<script type="text/javascript">
//...
function BeginTiming(){
AtlDemoObj.BeginTiming(this);
}
function TimingCallbackFunc(){
var obj = document.getElementById('timing');
var num = parseInt(obj.innerText);
obj.innerText = ++num;
}
</script>
</HEAD>
<BODY>
<object id="AtlDemoObj" classid="clsid:B0DA2962-C4C3-48CD-BFBC-4F43F9D03C56" width=0 height=0></object>
...
<input type="button" value="Begin Timing " onclick = "javascript:BeginTiming();"/>
<span id="timing">0</span>
</BODY>
</HTML>
拓展:ATL中回调js函数并传参的例子,参见《ATL创建的ActiveX(COM组件)实现JS回调》。
源码点此下载,如有问题请点此</</</a>a>a>。