现在的位置: 首页 > 综合 > 正文

ATL回调JavaScript

2013年12月07日 ⁄ 综合 ⁄ 共 5100字 ⁄ 字号 评论关闭

测试环境:WinXPSP3      VS2008SP1    IE6   IE8

     未验证,[1]这种方法拿出来的Script可不可以被不同的支线线程使用,还是第一次被哪根线程拿的,就只能这根线程用了。

[2]要不要调用CoUninitialize [3]要不要最后释放Script对象。这三个问题,等以后有时间了再折腾,因为,目前的,已经能满足

我的需要了,这三段代码贴出来,主要是给自己看的,免的以后又碰到同样的困难。

分配

void CSnapshot::InitCallBKFuncForHotKey()
{
	boost::lock_guard<boost::mutex> l(g_CallBKFuncInMainThread);//防止两个线程同时初始化m_pStream

	if(m_pStream!=NULL)
	{
		g_log.Add("[%s][%d]已经初始化marshallData!",__FILE__,__LINE__);
		return;
	}

	std::wstring wsTT = OLE2W(m_bstrCallbackFuncName);

	g_log.Add("[%s][%d][回调函数名称=%s] void CSnapshot::InitCallBKFuncForHotKey() 開始初始化!"
				,__FILE__,__LINE__,ws2s(wsTT).c_str());

	IDispatch* pScript;//CComPtr<IDispatch> pScript;
	CComBSTR   bstrMember(m_bstrCallbackFuncName);  
	CComPtr<IServiceProvider> ifsp;
	IWebBrowser2* browser;
	CComPtr<IHTMLDocument2> spDoc;  
	CComVariant vaResult; 	
	UINT nArgErr = (UINT)-1;   //   initialize   to   invalid   arg 
	HRESULT hr;

	//取ifsp
	hr = m_spUnkSite->QueryInterface( &ifsp );

	if(hr!=S_OK)
	{
		g_log.Add("CSnapshot::CallBKFuncInMainThread 取ifsp失败");
		return ;
	}

	//取browser
	hr = ifsp->QueryService(SID_SWebBrowserApp,IID_IWebBrowser2, 
		reinterpret_cast<void **>(&browser));
	if(FAILED(hr))
	{
		g_log.Add("CSnapshot::CallBKFuncInMainThread 取browser失败");
		return;
	}

    //取IHTMLDocument2	
	hr = browser->get_Document((IDispatch**)&spDoc);
	if(FAILED(hr))
	{
		g_log.Add("CSnapshot::CallBKFuncInMainThread 取spDoc失败");
		return;
	}
	browser->Release();

	//取Script
	hr = spDoc->get_Script(&pScript);  
	if(FAILED(hr))
	{
		g_log.Add("CSnapshot::CallBKFuncInMainThread 取Script失败");
	}

	//把Script对象放入容器中,传给支线线程,是因为只有主线程才能拿到script对象。
	//注意:刷新IE后,不需要重新去取Script对象,刷新IE不会导致析构。
	//只有没客户端继续引用我们的ATL控件,控件内的对象才会被析构
	if (pScript)
	{
		//Get Function 's dispid 
		hr = pScript->GetIDsOfNames(IID_NULL,&bstrMember,1,LOCALE_SYSTEM_DEFAULT,&m_dispidCallbackFunc);  

		if(hr==S_OK)
		{
			//创建IStream对象(容器)用来存放Script对象
			hr = CreateStreamOnHGlobal(NULL, TRUE, &m_pStream);
			if(SUCCEEDED(hr))
			{
				//把Script对象放入IStream对象(容器)中,MSHLFLAGS_TABLESTRONG标志意味着可以多次去取
				hr = ::CoMarshalInterface(m_pStream, IID_IDispatch,
					pScript,MSHCTX_INPROC,NULL,MSHLFLAGS_TABLESTRONG);
				
				if(FAILED(hr))
				{
					m_pStream->Release();
					m_pStream = NULL;
					g_log.Add("[%s][%d]CoMarshalInterface=[%x]",__FILE__,__LINE__,hr);
				} else {
					g_log.Add("script放入m_pStream(容器)成功!");					
				}
			}
		} else
		{
			g_log.Add("[%s][%d] void CSnapshot::InitCallBKFuncForHotKey() 取回調函數的ID失敗!"
				,__FILE__,__LINE__);
		}
	}
}

使用

void CSnapshot::CallBKFuncForHotKey(int status,std::wstring fileName)//调用回调函数
{
	boost::lock_guard<boost::mutex> l(g_CallBKFuncInMainThread);

	DISPPARAMS  dispparams; 
	EXCEPINFO   excepInfo; 
	CComVariant vaResult; 	
	HRESULT     hr;
	UINT        nArgErr = (UINT)-1;   //   initialize   to   invalid   arg 

	if(m_pStream!=NULL)
	{
		IDispatch* ipScript = NULL;//在支线,线程中每次调用都要取一次,否则ipScript对象会报E_ACCESSDENIED错误
		g_log.Add("CSnapshot::CallBKFuncForHotKey,从m_pStream取ipScript对象");
		
		::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);//如果没有这段代码,在IE6中调用CoUnmarshalInterface会出错
		
		LARGE_INTEGER li = {0};
		hr = m_pStream->Seek(li, STREAM_SEEK_SET, NULL);//不seek调用CoUnmarshalInterface会失败
		if(FAILED(hr))
		{
			g_log.Add("pStream->Seek(li, STREAM_SEEK_SET, NULL); => %x",hr);
			goto _EXIT;
		}
		hr = CoUnmarshalInterface(m_pStream,IID_IDispatch,(void**)&ipScript);
		if(FAILED(hr))
		{
			g_log.Add("[%s][%d]CoUnmarshalInterface=[%x]",__FILE__,__LINE__,hr);
			goto _EXIT;
		}
		m_ipScriptForHotKey = ipScript;//保留m_ipScriptForHotKey是为了在析构(或进程要退出)的时候释放它。
	}
	else
	{
		g_log.Add("CSnapshot::CallBKFuncForHotKey,还没有m_pStream对象");
		return;
	}

	if(!m_ipScriptForHotKey)
	{
		g_log.Add("CSnapshot::CallBKFuncForHotKey拿到的ipScript对象非法");
		goto _EXIT;
	}
	
	memset(&dispparams,0,sizeof(dispparams)); 
	dispparams.cArgs = 2;//表示有2个参数
	dispparams.rgvarg = new VARIANT[dispparams.cArgs];//表示对参数数组的引用。 

	//注意,参数顺序是反的。
	//CComBSTR  bstr  =  "111";   //   back   reading 
	//bstr.CopyTo(&dispparams.rgvarg[i].bstrVal); 
	dispparams.rgvarg[0].bstrVal = ::W2BSTR(fileName.c_str());
	dispparams.rgvarg[0].vt = VT_BSTR; 
	dispparams.rgvarg[1].intVal = status;
	dispparams.rgvarg[1].vt = VT_I4;
	
	dispparams.cNamedArgs =0;//表示命名参数的计数。 	

	memset(&excepInfo,0,sizeof(excepInfo)); 
	

	//进行调用	
	DWORD id =  GetCurrentThreadId();
	g_log.Add("[%s][%d][%d]Before Invoke",__FILE__,__LINE__,id);

	hr = m_ipScriptForHotKey->Invoke(m_dispidCallbackFunc, 
		IID_NULL,0,DISPATCH_METHOD,&dispparams,&vaResult,&excepInfo,&nArgErr);

	g_log.Add("[%s][%d][%d]After Invoke",__FILE__,__LINE__,id);

	if(FAILED(hr))
	{
		if(hr==E_ACCESSDENIED)
			g_log.Add("CSnapshot::CallBKFuncForHotKey 回调失败,E_ACCESSDENIED");
		else if(hr==0x80020101)
			g_log.Add("CSnapshot::CallBKFuncForHotKey 回调失败,JS函数中有语法错误");
		else if(hr==0x80020003)
			g_log.Add("CSnapshot::CallBKFuncForHotKey 回调失败,JS函数没有找到");
		else
			g_log.Add("CSnapshot::CallBKFuncForHotKey 回调失败,hr=[%x]",hr);
	}	
_EXIT:
	::CoUninitialize();//对应CoInitializeEx函数
}

释放

CSnapshot::~CSnapshot()
{
	if(m_pStream!=NULL)
	{
		LARGE_INTEGER li = {0};
		HRESULT hr = m_pStream->Seek(li, STREAM_SEEK_SET, NULL);//不Seek释放会失败!
		if(FAILED(hr))
		{
			g_log.Add("[%s][%d]seek失败[%x]!",
				__FILE__,__LINE__,hr);
		}
		hr = CoReleaseMarshalData(m_pStream);
		if(FAILED(hr))
		{
			g_log.Add("[%s][%d]释放MarshalData失败[%x]!",
				__FILE__,__LINE__,hr);
		}
		m_pStream = NULL;
	}

	if(m_ipScriptForHotKey!=NULL)
	{
		m_ipScriptForHotKey->Release();
		m_ipScriptForHotKey = NULL;
	}
	g_log.Add("[%s][%d]CSnapshot::~CSnapshot()析构完成!",__FILE__,__LINE__);
}

参考资料
[1]《CoGetInterfaceAndReleaseStream 退出窗体报错 解决方法》

http://blog.csdn.net/god00/article/details/6648078

[2]《超酷代码:来自 COM 经验的八个教训》

http://www.microsoft.com/china/MSDN/library/windev/COMponentdev/CDwickedtoc.mspx?mfr=true

[3]《如何跨单元中 Visual C++ 封送接口》
support.microsoft.com/kb/q206076/

【上篇】
【下篇】

抱歉!评论已关闭.