最近,工作需要,实现了JS与C++之间的相互访问。核心思想是通过一个隐藏域作为中转。JS发送数据给C++时,将数据写往某个隐藏域,同时C++每隔一段时间(如40ms)检测一次此域可有数据,有的话取出进行相关操作后清空。C++发送数据给JS时,将数据写往另外一个隐藏域后,直接使用COM触发某个隐藏Button的click事件,由它进行相关的操作。具体代码如下:
//File: WebNativeCall.js
var g_strInCommPortId = null;
var g_strOutCommPortId = null;
var g_strInCommCmdId = null;
var g_funcCallIn = null;
var funcOnLoad = function() {
if (g_strInCommPortId != null && g_strOutCommPortId != null
&& g_strInCommCmdId != null && g_funcCallIn != null) {
var txtInCommPort = document.createElement("input");
txtInCommPort.setAttribute("type", "hidden");
txtInCommPort.setAttribute("id", g_strInCommPortId);
txtInCommPort.setAttribute("name", g_strInCommPortId);
txtInCommPort.setAttribute("value", "");
document.body.appendChild(txtInCommPort);
var txtOutCommPort = document.createElement("input");
txtOutCommPort.setAttribute("type", "hidden");
txtOutCommPort.setAttribute("id", g_strOutCommPortId);
txtOutCommPort.setAttribute("name", g_strOutCommPortId);
txtOutCommPort.setAttribute("value", "");
document.body.appendChild(txtOutCommPort);
var btnInCommCmd = document.createElement("input");
btnInCommCmd.setAttribute("type", "button");
btnInCommCmd.style.setAttribute("display", "none");
btnInCommCmd.setAttribute("id", g_strInCommCmdId);
btnInCommCmd.setAttribute("name", g_strInCommCmdId);
btnInCommCmd.onclick = function() {
var data = document.getElementById(g_strInCommPortId).value;
if (data != null && data != "") {
g_funcCallIn(document.getElementById(g_strInCommPortId).value);
}
};
document.body.appendChild(btnInCommCmd);
};
};
if (window.addEventListener) {
window.addEventListener("load", funcOnLoad, false);
}
else if (window.attachEvent) {
window.attachEvent("onload", funcOnLoad);
}
function funcCallOut(strData)
{
document.getElementById(g_strOutCommPortId).value = strData;
}
//File: Test.html
<html>
<head>
<title>Test the Commucation between C++ and JS</title>
<script type="text/javascript" language="javascript" src="WebNativeCall.js"></script>
<script type="text/javascript" language="javascript">
g_strInCommPortId = "txtInCommPort";
g_strOutCommPortId = "txtOutCommPort";
g_strInCommCmdId = "btnInCommCmd";
g_funcCallIn = function(CommData) {
alert("Web Receive Data: " + CommData);
}
function TestCallOut() {
funcCallOut(document.getElementById("txtOutData").value);
}
</script>
</head>
<body>
<input type="text" id="txtOutData" name="txtOutData" value="" />
<input type="button" id="btnCallOut" name="btnCallOut" value="CallOut" οnclick="TestCallOut();" />
</body>
</html>
//File: VisitHtmlTool.h
/***************************************************************************
* 功能 : 网页本地访问类
* 输入参数 : 无
* 输出参数 : 无
* 返回值 : 无
***************************************************************************/
class CVisitHtmlTool //网页本地访问类
{
public:
typedef void (* Func_CallOut)(LPCTSTR lpszData);
CVisitHtmlTool(CHtmlView *pHtmlView, LPCTSTR lpszInCmdTagId
, LPCTSTR lpszInDataTagId, LPTSTR lpszOutDataTagId
, Func_CallOut pCallOut);
~CVisitHtmlTool(void);
BOOL CallIn(LPCTSTR lpszData); //向网页发数据
VOID StartReceive(); //开始自动接收数据
VOID StopReceive(); //停止自动接收数据
protected:
static DWORD WINAPI ReceiveThread(LPVOID lpThreadParameter);
static LRESULT CALLBACK WindowSubSolve(HWND hWnd, UINT iMsg, WPARAM wParam
, LPARAM lParam);
CHtmlView *m_pHtmlView; //HtmlView指针
LPCTSTR m_lpszInCmdTagId; //Web端数据接收触发器标识
LPCTSTR m_lpszInDataTagId; //Web端接收数据保存控件标志
LPTSTR m_lpszOutDataTagId; //Web端发送数据保存控件标志
IHTMLElement* m_pEle; //CallOut的数据结点
Func_CallOut m_pCallOut; //本地端接收数据回调函数
private:
HANDLE m_hLock;
BOOL m_bStarted;
WNDPROC m_pOldProc;
LONG m_pUserData;
};
//File: VisitHtmlTool.cpp
#include "StdAfx.h"
#include "VisitHtmlTool.h"
#define WM_PUMPCALLOUTDATA (WM_USER+1)
CVisitHtmlTool::CVisitHtmlTool(CHtmlView *pHtmlView,
LPCTSTR lpszInCmdTagId ,
LPCTSTR lpszInDataTagId,
LPTSTR lpszOutDataTagId,
Func_CallOut pCallOut)
{
m_pOldProc = NULL;
m_pUserData = NULL;
m_hLock = ::CreateEvent(NULL, FALSE, TRUE, NULL);
m_bStarted = FALSE;
m_pCallOut = pCallOut;
m_pHtmlView = pHtmlView;
m_lpszInCmdTagId = lpszInCmdTagId;
m_lpszInDataTagId = lpszInDataTagId;
m_lpszOutDataTagId = lpszOutDataTagId;
m_pEle = NULL;
}
CVisitHtmlTool::~CVisitHtmlTool(void)
{
StopReceive();
::CloseHandle(m_hLock);
if(m_pEle)
{
m_pEle->Release();
m_pEle = NULL;
}
}
BOOL CVisitHtmlTool::CallIn(LPCTSTR lpszData)
{
IDispatch* pDis = NULL;
IHTMLElement* pEle = NULL;
IHTMLElement* pEleBody = NULL;
IHTMLDocument2 *pDoc = NULL;
IHTMLElementCollection* pAllCollection = NULL;
IHTMLElementCollection* pBodyCollection = NULL;
LONG lLength = 0;
BSTR bstrTmp;
WaitForSingleObject(m_hLock, INFINITE);
if((pDoc=(IHTMLDocument2 *)m_pHtmlView->GetHtmlDocument()) == NULL)
{
SetEvent(m_hLock);
return FALSE;
}
pDoc->get_body(&pEleBody);
if (pEleBody == NULL)
{
SetEvent(m_hLock);
return FALSE;
}
BOOL bRet = FALSE;
pEleBody->get_children(&pDis);
pDis->QueryInterface(IID_IHTMLElementCollection, (void**)&pBodyCollection);
pDis->Release();
pEleBody->Release();
pBodyCollection->get_length(&lLength);
for (int i = 0; i < lLength; i++)
{
pBodyCollection->item(_variant_t(i), _variant_t(0), &pDis);
pDis->QueryInterface(IID_IHTMLElement, (void**)&pEle);
pDis->Release();
pEle->get_id(&bstrTmp);
if(!lstrcmp(m_lpszInDataTagId, bstrTmp))
{
pEle->setAttribute(BSTR(TEXT("value")), _variant_t(lpszData));
::SysFreeString(bstrTmp);
pEle->Release();
bRet = TRUE;
}
else if(!lstrcmp(m_lpszInCmdTagId, bstrTmp))
{
pEle->click();
break;
}
else
{
pEle->Release();
::SysFreeString(bstrTmp);
}
}
pBodyCollection->Release();
SetEvent(m_hLock);
return bRet;
}
VOID CVisitHtmlTool::StartReceive()
{
if(m_bStarted)
{
return;
}
m_bStarted = TRUE;
m_pUserData = ::SetWindowLong(m_pHtmlView->GetSafeHwnd(), GWL_USERDATA, (LONG)this);
m_pOldProc = (WNDPROC)::SetWindowLong(m_pHtmlView->GetSafeHwnd(), GWL_WNDPROC, (LONG)WindowSubSolve);
::CreateThread(NULL, 0, ReceiveThread, this, 0, NULL);
}
VOID CVisitHtmlTool::StopReceive()
{
if(!m_bStarted)
{
return;
}
m_bStarted = FALSE;
while(!m_bStarted)
{
SleepEx(100, TRUE);
}
::SetWindowLong(m_pHtmlView->GetSafeHwnd(), GWL_WNDPROC, (LONG)m_pOldProc);
::SetWindowLong(m_pHtmlView->GetSafeHwnd(), GWL_USERDATA, m_pUserData);
m_bStarted = FALSE;
}
DWORD WINAPI CVisitHtmlTool::ReceiveThread(LPVOID lpThreadParameter)
{
CVisitHtmlTool *pThis = reinterpret_cast<CVisitHtmlTool *>(lpThreadParameter);
while(pThis->m_bStarted)
{
SleepEx(40, TRUE);
pThis->m_pHtmlView->PostMessage(WM_PUMPCALLOUTDATA);
}
pThis->m_bStarted = TRUE;
return 0;
}
LRESULT CALLBACK CVisitHtmlTool::WindowSubSolve(HWND hWnd,
UINT iMsg,
WPARAM wParam,
LPARAM lParam)
{
CComVariant varData;
CVisitHtmlTool *pThis = reinterpret_cast<CVisitHtmlTool *>(::GetWindowLong(
hWnd, GWL_USERDATA));
if(iMsg != WM_PUMPCALLOUTDATA)
{
return CallWindowProc(pThis->m_pOldProc, hWnd, iMsg, wParam, lParam);
}
if(WaitForSingleObject(pThis->m_hLock, 0) == WAIT_TIMEOUT)
{
return 0;
}
if(pThis->m_pEle==NULL
|| pThis->m_pEle->getAttribute(CComBSTR(TEXT("value")), 0, &varData)!=S_OK)
{
IDispatch* pDis = NULL;
IHTMLElement* pEle = NULL;
IHTMLElement* pEleBody = NULL;
IHTMLDocument2 *pDoc = NULL;
IHTMLElementCollection* pAllCollection = NULL;
IHTMLElementCollection* pBodyCollection = NULL;
LONG lLength = 0;
BSTR bstrTmp;
if((pDoc=(IHTMLDocument2 *)pThis->m_pHtmlView->GetHtmlDocument()) == NULL)
{
SetEvent(pThis->m_hLock);
return 0;
}
pDoc->get_body(&pEleBody);
if (pEleBody == NULL)
{
SetEvent(pThis->m_hLock);
return 0;
}
pEleBody->get_children(&pDis);
pDis->QueryInterface(IID_IHTMLElementCollection, (void**)&pBodyCollection);
pDis->Release();
pEleBody->Release();
pBodyCollection->get_length(&lLength);
pThis->m_pEle = NULL;
for (int i = 0; i < lLength; i++)
{
pBodyCollection->item(_variant_t(i), _variant_t(0), &pDis);
pDis->QueryInterface(IID_IHTMLElement, (void**)&pEle);
pDis->Release();
pEle->get_id(&bstrTmp);
if(!lstrcmp(pThis->m_lpszOutDataTagId, bstrTmp))
{
pThis->m_pEle = pEle;
::SysFreeString(bstrTmp);
break;
}
else
{
pEle->Release();
::SysFreeString(bstrTmp);
}
}
pBodyCollection->Release();
}
if(pThis->m_pEle && pThis->m_pEle->getAttribute(CComBSTR(TEXT("value")), 0, &varData)==S_OK)
{
if(varData.vt!=VT_NULL && varData.bstrVal!=NULL && lstrcmp(varData.bstrVal, TEXT("")))
{
pThis->m_pEle->setAttribute(CComBSTR(TEXT("value")), _variant_t(""));
if(pThis->m_pCallOut)
{
pThis->m_pCallOut(varData.bstrVal);
}
}
}
SetEvent(pThis->m_hLock);
return 1;
}