天天看點

煮茶入門講座如何調試你的程式

主題:煮茶入門講座如何調試你的程式

主講:D10.天地弦

Demo Tools:DebugTest   和EurekaLog v4.57 Enterprise Delphi.BCB  (入門群裡有共享)

Demo下載下傳位址:

<a href="http://files.cnblogs.com/DKSoft/DebugTest.rar">http://files.cnblogs.com/DKSoft/DebugTest.rar</a>

<a href="http://www.delphifans.com/SoftView/SoftView_1682.html">http://www.delphifans.com/SoftView/SoftView_1682.html</a>

D10.天地弦 14:48:59

我在這個社群裡經常看到一些問題

adoMain.Sql.Text :='select * from Orders'+

+' and OrderId = ' + edOrderId.Text

+' and CustomerID = ' + edCustomerId.Text

D10.天地弦 14:49:05

為什麼不行啊,這樣的問題

D10.天地弦 14:49:24

這種問題講完以後如果還有人問大夥都不要理了...

D10.天地弦 14:53:02

隻是一個比喻...

D10.天地弦 14:52:43

大家打開DebugTest.bpg

 D10.天地弦 14:53:53

打開沒有?

D10.天地弦 14:52:29

現在開始第一個段落

風魂 14:54:00

開了

赤無極 14:54:04

打開聽

D10.天地弦 14:54:09

大家配置一下ADOConnection...

D10.天地弦 14:54:19

都會配置吧

D10.天地弦 14:54:32

是連到Northxxx資料庫的...

D10.天地弦 14:55:17

配置好了支一聲...

B13.往生無語 14:55:30

好了

B6.銀狼 14:55:35

 D10.天地弦 14:55:47

可以運作程式了..

在CustomerId輸入值 'VINET'

D10.天地弦 14:57:43

Start Query..

D10.天地弦 14:58:55

Start Query了沒有?

B6.銀狼 14:58:58

輸入VINET,出錯

D10.天地弦 14:59:03

y

B6.銀狼 14:59:06

不輸入,OK

D10.天地弦 14:59:10

en

D10.天地弦 14:59:16

大家先做到這一部

 D10.天地弦 15:01:09

好了,現在我們來開始調試

D10.天地弦 15:01:46

即然是StartQuery出來的錯誤,那我們在BtnStartQuery.Click事件中開始分析

D10.天地弦 15:02:01

回到Delphi界面

D10.天地弦 15:02:15

打開主窗體...

D10.天地弦 15:02:31

看到procedure TFMainForm.btnStartQueryClick(Sender: TObject);

const

  MAINSQL = 'select * from Orders';

var

  lcondi: string;

  lsql: string;

begin

  lcondi := InnerGetQueryCondition;

  lsql := MAINSQL;

  if lcondi &lt;&gt; '' then lsql := lsql + ' Where ' + lcondi;

  adoMain.Close;

  adoMain.CommandText := lsql;

  adoMain.Open;

end;

D10.天地弦 15:02:35

這個函數...

D10.天地弦 15:03:32

在函數體的第一個設定一個斷點開始查找為什麼會出錯...

尖椒牛柳 15:03:33

看懂了

D10.天地弦 15:04:12

這堂課主要是教大家如果調試,以後可以自己搞定自己的問題...

D10.天地弦 15:04:22

D10.天地弦 15:04:36

設定斷點....按F9運作程式....

 D10.天地弦 15:05:55

在CustomerId輸入值按StartQuery...

D10.天地弦 15:06:21

程式會傳回到Delphi設定了斷點的地方

{

D10.天地弦 15:06:49

這裡提一下F7,F8,F9這三個功能

D10.天地弦 15:07:42

F7是進入函數體....

F8是運作這一行,不進入函數體

F9從目前行開始運作

}

 D10.天地弦 15:08:00

現在按F8

D10.天地弦 15:08:06

執行目前行....

D10.天地弦 15:08:43

按F8直到彈出錯誤提示...

D10.天地弦 15:09:37

大夥可以找到是哪一行執行的時候出現了錯誤?

D10.天地弦 15:10:47

打開一個資料集的時候出錯,結合彈出的提示可以看出來是CommandText設定出錯了...

D10.天地弦 15:10:49

對不...

F6.Jeanvi 15:11:14

嗯.

D10.天地弦 15:11:20

這樣的錯誤是最好找的....

D10.天地弦 15:11:31

好,我們再來一次

小小 15:11:51

好 

小小 15:12:02

繼續啊 

D10.天地弦 15:12:10

在adoMain.Open這一行停下來....

 D10.天地弦 15:14:52

我們現在在Open之前看看到底Sql語句是什麼東東

B6.銀狼 15:16:12

怎麼出來的啊

D10.天地弦 15:16:13

可以看到這個adoMain.CommandText的值(Sql)是什麼了...

D10.天地弦 15:16:31

在adoMain.Open這一行停下來

F6.Jeanvi 15:16:35

ctr+f7

D10.天地弦 15:16:45

adoMain.CommandText標明這個

D10.天地弦 15:16:51

按Ctrl + F7

D10.天地弦 15:17:16

select * from Orders Where  CustomerID = VINET

看到這個值後,覺得這個值有錯嗎?

D10.天地弦 15:17:50

如果覺得沒有錯馬上打開

SQL 查詢分析器将剛剛看到的值Sql粘貼到查詢分析器裡

B6.銀狼 15:18:50

收到

D10.天地弦 15:19:09

再執行...

D10.天地弦 15:19:29

在這裡可以發現是不是Sql語句的問題....

D10.天地弦 15:19:46

多數是Sql語句不正确引起的...

F2.歪歪寶 15:19:49

D10.天地弦 15:20:29

可以發現CustomerId是個字元字段。應該

select * from Orders Where  CustomerID = 'VINET'

D10.天地弦 15:20:32

這樣....

D10.天地弦 15:20:47

這樣我們就可以找到錯誤所在

D10.天地弦 15:21:36

現在大家都知道修改程式找到這個錯誤....

D10.天地弦 15:21:43

可不可以....

D10.天地弦 15:22:14

可以的吱一聲....

尖椒牛柳 15:22:16

可以

總結:

D10.天地弦 15:25:15

上面這一小節,要學到怎麼樣在程式出錯的時候

1.大概找到程式出錯的位置

2.根據錯誤提示,初步判斷出錯的東東

3.檢視對象變量的值.

D10.天地弦 15:25:47

還有要懂得在調試程式中活用F7,F8,F9

{暫時告一段落}

B6.銀狼 15:28:51

繼續DLL吧

B13.往生無語 15:28:58

是的。。生成的檔案有目前的cpu資訊。資訊。還有出錯的語句

 B13.往生無語 15:29:19

但是出現記憶體錯誤的時候,比如記憶體洩漏就不行。 

 D10.天地弦 15:29:33

繼續運作

看第二個錯誤...

mmoSql裡的的sql語句有錯麼?

D10.天地弦 15:32:31

為什麼會出錯...

尖椒牛柳 15:32:39

D10.天地弦 15:32:46

如果删除掉注釋看看...

尖椒牛柳 15:33:13

删除注釋是可以的

B6.銀狼 15:33:42

是啊

D10.天地弦 15:33:46

可以确定是注釋惹的禍

D10.天地弦 15:33:54

難道不用能注釋?

D10.天地弦 15:34:00

想想

D10.天地弦 15:34:18

/*-------------------------------

-------------------------------*/

SELECT *

       FROM Orders

       ORDER BY OrderId

D10.天地弦 15:34:38

發現變成這樣也不會出錯

D10.天地弦 15:34:50

原來注釋是可以用的...

 D10.天地弦 15:36:59

有些問題要靠自己去想象,,,

D10.天地弦 15:37:10

去不斷去測試

D10.天地弦 15:38:33

在設計時将mmoSql的值copy到adoMain.CommandText裡面

 D10.天地弦 15:37:41

---------------------------

Debugger Exception Notification

Project DebugTest.exe raised exception class EOleException with message '不正常地定義參數對象。提供了不一緻或不完整的資訊。'. Process stopped. Use Step or Run to continue.

OK   Help  

D10.天地弦 15:38:07

可以從錯誤資訊上看出是參數惹的禍

 D10.天地弦 15:39:47

再設定adoMain的Acitve為true

JERRY 15:40:07

那是什麼東東

D10.天地弦 15:40:17

發現出現了同樣的錯誤對不以

尖椒牛柳 15:40:41

超過字元長度了

D10.天地弦 15:40:46

看看他的Parmaters

D10.天地弦 15:40:47

D10.天地弦 15:40:50

不是

尖椒牛柳 15:41:01

哦有“:

D10.天地弦 15:41:16

發現他的Parameters裡無故出錯了幾個參數...

D10.天地弦 15:41:17

是不是

尖椒牛柳 15:41:35

有“:”的話,Delphi會認為是參數的

D10.天地弦 15:41:43

對...

D10.天地弦 15:41:50

這也是Delphi的不對了...

尖椒牛柳 15:41:58

上次這個問題我也調試了半天

D10.天地弦 15:42:09

把注釋裡的東東都解析出參數來搞...

再看下個列子

D10.天地弦 15:46:45

大夥把

FMainForm裡的

  / ebugIntf := CreateDebugService;

D10.天地弦 15:46:58

constructor TFMainForm.Create(AOwner: TComponent);

  inherited;

  // FebugIntf := CreateDebugService;

這個注釋搞掉

再運作程式...

D10.天地弦 15:48:43

還是運作主程式...

D10.天地弦 15:49:01

D10.天地弦 15:49:10

是這一行惹的禍...

D10.天地弦 15:49:20

function CreateDebugService(): IDebugIntf;

D10.天地弦 15:49:27

看看這個函數,,有沒有什麼問題...

D10.天地弦 15:50:20

這樣的錯誤,不注意還很難找...

 D10.天地弦 15:54:01

function CreateDebugService: IDebugIntf; stdcall;

  result := TDebugService.Create();

D10.天地弦 15:54:08

看到沒有

飄塵 15:54:10

function CreateDebugService(): IDebugIntf;stdcall

D10.天地弦 15:54:24

讓大家找隻是告訴大夥不要粗心...

D10.天地弦 15:54:35

DLL内的原型是有stdcall的

B6.銀狼 15:54:48

沒看到啊

D10.天地弦 15:54:57

在function CreateDebugService(): IDebugIntf;加上

D10.天地弦 15:55:00

就可以運作了

D10.天地弦 15:55:15

  DebugIntf;

function CreateDebugService(): IDebugIntf;stdcall;

implementation

function CreateDebugService; external 'dkTools.dll' name 'CreateDebugService';

end.

 D10.天地弦 15:55:22

這樣再運作就可以了...

B6.銀狼 15:55:53

unit DebugIntf;

interface

type

  IDebugIntf = interface

    function RandomFormNo: WideString; safecall;

    function GetGuid: WideString; safecall;

  end;

B6.銀狼 15:55:55

F6.Jeanvi 15:55:55

赤無極 15:55:59

找到啦

D10.天地弦 15:56:04

DLL要注意函數的導出原形...

Locet 15:56:13

到哪裡了?剛才在忙

赤無極 15:56:17

library dkTools;

{ Important note about DLL memory management: ShareMem must be the

  first unit in your library's USES clause AND your project's (select

  Project-View Source) USES clause if your DLL exports any procedures or

  functions that pass strings as parameters or function results. This

  applies to all strings passed to and from your DLL--even those that

  are nested in records and classes. ShareMem is the interface unit to

  the BORLNDMM.DLL shared memory manager, which must be deployed along

  with your DLL. To avoid using BORLNDMM.DLL, pass string information

  using PChar or ShortString parameters. }

uses

  ExceptionLog,

  SysUtils,

  Classes,

  DebugIntfImp in 'DebugIntfImp.pas',

  DebugIntf in '..\DebugIntf.pas';

{$R *.res}

exports

  CreateDebugService;

D10.天地弦 15:56:36

D10.天地弦 15:56:53

好了馬上要結束這場入門課了...

D10.天地弦 15:57:13

要調式DLL内的程式,

Locet 15:57:14

.........

F6.Jeanvi 15:57:15

D10, 用DLL的子塊可否有辦法直接共享一個資料連接配接? 如果不用包的話.

D10.天地弦 15:57:21

先切換到DLL工程

飄塵 15:57:42

DLL内部的的調試。哈哈

D10.天地弦 15:57:50

F6,用包來得更爽,講完再讨論這個...

D10.天地弦 15:58:08

切換到DLL工程

飄塵 15:58:09

你還沒有講了

D10.天地弦 15:58:23

設定一下

D10.天地弦 15:58:35

設定一下Run Paramters就可以了

D10.天地弦 15:59:17

設定好了就可以像單個程式一樣,在DLL設定斷點,調試程式了

F2.歪歪寶 15:59:46

這不是必須用那個工具了

D10.天地弦 15:59:54

D10.天地弦 15:59:58

不是...

D10.天地弦 16:00:04

這個是自帶的功能

D10.天地弦 16:00:16

那個隻是我推薦的一個調試工具

D10.天地弦 16:00:21

幫了我不少忙了....

D10.天地弦 16:00:41

設定好沒有?

D10.天地弦 16:00:53

設定好了就按F9

赤無極 16:01:03

好啦

D10.天地弦 16:01:05

還有,剛剛調用方式那裡改了沒有

D10.天地弦 16:01:12

Stdcall加上沒有

D10.天地弦 16:01:18

加上也要編譯一下主程式...

D10.天地弦 16:02:00

運作程式發現程式斷了...

D10.天地弦 16:02:06

這一句是

B6.銀狼 16:02:19

看不到圖檔

D10.天地弦 16:02:23

剛剛在主程式的constructor函數内調用的...

D10.天地弦 16:02:35

  FDebugIntf := CreateDebugService;

D10.天地弦 16:02:43

建立DLL内的一個服務...

D10.天地弦 16:03:06

因為這裡執行這句,是以會調用到DLL裡的那個函數了...

小小 16:03:39

嗯,Continue...

D10.天地弦 16:03:40

來到這裡沒有?

D10.天地弦 16:03:50

按F7進去吧

D10.天地弦 16:03:59

constructor TDebugService.Create;

  inherited Create();

  FDate := FormatDateTime('YYYYMMDD', Now());

D10.天地弦 16:04:03

進入了這裡...

D10.天地弦 16:04:18

建立這個對象...

D10.天地弦 16:04:34

如果剛剛那裡按F8就不會來到這裡了...

D10.天地弦 16:04:44

知道為什麼吧...

D10.天地弦 16:04:58

這就是F7,F8的差別...

D10.天地弦 16:05:27

D10.天地弦 16:05:49

這兩個Btn都是通過調用DLL内的函數來服務的...

D10.天地弦 16:06:14

現在我們來看看BtnGetFormNo是怎麼工作的...

D10.天地弦 16:06:37

先看看主程式裡,是調用的什麼涵數...

D10.天地弦 16:06:47

procedure TFMainForm.btnGetFormNoClick(Sender: TObject);

  edtFormNo.Text := FDebugIntf.RandomFormNo;

D10.天地弦 16:06:54

來了沒有?

D10.天地弦 16:07:12

可以發現是

D10.天地弦 16:07:23

調用的是RandomFormNo這個方法...

D10.天地弦 16:07:41

那我們在RandomFormNo中設定一個斷點..

D10.天地弦 16:08:10

function TDebugService.RandomFormNo: WideString;

  result := FDate + '-' + InnerGetRandom(999);

D10.天地弦 16:08:26

運作程式

D10.天地弦 16:08:37

單擊BtnGetFormNo按扭

D10.天地弦 16:08:50

就會在這裡斷了...

D10.天地弦 16:08:55

這樣可以跟蹤程式

D10.天地弦 16:09:06

如果DLL有錯也是這樣跟蹤除錯的....

D10.天地弦 16:09:37

有時候DLL内經常調試不到

D10.天地弦 16:10:04

大夥可以用ShowMessage的方法顯示出值來看,隻是麻煩一點...

D10.天地弦 16:10:13

完了,今天的課完了...

B6.銀狼 16:10:25

3Q,D10

小小 16:10:28

嗯,也可以用Watch視窗看的

 B6.銀狼 16:10:52

那就問點進階的 了

D10.天地弦 16:10:55

 嗯,是可以...

D10.天地弦 16:11:06

在for中間Watch用了...

小小 16:11:22

是啊 

D10.天地弦 16:11:34

下課...。自由活動...