亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > Delphi > 正文

Delphi中兩個BUG的分析與修復

2019-11-18 18:05:52
字體:
來源:轉載
供稿:網友
 

Delphi中兩個BUG的分析與修復

  在使用Delphi 7進行三層數據庫開發時,遇到了兩個小問題,通過反復試驗,終于找出了Delphi 7中的兩個小BUG并進行了修復(好像Delphi 6中也有相同的BUG),撰寫此文與大家一起分享成功的喜悅。我也是初學Delphi,文中一定存在不少說的不對的地方,還請各位朋友多多指正。

  BUG1.傳參時中文被截斷的問題:

  BUG再現的方法:

  后臺用SQL Server 2000,里面有一個XsHeTong表用于試驗,您可以根據您的實際情況進行調整。

  先創建一個數據服務器:新建項目,創建一個遠程數據模塊,上面放置ADOConnection、ADODataSet、DataSetPRovider各一,并做好相應設置,其中ADODataSet的ComamndText留空,并把它的Option中的poAllowCommandText設置為True。編譯運行。

  再創建客戶端程序:新建項目,在窗體上放置DCOMConnection,連上前面上創建的數據服務器,再放置一個ClientDataSet,把它的連接設成這里的DCOMConnection,并設置它的ProviderName為上面的服務器上的DataSetProvider的名字。最后放置DataSource和DBGrid各一并作相應設置用于查看結果,再放置一Button用于測試。

  在Button的OnClick中寫下類似于下面的代碼(這里我用了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只在三層構架上出現。

   用SQL Server事件探查器探查提交到SQL Server上運行的語句,發現兩層構架與三層構架的情況有以下不同:

  兩層構架:
  exec sp_executesql N'Insert into XsHeTong(HTH, GCMC) values(@P1,@P2)', N'@P1 varchar(15),@P2 varchar(100)', '12345', '會截斷的中文字'

  三層構架:
  exec sp_executesql N'Insert into XsHeTong(HTH, GCMC) values(@P1,@P2)', N'@P1 varchar(5),@P2 varchar(7)', '12345', '會截斷

  顯然,兩層構架時,參數的長度是按實際庫結構傳的,三層構架時,參數長度是按實際參數的字符串長度傳的,而實際字符串長度又似乎是算錯了,沒有把一個中文當兩個字符長度處理。

  沒有辦法只好進行跟蹤調試,為了調試Delphi的VCL庫,需要在工程選項的“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
   if VarIsNull(Value) then
    Result := -1
   else if VarIsArray(Value) then
    Result := VarArrayHighBound(Value, 1) + 1
   else if TVarData(Value).VType = varOleStr then
    begin
     Result := Length(PWideString(@TVarData(Value).VOleStr)^); //出問題的行
     if Result = 0 then
      Result := -1;
    end
   else
    Result := SizeOf(OleVariant);
  end;

  就是在這個函數中計算實參的長度的,它把Value中的值取出地址,并把它作為一個WideString的指針去求字符串長度,結果就導致了“會截斷的中文字”這個字符串的長度變成了7,而不是14。

  問題找到了,解決起來也就不困難了,只要簡單的把
  Result := Length(PWideString(@TVarData(Value).VOleStr)^); //出問題的行
  改成
  Result := Length(PAnsiString(@TVarData(Value).VOleStr)^); //沒問題了
  就可以了。

  但是這樣就會導致求英文字符串的長度時長度被加倍了,所以也可以把這一行改成:
  Result := Length(Value);

  這樣,不管是中文還是英文還是中英混合的字符串就都可求得正確的長度了。這就我至今仍百思不解的問題,為什么Borland要繞個圈子通過指針去求參數值的長度呢?哪位朋友知道的話還請給我解釋一下,非常感謝!

  有些朋友可能會有疑問,為什么在不通過三層構架來做的時候不產生這個字符串被截斷的問題呢?答案并不復雜,在直接通過ADOCommand來向SQL Server發送命令時,它是按表結構來決定參數長度的。它會先向SQL Server發一條

  SET FMTONLY ON select HTH,GCMC from XsHeTong SET FMTONLY OFF

  來獲取表結構。而在三層構架下,TCustomADODataSet內部雖然也是用TADOCommand對象來發命令,但它卻在取得表結構的后,并不用這個值來作為傳參長度,而是重新去按實際參數來計算長度,結果就導致了錯誤。

 

  BUG2.ClientDataSet的Lookup字段的問題:

  BUG再現的方法:

  新建工程,在上面放置兩個ClientDataSet,分別為cds1和cds2,它的數據來源任意,其中cds1為主數據集,在里面增加一個新的Lookup字段,這個Lookup字段根據cds1中的一個字符型的字段值到cds2中找出對應值來。

  運行程序,一般來說是正常的,但是一旦cds1的被Lookup字段中的值出現了一個單引號"'"(您可以修改或新增一條記錄,輸入單引號試試),立即會導致出錯: Unterminated string 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
   if (i = Fields.Count - 1) and (loPartialKey in Options) then
    ValStr := Format('''%s*''',[VarToStr(Value)]) else
    ValStr := Format('''%s''',[VarToStr(Value)]);

  改成:

  ftString, ftFixedChar, ftWideString, ftGUID:
   if (i = Fields.Count - 1) and (loPartialKey in Options) then
    ValStr := Format('''%s*''',[ StringReplace(VarToStr(Value),'''','''''',[rfReplaceAll])])
   else
    ValStr := Format('''%s''',[ StringReplace(VarToStr(Value),'''','''''',[rfReplaceAll])]);

  也就是在生成過濾條件字符串時把條件的過濾值中的單引號全部一個變兩。

  為了確保這樣修改的正確性,我查看了TCustomADODataSet中的對應的LocateRecord過程(在用TADODataSet中的Lookup字段時不會因單引號出錯,只在用TCustomClientDataSet時有這樣的情況),它的處理方法與TCustomClientDataSet稍有不同,它是通過GetFilterStr函數來構造過濾條件的,但在GetFilterStr中,它正確處理了單引號的問題。所以這樣來看,沒有在TCustomClientDataSet的LocateRecord中正確處理單引號的問題,確實是Borland一個不大不小的疏漏。


上一篇:Delphi,編譯文件(第8頁)

下一篇:Delphi的組件讀寫機制

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
學習交流
熱門圖片

新聞熱點

疑難解答

圖片精選

網友關注

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美高清视频一区二区| 26uuu久久噜噜噜噜| 欧美疯狂xxxx大交乱88av| 亚洲最新av网址| 中文字幕日本欧美| 国产精品久久二区| 欧美天天综合色影久久精品| 欧美高清无遮挡| 欧美高清视频在线| 欧美成人精品一区二区| 国产精品一区二区av影院萌芽| 亚洲精品456在线播放狼人| 91国产高清在线| 精品日本高清在线播放| 激情成人中文字幕| 美女av一区二区三区| 欧美国产日韩一区二区| 日韩视频在线观看免费| 日韩中文字幕视频在线观看| 色综合久久精品亚洲国产| 国产成人综合久久| 狠狠色狠狠色综合日日五| 中文字幕日本欧美| 色天天综合狠狠色| 国产精品444| 日韩在线中文视频| 高清一区二区三区日本久| 成人xxxx视频| 蜜月aⅴ免费一区二区三区| 日本亚洲欧洲色α| 国产小视频91| 国产精品专区h在线观看| 亚洲a成v人在线观看| 色诱女教师一区二区三区| 国产精品综合不卡av| 日韩精品免费视频| 国产精品私拍pans大尺度在线| 欧美日韩激情小视频| 26uuu另类亚洲欧美日本一| 国产视频亚洲视频| 亚洲成人av在线播放| 亚洲视频电影图片偷拍一区| 亚洲一区二区三区xxx视频| 国产视频精品一区二区三区| 国产精品入口夜色视频大尺度| 精品久久久久久久久久久久久| 亚洲精品久久久久国产| 欧美性猛交xxxx乱大交| 国产精品成人v| 国模叶桐国产精品一区| 欧美性xxxx极品高清hd直播| 国产精品私拍pans大尺度在线| 国产精品偷伦免费视频观看的| 欧美wwwxxxx| 久久人人爽人人爽人人片亚洲| 久久人人爽人人爽人人片av高请| 欧美日韩午夜剧场| 欧美最近摘花xxxx摘花| 国内精品模特av私拍在线观看| 成人xxxx视频| 中国日韩欧美久久久久久久久| 欧美超级乱淫片喷水| 亚洲欧洲第一视频| 一本一道久久a久久精品逆3p| 欧美一区二区三区艳史| 亚洲欧美日韩一区二区三区在线| 亚洲国产精品系列| 久久久久久中文字幕| zzijzzij亚洲日本成熟少妇| 成人黄色大片在线免费观看| 亚洲男人天堂手机在线| 中文字幕日韩在线播放| 91高清视频免费观看| 亚洲国产一区二区三区四区| 日韩精品极品毛片系列视频| 成人av在线天堂| 日韩在线观看免费av| 国产精品电影网站| 精品人伦一区二区三区蜜桃网站| 国产精品高潮呻吟视频| 91精品国产91久久久久久吃药| 国产日韩欧美夫妻视频在线观看| 亚洲美女精品久久| 亚州av一区二区| 欧美激情中文字幕在线| 亚洲精品久久久久中文字幕欢迎你| 97国产真实伦对白精彩视频8| 奇米四色中文综合久久| 午夜精品视频网站| 国产精品6699| 亚洲色图狂野欧美| 91久久久国产精品| 日韩免费电影在线观看| 日韩免费视频在线观看| 欧美一区二区三区精品电影| 日韩激情第一页| 亚洲欧美精品在线| 日韩精品在线观| 日韩欧美在线中文字幕| 日韩在线观看免费高清| 日韩69视频在线观看| 国产免费观看久久黄| 亚洲欧美一区二区三区四区| 国产一区二区三区直播精品电影| 精品国产美女在线| 亚洲视频欧美视频| 国产91久久婷婷一区二区| 欧美日韩成人在线视频| 欧美日韩亚洲视频| 美女视频黄免费的亚洲男人天堂| 日韩免费在线播放| 欧美视频裸体精品| 大伊人狠狠躁夜夜躁av一区| 色播久久人人爽人人爽人人片视av| 久久精品精品电影网| 亚洲精品视频二区| 97人人爽人人喊人人模波多| www日韩中文字幕在线看| 亚洲伊人第一页| 欧美一级大片在线观看| 国产69久久精品成人| 欧美另类xxx| 欧美精品激情blacked18| 国产+成+人+亚洲欧洲| 91精品国产777在线观看| 日本亚洲欧洲色| 不卡伊人av在线播放| 日韩免费av一区二区| 一区二区日韩精品| 91精品国产网站| 亚洲精品国精品久久99热一| 成人av.网址在线网站| 色悠悠久久久久| 97av在线播放| 日韩中文字幕免费视频| 成人国产精品色哟哟| 亚洲精品久久久久久久久| 久久久久久18| 日韩高清av一区二区三区| 久久精品国亚洲| 亚洲大胆人体视频| 日韩欧美一区二区三区| 91精品国产高清久久久久久91| 欧美精品久久久久久久久久| 91在线国产电影| 久久久久久国产免费| 茄子视频成人在线| 一本一本久久a久久精品综合小说| 国产精品视频xxxx| 精品久久久久久中文字幕大豆网| 日韩av免费网站| 5566日本婷婷色中文字幕97| 九九热精品视频| 国产精品xxx视频| 日韩福利在线播放| 久久青草精品视频免费观看| 91在线精品视频| 91国产在线精品| 国产精品av在线播放| 国产精品99久久久久久久久久久久| 91av在线播放视频| 国产亚洲精品美女久久久| 日韩欧美成人免费视频| 欧美日韩电影在线观看|