NtUtilsは、Delphi での Windows システム プログラミング用のフレームワークで、通常の Winapi/Ntapi ヘッダーよりも優れたエラー処理と言語統合を備えた一連の関数を、頻繁に使用されるコード スニペットとインテリジェントなデータ型と組み合わせて提供します。
専用のリポジトリにサンプル コードがいくつかあります。
ライブラリは、合計 3 つのレイヤーからなる階層構造になっています。
Winapi.*.pas
とライブラリNtapi.*.pas
ヘッダーをプログラム内で混在させることができます。ただし、名前が競合する場合には、名前空間プレフィックスを明示的に指定する必要がある場合があります。System.SysUtils
、 System.Rtti
、およびSystem.Generics.Collections
に依存します。したがって、必要なものはすべて、Delphi の最新の無料バージョンにすでに含まれています。おまけに、RTTI (別名リフレクション) なしでコンソール アプリケーションをコンパイルすると、非常に小さな実行可能ファイルが生成されます。詳細については、例を参照してください。
ライブラリのすべてのファイルをプロジェクトに含めるのは通常冗長であるため、ファイルの自動検出用に Delphi を設定できます。このようにして、 uses
セクションでユニットを指定すると、Delphi はそのユニットとその依存関係をプロジェクトに自動的に含めます。 Delphi が検索を実行するフォルダーを設定するには、[プロジェクト] -> [オプション] -> [ビルド] -> [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
をインポートせずに例外を使用したい場合を除き (これは可能です)、専用のENtError
例外クラス (組み込みのEOSError
から派生) をもたらす NtUiLib.Exceptions を含めることをお勧めします。
NtUiLib.Errors は、 TNtxStatus
値を文字列として表現するための 4 つのメソッドをアタッチします。たとえば、値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
ファイル ([プロジェクト] -> [オプション] -> [ビルド] -> [Delphi コンパイラ] -> [リンク] -> [マップ ファイル] で設定) しか出力できませんが、これでは一般に十分ではありません。シンボル API が理解できるように、それらを.dbg
ファイルに変換するには、サードパーティのmap2dbgツールが必要です。 .dbg
ファイルで十分かもしれませんが、 cv2pdbを介して最新の.pdb
に変換してさらに処理することをお勧めします。
デバッグ シンボルを自動的に生成するには、次のビルド後のイベントをプロジェクトに追加します。
map2dbg.exe $(OUTPUTPATH)
cv2pdb64.exe -n -s. -p$(OUTPUTNAME).pdb $(OUTPUTPATH)
Delphi にはガベージ コレクターが含まれていないため、すぐに使用できるのはレコード、文字列、動的配列、インターフェイスなどの少数の型だけです。一方、クラスとポインタは明示的なクリーンアップを必要とし、(安全な形式では) try-finallyブロックを使用する必要があるため、プログラムが大幅に複雑になります。この問題に対処するために、ライブラリには、DelphiUtils.AutoObjects に実装された、メモリおよびその他のリソースの自動有効期間管理機能が含まれています。このモジュールの型を使用することで、参照をカウントし、関数エピローグでオブジェクトを自動的に解放するための例外安全なコードを自動的に生成するようにコンパイラーに指示します。このモジュールは、クリーンアップが必要な可能性のあるさまざまなタイプのリソース用のいくつかのインターフェイスを定義します。次の階層が導入されます。
グラフLR。
サブグラフ ID1[任意のリソース]
IAutoReleaseable
終わり
サブグラフ ID2[A THandle 値]
Iハンドル
終わり
サブグラフ id3[Delphi クラス]
IAutoObject[IAutoObject<T>]
終わり
サブグラフ ID4[ポインタ]
IAutoPointer[IAutoPointer<P>]
終わり
サブグラフ ID5[メモリ領域]
Iメモリ[IMメモリ<P>]
終わり
IAutoReleasable --> IHandle;
IAutoReleasable --> IAutoObject;
IAutoReleasable --> IAutoPointer;
IAutoPointer --> IMemory;
IAutoReleasable
、(自動) クリーンアップのアクションを実行する必要があるすべてのリソースの基本タイプです。 IHandle
THandle 値によって定義されたリソースのラッパーとして機能します。 IAutoObject<T>
は、Delphi クラス (TObject から派生したもの) を自動的に解放するための汎用ラッパーです。 IAutoPointer<P>
動的に割り当てられたポインターを解放するための同様のインターフェイスを定義します (領域のサイズは無関係です)。 IMemory<P>
、マネージドおよびアンマネージドのボックス化レコードなど、型付きポインターを介してアクセスできる既知のサイズのメモリ領域のラッパーを提供します。
この機能を使用するためのレシピは次のとおりです。
いずれかのインターフェイスを使用して、オブジェクトに対する (潜在的に共有された) 所有権を維持する必要があるすべての変数を定義します。
自動オブジェクトの割り当て、コピー、キャプチャにはAutoヘルパーを使用します。
必要に応じて、型情報の重複を回避し、構文を短縮できる左側キャストを使用します。
たとえば、従来のアプローチを使用して 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 は参照カウントを使用するため、2 つのオブジェクトに循環依存関係がある場合、メモリ リークが発生する可能性があります。弱い参照を使用すると、この問題の発生を防ぐことができます。このような参照は生存期間の延長にはカウントされず、対象のオブジェクトが破棄されると、それらを格納する変数は自動的に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 など) + アクション + ターゲット/オブジェクト タイプなどの名前規則を使用します。関数名にも CamelCase が使用されます。
このライブラリは、Windows 7 以降の 32 ビット エディションと 64 ビット エディションの両方を対象としています。ただし、一部の機能は、最新の 64 ビット バージョンの Windows 11 でのみ利用できる場合があります。例としては、AppContainers や ntdll syscall アンフックなどがあります。ライブラリ関数が Windows 7 に存在しない可能性のある API に依存している場合、遅延インポートを使用し、実行時に可用性をチェックします。
Delphi には、ライブラリがNtUiLib層内で利用する豊富なリフレクション システムが付属しています。ヘッダーレイヤーで定義されている型のほとんどは、これを実現するためにカスタム属性 (DelphiApi.Reflection を参照) で装飾されています。これらの装飾は、ライブラリが実行時に複雑なデータ型 (PEB、TEB、USER_SHARED_DATA など) を正確に表現し、1 行のコードで驚くべきレポートを生成するのに役立つ有用なメタデータを生成します。
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 | 一般的なライブラリの種類 |
NtUtils.SysUtils | 文字列操作 |
NtUtils.Errors | エラーコード変換 |
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.Console | コンソール I/O ヘルパー |
NtUiLib.TaskDialog | TaskDialogベースのGUI |
NtUiLib.エラー.ダイアログ | GUIエラーダイアログ |
NtUiLib.Exceptions.ダイアログ | GUI例外ダイアログ |
本体 | 説明 |
---|---|
NtUtils.ActCtx | アクティベーションコンテキスト |
NtUtils.AntiHooking | アンフックおよび直接システムコール |
NtUtils.Com | COM、IDispatch、WinRT |
NtUtils.Csr | CSRSS/SxS登録 |
NtUtils.DbgHelp | DbgHelp およびデバッグ シンボル |
NtUtils.Debug | オブジェクトのデバッグ |
NtUtils.Dism | DISM API |
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.ジョブ | ジョブオブジェクトとサイロ |
NtUtils.Jobs.Remote | クロスプロセスジョブオブジェクトクエリ |
NtUtils.Ldr | LDR ルーチンと解析 |
NtUtils.Lsa | LSA ポリシー |
NtUtils.Lsa.Audit | 監査ポリシー |
NtUtils.Lsa.Sid | SID ルックアップ |
NtUtils.Lsa.Logon | ログオンセッション |
NtUtils.マニフェスト | Fusion/SxS マニフェスト ビルダー |
NtUtils.Memory | メモリ操作 |
NtUtils.MiniDumps | ミニダンプ形式の解析 |
NtUtils.Objects | カーネルオブジェクトとハンドル |
NtUtils.Objects.スナップショット | スナップショットの処理 |
NtUtils.Objects.Namespace | NT オブジェクト名前空間 |
NtUtils.Objects.Remote | クロスプロセスハンドル操作 |
NtUtils.Objects.Compare | ハンドル比較 |
NtUtils.Packages | アプリパッケージとパッケージファミリー |
NtUtils.Packages.SRCache | 状態リポジトリ キャッシュ |
NtUtils.Packages.WinRT | WinRT ベースのパッケージ情報 |
NtUtils.Power | 電源関連機能 |
NtUtils.プロセス | プロセスオブジェクト |
NtUtils.プロセス.情報 | 処理クエリ/設定情報 |
NtUtils.Processes.Info.Remote | コードインジェクションによるクエリ/設定の処理 |
NtUtils.Processes.Modules | クロスプロセス LDR 列挙 |
NtUtils.Processes.Snapshots | プロセスの列挙 |
NtUtils.Processes.Create | 共通のプロセス作成定義 |
NtUtils.Processes.Create.Win32 | Win32プロセスの作成方法 |
NtUtils.Processes.Create.Shell | シェルプロセスの作成方法 |
NtUtils.Processes.Create.Native | NtCreateUserProcess など |
NtUtils.Processes.Create.Manual | NtCreateProcessEx |
NtUtils.Processes.Create.Com | COMベースのプロセス作成 |
NtUtils.Processes.Create.Csr | SbApiPortによるプロセス作成 |
NtUtils.Processes.Create.Package | アプリのアクティベーション |
NtUtils.Processes.Create.Remote | コードインジェクションによるプロセス作成 |
NtUtils.Processes.Create.Clone | プロセスのクローン作成 |
NtUtils.プロファイル | ユーザーとAppContainerのプロファイル |
NtUtils.Registry | レジストリキー |
NtUtils.Registry.Offline | オフラインでのハイチ操作 |
NtUtils.Registry.VReg | サイロベースのレジストリ仮想化 |
NtUtils.Sam | SAMデータベース |
NtUtils.セクション | セクション/メモリ投影オブジェクト |
NtUtils.セキュリティ | セキュリティ記述子 |
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 | SCMサービス |
NtUtils.Svc.SingleTaskSvc | サービスの実装 |
NtUtils.同期 | 同期プリミティブ |
NtUtils.System | システム情報 |
NtUtils.TaskScheduler | タスクスケジューラ |
NtUtils.Threads | スレッドオブジェクト |
NtUtils.Tokens.情報 | スレッドのクエリ/設定情報 |
NtUtils.Threads.Worker | スレッド ワーカー (スレッド プール) |
NtUtils.トークン | トークンオブジェクト |
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.AutoCompletion | 編集コントロールのオートコンプリート |
NtUiLib.AutoCompletion.Namespace | NT オブジェクト名前空間の自動補完 |
NtUiLib.AutoCompletion.Sid | SID の自動補完 |
NtUiLib.AutoCompletion.Sid.Common | 単純な SID 名のプロバイダー/レコグナイザー |
NtUiLib.AutoCompletion.Sid.AppContainer | AppContainer およびパッケージ SID プロバイダー/レコグナイザー |
NtUiLib.AutoCompletion.Sid.Capabilities | 機能 SID プロバイダー/レコグナイザー |
NtUiLib.WinCred | 認証情報ダイアログ |