NtUtils是 Delphi 中的 Windows 系統程式設計框架,它提供了一組比常規 Winapi/Ntapi 標頭具有更好的錯誤處理和語言整合的函數,並結合了常用的程式碼片段和智慧資料類型。
您可以在專用儲存庫中找到一些範例程式碼。
該庫具有分層結構,總共三層:
Winapi.*.pas
和庫Ntapi.*.pas
標頭;不過,如果名稱發生衝突,可能需要明確指定名稱空間前綴。System.SysUtils
、 System.Rtti
和System.Generics.Collections
。因此,您需要的一切都已包含在最新的免費版本 Delphi 中。作為獎勵,在沒有 RTTI(也稱為反射)的情況下編譯控制台應用程式會產生非常小的可執行檔。有關更多詳細信息,請參閱示例。
由於將庫中的每個檔案包含到專案中通常是多餘的,因此您可以設定 Delphi 進行檔案自動發現。這樣,您可以在uses
部分指定一個單元,Delphi 將自動將其及其依賴項包含到專案中。若要設定 Delphi 執行搜尋的資料夾,請前往 Project -> Options -> Building -> Delphi Compiler 並將下列行新增至搜尋路徑:
.NtUtilsLibrary
.NtUtilsLibraryHeaders
.NtUtilsLibraryNtUiLib
如果您的專案的資料夾名稱或位置不同,您需要相應地調整這些行。
該程式庫透過傳回不成功的TNtxStatus值來向呼叫者指示失敗。 TNtxStatus
(在 NtUtils.pas 中定義)是一個結構,用於儲存錯誤代碼(與NTSTATUS
、 HRESULT
和 Win32 錯誤相容)以及有關嘗試操作性質的元數據,例如失敗的位置、堆疊追蹤和其他詳細信息,例如開放呼叫的預期/請求的存取遮罩或查詢/設定呼叫的資訊類別值。若要檢查TNtxStatus
是否成功,請使用其IsSuccess
方法。若要存取或設定底層錯誤代碼(取決於其類型和呼叫者的首選項),請使用Status
、 HResult
、 HResultAllowFalse
、 Win32Error
、 Win32ErrorOrSuccess
、 IsHResult
、 IsWin32
等屬性。
如果您喜歡使用異常,則可以隨時對給定的TNtxStatus
呼叫RaiseOnError()
。請注意,除非您確實想在不導入System.SysUtils
的情況下使用異常(這是可能的),否則最好包含 NtUiLib.Exceptions ,它帶來專用的ENtError
異常類別(派生自內建EOSError
)。
NtUiLib.Errors 附加了四種將TNtxStatus
值表示為字串的方法。例如,如果值0xC0000061
的錯誤來自於嘗試變更令牌的會話 ID,則這些方法將傳回下列資訊:
方法 | 傳回的字串 |
---|---|
Name | STATUS_PRIVILEGE_NOT_HELD |
Description | A required privilege is not held by the client |
Summary | Privilege Not Held |
ToString | NtSetInformationToken returned STATUS_PRIVILEGE_NOT_HELD |
如果您想更進一步並向使用者顯示一個漂亮的訊息框,NtUiLib.Errors.Dialog 提供了ShowNtxStatus()
。此外,包含 NtUiLib.Exceptions.Dialog 將帶來必要的反射支援並進一步豐富對話框。下面是它的外觀範例:
TNtxStatus
支援捕獲堆疊追蹤(預設為停用)。若要啟用它,請將NtUtils.CaptureStackTraces
設為 True。請記住,以有意義的方式顯示堆疊追蹤需要為可執行檔配置偵錯符號的產生。不幸的是,Delphi 只能輸出.map
檔案(透過 Project -> Options -> Building -> Delphi Compiler -> Linking -> Map File 配置),這通常是不夠的。您需要第 3 方map2dbg工具將它們轉換為.dbg
文件,以便符號 API 可以理解它們。雖然.dbg
檔案可能就足夠了,但最好透過cv2pdb轉換為現代.pdb
來進一步處理它們。
若要自動產生偵錯符號,請將以下建置後事件新增至您的專案:
map2dbg.exe $(OUTPUTPATH)
cv2pdb64.exe -n -s. -p$(OUTPUTNAME).pdb $(OUTPUTPATH)
Delphi 不包含垃圾收集器,因此只有幾種類型是開箱即用的:記錄、字串、動態陣列和介面。另一方面,類別和指標需要明確清理,這(以其安全形式)需要使用try-finally區塊,因此使程式顯著複雜化。為了解決這個問題,該程式庫包含了記憶體和其他資源的自動生命週期管理工具,在 DelphiUtils.AutoObjects 中實作。透過使用此模組中的類型,我們指示編譯器自動產生異常安全碼,用於計算引用並自動釋放函數尾聲中的物件。此模組為可能需要清理的各種類型的資源定義了多個介面。它引入了以下層次結構:
圖 LR;
子圖 id1[任何資源]
自動釋放
結尾
子圖 id2[THandle 值]
句柄
結尾
子圖 id3[Delphi 類別]
IAutoObject[IAutoObject<T>]
結尾
子圖 id4[A 指標]
IAutoPointer[IAutoPointer<P>]
結尾
子圖id5[記憶體區域]
記憶體[記憶體<P>]
結尾
IAutoReleasable --> IHandle;
IAutoReleasable --> IAutoObject;
IAutoReleasable --> IAutoPointer;
IAutoPointer --> IMemory;
IAutoReleasable
是需要對(自動)清理採取操作的所有資源的基本型別。 IHandle
充當 THandle 值定義的資源的包裝器。 IAutoObject<T>
是一個通用包裝器,用於自動釋放 Delphi 類別(即從 TObject 派生的任何內容)。 IAutoPointer<P>
定義了一個類似的接口,用於釋放動態分配的指標(其中區域的大小無關)。 IMemory<P>
為已知大小的記憶體區域提供了包裝器,可以透過類型化指標存取這些記憶體區域,例如託管和非託管裝箱記錄。
使用該設施的方法如下:
使用以下介面之一定義需要維護(可能共用)物件所有權的每個變數:
使用自動助手來分配/複製/捕獲自動物件:
必要時,使用左側轉換有助於避免重複類型資訊並可以縮短語法。
例如,以下是使用經典方法處理 TStringList 的安全性程式碼:
var
x: TStringList;
begin
x := TStringList.Create;
try
x.Add( ' Hi there ' );
x.SaveToFile( ' test.txt ' );
finally
x.Free;
end ;
end ;
正如您可以想像的那樣,在此函數中使用更多物件將顯著且非線性地增加其複雜性。或者,這裡是使用IAutoObject的等效程式碼,並且可以更好地擴展:
uses
DelphiUtils.AutoObjects;
var
x: IAutoObject<TStringList>;
begin
x := Auto.From(TStringList.Create);
x.Self.Add( ' Hi there ' );
x.Self.SaveToFile( ' test.txt ' );
end ;
編譯器將必要的清理程式碼傳送到函數尾聲中,並確保即使發生異常也能執行。此外,這種方法允許維護底層物件的共享所有權,這使您可以保存比當前函數壽命更長的參考(例如,透過在匿名函數中捕獲它並返回它)。如果您不需要此功能並希望維護一個在函數退出時釋放物件的擁有者,您可以進一步簡化語法:
uses
NtUtils;
var
x: TStringList;
begin
x := Auto.From(TStringList.Create).Self;
x.Add( ' Hi there ' );
x.SaveToFile( ' test.txt ' );
end ;
這段程式碼仍然與最初的程式碼等效。在內部,它會建立一個隱藏的局部變數來儲存介面並隨後釋放該物件。
當使用動態記憶體分配時,可以方便地使用左側轉換,如下所示:
var
x: IMemory<PByteArray>;
begin
IMemory(x) := Auto.AllocateDynamic( 100 );
x.Data[ 15 ] := 20 ;
end ;
您也可以建立裝箱(在堆上指派)託管記錄,允許共用值類型,就好像它們是參考類型一樣。請注意,它們還可以包括 Delphi 字串和動態數組等託管欄位 - 編譯器會發出自動釋放它們的程式碼:
type
TMyRecord = record
MyInteger: Integer;
MyArray: TArray<Integer>;
end ;
PMyRecord = ^TMyRecord;
var
x: IMemory<PMyRecord>;
begin
IMemory(x) := Auto.Allocate<TMyRecord>;
x.Data.MyInteger := 42 ;
x.Data.MyArray := [ 1 , 2 , 3 ];
end ;
由於Delphi使用引用計數,如果兩個物件具有循環依賴關係,仍然有可能洩漏記憶體。您可以透過使用弱引用來防止這種情況發生。這樣的引用並不能延長生命週期,並且當目標物件被銷毀時,儲存它們的變數會自動變成nil 。您需要先將弱引用升級為強引用,然後才能使用它。有關詳細信息,請參閱 DelphiUtils.AutoObjects 中的Weak<I> 。
有一些別名可用於常用的可變大小指標類型,以下是一些範例:
句柄使用IHandle類型(請參閱 DelphiUtils.AutoObjects),它遵循上面討論的邏輯,因此它們不需要明確關閉。您也可以找到 IHandle 的一些別名(IScmHandle、ISamHandle、ILsaHandle 等),這些別名只是為了程式碼可讀性而提供。
如果您需要將句柄值的所有權放入 IHandle,則需要一個實作此介面並知道如何釋放底層資源的類別。例如, NtUtils.Objects 為需要呼叫NtClose
的核心物件定義了此類。它還為Auto
附加了一個輔助方法,允許透過Auto.CaptureHandle(...)
按值擷取核心句柄。要建立非擁有的 IHandle,請使用Auto.RefHandle(...)
。
記錄、類別和枚舉的名稱以T
開頭並使用 CamelCase(例如: TTokenStatistics
)。指向記錄或其他值類型的指標以P
開頭(例如: PTokenStatistics
)。介面名稱以I
開頭(例如: ISid
)。常數使用 ALL_CAPITALS。標頭層中具有已知正式名稱(例如 Windows SDK 中定義的類型)的所有定義均使用指定此名稱的SDKName
屬性進行標記。
大多數函數使用以下命名約定:子系統的前綴,末尾帶有x (Ntx、Ldrx、Lsax、Samx、Scmx、Wsx、Usrx,...)+ 操作 + 目標/物件類型/等。函數名稱也使用駝峰命名法。
該程式庫面向 Windows 7 或更高版本,包括 32 位元和 64 位元版本。不過,某些功能可能僅在最新的 64 位元版本的 Windows 11 上可用。如果函式庫函數依賴 Windows 7 上可能不存在的 API,它將使用延遲導入並在執行時間檢查可用性。
Delphi 附帶了豐富的反射系統,該函式庫在NtUiLib層中使用該系統。 Headers層中定義的大多數類型都使用自訂屬性(請參閱 DelphiApi.Reflection)進行修飾來實現它。這些裝飾發出有用的元數據,幫助庫在運行時精確表示複雜的數據類型(如 PEB、TEB、USER_SHARED_DATA),並用一行程式碼產生令人驚訝的報告。
以下是使用 NtUiLib.Reflection.Types 從 Ntapi.NtSecApi 表示TSecurityLogonSessionData
的範例:
這裡概述了不同模組的用途。
支援單位 | 描述 |
---|---|
DelphiUtils.AutoObjects | 自動資源生命週期管理 |
DelphiUtils.AutoEvents | 多訂閱者匿名事件 |
DelphiUtils.Arrays | TArray 助手 |
DelphiUtils.Lists | 遺傳雙鍊錶原語 |
DelphiUtils.Async | 非同步 I/O 支援定義 |
DelphiUtils.ExternalImport | Delphi 外部關鍵字 IAT 幫助程序 |
DelphiUtils.RangeChecks | 範圍檢查助手 |
實用程式 | 常見庫類型 |
NtUtils.SysUtils | 字串操作 |
NtUtils.錯誤 | 錯誤碼轉換 |
NtUiLib.錯誤 | 錯誤代碼名稱查找 |
NtUiLib.Exceptions | SysUtils異常集成 |
DelphiUiLib.Strings | 字串美化 |
DelphiUiLib.Reflection | 基本 RTTI 支持 |
DelphiUiLib.Reflection.Numeric | 數字類型的 RTTI 表示 |
DelphiUiLib.Reflection.Records | 記錄類型的 RTTI 表示 |
DelphiUiLib.Reflection.Strings | 字串的 RTTI 美化 |
NtUiLib.Reflection.Types | 常見類型的 RTTI 表示 |
NtUiLib.控制台 | 控制台 I/O 助手 |
NtUiLib.任務對話框 | 基於任務對話框的 GUI |
NtUiLib.Errors.Dialog | GUI 錯誤對話框 |
NtUiLib.Exceptions.Dialog | GUI 例外對話框 |
系統單位 | 描述 |
---|---|
NtUtils.ActCtx | 啟動上下文 |
NtUtils.AntiHooking | 取消掛鉤並直接系統調用 |
NtUtils.com | COM、IDispatch、WinRT |
NtUtils.Csr | CSRSS/SxS 註冊 |
NtUtils.Dbg幫助 | DbgHelp 和除錯符號 |
NtUtils.調試 | 偵錯物件 |
NtUtils.Dism | DISM接口 |
NtUtils.Environment | 環境變數 |
NtUtils.Environment.User | 使用者環境變數 |
NtUtils.Environment.Remote | 其他行程的環境變數 |
NtUtils.Files | Win32/NT 檔名 |
NtUtils.Files.Open | 文件和管道打開/創建 |
NtUtils.Files.Operations | 文件操作 |
NtUtils.Files.Directories | 檔案目錄枚舉 |
NtUtils.Files.FltMgr | 過濾器管理器API |
NtUtils.Files.Mup | 多個 UNC 提供者 |
NtUtils.Files.Volumes | 成交量操作 |
NtUtils.Files.Control | FSCTL 操作 |
NtUtils.ImageHlp | PE解析 |
NtUtils.ImageHlp.Syscalls | 系統呼叫號檢索 |
NtUtils.ImageHlp.DbgHelp | 沒有 DbgHelp 的公共符號 |
NtUtils.Jobs | 作業對象和孤島 |
NtUtils.Jobs.Remote | 跨行程作業物件查詢 |
NtUtils.Ldr | LDR例程和解析 |
NtUtils.Lsa | LSA政策 |
NtUtils.Lsa.審計 | 審核政策 |
NtUtils.Lsa.Sid | SID查找 |
NtUtils.Lsa.登入 | 登入會話 |
NtUtils.Manifests | Fusion/SxS 清單產生器 |
NtUtils.內存 | 內存操作 |
NtUtils.MiniDumps | Minidump格式解析 |
NtUtils.Objects | 內核物件和句柄 |
NtUtils.Objects.Snapshots | 處理快照 |
NtUtils.Objects.命名空間 | NT 物件命名空間 |
NtUtils.Objects.Remote | 跨進程句柄操作 |
NtUtils.Objects.Compare | 手柄對比 |
NtUtils.Packages | 應用程式包和包系列 |
NtUtils.Packages.SRCache | 狀態儲存庫快取 |
NtUtils.Packages.WinRT | 基於 WinRT 的套件信息 |
NtUtils.Power | 電源相關功能 |
NtUtils.進程 | 處理對象 |
NtUtils.Processes.Info | 處理查詢/設定訊息 |
NtUtils.Processes.Info.Remote | 透過程式碼注入處理查詢/設置 |
NtUtils.進程.模組 | 跨進程LDR枚舉 |
NtUtils.Processes.Snapshots | 行程列舉 |
NtUtils.Processes.Create | 通用流程建立定義 |
NtUtils.Processes.Create.Win32 | Win32進程建立方法 |
NtUtils.Processes.Create.Shell | Shell進程建立方法 |
NtUtils.Processes.Create.Native | NtCreateUserProcess 等。 |
NtUtils.Processes.Create.Manual | Nt建立進程Ex |
NtUtils.Processes.Create.Com | 基於COM的流程創建 |
NtUtils.Processes.Create.Csr | 透過 SbApiPort 建立進程 |
NtUtils.Processes.Create.Package | 應用程式激活 |
NtUtils.Processes.Create.Remote | 透過程式碼注入創建進程 |
NtUtils.Processes.Create.Clone | 行程複製 |
NtUtils.Profiles | 用戶和 AppContainer 配置文件 |
NtUtils.Registry | 註冊表項 |
NtUtils.Registry.Offline | 離線蜂巢操作 |
NtUtils.Registry.VReg | 基於筒倉的註冊表虛擬化 |
NtUtils.Sam | SAM資料庫 |
NtUtils.Sections | 節/內存投影對象 |
NtUtils.Security | 安全描述符 |
NtUtils.Security.Acl | ACL 和 ACE |
NtUtils.Security.Sid | SID |
NtUtils.Security.AppContainer | AppContainer 和功能 SID |
NtUtils.Shellcode | 程式碼注入 |
NtUtils.Shellcode.Dll | DLL注入 |
NtUtils.Shellcode.exe | EXE注入 |
NtUtils.Svc | 供應鏈服務 |
NtUtils.Svc.SingleTaskSvc | 服務實施 |
NtUtils.Synchronization | 同步原語 |
NtUtils.系統 | 系統資訊 |
NtUtils.TaskScheduler | 任務調度程序 |
NtUtils.Threads | 執行緒對象 |
NtUtils.Tokens.Info | 線程查詢/設定訊息 |
NtUtils.Threads.Worker | 線程工作者(線程池) |
NtUtils.Tokens | 令牌對象 |
NtUtils.Tokens.Impersonate | 令牌冒充 |
NtUtils.Tokens.Logon | 用戶和 S4U 登入 |
NtUtils.Tokens.AppModel | 令牌 AppModel 政策 |
NtUtils.Transactions | 交易(TmTx)對象 |
NtUtils.Transactions.Remote | 強制流程進入事務 |
NtUtils.UserManager | 使用者管理器服務 (Umgr) API |
NtUtils.Wim | Windows 映像 (*.wim) API |
NtUtils.WinSafer | 更安全的API |
NtUtils.WinStation | 終端伺服器API |
NtUtils.WinUser | User32/GUI API |
NtUtils.WinUser.WindowAffinity | 窗口親和力修飾 |
NtUtils.WinUser.WinstaLock | 鎖定和解鎖視窗站 |
NtUtils.XmlLite | 透過 XmlLite 進行 XML 解析和製作 |
NtUiLib.自動完成 | 編輯控制項的自動完成 |
NtUiLib.AutoCompletion.Namespace | NT 物件命名空間自動完成 |
NtUiLib.自動完成.Sid | SID自動完成 |
NtUiLib.AutoCompletion.Sid.Common | 簡單 SID 名稱提供者/識別器 |
NtUiLib.AutoCompletion.Sid.AppContainer | AppContainer 和套件 SID 提供者/識別器 |
NtUiLib.AutoCompletion.Sid.Capability | 能力 SID 提供者/識別器 |
NtUiLib.WinCred | 憑證對話框 |