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 上可用。一些示例包括 AppContainers 和 ntdll 系统调用取消挂钩。如果库函数依赖于 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 | 凭据对话框 |