人人做人人澡人人爽欧美,国产主播一区二区,久久久精品五月天,羞羞视频在线观看免费

當前位置:蘿卜系統下載站 > 技術開發教程 > 詳細頁面

CLR 調試接口的架構與應用 [2] 調試框架

CLR 調試接口的架構與應用 [2] 調試框架

更新時間:2022-06-06 文章作者:未知 信息來源:網絡 閱讀次數:

如 Don Box 在《.NET本質論 第1卷:公共語言運行庫》一書的第10章中介紹, CLR 調試框架是一個由 CLR 提供的,面向工具開發商的,支持調試功能的最小功能集。與 JVM 的 JDI (Java Debug Interface)不同,CLR 調試框架不僅僅關注于虛擬機一級的調試,同時也提供了 Native 一級調試的統一接口。使得現有工具開發商能夠以最小代價移植并支持 CLR 調試功能。而對 CLR 調試更高層次或更細粒度的支持,則是由前面提到的 Profiling API 完成。
CLR 調試接口主要通過 mscordbi.dll 提供的 ICorDebug 接口,讓調試器通過進程內或進程外方式,對被調試 CLR 進行監控。而 ICorDebug 接口可以通過 .NET Framework SDK 中 includecordebug.idl 或 includecordebug.h 直接使用。對 C#/Delphi 也可以直接 reference/import 在 SDK 的 lib 目錄下的 cordebug.tlb 類型庫,獲得調用包裝類。下面示例將都使用 C# 作為描述語言。
在使用時,可以直接獲取ICorDebug接口,并調用其Initialize/Terminate方法進行初始化和析構操作,框架代碼如下:


以下為引用:

using CORDBLib;

namespace cordbg
{
public class Debugger : IDisposable
{
private ICorDebug _dbg;

public void Run()
{
_dbg = new CorDebugClass();

try
{
_dbg.Initialize();

// 構造調試環境

// 處理調試事件
}
finally
{
_dbg.Terminate();
}
}
...
}
[MTAThread]
static void Main(string[] args)
{
using(Debugger dbg = new Debugger())
{
dbg.Run();
}
}
}





注意 CLR 調試環境必須在 MTA 的線程套間上下文(Thread Apartment Context)中運行,因此必須將入口函數的 STAThread 屬性改成 MTAThread,否則會在調試接口調用回調函數時出現異常。對應于 COM 中的 CoInitializeEx(NULL, COINIT_MULTITHREADED) 調用。
在創建了 ICorDebug 調試接口后,需要針對托管和非托管調試事件,提供調試事件回調接口。可以將實現了調試事件接口 ICorDebugManagedCallback/ICorDebugUnmanagedCallback 的實例,使用 ICorDebug 接口的 SetManagedHandler/SetUnmanagedHandler 方法,掛接到調試系統上,在適當的時候由調試系統回調,通知調試器有調試事件發生。實現上可以通過 ManagedEventHandler/UnmanagedEventHandler 兩個單獨的類,抽象出對托管和非托管調試事件的處理機制,將之掛接到調試器上,如:

以下為引用:

namespace cordbg
{
public class DebugEventHandler
{
protected Debugger _dbg;

public DebugEventHandler(Debugger dbg)
{
this._dbg = dbg;
}
}

public class ManagedEventHandler : DebugEventHandler, ICorDebugManagedCallback
{
public ManagedEventHandler(Debugger dbg) : base(dbg)
{
}

// 實現 ICorDebugManagedCallback 接口
}

public class UnmanagedEventHandler : DebugEventHandler, ICorDebugUnmanagedCallback
{
public UnmanagedEventHandler(Debugger dbg) : base(dbg)
{
}

// 實現 ICorDebugUnmanagedCallback 接口
}

public class Debugger : IDisposable
{
public void Run()
{
//...

_dbg.SetManagedHandler(new ManagedEventHandler(this));
_dbg.SetUnmanagedHandler(new UnmanagedEventHandler(this));

//...
}
}
}





在準備好了調試事件處理器后,就可以根據需要,創建或者附加到目標調試進程上。ICorDebug 提供了 CreateProcess 方法對 Win32 API 中 CreateProcess 函數進行了包裝。

以下為引用:

public abstract interface ICorDebug
{
public abstract new void CreateProcess (
string lpApplicationName,
string lpCommandLine,
_SECURITY_ATTRIBUTES lpProcessAttributes,
_SECURITY_ATTRIBUTES lpThreadAttributes,
int bInheritHandles,
uint dwCreationFlags,
IntPtr lpEnvironment,
System.String lpCurrentDirectory,
uint lpStartupInfo,
uint lpProcessInformation,
CorDebugCreateProcessFlags debuggingFlags,
ICorDebugProcess ppProcess)
}

BOOL CreateProcess(
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);





可以看到這兩個函數的參數基本上是一一對應的,只不過ICorDebug.CreateProcess函數多了一個輸入debuggingFlags參數指定調試標志和一個輸出ppProcess參數返回創建進程的控制接口。
兩個 _SECURITY_ATTRIBUTES 類型的安全屬性,一般來說可以設置為空,使用缺省設置。

以下為引用:

_SECURITY_ATTRIBUTES sa = new _SECURITY_ATTRIBUTES();

sa.nLength = (uint)Marshal.SizeOf(sa);
sa.bInheritHandle = Win32.BOOL.FALSE;
sa.lpSecurityDescriptor = IntPtr.Zero;





值得注意的是 dwCreationFlags 指定了創建進程是否支持 Native 模式的調試,也就是前面 SetUnmanagedHandler 方法調用的接口是否起作用?梢愿鶕闆r如命令行選項決定是否支持 Native 調試模式,如

以下為引用:

namespace Win32
{
public struct CreationFlag
{
public const uint DEBUG_PROCESS = 0x00000001;
public const uint DEBUG_ONLY_THIS_PROCESS = 0x00000002;

public const uint CREATE_SUSPENDED = 0x00000004;

public const uint DETACHED_PROCESS = 0x00000008;

public const uint CREATE_NEW_CONSOLE = 0x00000010;

public const uint NORMAL_PRIORITY_CLASS = 0x00000020;
public const uint IDLE_PRIORITY_CLASS = 0x00000040;
public const uint HIGH_PRIORITY_CLASS = 0x00000080;
public const uint REALTIME_PRIORITY_CLASS = 0x00000100;

public const uint CREATE_NEW_PROCESS_GROUP = 0x00000200;
public const uint CREATE_UNICODE_ENVIRONMENT = 0x00000400;

public const uint CREATE_SEPARATE_WOW_VDM = 0x00000800;
public const uint CREATE_SHARED_WOW_VDM = 0x00001000;
public const uint CREATE_FORCEDOS = 0x00002000;

public const uint BELOW_NORMAL_PRIORITY_CLASS = 0x00004000;
public const uint ABOVE_NORMAL_PRIORITY_CLASS = 0x00008000;

public const uint CREATE_BREAKAWAY_FROM_JOB = 0x01000000;
}
}

namespace cordbg
{
public class Debugger : IDisposable
{
private void Run()
{
//...

uint dwCreationFlag = CreationFlag.CREATE_NEW_CONSOLE;

if(Options.NativeMode)
{
dwCreationFlag |= CreationFlag.DEBUG_ONLY_THIS_PROCESS | CreationFlag.DEBUG_PROCESS;
}

//...
}
}
}





比較麻煩的是指定啟動參數的 lpStartupInfo 參數和返回進程信息的 lpProcessInformation 參數。C# 在導入 cordebug.tlb 類型庫時,都沒有處理這兩個類型,必須自己定義之:

以下為引用:

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
public struct STARTUPINFO
{
public uint cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public uint dwX;
public uint dwY;
public uint dwXSize;
public uint dwYSize;
public uint dwXCountChars;
public uint dwYCountChars;
public uint dwFillAttribute;
public uint dwFlags;
public ushort wShowWindow;
public ushort cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}

[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public uint dwProcessId;
public uint dwThreadId;
}





使用的時候則需要先在堆棧中構造此結構的值類型對象,然后通過 unsafe 形式指針,或者 Marshal 手工處理將之轉換為地址。這里為了避免使用較為 dirty 的 unsafe 方式,通過 Marshal.AllocHGlobal 分配全局內存;然后調用 Marshal.StructureToPtr 將結構復制到內存中;調用 CreateProcess 時使用此內存的地址;調用返回后使用 Marshal.PtrToStructure 從內存中獲得結構的內容;最后調用 Marshal.FreeHGlobal 釋放全局內存。簡要代碼如下:

以下為引用:

//...

STARTUPINFO si = new STARTUPINFO(); // 構造時所有字段已清零

si.cb = (uint)Marshal.SizeOf(si);

PROCESS_INFORMATION pi = new PROCESS_INFORMATION();

IntPtr ppi = Marshal.AllocHGlobal(Marshal.SizeOf(pi)),
psi = Marshal.AllocHGlobal(Marshal.SizeOf(si));

Marshal.StructureToPtr(si, psi, true);
Marshal.StructureToPtr(pi, ppi, true);

_dbg.CreateProcess(Options.FileInfo.FullName, Options.CommandLine,
ref sa, ref sa, BOOL.FALSE, dwCreationFlag, IntPtr.Zero,
Options.CurrentDirectory, (uint)psi.ToInt32(), (uint)ppi.ToInt32(),
CorDebugCreateProcessFlags.DEBUG_NO_SPECIAL_OPTIONS, out _proc);

pi = (PROCESS_INFORMATION)Marshal.PtrToStructure(ppi, typeof(PROCESS_INFORMATION));

Marshal.FreeHGlobal(ppi);
Marshal.FreeHGlobal(psi);

Native.CloseHandle(pi.hProcess);

//...





而將調試器附加到現有進程上則相對簡單得多,接口方法如下:

以下為引用:

public abstract interface ICorDebug
{
public abstract new void DebugActiveProcess(uint id, int win32Attach, ICorDebugProcess ppProcess)
}

BOOL DebugActiveProcess(
DWORD dwProcessId
);





與 Win32 API 的 DebugActiveProcess 相比,ICorDebug.DebugActiveProcess 增加的 win32Attach 指定是否允許 Native 模式調試,ppProcess 返回目標調試進程的控制接口。

以上簡要介紹了 CLR 調試接口在使用時如何構造調試環境,以及對調試目標進程的創建和附加的方法。下一節將整體上對托管和非托管的各種調試事件做一個介紹,然后再針對不同的調試功能開始

溫馨提示:喜歡本站的話,請收藏一下本站!

本類教程下載

系統下載排行

網站地圖xml | 網站地圖html
主站蜘蛛池模板: 保亭| 信丰县| 华池县| 岗巴县| 枣强县| 乌拉特前旗| 凉山| 铜梁县| 石台县| 镇原县| 文登市| 临澧县| 堆龙德庆县| 调兵山市| 伽师县| 平湖市| 恩平市| 韩城市| 寿阳县| 墨竹工卡县| 阿巴嘎旗| 绍兴县| 宁远县| 丹江口市| 全州县| 武穴市| 晋江市| 望江县| 买车| 葵青区| 屏东县| 长武县| 莱芜市| 松原市| 昭觉县| 信宜市| 屏东市| 遂川县| 九寨沟县| 鸡西市| 禄劝|