delphi修复access:Delphi中两个BUG的分析与修复来源: 发布时间:星期四, 2009年2月12日 浏览:88次 评论:0
Delphi中两个BUG分析和修复 在使用Delphi 7进行 3层数据库开发时遇到了两个小问题通过反复试验终于找出了Delphi 7中两个小BUG并进行了修复(好像Delphi 6中也有相同BUG)撰写此文和大家起分享成功喜悦我也是初学Delphi文中定存在不少说不对地方还请各位朋友多多指正 BUG1.传参时中文被截断问题: BUG再现思路方法: 后台用SQL Server 2000里面有个XsHeTong表用于试验您可以根据您实际情况进行调整 先创建个数据服务器:新建项目创建个远程数据模块上面放置ADOConnection、ADODataSet、DataSetProvider各并做好相应设置其中ADODataSetComamndText留空并把它Option中poAllowCommandText设置为True编译运行 再创建客户端:新建项目在窗体上放置DCOMConnection连上前面上创建数据服务器再放置个ClientDataSet把它连接设成这里DCOMConnection并设置它ProviderName为上面服务器上DataSetProvider名字最后放置DataSource和DBGrid各并作相应设置用于查看结果再放置Button用于测试 在ButtonOnClick中写下类似于下面代码(这里我用了XsHeTong表和它两个字段HTH(char 15)、GCMC(varchar 100)您可以根据你实际测试情况进行调整): with ClientDataSet1 do begin Close; CommandText := \'Insert Into XsHeTong(HTH, GCMC) values(:HTH,:GCMC)\'; Params[0].AsString := \'12345\'; Params[1].AsString := \'会截断中文字\'; Execute; Close; CommandText := \'Select * from XsHeTong\'; Open; end; 运行点击按钮看到记录被插入了可惜结果并不正确“会截断中文字”变成了“会截断”但没有中文“12345”倒是正确插入了 BUG分析和修复: 为了对照起见我试着直接用个ADOConnection和ADOCommand、ADOTable进行C/S构架测试结果是正确中文字不会被切断这介绍说明了此BUG只在 3层构架上出现 用SQL Server事件探查器探查提交到SQL Server上运行语句发现两层构架和 3层构架情况有以下区别: 两层构架: exec sp_executesql N\'Insert o XsHeTong(HTH, GCMC) values(@P1,@P2)\', N\'@P1 varchar(15),@P2 varchar(100)\', \'12345\', \'会截断中文字\' 3层构架: exec sp_executesql N\'Insert o XsHeTong(HTH, GCMC) values(@P1,@P2)\', N\'@P1 varchar(5),@P2 varchar(7)\', \'12345\', \'会截断 显然两层构架时参数长度是按实际库结构传 3层构架时参数长度是按实际参数串长度传而实际串长度又似乎是算错了没有把个中文当两个长度处理 没有办法只好进行跟踪调试为了调试DelphiVCL库需要在工程选项“Compiler Options”中选上“Use Debug DCUs” 先跟踪客户端ClientDataSet1.Execute后先后经历了TCustomClientDataSet.Exectue、TCustomeClientDataSet.PackageParams、TCustomClientDataSet.DoExecute等系列直到AppServer.AS_Execute(ProviderName, CommandText, Params, OwnerData); 把请求提交到服务器均没有什么异常情况看来问题出在服务器端 对服务器进行跟踪反复试验后我把重点落在了TCustomADODataSet.PSSetCommandText身上经过反复细致跟踪目标越来越精确:TCustomADODataSet.PSSetParams、TParameter.Assign、TParameter.SetValue、VarDataSize终于找到了BUG源头:VarDataSize下面是它代码: function VarDataSize(const Value: OleVariant): Integer; begin VarIsNull(Value) then Result := -1 VarIsArray(Value) then Result := VarArrayHighBound(Value, 1) + 1 TVarData(Value).VType = varOleStr then begin Result := Length(PWideString(@TVarData(Value).VOleStr)^); //出问题行 Result = 0 then Result := -1; end Result := SizeOf(OleVariant); end; 就是在这个中计算实参长度它把Value中值取出地址并把它作为个WideString指针去求串长度结果就导致了“会截断中文字”这个串长度变成了7而不是14 问题找到了解决起来也就不困难了只要简单把 Result := Length(PWideString(@TVarData(Value).VOleStr)^); //出问题行 改成 Result := Length(PAnsiString(@TVarData(Value).VOleStr)^); //没问题了 就可以了 但是这样就会导致求英文串长度时长度被加倍了所以也可以把这行改成: Result := Length(Value); 这样不管是中文还是英文还是中英混合串就都可求得正确长度了这就我至今仍百思不解问题为什么Borland要绕个圈子通过指针去求参数值长度呢?哪位朋友知道话还请给我解释下非常感谢! 有些朋友可能会有疑问为什么在不通过 3层构架来做时候不产生这个串被截断问题呢?答案并不复杂在直接通过ADOCommand来向SQL Server发送命令时它是按表结构来决定参数长度它会先向SQL Server发条 SET FMTONLY _disibledevent=> 来获取表结构而在 3层构架下TCustomADODataSet内部虽然也是用TADOCommand对象来发命令但它却在取得表结构后并不用这个值来作为传参长度而是重新去按实际参数来计算长度结果就导致了 BUG2.ClientDataSetLookup字段问题: BUG再现思路方法: 新建工程在上面放置两个ClientDataSet分别为cds1和cds2它数据来源任意其中cds1为主数据集在里面增加个新Lookup字段这个Lookup字段根据cds1中个型字段值到cds2中找出对应值来 运行般来说是正常但是旦cds1被Lookup字段中值出现了个单引号\"\'\"(您可以修改或新增条记录输入单引号试试)立即会导致出错: Unterminated constant(未结束串常量) BUG分析和修复: 这个BUG产生原因要比上个明显得多定是没有正确处理单引号带来副作用引起 同样我们来跟踪VCL源码: 运行出错时打开Call Stack窗口(在View->Debug Windows)菜单中查看情况前面些是显而易见没有问题我们从跟Lookup有关地方开始查原因第个和Lookup有关是TField.CalcLookupValue我们在这个中设置断点重新运行中断下来后进行单步调试 TCustomClientDataSet.Lookup->TCustomClientDataSet.LocateRecord 经过上面几次很快我们就把目标定在了LocateRecord过程中在这个过程中它根据Lookup字段设置情况生成相应过滤条件然后到目标数据集中把对应值找到错就错在过滤条件生成上了比如我们要按cds1中Cust字段(假设是001)值到cds2中按CustID字段值找到对应CustName字段值那生成条件就应该是[CustID] = \'001\'但如果Cust值是aa\'bb按生成条件就会变成[CustID] = \'aa\'bb\'显然导致了个未结束串常量 通常我们解决单引号中又出现单引号情况只需把引号中引号写两就行了这里也是样只要让生成条件变成[CustID] = \'aa\'\'bb\'就不会出错了所以可以这样修改源代码: 在LocateRecord过程中找到下面代码: ftString, ftFixedChar, ftWideString, ftGUID (i = Fields.Count - 1) and (loPartialKey in Options) then ValStr := Format(\'\'\'%s*\'\'\',[VarToStr(Value)]) ValStr := Format(\'\'\'%s\'\'\',[VarToStr(Value)]); 改成: ftString, ftFixedChar, ftWideString, ftGUID: (i = Fields.Count - 1) and (loPartialKey in Options) then ValStr := Format(\'\'\'%s*\'\'\',[ StringReplace(VarToStr(Value),\'\'\'\',\'\'\'\'\'\',[rfReplaceAll])]) ValStr := Format(\'\'\'%s\'\'\',[ StringReplace(VarToStr(Value),\'\'\'\',\'\'\'\'\'\',[rfReplaceAll])]); 也就是在生成过滤条件串时把条件过滤值中单引号全部个变两 为了确保这样修改正确性我查看了TCustomADODataSet中对应LocateRecord过程(在用TADODataSet中Lookup字段时不会因单引号出错只在用TCustomClientDataSet时有这样情况)它处理思路方法和TCustomClientDataSet稍有区别它是通过GetFilterStr来构造过滤条件但在GetFilterStr中它正确处理了单引号问题所以这样来看没有在TCustomClientDataSetLocateRecord中正确处理单引号问题确实是Borland个不大不小疏漏 0
相关文章读者评论发表评论 |
|