日期:2014-05-16 浏览次数:20361 次
项目中经常碰到JS与Com组件交互的问题,通过查找网络资料和项目实际需要,总结如下:
JS中定义函数
function printMsg(msg1, msg2, msg3, msg4, msg5) { alert(msg1); alert(msg2); alert(msg3); alert(msg4); alert(msg5); }
Com组件中定义函数接口,以便将JS中定义的函数作为参数传递给Com组件
头文件中定义IDispatch接口:
static CComPtr<IDispatch> m_spCallback;
STDMETHODIMP CJSCallBack::SetJsCallbackFunc(VARIANT scriptCallback) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); // TODO: 在此添加实现代码 if (scriptCallback.vt == VT_DISPATCH) { m_spCallback = scriptCallback.pdispVal; } return S_OK; }
在JS中调用SetJsCallbackFunc接口注册回调函数
var obj = new ActiveXObject("ComForJS.JSCallBack.1");
result = obj.SetJsCallbackFunc(printMsg);
Com组件中调用JS函数:
void CJSCallBack::CallJsFunction() { CComVariant avarParams[5]; avarParams[0] = “AAA”; //指定回调函数的参数 avarParams[1] = "BBB"; avarParams[2] = "CCC"; avarParams[3] = "DDD"; avarParams[4] = "EEE"; DISPPARAMS params = { avarParams, NULL, 5, 0 }; if(m_spCallback) { HRESULT hr = m_spCallback->Invoke(0, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, ¶ms, NULL, NULL, NULL); } }
几点注意的地方:
1,DISPPARAMS params = { avarParams, NULL, 5, 0 },中的第三个参数将决定要传递到JS函数中的参数的个数
2,在JS函数中参数的顺序与Com组件中赋值的顺序是反的,根据本例代码,参数数组中第一个是"AAA",最后一个是"EEE",但是实际在JS代码中,msg1的值是"EEE",msg5的值是"AAA"
调用过程:
obj.Print("AAA",0);
STDMETHODIMP CJSCallBack::Print(BSTR bstrToPrint, LONG lWait) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); CallJsFunction(); return S_OK; }
可以发现,以上调用是单线程调用,那么,如果在Com组件中,需要多线程操作,并且要在别的线程完成一定任务之后才需要调用JS函数,而在子线程是没有办法直接调用在主线程中注册的JS函数的,又该如何处理呢?
针对这个问题,查阅了大量资料,基本上都是关于IDispatch接口在多线程中使用需要列集散集等概念,可能理解能力较差,最终也没能够通过这样的方式来解决实际项目中的问题,后来联想到以前项目中曾经用到过的消息窗口,于是决定尝试一番:
定义MsgWnd类:
class CJSCallBack;//接口类 class MsgWnd : public CWnd { public: MsgWnd() {} MsgWnd( CJSCallBack* p ); ~MsgWnd() {} protected: afx_msg LRESULT OnRecvMsg(WPARAM wParam, LPARAM lParam); DECLARE_MESSAGE_MAP() private: MsgWnd(const MsgWnd&); MsgWnd& operator=(const MsgWnd&); CJSCallBack* m_pParent; };
为MsgWnd类实现构造函数以及消息处理函数,并设置MessageMap
#define WM_COMM WM_USER+200 BEGIN_MESSAGE_MAP(MsgWnd, CWnd) ON_MESSAGE(WM_COMM, OnRecvMsg) END_MESSAGE_MAP() LRESULT MsgWnd::OnRecvMsg(WPARAM wPar, LPARAM lPar) { if ( NULL != m_pParent ) { m_pParent->CallJsFunction();//调用Parent,也就是接口类中的函数 } return 1; } MsgWnd::MsgWnd( CJSCallBack* p ) { if ( NULL != p ) { m_pParent = p; } }
在接口类中声明消息窗口:
MsgWnd* m_pMsgWnd;
声明线程函数:
static UINT CallJspFunctionThread(LPVOID param);
定义线程函数:
UINT CJSCallBack::CallJspFunctionThread(LPVOID param) { CJSCallBack* pCallBack = (CJSCallBack*)param; while(m_bRunThread) { while(m_bPrint) { MsgWnd* pMsgWnd = pCallBack->GetMsgWnd(); if ( NULL != pMsgWnd ) { pMsgWnd->PostMessage( WM_COMM ); } } sleep(20000); } return 0; }
创建消息窗口并启动线程函数:
STDMETHODIMP CJSCallBack::Initialize(void) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); // TODO: 在此添加实现代码 if ( NULL == m_pMsgWnd ) { m_pMsgWnd = new MsgWnd( this ); m_pMsgWnd->CreateEx(WS_EX_CLI