麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 編程 > Delphi > 正文

Delphi中兩個BUG的分析與修復(fù)

2019-11-18 18:05:52
字體:
供稿:網(wǎng)友
 

Delphi中兩個BUG的分析與修復(fù)

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

  BUG1.傳參時中文被截?cái)嗟膯栴}:

  BUG再現(xiàn)的方法:

  后臺用SQL Server 2000,里面有一個XsHeTong表用于試驗(yàn),您可以根據(jù)您的實(shí)際情況進(jìn)行調(diào)整。

  先創(chuàng)建一個數(shù)據(jù)服務(wù)器:新建項(xiàng)目,創(chuàng)建一個遠(yuǎn)程數(shù)據(jù)模塊,上面放置ADOConnection、ADODataSet、DataSetPRovider各一,并做好相應(yīng)設(shè)置,其中ADODataSet的ComamndText留空,并把它的Option中的poAllowCommandText設(shè)置為True。編譯運(yùn)行。

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

  在Button的OnClick中寫下類似于下面的代碼(這里我用了XsHeTong的表和它的兩個字段HTH(char 15)、GCMC(varchar 100),您可以根據(jù)你的實(shí)際測試情況進(jìn)行調(diào)整):

  with ClientDataSet1 do
  begin
   Close;
   CommandText := 'Insert Into XsHeTong(HTH, GCMC) values(:HTH,:GCMC)';
   Params[0].AsString := '12345';
   Params[1].AsString := '會截?cái)嗟闹形淖?;
   Execute;
   Close;
   CommandText := 'Select * from XsHeTong';
   Open;
  end;

  運(yùn)行程序,點(diǎn)擊按鈕,看到記錄被插入了,可惜結(jié)果并不正確,“會截?cái)嗟闹形淖帧弊兂闪恕皶財(cái)唷保珱]有中文的“12345”倒是正確的插入了。

  BUG分析與修復(fù):

  為了對照起見,我試著直接用一個ADOConnection和ADOCommand、ADOTable進(jìn)行C/S構(gòu)架測試,結(jié)果是正確的,中文字不會被切斷。這說明了此BUG只在三層構(gòu)架上出現(xiàn)。

   用SQL Server事件探查器探查提交到SQL Server上運(yùn)行的語句,發(fā)現(xiàn)兩層構(gòu)架與三層構(gòu)架的情況有以下不同:

  兩層構(gòu)架:
  exec sp_executesql N'Insert into XsHeTong(HTH, GCMC) values(@P1,@P2)', N'@P1 varchar(15),@P2 varchar(100)', '12345', '會截?cái)嗟闹形淖?

  三層構(gòu)架:
  exec sp_executesql N'Insert into XsHeTong(HTH, GCMC) values(@P1,@P2)', N'@P1 varchar(5),@P2 varchar(7)', '12345', '會截?cái)?/FONT>

  顯然,兩層構(gòu)架時,參數(shù)的長度是按實(shí)際庫結(jié)構(gòu)傳的,三層構(gòu)架時,參數(shù)長度是按實(shí)際參數(shù)的字符串長度傳的,而實(shí)際字符串長度又似乎是算錯了,沒有把一個中文當(dāng)兩個字符長度處理。

  沒有辦法只好進(jìn)行跟蹤調(diào)試,為了調(diào)試Delphi的VCL庫,需要在工程選項(xiàng)的“Compiler Options”中選上“Use Debug DCUs”。

  先跟蹤客戶端程序,ClientDataSet1.Execute后,先后經(jīng)歷了TCustomClientDataSet.Exectue、TCustomeClientDataSet.PackageParams、TCustomClientDataSet.DoExecute等一系列函數(shù),一直到AppServer.AS_Execute(ProviderName, CommandText, Params, OwnerData); 把請求提交到服務(wù)器均沒有什么異常情況,看來問題出在服務(wù)器端。

  對服務(wù)器進(jìn)行跟蹤,反復(fù)試驗(yàn)后,我把重點(diǎn)落在了TCustomADODataSet.PSSetCommandText函數(shù)身上,經(jīng)過反復(fù)細(xì)致的跟蹤,目標(biāo)越來越精確:TCustomADODataSet.PSSetParams、TParameter.Assign、TParameter.SetValue、VarDataSize。終于找到了BUG的源頭:VarDataSize函數(shù),下面是它的代碼:

  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;

  就是在這個函數(shù)中計(jì)算實(shí)參的長度的,它把Value中的值取出地址,并把它作為一個WideString的指針去求字符串長度,結(jié)果就導(dǎo)致了“會截?cái)嗟闹形淖帧边@個字符串的長度變成了7,而不是14。

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

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

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

  有些朋友可能會有疑問,為什么在不通過三層構(gòu)架來做的時候不產(chǎn)生這個字符串被截?cái)嗟膯栴}呢?答案并不復(fù)雜,在直接通過ADOCommand來向SQL Server發(fā)送命令時,它是按表結(jié)構(gòu)來決定參數(shù)長度的。它會先向SQL Server發(fā)一條

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

  來獲取表結(jié)構(gòu)。而在三層構(gòu)架下,TCustomADODataSet內(nèi)部雖然也是用TADOCommand對象來發(fā)命令,但它卻在取得表結(jié)構(gòu)的后,并不用這個值來作為傳參長度,而是重新去按實(shí)際參數(shù)來計(jì)算長度,結(jié)果就導(dǎo)致了錯誤。

 

  BUG2.ClientDataSet的Lookup字段的問題:

  BUG再現(xiàn)的方法:

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

  運(yùn)行程序,一般來說是正常的,但是一旦cds1的被Lookup字段中的值出現(xiàn)了一個單引號"'"(您可以修改或新增一條記錄,輸入單引號試試),立即會導(dǎo)致出錯: Unterminated string constant(未結(jié)束的字符串常量)。

  BUG分析與修復(fù):

  這個BUG的產(chǎn)生原因要比上一個明顯得多,一定是沒有正確處理單引號帶來的副作用引起的。

  同樣的,我們來跟蹤VCL的源碼

  運(yùn)行程序,出錯時打開Call Stack窗口(在View->Debug Windows)菜單中,查看函數(shù)調(diào)用情況,前面的一些調(diào)用是顯而易見的,沒有問題,我們從跟Lookup有關(guān)的地方開始查原因,第一個與Lookup有關(guān)的函數(shù)調(diào)用是TField.CalcLookupValue,我們在這個函數(shù)中設(shè)置斷點(diǎn),重新運(yùn)行程序,中斷下來后,進(jìn)行單步調(diào)試。

  TCustomClientDataSet.Lookup->TCustomClientDataSet.LocateRecord

  經(jīng)過上面的幾次函數(shù)調(diào)用,很快的,我們就把目標(biāo)定在了LocateRecord過程中,在這個過程中,它根據(jù)Lookup字段的設(shè)置情況,生成相應(yīng)的過濾條件,然后到目標(biāo)數(shù)據(jù)集中把對應(yīng)的值找到,錯就錯在過濾條件的生成上了。比如,我們要按cds1中Cust字段(假設(shè)是001)的值到cds2中按CustID字段值找到對應(yīng)的CustName字段值。那生成的條件就應(yīng)該是[CustID] = '001',但如果Cust的值是aa'bb,按生成的條件就會變成[CustID] = 'aa'bb',顯然導(dǎo)致了一個未結(jié)束的字符串常量。

  通常我們解決單引號中又出現(xiàn)單引號的情況,只需把引號中的引號寫兩就行了,這里也是一樣,只要讓生成的條件變成[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中的對應(yīng)的LocateRecord過程(在用TADODataSet中的Lookup字段時不會因單引號出錯,只在用TCustomClientDataSet時有這樣的情況),它的處理方法與TCustomClientDataSet稍有不同,它是通過GetFilterStr函數(shù)來構(gòu)造過濾條件的,但在GetFilterStr中,它正確處理了單引號的問題。所以這樣來看,沒有在TCustomClientDataSet的LocateRecord中正確處理單引號的問題,確實(shí)是Borland一個不大不小的疏漏。


發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表

圖片精選

主站蜘蛛池模板: 国产高潮国产高潮久久久91 | 色综合久久久久久 | 久久久日韩精品一区二区三区 | 久久艹精品 | 国产1区在线 | 91精品久久久久久久久 | 国产成人免费精品 | 精品国产一区二区三区久久久 | 久久久一区二区精品 | 欧美一级做a | 久久性生活免费视频 | 国产精品午夜在线观看 | 午夜av男人的天堂 | 久久精品欧美视频 | 99精品视频在线免费观看 | 亚洲国产高清自拍 | 欧美一级黄视频 | 国产亚洲精品视频中文字幕 | 久久精品毛片 | 全黄裸片武则天一级第4季 偿还电影免费看 | 好吊色欧美一区二区三区四区 | 一色桃子av大全在线播放 | 特级毛片免费视频 | 福利免费在线观看 | 欧美一区二区三区中文字幕 | 国产又粗又爽又深的免费视频 | 成熟女人特级毛片www免费 | 一级毛片真人免费播放视频 | 国产精品成人一区二区三区电影毛片 | 日韩三区视频 | 国产激情精品一区二区三区 | 久久久久久久不卡 | 91社 | 免费看性xxx高清视频自由 | 一级网站 | 国产精品视频中文字幕 | 免费久久精品 | 亚洲啊v在线观看 | 最新欧美精品一区二区三区 | 久久精品一级 | 五月天堂av91久久久 |