在.Net框架中,公共語言基礎結構使用公共語言規范來綁定不同的語言。通過要求不同的語言至少要實現公共類型系統(CTS)包含在公共語言規范中的部分,公共語言基礎結構允許不同的語言使用.Net框架。因此在.Net框架中,所有的語言(C#,VB.Net,Effil.Net等)最后都被轉換為了一種通用語言:微軟中間語言(MSIL)。
MSIL是將.Net代碼轉化為機器語言的一個中間過程。它是一種介于高級語言和基于Intel的匯編語言的偽匯編語言。當用戶編譯一個.Net程序時,編譯器將源代碼翻譯成一組可以有效地轉換為本機代碼且獨立于CPU 的指令。當執行這些指令時,實時(JIT)編譯器將它們轉化為CPU特定的代碼。由于公共語言運行庫支持多種實時編譯器,因此同一段MSIL代碼可以被不同的編譯器實時編譯并運行在不同的結構上。從理論上來說,MSIL將消除多年以來業界中不同語言之間的紛爭。在.Net的世界中可能出現下面的情況:一部分代碼可以用Effil實現,另一部分代碼使用C#或VB完成的,但是最后這些代碼都將被轉換為中間語言。這給程序員提供了極大的靈活性,程序員可以選擇自己熟悉的語言,并且再也不用為學習不斷推出的新語言而煩惱了。
解密微軟中間語言的系列文章將通過一些簡單易懂的方式來揭示中間語言的復雜原理。這些原理通過詳細的例子來闡述。 <!-- leadcode('Content823012'); //-->
解密微軟中間語言MSIL之中間語言概述 Xinsoft,2004-03-02 08:27:25
在.Net框架中,公共語言基礎結構使用公共語言規范來綁定不同的語言。通過要求不同的語言至少要實現公共類型系統(CTS)包含在公共語言規范中的部分,公共語言基礎結構允許不同的語言使用.Net框架。因此在.Net框架中,所有的語言(C#,VB.Net,Effil.Net等)最后都被轉換為了一種通用語言:微軟中間語言(MSIL)。
MSIL是將.Net代碼轉化為機器語言的一個中間過程。它是一種介于高級語言和基于Intel的匯編語言的偽匯編語言。當用戶編譯一個.Net程序時,編譯器將源代碼翻譯成一組可以有效地轉換為本機代碼且獨立于CPU 的指令。當執行這些指令時,實時(JIT)編譯器將它們轉化為CPU特定的代碼。由于公共語言運行庫支持多種實時編譯器,因此同一段MSIL代碼可以被不同的編譯器實時編譯并運行在不同的結構上。從理論上來說,MSIL將消除多年以來業界中不同語言之間的紛爭。在.Net的世界中可能出現下面的情況:一部分代碼可以用Effil實現,另一部分代碼使用C#或VB完成的,但是最后這些代碼都將被轉換為中間語言。這給程序員提供了極大的靈活性,程序員可以選擇自己熟悉的語言,并且再也不用為學習不斷推出的新語言而煩惱了。
解密微軟中間語言的系列文章將通過一些簡單易懂的方式來揭示中間語言的復雜原理。這些原理通過詳細的例子來闡述。在一些例子中同時給出了源代碼和中間代碼,通過比較源代碼和中間代碼,我們可以更好地理解編譯器的局限性,指導我們編寫出更好更快的代碼。
微軟中間語言概述
1.用中間語言編寫的一個簡單程序
讓我們從經典的Hello World例子開始。首先在一個文本編輯器中輸入以下的代碼,并保存為HelloWorld.il:
.assembly HelloWorldIL {}
.method static void HelloWorld()
{
.entrypoint
ldstr "Hello World."
call void [mscorlib]System.Console::WriteLine(class System.String)
ret
}
在一個中間語言程序中,如果某一行以“.”開始,則代表這是一個傳輸給匯編工具的指令,該指令要求匯編工具執行某些操作,例如生成一個函數或類。而沒有以“.”開始的行是中間語言的代碼。在中間語言中方法通過匯編命令method來定義,匯編命令后跟方法的返回值、名稱和參數。方法體被包含在{}中。例子中的ret代表該方法的結束。
一個中間語言文件可以包含很多函數,匯編工具沒有辦法分辨應該首先執行哪一個方法。在諸如C#或VB這一類高級語言中,程序的入口方法通常都有特定的名稱,例如在C#中的public static void Main()。這就是上面的匯編工具發出錯誤提示的原因。在中間語言中,第一個被執行的方法被稱為入口函數(EntryPoint Function)。為了告訴匯編工具HelloWorld是入口函數,我們需要在代碼中增加一條匯編命令entrypoint,該命令可以放在方法體中的任何位置。需要注意的是在一個程序集中只能有一個入口函數。
中間語言代碼通常被編譯成一個模塊,該模塊隸屬于一個程序集。在.Net中模塊和程序集的概念非常重要,因此開發人員需要很清楚地了解它們。在后面的文章中我們將詳細討論.Net程序的結構。通過在代碼中加入assembly命令,可以告訴匯編工具中間代碼隸屬于那個程序集。assembly命令的格式如下:
.assembly <程序集名稱> {}
需要注意在method命令后加入了static關鍵字,這是因為每個入口函數必須是靜態的,例如在C#中我們將Main方法定義為public static void Main()。
接下來我們需要調用WriteLine方法將HelloWorld字符串輸出到屏幕。通過使用call指令(Instruction)我們可以達到這個目的。指令的格式如下:
call <return type> <namespace>.<class name>::<method name>
這里我們可以看到當調用一個方法時,中間語言和其他的編程語言有很大的區別。在中間語言中,如果需要調用一個方法,需要指定方法的全名,包括他的名稱域(namespace)、類名、返回值類型和參數的數據類型。這樣就保證了匯編工具能夠找到正確的方法。
在調用WriteLine方法時需要一個字符串參數。所有傳遞給方法或函數的參數都被保存在內存的堆棧中。在中間語言中有一個指令ldstr可以從堆棧中加載一個字符串。(堆棧是內存中的一塊區域,它被用于將參數傳輸給方法,在后面我們會詳細討論堆棧的問題)。所有的方法都從堆棧中獲取它們的參數,因此ldstr指令是必不可少的。ldstr指令的格式如下所示:
ldstr <parameter string>
我們可以用ILAsm.exe來編譯這個程序。在運行ILAsm.exe之前,首先需要確認一下該程序已經包含在了Windows操作系統的Path環境變量中。ILAsm.exe 可在下面的路徑中找到:
%windir%\Microsoft.NET\Framework\v1.0.xxxx
其中xxxx是正在使用的.NET框架的內部版本號。例如我使用的版本號是3705,則應該如下設置Path環境變量:
Set Path = %Path%;c:\Windows\Microsoft.NET\Framework\v1.0.3705
然后運行cmd.exe(開始->運行->輸入cmd->按下確認鍵)。在彈出的命令窗口中輸入:
J:\Testcode>ilasm HelloWorld.il
匯編代碼后運行程序就可以看到Hello World.的輸出。
通過上面的例子,我們了解了中間語言的程序結構,一些命令和指令。同時需要提醒大家的是中間語言是區分大小寫的。
2.改進的HelloWorld例子
在.Net中的所有語言都是面向對象的語言,但是上面的HelloWorld例子是一個結構化的例子。下面讓我們來看一下如何將它轉化為面向對象的代碼。在面向對象的編程中,我們將操作定義在類中。為了將上面的HelloWorld例子轉化為面向對象的代碼,可以使用class命令:
.class HelloWorld
{
}
class命令后緊跟的是類的名稱。類的名稱在中間語言中是可選的。同時我們還需要為該指令添加一些屬性,例如存取控制類在內存中的布局和互用性等。這樣代碼就變成了:
.assembly HelloWorldIL {}
.class public auto ansi HelloWorld extends [mscorlib]System.Object
{
.method public hidebysig static void HelloWorld() cil managed
{
.entrypoint
ldstr "Hello World."
call void [mscorlib]System.Console::WriteLine(class System.String)
ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
}
}
在代碼中用到了三個屬性:
· public:public是訪問控制屬性,它表明了對于訪問該類的成員沒有限制。
· auto:auto屬性表明了當類被加載到內存中時,在內存中的布局是由公共運行庫而不是程序決定的。
· ansi:指定ansi屬性是為了在沒有被管理和被管理的代碼之間實現無縫的轉化。在.Net中,那些不可直接應用在公共語言基礎設施之上的代碼被稱為沒有被管理的代碼,例如C、C++和VB6的代碼。我們需要一個屬性來處理被管理的代碼和沒有被管理的代碼之間的互用性。在被管理的代碼中,字符串用雙字節的Unicode字符表示,而在被管理的代碼中,字符串有可能用單字節的ANSI字符表示。指定了ansi屬性就可以在不同的代碼間轉化字符串了。
我們知道在.Net框架中,所有的類都直接或間接地繼承了System.Object類。在代碼中我們明確指定了HelloWorld繼承了System.Object。
在HelloWorld方法中加入了public、hidebysig、cil managed屬性,下面是對這些屬性的解釋:
· public:在C#或VB.Net中,當我們定義一個方法時,需要指定方法的訪問修飾符。訪問修飾符可以是public、protected、internal或private 。
· hidebysig:一個類可以繼承其他的類,hidebysig屬性保證當前類中的方法在作為父類時不會被子類繼承。例如如果HelloWorldChild類繼承了HelloWorld類,在HelloWorldChild中不會看到HelloWorld方法。
· cil managed:該屬性將在后面討論。
在高級語言中(C#,VB.Net等),每個類必須有構造函數,而且構造函數的第一行需要調用基類的構造函數。如果類中沒有構造函數,基類的構造函數將被自動調用。通常這是由編譯器自動完成的,現在我們要在的代碼中加入構造函數,該構造函數通過.ctor命令調用基類的構造函數。
小結
本文我們從經典的Hello World例子開始,通過實例了解了微軟中間語言的基本語法規則以及中間語言與其他開發語言的關系。在下一篇文章中,我們將在此基礎上,運用實例程序講述.net應用程序的格式和結構等內容。 <!-- leadcode('Content823014'); //-->
解密微軟中間語言MSIL之解析.Net應用程序 Xinsoft,2004-03-02 08:29:37
.Net應用程序由一個或多個可執行程序組成,每個可執行程序中都有元數據和可管理的代碼。.Net應用程序通常被稱為程序集。一個程序集由一個或多個部署在一起的文件組成,它通常保存一份清單,該清單確定程序集標識,指定組成程序集實現的文件,指定組成程序集的類型和資源,列舉對其他程序集的編譯時依賴項,并指定為保證程序集正確運行所需要的權限集。在運行時使用此信息來解析引用,強制版本綁定策略,并驗證已加載的程序集的完整性。
不含程序集清單的中間語言文件被稱為模塊。程序集可以是單模塊的,也可以是多模塊的。每個程序集只能夠有一個清單,該清單駐留在擁有入口函數的模塊中。圖一顯示了一個單模塊程序集的結構:
圖一 單模塊程序集的結構
從圖一中我們可以看到程序集中包含了程序集標識段,元數據段和中間語言代碼段。讓我們來看一下HelloWorld中的代碼,其中的assembly命令代表是程序集標識段,但是在其中沒有包含版本、名稱、區域性、安全性和模塊信息。讓我們在代碼中加入下面的行(代碼重用黑體標出):
.assembly DemystifyingILChapter1
{
.hash algorithm 0x00008004
.ver 1:0:0:0}
.class public auto ansi HelloWorld extends [mscorlib]System.Object
{
…
}
上面的代碼擴充了assembly命令的內容。事實上assembly命令可以包含很多其它的命令,在上面的代碼中使用了hash和ver命令。
· hash:該命令告訴VSE實現安全性所使用的哈希算法。數字0x00008004表示使用SHA1,這也是系統的缺省設置。
· ver:程序集的版本號,由四個32位整數組成。
前面在討論.Net應用程序的格式的時候,曾提到在一個可執行的應用程序中可以包含對其它模塊的引用。到目前為止我們還沒有使用任何命令來告知程序集應該生成哪一個模塊。在HelloWorld例子中我們引用了一個外部程序mscorlib。那么匯編工具正確編譯了代碼嗎?答案是肯定的。ILAsm能夠自動將HelloWorld中的代碼定義為基本模塊,并在其中引用mscorlib程序集。在下面的代碼中我們將使用命令告訴編譯器如何集成模塊。我們使用的命令仍然是assembly,不過現在帶上了extern屬性。為了正確地引用一個程序集,至少需要創作者的公鑰或公鑰Token以及程序集的版本信息。公鑰Token是SHA1哈希碼的低八位字節,它能夠唯一確定一個程序集。我們可以在C:\Winnt\assembly目錄下找到程序集的相關信息(如圖二所示)。
圖二 程序集的相關信息
在C:\Winnt\assembly中可以看到計算機上安裝的mscorlib版本是1.0.3300.0。公鑰Token是B77A5C561934E089。也許你計算機上安裝了不同版本的mscorlib。在下面的代碼中你需要用正確的版本號替代1.0.3300.0。
.module Hello.exe
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89)
.ver 1:0:3300:0
}
.assembly DemystifyingILChapter1
{
…
}
.class public auto ansi HelloWorld extends [mscorlib]System.Object
{
…
}
現在我們擁有了一個正確的.Net應用程序,該程序中提供了.Net框架所有必要的信息。下面我們將用C#和VB.Net中編寫HelloWorld程序,并將它們編譯成中間代碼。
在高級語言中實現HelloWorld例子程序
我們將在C#和VB.Net中編寫HelloWorld程序,將它們編譯成可執行程序,然后用反匯編工具ildasm.exe將執行程序反匯編為中間代碼,把它們和上面的例子進行比較。
C#
public class HelloWorld
{
public static void Main()
{
System.Console.WriteLine("Hello World.");
}
}
當生成可執行文件后,用ildasm.exe來反匯編才生成的HelloWorld.exe。在命令行中輸入:
ildasm /out=HelloWorld.txt HelloWorld.exe
查看生成的HelloWorld.txt文件,我們可以看到:
// Microsoft (R) .NET Framework IL Disassembler. Version 1.0.3705.0
// Copyright (C) Microsoft Corporation 1998-2001. All rights reserved.
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 1:0:3300:0
}
.assembly HelloWorld
{
// --- The following custom attribute is added automatically, ---
// --- do not uncomment -------
// .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::
// .ctor(bool, bool) = ( 01 00 00 01 00 00 )
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module HelloWorld.exe
// MVID: {E63F9CA9-D4C4-4826-9BE1-2B0EE3694289}
.imagebase 0x00400000
.subsystem 0x00000003
.file alignment 512
.corflags 0x00000001
// Image base: 0x03000000
//
// ============== CLASS STRUCTURE DECLARATION ==================
//
.class public auto ansi beforefieldinit HelloWorld
extends [mscorlib]System.Object
{
} // end of class HelloWorld
// =============== CLASS MEMBERS DECLARATION ===================
// note that class flags, 'extends' and 'implements' clauses
// are provided here for information only
.class public auto ansi beforefieldinit HelloWorld
extends [mscorlib]System.Object
{
.method public hidebysig static void Main() cil managed
{
.entrypoint
// Code size 11 (0xb)
.maxstack 1
IL_0000: ldstr "Hello World."
IL_0005: call void [mscorlib]System.Console::WriteLine(string)
IL_000a: ret
} // end of method HelloWorld::Main
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 1
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method HelloWorld::.ctor
} // end of class HelloWorld
// =============================================================
//*********** DISASSEMBLY COMPLETE ***********************
// WARNING: Created Win32 resource file HelloWorld.res
如果仔細察看一下上面的文件,我們會發現其中大部分的命令和指令在前面已經作了闡述。如果用VB.Net來編寫HelloWorld程序,編譯器的輸出基本上和C#一樣。因此雖然使用的語言不一樣,但是源代碼最終會編譯成相同的中間代碼,因此由于語言之間的差別產生的種種問題在.Net中都不足為道了。
小結
本文我們延續使用了《解密微軟中間語言MSIL之中間語言概述 》Hello World例子程序,詳細分析了.net應用程序的格式和結構。接下來,我們將在下一篇文章中完成對程序的調試工作。 <!-- leadcode('Content823016'); //-->
解密微軟中間語言MSIL之調試程序(1) Xinsoft,2004-03-02 08:31:39
沒有程序員敢保證沒有經過調試的代碼絕對沒有錯誤,無論他/她智商多么高,開發出來的代碼總是或多或少帶有一些錯誤(當然是無意的:-))。這些錯誤可能是簡單的語法錯誤或者復雜的邏輯錯誤。因此和其他語言一樣,我們需要中間語言的調試工具/方法。由于中間語言是比較底層的語言,因此調試工具/方法對于程序員來說更加重要。
最簡單的調試方法莫過于在程序中加入WriteLine方法,但是在中間語言中使用這種方法非常繁瑣,因為調用WriteLine方法需要三行代碼:
ldstr "Hello World"
call void [mscorlib]System.Console::WriteLine(string)
ret
如果你需要調試一個比較大的應用程序,顯然上面的方法行不通。幸運的是,微軟在.Net中提供了兩個調試工具用于調試.Net的程序集。
調試工具
.Net提供了兩個非常好的調試工具,分別是CLR調試器和運行庫調試器:
· CLR調試器(DbgCLR.exe):提供圖形界面幫助開發者調試程序。
· 運行庫調試器(Cordbg.exe):使用運行庫調試API,通過命令行對程序進行調試。
初看這兩個工具提供的是相同的功能。但是事實上它們的功能還是有所區別的。DbgCLR.exe是一個Windows應用程序,提供了用戶界面,并且很容易定義斷點和即時窗口;而Cordbg.exe使一個命令行工具,它允許開發人員通過調試腳本的方式來調試程序。本文側重于介紹DgbCLR.exe。
CLR調試器
為了展示CLR調試器的功能,我們需要編寫一個帶有錯誤的程序。下面是一個C#的程序,編譯后我們將通過利用ildasm.exe獲得它的中間語言代碼。
using System;
namespace ErrorneousApp
{
class ErrorneousClass
{
[STAThread]
static void Main(string[] args)
{
int operand1;
int operand2;
int sum;
operand1 = int.Parse(args[0]);
operand2 = int.Parse(args[1]);
sum = Add(operand1 , operand2);
Console.WriteLine(sum);
}
private static int Add(int op1, int op2)
{
// 很明顯的邏輯錯誤 :-)
return 5 * (op1 + op2);
}
}
}
我們可以看到在這段代碼中存在兩個錯誤:
· 邏輯錯誤:Add方法返回了不正確的值
· 方法錯誤:如果調用方法是沒有提供兩個參數,程序將報錯。
需要提醒大家的是,CLR調試器能夠調試的錯誤遠遠不止以上兩種。現在編譯這段代碼,生成可執行文件,然后用反匯編工具生成中間語言,其中去除了一些不重要的代碼:
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89) .ver 1:0:3300:0
}
.assembly ErrorneousApp
{
.ver 1:0:1026:17140
}
.module ConsoleApplication1.exe
.namespace ErrorneousApp
{
.class private auto ansi beforefieldinit ErrorneousClass
extends [mscorlib]System.Object
{
.method private hidebysig static void
Main(string[] args) cil managed
{
.entrypoint
.locals init ([0] int32 operand1, [1] int32 operand2, [2] int32 sum)
ldarg.0
ldc.i4.0
ldelem.ref
call int32 [mscorlib]System.Int32::Parse(string)
stloc.0
ldarg.0
ldc.i4.1
ldelem.ref
call int32 [mscorlib]System.Int32::Parse(string)
stloc.1
ldc.i4.5
ldloc.0
ldloc.1
add
mul
stloc.2
ldloc.2
call void [mscorlib]System.Console::WriteLine(int32)
ret
} // end of method ErrorneousClass::Main
.method private hidebysig static int32
Add(int32 op1, int32 op2) cil managed
{
.locals init ([0] int32 CS$00000003$00000000)
ldc.i4.5
ldarg.0
ldarg.1
add
mul
stloc.0
ldloc.0
ret
} // end of method ErrorneousClass::Add
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
} // end of method ErrorneousClass::.ctor
} // end of class ErrorneousClass
} // end of namespace ErrorneousApp
在使用代碼以前先解釋一下代碼。我們使用了Consle.WriteLine和Int.Parse方法,這兩個方法定義在外部程序集mscorlib中,因此我們需要創建一個對它們的引用。通過使用帶external參數的assembly命令我們可以達到這個目的。
然后代碼中通過class命令定義了ErrorneousClass類,并在該類中用method命令創建了Main方法,該方法是程序的入口方法。接著用local命令初始化了三個本地變量:
.locals init ([0] int32 operand1, [1] int32 operand2, [2] int32 sum)
接下來需要給這些本地變量賦值。代碼通過ldelm命令從程序的參數數組中提取相應的值賦給變量,但是在賦值之前需要用ldarg將參數(由指定索引值引用)加載到堆棧上,然后ldc命令將真正的值推送到計算堆棧上。ldc.i4.0的表示將0作為int32類型推送到計算堆棧上。接下來代碼調用System.Int32.Parse方法將字符串轉換為整數。當所有的變量都完成初始化后,代碼調用Add方法計算operand1和operand2的和。最后通過調用System.Console.WriteLine方法來顯示計算結果。
Add方法的實現也很簡單。需要提醒大家的是由于MSIL工作在基于堆棧的內存結構上,因此最后使用的變量需要最先保存。由于算法是將兩個數相加在乘以5,因此需要先用ldc.i4.5命令將5放入堆棧中,然后加載兩個被操作的數,使用add命令計算它們的和(add命令自動將堆棧中最頂層的兩個數相加),將得到的結果放回堆棧中。最后代碼調用mul命令將兩個數相乘。
如果仔細察看中間代碼,我們會發現在Main方法中并沒有直接調用Add方法。這是因為C#編譯器用內嵌代碼替代了對靜態方法的調用。
調試MSIL代碼
調試中間代碼的同時我們需要程序數據庫文件ErrorneuosApp.pdb,在該文件中包含了調試和工程狀態信息。我們可以利用ilasm工具來獲得該文件。
ilasm errorneousApp.il /debug
現在運行DbgClr.exe并打開ErroneousApp.il文件(如圖一所示),然后設定需要調試的可執行文件(如圖二所示)。現在就可以調試程序了。開發人員可以設定斷點,查看寄存器和內存中的值等。下面讓我們一一了解這些功能。
圖一 CLR調試器
圖二 選擇要調試的程序 <!-- leadcode('Content823018'); //-->
解密微軟中間語言MSIL之調試程序(2) Xinsoft,2004-03-02 08:34:14
中斷程序的執行
CLR調試器最基本的目的是顯示被調試程序的狀態信息。有很多工具可以監視和修改程序的狀態,但是大部分的工具在程序中斷時才能夠使用。調試器在程序運行到一個斷點或遇到錯誤時將中斷程序的運行。
開發人員可以點擊中間代碼中任何可執行的行左邊的空白處就可以設置位置斷點了;也可以通過菜單上調試->新斷點來設置位置斷點(如圖三所示)。第二種方法允許開發人員設定帶有條件的位置斷點,開發人員可以設定三種類型的斷點:
· "函數斷點"使程序在執行到達指定函數內的指定位置時中斷。
· "文件斷點"使程序在執行到達指定文件內的指定位置時中斷。
· "地址斷點"使程序在執行到達指定的內存地址時中斷。
圖三 設置新斷點
開發人員還可以根據設定條件判斷是否需中斷程序,或者根據斷點的命中次數來確定是否在斷點中斷程序(點擊次數是斷點被點擊的次數。對于位置斷點,它是程序執行達到指定位置并滿足可能有的斷點條件的次數,命中次數決定執行中斷前點擊發生的次數)。當程序執行到斷點位置時,調試器就會就會計算實現設定好的表達式。如果表達式的結果為真(在"斷點條件"對話框中是"為真")或者表達式的值更改時(在"斷點條件"對話框中為"已更改")則調試器點擊斷點,如果點擊斷點并且點擊次數正確,調試器將中斷程序執行。
局部變量窗口
在局部變量窗口中顯示了在當前上下文關系中的局部變量變量和它們的值。開發人員可以通過選擇調用堆棧或線程來切換上下文。使用局部變量窗口的好處在于能夠在程序運行的時候改變某個變量的值,開發人員只需要雙擊變量的值,輸入新的值就可以了。
圖四 局部變量窗口
快速監視窗口
開發人員可以通過快速監視窗口獲得一個變量或者表達式的值。
監視窗口
監視窗口同快速監視窗口的功能類似,唯一不同的是快速監視窗口是模態窗口。
寄存器窗口
寄存器窗口中顯示當前寄存器中的狀態。最近被改變了的寄存器值用紅色顯示。
圖五 寄存器窗口
調用堆棧窗口
通過調用堆棧窗口,開發人員可以看到當前在堆棧中的方法或函數。除了方法或函數的名稱外,窗口中還提供了諸如參數名稱和類型、參數值、行號和偏移量等信息。
圖六 調用堆棧窗口
內存窗口
開發人員可以用內存窗口來監視大的緩沖區、字符串或者其他在監視窗口中無法完全顯示的數據。如果開發人員希望跳轉到內存的某個地址,只需要在地址欄填入相應的地址或者表達式即可。缺省情況下,內存窗口動態監視表達式,當表達式的結果發生變化時,地址窗口也會相應發生變化。
調試程序
現在我們有了這些調試工具,調試中間代碼程序就變得很容易。首先打開局部變量窗口監視局部變量。如果我們輸入的參數是3和4,當單步執行到sum的值從0變為35時,可以注意到代碼中不僅有加法,而其還有乘法。我們可以去掉乘法來修正這個錯誤。修改的代碼如下所示:
.method private hidebysig static void
Main(string[] args) cil managed
{
…
//ldc.i4.5
ldloc.0
ldloc.1
add
//mul
stloc.2
…
} // end of method ErrorneousClass::Main
調試庫文件
在CRL調試器中,開發人員可以逐語句執行庫文件。如果需要調試庫文件的話,開發人員需要一個測試裝置(Test Harness)文件,引用了庫中的類或方法的可執行文件,以及可執行文件和庫文件的程序數據庫(.pdb)文件。開發人員需要在CLR調試器中打開測試裝置文件,然后在調試時就可以進入需要調試的外部庫的代碼行。
小結
在這篇文章中我們介紹了用與調試中間語言程序的調試工具。盡管中間語言平時不會經常使用到,但是對中間語言的熟悉可以幫助程序員深刻理解.Net程序是如何工作的,并且根據.Net程序的特點編寫出快速高效的代碼。同時中間語言也可以應用到反向工程中,當然這涉及到知識產權的問題。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
