日期:2014-05-16  浏览次数:20351 次

Com组件调用JS代码并传递多个参数个JS函数

项目中经常碰到JS与Com组件交互的问题,通过查找网络资料和项目实际需要,总结如下:

一、Com组件简单回调JS代码

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, 
			&params, 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函数的,又该如何处理呢?

 

二、Com组件中,多线程调用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