求助:activex 控件如何轮询页面填值

landcloud 2015-02-07 09:38:15
小弟刚学习activex开发,目前遇到个小项目要做个控件,大体功能是,当页面打开时activex控件轮询IC读卡器,当检测到有卡时,读取卡号到页面指定ID的文本框。当有检测到新卡时,如果卡内容于目前文本框内不一致则替换。
简单的说就是控件从网页加载完毕后不断查询指定id页面元素的值,并按照一定逻辑从外部读取数据修改。(主要是为了简化页面操作人员的填表操作)
目前我在activex控件里做了一个方法,实现了上述功能,但只能靠页面调用单次运行改变文本框的值(比如点击一个按钮,检测一次),如果在成员函数中写成循环,每隔500ms轮询则调用后页面假死,无法操作,注释掉while循环一切正常。请问有何方法能解决这个问题。小弟初次接触activex,可能有些概念不很理解,恳请大家说的稍微具体一些,谢谢
我的代码如下
BSTR CICReaderCtlCtrl::setTextBox(LPCTSTR name)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());

CString cardSN;
long long cardNum(0);
char tempstr[261];
GetATR myReader;
CComPtr<IOleClientSite> ClientSite;
CComPtr<IOleContainer> Container;

int ret(0);
int lastState(0);//上次读卡状态
CString lastValue("");

ClientSite = this->GetClientSite();
ClientSite->GetContainer(&Container);
CComQIPtr<IHTMLDocument2, &IID_IHTMLDocument2> spDoc(Container);
IHTMLDocument3 * spDoc3 = NULL;
CComPtr<IHTMLElement> spElem;
spDoc->QueryInterface(IID_IHTMLDocument3, (PVOID*)&spDoc3);
HRESULT hr;
hr = spDoc3->getElementById(CComBSTR(name),&spElem);
if (FAILED(hr))
{
return CString(name).AllocSysString();
}
while (1)
{
Sleep(500);
myReader.clearBuffers();
myReader.OnInit();
if (myReader.OnConnect() == SCARD_S_SUCCESS)
{
//读出卡号
myReader.OnGetATR();
myReader.makeCmd(1);
myReader.sendCmd();
if(!myReader.recvAnalyze(1))
{
memset(tempstr,0,261);
for( int index = 0; index < 4 ; index++ )
{
sprintf( tempstr, "%s%02X", tempstr, myReader.RecvBuff[index] );
}
sprintf( tempstr, "%s\n", tempstr );
cardNum=strtol(tempstr,(char**)NULL, 16);
}
cardSN.Format("%lld", cardNum);
myReader.OnDisconnect();
//return cardSN.AllocSysString();

//检测文本框数据1,是否为空 2,是否缺失 3,是否为本次卡号


if (spDoc3)
{
BSTR textValue =NULL;
spElem->get_innerText(&textValue);

if ( ( CString(textValue).Compare(lastValue) != 0 ) || ( CString(textValue).IsEmpty() != 0 ) )//与上次不同或为空
{
spElem->put_innerText(CComBSTR(cardSN));//填写文本框
lastValue = cardSN;
}
SysFreeString(textValue);
}
}
//return lastValue.AllocSysString();
}
}


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>ActiveX test</title>
<script language = 'javascript'>
function getUID()
{
var iResult = MYAvtiveX.getCardNum();//get cardNum
alert(iResult);
}
function getATS()
{
var iResult = MYAvtiveX.getATS();//get cardNum
alert(iResult);
}
function getBlock()
{
var iResult1 = MYAvtiveX.readBlock(0x05,0x10);//read block
alert(iResult1);
}
function connect()
{
var iResult2 = MYAvtiveX.connect();//check connect
alert(iResult2);
}
function writeTest()
{
var a = "123wwe222";
var iResult3 = MYAvtiveX.writeBlock(0x05,a);//write block
alert(iResult3);
}
function getCardNum()
{
var a = "MyText";
var iResult3 = MYAvtiveX.setTextBox(a);//get url
//alert(iResult3);
}

</script>
</head>

<body onload="getCardNum()">
<object id="MYAvtiveX" classid="clsid:BE09B27D-6B4A-456C-9DB7-98495655EFED"> </object>
<fieldset>
<legend>MyActiveX test</legend>
<table width="100%" border="1">
<tr><td width="20%"><input type='button' onclick='getUID()' value='getUID'> </td> </tr>
<tr><td width="20%"><input type='button' onclick='getATS()' value='getATS'> </td> </tr>
<tr><td width="20%"><input type='button' onclick='getBlock()' value='getBlock'> </td> </tr>
<tr><td width="20%"><input type='button' onclick='connect()' value='connect'> </td> </tr>
<tr><td width="20%"><input type='button' onclick='writeTest()' value='writeTest'> </td> </tr>
// <tr><td width="20%"><input type='button' onclick='getCardNum()' value='getCardNum'> </td> </tr>
</table>
<textarea id="MyText" name="yj" clos="20" rows="5"></textarea>
</fieldset>
</body>
</html>
...全文
165 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
encoderlee 2015-02-10
  • 打赏
  • 举报
回复
WM_TIMER也是依赖窗口才能实现的,长宽设置为0的话,其实绘制频率是很低的。 你可以试一下javascript计时器看看
yeah2000 2015-02-09
  • 打赏
  • 举报
回复
使用多线程实现
landcloud 2015-02-09
  • 打赏
  • 举报
回复
在html里面把常宽变成0倒也能解决这个问题,但是无缘无故绘制个窗口增加了开销总感觉不妥当。 如果说无窗口的话无法响应WM_CREATE事件也能理解,但是无窗口不能SetTimer设置定时器,并响应WM_TIMER就不好理解了
landcloud 2015-02-09
  • 打赏
  • 举报
回复
刚试了下确实是这个问题,在控件里画一个椭圆变成有窗口的控件,就能正常响应WM_CREATE事件设置定时器,好奇怪,哪位大侠能解释下这个问题,谢谢
landcloud 2015-02-09
  • 打赏
  • 举报
回复
我的控件式无窗口控件,我非常怀疑是由于这个问题造成WM_CREATE和WM_TIMER无法响应尼?
static const DWORD _dwICReaderCtlOleMisc =
	OLEMISC_INVISIBLEATRUNTIME |
	OLEMISC_ACTIVATEWHENVISIBLE |
	OLEMISC_SETCLIENTSITEFIRST |
	OLEMISC_INSIDEOUT |
	OLEMISC_CANTLINKINSIDE |
	OLEMISC_RECOMPOSEONRESIZE;
worldy 2015-02-08
  • 打赏
  • 举报
回复
无论在activex读取还是主界面,都不要使用循环,使用循环会使你的程序卡死,使用定时器则不会有一个问题,因为通过设置合理的定时值,定时器处理后有足够的时间让别的代码运行
worldy 2015-02-08
  • 打赏
  • 举报
回复
你的界面中能使用按钮触发处理,那么你就可以使用定时器触发。关键是定时时间必须大于每次的处理时间;但你的定时器最好不要创建在activex中,而创建在主界面中比较好
encoderlee 2015-02-08
  • 打赏
  • 举报
回复
打好断点,用附加调试功能调试一下代码看看吧,注意要选对iexplore进程,现在的IE打开一个页面也会有好几个iexplore进程。 实在不行就用javascript计时器吧,让js每隔500ms调用一下Activex的方法
encoderlee 2015-02-08
  • 打赏
  • 举报
回复
在OnTimer的if (nIDEvent == 1000)中用MessageBox()弹个消息框看看,以此来判断代码有没有被执行,是不是每隔500ms就会弹一个消息框。 另外用IE浏览器打开页面后,加载出Activex后,可以用VisualStudio的“调试”-“附加到进程”,选择iexplore进程,进行调试,这样就可以在代码上打断点单步调试看看问题出在哪里。
landcloud 2015-02-08
  • 打赏
  • 举报
回复
刚刚说的有点问题,应该是定时器一次都没有生效,我忘记注掉html里面的onload方法,导致执行了一次。如果把SetTimer(1000, 10, NULL); 放到成员函数(注释掉其他逻辑代码)中用js按钮调用则会出现错误。页面上没有其他定时器,排除了冲突的可能。
如下
landcloud 2015-02-08
  • 打赏
  • 举报
回复
学习了下CharlesSimonyi大大的方案,增加WM_CREATE消息处理函数onCreate中定义计时器,然后增加WM_TIMER消息处理函数onTimer。不过很奇怪的是,页面只在加载以后文本框的值改变了,以后无论文本框怎么改变,都不会自动填写了。 逻辑应该没啥问题,我手单次连续调成员函数是可以改变文本框内容的
void CICReaderCtlCtrl::OnTimer(UINT_PTR nIDEvent)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	if (nIDEvent == 1000)
	{
		CComPtr<IOleClientSite>    ClientSite;
		CComPtr<IOleContainer>    Container;
		CString lastValue("");
		ClientSite = this->GetClientSite();
		ClientSite->GetContainer(&Container);
		CComQIPtr<IHTMLDocument2, &IID_IHTMLDocument2> spDoc(Container);
		IHTMLDocument3 * spDoc3 = NULL;
		CComPtr<IHTMLElement> spElem;
		spDoc->QueryInterface(IID_IHTMLDocument3, (PVOID*)&spDoc3);
		HRESULT hr;
		hr = spDoc3->getElementById(CComBSTR(L"MyText"),&spElem);
		if (FAILED(hr))
		{
			return;
		}
		if (spDoc3)
		{   
			BSTR textValue =NULL;
			spElem->get_innerText(&textValue);

			if (  ( CString(textValue).Compare(lastValue) != 0 ) || ( CString(textValue).IsEmpty() != 0 ) )//与上次不同或为空
			{
				spElem->put_innerText(CComBSTR(L"hello world!!"));//填写文本框
				lastValue = "";
			}
			spElem->put_innerText(CComBSTR(L"hello world!!"));//填写文本框
			lastValue = "";
			SysFreeString(textValue);
		} 

	}
	COleControl::OnTimer(nIDEvent);
}
encoderlee 2015-02-07
  • 打赏
  • 举报
回复
当然,也可以在合适的时候启动计时器,由js来调用Activex的方法来启动计时器等,不一定要在Activex刚加载创建出来时就启动计时器。
encoderlee 2015-02-07
  • 打赏
  • 举报
回复
不仅是Activex控件,所有的Windows程序都不能在主线程中while (1),那样主线程无法处理消息循环便会造成界面卡死。 如果你要实现每隔500ms执行一些代码,应该用计时器来实现,使用计时器有两种方法: 方法1.使用MFC中的计时器,用类向导给CICReaderCtlCtrl添加WM_CREATE消息处理函数,在消息处理函数中设置定时器:

//该函数在Activex控件被加载,初始化并创建出窗口的时候被调用,相当于在Activex刚被加载出来的时候就设置一个计时器。
int CICReaderCtlCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (COleControl::OnCreate(lpCreateStruct) == -1)
		return -1;
       //设置一个ID为1000,触发间隔为500ms的计时器
	SetTimer(1000, 500, NULL);

	return 0;
}
然后打开类向导给CICReaderCtlCtrl添加WM_TIMER消息处理函数:

void CICReaderCtlCtrl::OnTimer(UINT_PTR nIDEvent)
{
	if (nIDEvent == 1000)
	{
		//在这里执行你的查询代码
	}

	COleControl::OnTimer(nIDEvent);
}
这样从Activex被加载启动开始,每隔500毫秒就会执行你的查询代码。 方法2.使用javascript计时器,javascript也可以设置计时器,间隔500ms调用Activex的函数进行查询,关于javascript如何设置计时器,网上已经有很多了,搜一搜就知道了。

3,245

社区成员

发帖
与我相关
我的任务
社区描述
ATL,Active Template Library活动(动态)模板库,是一种微软程序库,支持利用C++语言编写ASP代码以及其它ActiveX程序。
社区管理员
  • ATL/ActiveX/COM社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧