- Web Seminar
- 2023/02/14 08:50-16:00(CST) DelphiCon 2023 Day 1
- 2023/02/15 08:50-17:00(CST) DelphiCon 2023 Day 2
- 2023/02/16 08:50-16:00(CST) DelphiCon 2023 Day 3
- 2023/02/28 17:00-17:45(JST) 「Delphi 28回目のバースディ」 - エンバカデロ・デベロッパーTV(2023年2月)
- Live Seminar
- Community Event
2023年2月1日
2023/02開催のセミナー
2023年1月29日
[書籍][ebook]プログラマーのためのCPU入門
ラムダノートで
プログラマーのためのCPU入門 (Amazon)/Takenobu Tani著/ラムダノート/ISBN 978-4-908686-16-0/3,740円(紙書籍+電子書籍(PDF))、3,520円(電子書籍(PDF)のみ)
を購入して、書籍版が配送されてきました。
プログラマーのためのCPU入門 (Amazon)/Takenobu Tani著/ラムダノート/ISBN 978-4-908686-16-0/3,740円(紙書籍+電子書籍(PDF))、3,520円(電子書籍(PDF)のみ)
を購入して、書籍版が配送されてきました。
2023年1月11日
2023年1月1日
2023/01開催のセミナー
- Web Seminar
- 2023/01/31 17:00-17:45(JST) 「Delphiの関数構文を極めよう」 - エンバカデロ・デベロッパーTV(2023年1月)
- Live Seminar
- Community Event
2022年12月22日
ユーザを偽装してSYSTEMサービスからネットワークリソースにアクセスする
このアーティクルはDelphi Advent Calendar 2022の22日目の記事です(20日ぶり8回目)。
以前WNetAddConnection2を使って他のPCのネットワーク共有にアクセスする方法について書きましたが、これをサービスから実行するとWNetAddConnection2が時々エラー1312(ERROR_NO_SUCH_LOGON_SESSION)になる、という現象が発生します。調べてみたところ、サービスの場合はSystemではなくNetworkServiceで動作していないとこのような状態になるようです(常にエラーになるわけではなく、成功したりエラーになったりという感じ)。もちろんサービスをNetworkServiceで実行すればネットワークアクセスでエラーになることはないのですが、ローカルコンピュータに対するアクセスがUsersグループ相当に制限されてしまいます(サービスで使用する(Local)System/LocalService/NetworkServiceアカウントについてはWindowsテクニカルドキュメント(旧MSDN)のService User Accounts、LocalService Account、NetworkService Account、LocalSystem Accountや@ITのWindowsのサービスで使用される「System」「Local Service」「Network Service」アカウントとは?:Tech TIPSを参照)。
そこでSystemアカウントで動作するサービスから実行することを前提に、ネットワークリソースにアクセスするときだけNetworkServiceアカウントに偽装するようにしてみます。
まずLogonUserでNetworkServiceとしてログインし、返されたトークンハンドルでImpersonateLoggedOnUserを呼び出すことでユーザを偽装します。
切断するときはまずWNetCancelConnection2を呼び出した後で、RevertToSelfで偽装を解除し、CloseHandleでトークンハンドルをクローズすることでログオフします。
以前WNetAddConnection2を使って他のPCのネットワーク共有にアクセスする方法について書きましたが、これをサービスから実行するとWNetAddConnection2が時々エラー1312(ERROR_NO_SUCH_LOGON_SESSION)になる、という現象が発生します。調べてみたところ、サービスの場合はSystemではなくNetworkServiceで動作していないとこのような状態になるようです(常にエラーになるわけではなく、成功したりエラーになったりという感じ)。もちろんサービスをNetworkServiceで実行すればネットワークアクセスでエラーになることはないのですが、ローカルコンピュータに対するアクセスがUsersグループ相当に制限されてしまいます(サービスで使用する(Local)System/LocalService/NetworkServiceアカウントについてはWindowsテクニカルドキュメント(旧MSDN)のService User Accounts、LocalService Account、NetworkService Account、LocalSystem Accountや@ITのWindowsのサービスで使用される「System」「Local Service」「Network Service」アカウントとは?:Tech TIPSを参照)。
そこでSystemアカウントで動作するサービスから実行することを前提に、ネットワークリソースにアクセスするときだけNetworkServiceアカウントに偽装するようにしてみます。
まずLogonUserでNetworkServiceとしてログインし、返されたトークンハンドルでImpersonateLoggedOnUserを呼び出すことでユーザを偽装します。
const
LOGON32_LOGON_NEW_CREDENTIALS = 9;
{$EXTERNALSYM LOGON32_LOGON_NEW_CREDENTIALS}
var
LogonUserUser: String;
LogonUserDomain: String;
LogonUserLogonType: DWORD;
LogonUserLogonProvider: DWORD;
hToken: THandle;
begin
{ Logon as 'NetworkService' user }
LogonUserUser := 'NetworkService';
LogonUserDomain := 'NT AUTHORITY';
LogonUserLogonType := LOGON32_LOGON_NEW_CREDENTIALS;
LogonUserLogonProvider := LOGON32_PROVIDER_DEFAULT;
if LogonUser(PChar(LogonUserUser),PChar(LogonUserDomain),nil,
LogonUserLogonType,LogonUserLogonProvider,hToken) = False then
begin
RaiseLastOSError;
end;
{ Impersonate }
if ImpersonateLoggedOnUser(hToken) = False then
begin
RaiseLastOSError;
end;
これでこの後WNetAddConnection2で接続を試みるときにSystemアカウントではなくNetworkServiceアカウントであるかのように扱われます。切断するときはまずWNetCancelConnection2を呼び出した後で、RevertToSelfで偽装を解除し、CloseHandleでトークンハンドルをクローズすることでログオフします。
{ Revert impersonation }
if RevertToSelf() = False then
begin
RaiseLastOSError;
end;
{ Loggoff }
CloseHandle(hToken);
hToken := INVALID_HANDLE_VALUE;
なおプロセス上で複数のスレッドが動作している場合(特にサービス)など、同一の共有リソース('\\<computername>\<sharename>')に対してWNetAddConnection2をネストして呼び出すとERROR_ALREADY_ASSIGNEDでエラーになります。これを防ぐには同一の共有リソースに対してWNetAddConnection2/WNetCancelConnection2が1回ずつ呼び出されるように、接続している共有リソースをプロセス全体で適切に管理する必要もあります。
2022年12月14日
2022年12月5日
2022年12月2日
InterBase 2020 Update 4
InterBase 2020 Update 4がリリースされています。バージョンは14.4.0.804となっています。
InterBase 2020U4 リリースノート - InterBase (en)
InterBase 2020 Update 4 の新機能 - InterBase (en)
Resolved Defects - InterBase (en)
Embarcadero InterBase 2020 Update 4 のリリース (en)
InterBase 2020U4 リリースノート - InterBase (en)
InterBase 2020 Update 4 の新機能 - InterBase (en)
Resolved Defects - InterBase (en)
Embarcadero InterBase 2020 Update 4 のリリース (en)
レコード型のフィールドのオフセットを取得する
このアーティクルはDelphi Advent Calendar 2022の2日目の記事です(3年ぶり7回目)。
Delphiのレコード型(Cの構造体に相当)は、スタックに配置することができるということ以外にも、特定のメモリレイアウトを定義することができることから、固定長ファイルや指定されたメモリ上のデータを解釈するために使われることがあります。このような場合に、定義したレコード型の特定のフィールドのオフセットが(主にデバッグ用に)欲しくなったりするのですが、Delphiには(
delphi - Get Position of a struct var AKA Offset of record field - Stack Overflow
Delphi: Offset of record field - Stack Overflow
ではちょっと試してみましょう。まずレコード型とそのポインタ型を定義します。
しかし最初に書いたように、特定の(外部で規定された)メモリレイアウトに簡単にアクセスできるようにするためにレコード型を使用する、という目的を考えると、レコード型には
逆アセンブルを見ると、コンパイラは各メンバのオフセットを知ってて
ところで、
このようなことから、レコード型を使用するときは、
なお
Delphiのレコード型(Cの構造体に相当)は、スタックに配置することができるということ以外にも、特定のメモリレイアウトを定義することができることから、固定長ファイルや指定されたメモリ上のデータを解釈するために使われることがあります。このような場合に、定義したレコード型の特定のフィールドのオフセットが(主にデバッグ用に)欲しくなったりするのですが、Delphiには(
SizeOfはあるのに)C/C++のoffsetofマクロ(stddef.h)のようなものが存在しません(RSP-39559)。そこで軽く検索してみたところStack Overflowにそのものずばりな投稿がありました。delphi - Get Position of a struct var AKA Offset of record field - Stack Overflow
Delphi: Offset of record field - Stack Overflow
ではちょっと試してみましょう。まずレコード型とそのポインタ型を定義します。
type
TFoo1 = record
Bar: Boolean;
Baz: Double;
Qux: array [0..8] of Byte;
Quux: Integer;
end;
PFoo1 = ^TFoo1;
これでprocedure TForm1.Button1Click(Sender: TObject);
begin
Memo1.Lines.Add(Format('TFoo1: SizeOf=%d',[SizeOf(TFoo1)]));
Memo1.Lines.Add(Format('TFoo1.Bar: offset=%d',[NativeUInt(@(PFoo1(nil)^.Bar))]));
Memo1.Lines.Add(Format('TFoo1.Baz: offset=%d',[NativeUInt(@(PFoo1(nil)^.Baz))]));
Memo1.Lines.Add(Format('TFoo1.Qux: offset=%d',[NativeUInt(@(PFoo1(nil)^.Qux))]));
Memo1.Lines.Add(Format('TFoo1.Quux: offset=%d',[NativeUInt(@(PFoo1(nil)^.Quux))]));
end;
のように、nilをレコード型へのポインタにキャスト→フィールドを参照→そのアドレスを取得→整数に変換とすることで、そのフィールドのオフセット値を取得できます(アドレスと同じサイズの符号なし整数はNativeUInt型)。では実行してみましょう。TFoo1: SizeOf=32
TFoo1.Bar: offset=0
TFoo1.Baz: offset=8
TFoo1.Qux: offset=16
TFoo1.Quux: offset=28
Delphiのレコード型のデフォルトのアライメントマスクに従って配置されていることがわかります(TFoo1.QuuxのオフセットがDelphiのデフォルトのフィールドのアライメント(構造体アライメント)の8バイトに従った32ではなく、型(Integer)のサイズである4バイトで28になっていることに注意)。しかし最初に書いたように、特定の(外部で規定された)メモリレイアウトに簡単にアクセスできるようにするためにレコード型を使用する、という目的を考えると、レコード型には
packedを指定して、明示的にパディングするような場合が多いと思われます。ではレコード型をpacked recordにしてみましょう。type
TFoo2 = packed record
Bar: Boolean;
Baz: Double;
Qux: array [0..8] of Byte;
Quux: Integer;
end;
PFoo2 = ^TFoo2;
procedure TForm1.Button2Click(Sender: TObject);
begin
Memo1.Lines.Add(Format('TFoo2: SizeOf=%d',[SizeOf(TFoo2)]));
Memo1.Lines.Add(Format('TFoo2.Bar: offset=%d',[NativeUInt(@(PFoo2(nil)^.Bar))]));
Memo1.Lines.Add(Format('TFoo2.Baz: offset=%d',[NativeUInt(@(PFoo2(nil)^.Baz))]));
Memo1.Lines.Add(Format('TFoo2.Qux: offset=%d',[NativeUInt(@(PFoo2(nil)^.Qux))]));
Memo1.Lines.Add(Format('TFoo2.Quux: offset=%d',[NativeUInt(@(PFoo2(nil)^.Quux))]));
end;
実行してみます。TFoo2: SizeOf=22
TFoo2.Bar: offset=0
TFoo2.Baz: offset=1
TFoo2.Qux: offset=9
TFoo2.Quux: offset=18
意図通り、すべてのフィールドがパディングされることなく並んでいることがわかります。逆アセンブルを見ると、コンパイラは各メンバのオフセットを知ってて
nil(=0)に加算しているので、最初からこれをもらう方法があれば…とは思います。また上記のStack Overflowの2番目の投稿にはRTTIを使った方法も紹介されていますが、RTTIのテーブルでループを回しながら文字列の比較をする必要があるため、それに比べればnilを使った方法のほうが優れていると考えられます。ところで、
packedを指定せずデフォルトのアライメントマスクに従って配置された場合、順序型はそのサイズでアライメントされる、との記述があります。つまりtype
TFoo3 = record
Bar: array [0..8] of Byte;
Baz: Boolean;
Qux: array [0..8] of Byte;
end;
PFoo3 = ^TFoo3;
のように1バイトアライメントされている配列のフィールドの次に1バイトの順序型のフィールドがあると、デフォルトのアライメントの8バイトに従ったパディングが行われることなく、連続した配置になる、ということになります(逆も同じ)。procedure TForm1.Button3Click(Sender: TObject);
begin
Memo1.Lines.Add(Format('TFoo3: SizeOf=%d',[SizeOf(TFoo3)]));
Memo1.Lines.Add(Format('TFoo3.Bar: offset=%d',[NativeUInt(@(PFoo3(nil)^.Bar))]));
Memo1.Lines.Add(Format('TFoo3.Baz: offset=%d',[NativeUInt(@(PFoo3(nil)^.Baz))]));
Memo1.Lines.Add(Format('TFoo3.Qux: offset=%d',[NativeUInt(@(PFoo3(nil)^.Qux))]));
end;
実行してみると、TFoo3: SizeOf=19
TFoo3.Bar: offset=0
TFoo3.Baz: offset=9
TFoo3.Qux: offset=10
と、確かにその通りになっています。またDelphi 2007までは型仕様が共通のフィールド(A, B: Extended;のように複数のフィールドが","で並べられて同じ型が指定されているフィールド)は暗黙にpackedとなる、という仕様も存在していました(いま知りました)。このようなことから、レコード型を使用するときは、
packedを指定せず完全にコンパイラにお任せにして特定のメモリレイアウトを必要としないようにするか、packedを指定してパディングも明示的に配置して特定のメモリレイアウトになるようにコーディングするか、どちらかにするべきだと考えられます(この結論そのものは当たり前すぎるものですが)。なお
packedを指定してメモリレイアウトを完全に自分で制御する場合など、定義したレコード型が正しいサイズになっているかどうかは{$IF SizeOf(TFoo2) <> 22}
{$MESSAGE ERROR 'SizeOf(TFoo2) is not 22 bytes.'}
{$IFEND}
のようにコンパイル時にチェックすることができます(Delphi 11.2 Alexandriaだとこれを書いた瞬間にLSPで判定が行われるため、{$MESSAGE}の行がエラー表示になるか淡色表示になるかでわかりますが)。
2022年12月1日
2022/12開催のセミナー
- Web Seminar
- 2022/12/27 17:00-17:45(JST) 「年末年始はプログラミングの基礎技術を高めよう!Delphiのストリームを使う」 - エンバカデロ・デベロッパーTV(2022年12月)
- Live Seminar
- Community Event
2022年11月9日
2022年11月1日
2022/11開催のセミナー
- Web Seminar
- 2022/11/29 17:00-17:45(JST) 「新オフィスから生中継!アイデラグループ3社の活動に迫る」 - エンバカデロ・デベロッパーTV(2022年11月)
- Live Seminar
- Community Event
2022年10月17日
2022年10月12日
2022年10月5日
RAD Studio/Delphi/C++Builder 11.2 Alexandria Patch 1
RAD Studio/Delphi/C++Builder 11.2 AlexandriaのPatch 1がリリースされています。iOSシミュレータおよびカメラコンポーネント、バルーンヘルプコンポーネントなどの不具合の修正が含まれています。またWindows x64のライブラリパスが正しくない問題についても対応策が示されています。GetItまたはポータルサイトからダウンロード、インストールすることができます。
RAD Studio 11.2 Patch 1
RAD Studio 11.1 Alexandria Patch 1 のリリース (en)
RAD Studio 11.2 Patch 1
RAD Studio 11.1 Alexandria Patch 1 のリリース (en)
2022年10月1日
2022/10開催のセミナー
- Web Seminar
- 2022/10/20 17:00-17:45(JST) 「10月の配信は20日(木)!キーコンポーネントの使い方を極めよう」 - エンバカデロ・デベロッパーTV(2022年10月)
- Live Seminar
- Community Event
2022年9月14日
2022年9月8日
RAD Studio/Delphi/C++Builder 11 Alexandria Release 2 (11.2)
RAD Studio/Delphi/C++Builder 11 AlexandriaのRelease 2 (11.2)がリリースされています。インストールには有効なUpdate Subscriptionが必要です。Registered Products Portalからダウンロードできます。
RAD Studio, Delphi, C++Builder 11.2 ISO
RAD Studio, Delphi, C++Builder 11.2 Web Install
11 Alexandria - Release 2 (en)
New features and customer reported issues fixed in RAD Studio 11.2
List of publicly reported bugs fixed in 11.2
RAD Studio 11.2 製品機能一覧
RAD Studio 11.2 Alexandria リリースのお知らせ (en)
RAD Studioの次期アップデートでサポート予定のDelphi iOS Simulatorについて (en)
RAD Studio New Release: What’s Coming in 11.2
RAD Studio 11.2の新機能プレビュー:MarkdownのサポートとHelpInsightの改善 (en)
Coming in 11.2: the Little Things that make Big Quality
RAD Studio 11.2向けのEmbarcadero GetItパッケージダウンロード (en)
RAD Studio, Delphi, C++Builder 11.2 ISO
RAD Studio, Delphi, C++Builder 11.2 Web Install
11 Alexandria - Release 2 (en)
New features and customer reported issues fixed in RAD Studio 11.2
List of publicly reported bugs fixed in 11.2
RAD Studio 11.2 製品機能一覧
RAD Studio 11.2 Alexandria リリースのお知らせ (en)
RAD Studioの次期アップデートでサポート予定のDelphi iOS Simulatorについて (en)
RAD Studio New Release: What’s Coming in 11.2
RAD Studio 11.2の新機能プレビュー:MarkdownのサポートとHelpInsightの改善 (en)
Coming in 11.2: the Little Things that make Big Quality
RAD Studio 11.2向けのEmbarcadero GetItパッケージダウンロード (en)
2022年9月5日
[書籍]インサイドWindows 第7版 下
紀伊國屋書店新宿本店でインサイドWindows 第6版 下の改訂版(ではもはやないかも)でWindows Internals, Part 2, 7th Edition (Amazon US)の翻訳である
インサイドWindows 第7版 下 (Amazon)/Andrea Allievi、Mark E. Russinovich、Alex Ionescu、David A. Solomon著、山内和朗訳/日経BP/ISBN 9784296080205/8,800円
を購入。上巻(Part 1)が刊行されて4年超、おめでとうございます。
インサイドWindows 第7版 下 (Amazon)/Andrea Allievi、Mark E. Russinovich、Alex Ionescu、David A. Solomon著、山内和朗訳/日経BP/ISBN 9784296080205/8,800円
を購入。上巻(Part 1)が刊行されて4年超、おめでとうございます。
2022年9月1日
2022/09開催のセミナー
- Web Seminar
- 2022/09/27 17:00-17:45(JST) 「特集!RAD Studio 11.2の新機能」 - エンバカデロ・デベロッパーTV(2022年9月)
- Live Seminar
- Community Event
登録:
投稿 (Atom)