Topaz ActiveX的LCDWriteString方法写入超LCD垂直限制致运行时错误
Topaz 1x5 LCD文本显示越界问题排查与修复
问题背景
在Topaz 1x5 LCD屏幕上展示长文本段落,通过「继续」按钮分页切换,待文本全部展示后采集签名。已通过GetLCDSize()获取屏幕最大尺寸,将垂直最大值存入yMax;使用LCDWriteString()在当前yPos位置写入文本行,通过GetLCDStringSize()获取行尺寸后递增yPos,虽添加了yPos与yMax的校验逻辑,但仍出现越界触发运行时错误。
现有代码
//----------------------------------------------------------------------------------- //will display a chuck full of words up to the point where display fills up //method returns the point in aWords where it stoped. //aWords is an array of words to be written METHOD ShowText( aWords, i ) Local xSize, ySize := 0 Local cLine := '' Local cTmp := '' Local yPos := 0 While ++i <= Len( aWords ) .and. yPos < ( ::yMax - ySize ) cTmp += aWords[ i ] + ' ' xSize := nLoWord( ::SigPlus1:GetLCDStringSize( cTmp ) ) //width in LCD pixels of line to be written ySize := nHiWord( ::SigPlus1:GetLCDStringSize( cTmp ) ) //height of line in LCD pixels If xSize < ::xMax //if line width does not exceed LCD width cLine := cTmp End //if line exceed LCD width or at last word then write line of text on current at yPos if xSize >= ( ::xMax-5 ) .OR. i == Len( aWords ) xSize := nLoWord( ::SigPlus1:GetLCDStringSize( cLine ) ) ::SigPlus1:LCDWriteString( 0, 2, 0, yPos, xSize, ySize, 0, cLine ) //advance yPos to next line yPos += nHiWord( ::SigPlus1:GetLCDStringSize( cLine ) ) cLine := '' cTmp := '' i += iif( i == Len( aWords ), 0, -1 ) //if not at end, then go back 1 word. End End RETURN i
问题根源分析
- 循环条件校验时机错误:原循环用临时行
cTmp的高度ySize预判剩余空间,但临时行未触发换行时ySize并非最终行高,会导致错误允许继续添加单词,最终写入时yPos加行高超出yMax。 - 写入前无精准空间校验:仅在循环开始前预判,未在实际执行
LCDWriteString()前检查当前行高加yPos是否超过屏幕垂直边界。 - 尺寸计算冗余且易出错:多次重复调用
GetLCDStringSize(),可能因字符串临时变化导致尺寸值不一致。
修复方案
修改后的代码
//----------------------------------------------------------------------------------- // 显示文本至屏幕填满,返回停止时的单词索引 // aWords 为待写入的单词数组 METHOD ShowText( aWords, i ) Local xSize, lineHeight := 0 Local cLine := '' Local cTmp := '' Local yPos := 0 Local totalWords := Len( aWords ) While ++i <= totalWords // 临时拼接当前单词 cTmp += aWords[ i ] + ' ' // 获取临时行的尺寸 Local tmpSize := ::SigPlus1:GetLCDStringSize( cTmp ) Local tmpWidth := nLoWord( tmpSize ) Local tmpHeight := nHiWord( tmpSize ) // 如果临时行宽度未超过屏幕,更新当前行内容与行高 If tmpWidth < ::xMax cLine := cTmp lineHeight := tmpHeight End // 触发换行条件:行宽接近屏幕边界 或 已到最后一个单词 If tmpWidth >= ( ::xMax - 5 ) .OR. i == totalWords // 检查当前行是否能放入剩余屏幕空间 If yPos + lineHeight > ::yMax // 剩余空间不足,回退当前单词并终止循环 i -= 1 Exit End // 获取最终行宽度并写入屏幕 xSize := nLoWord( ::SigPlus1:GetLCDStringSize( cLine ) ) ::SigPlus1:LCDWriteString( 0, 2, 0, yPos, xSize, lineHeight, 0, cLine ) // 更新yPos到下一行起始位置 yPos += lineHeight cLine := '' cTmp := '' // 非最后一个单词时,回退索引以便重新处理当前单词 If i != totalWords i -= 1 End End End RETURN i
关键修复点
- 写入前精准校验空间:在执行
LCDWriteString()前,明确检查yPos + lineHeight是否超过yMax,超出则回退单词并终止循环,彻底避免越界。 - 统一行高变量:用
lineHeight存储当前待写入行的高度,减少重复调用GetLCDStringSize()的冗余操作,避免尺寸值不一致。 - 简化逻辑判断:将原循环中依赖临时值的预判逻辑移至换行触发时,逻辑更清晰可靠;用直观的条件判断替换
iif,降低代码复杂度。
内容的提问来源于stack exchange,提问作者reinaldo Crespo
相关产品推荐
相关产品推荐

