WPR (Windows Performance Recorder)
CLI 版 wpr.exe は Windows 10 で OS 標準のツールとなった。
GUI 版 wprui.exe は Windows SDK をインストールする必要がある。SDK の機能の中でインストールするのは Windows Performance Toolkit
だけでよい。
CLI 版の使い方
- 管理者権限のコマンドプロンプトを起動する
- 組み込みのプロファイル(トレースで収集したいものを表すプリセット)の一覧を表示する
wpr -profiles
プロファイル名を指定してトレースを開始する
wpr -start <profile> [-start <profile> ...]
Ex. CPU のみトレースする場合
wpr -start CPU
Ex. CPU と Heap をトレースする場合
wpr -start CPU -start Heap
WPR の現在の実行状況を表示する
wpr -status
トレースを終了する
wpr -stop <save filename>
ロギングモード
何も指定しなければ既定ではメモリモード。メモリ内の循環バッファーに記録される。
ファイルモードにするには -filemode
を指定する。ファイルモードの場合、ファイルサイズはディスクの空き領域を使い果たすまで大きくなるので注意。
wpr -start CPU -filemode
OS Boot Trace
autologger (boot trace) は、システムの起動時に自動的に開始されるトレースセッション。Windows では、レジストリ設定を使用してブートプロセスの初期段階でトレースセッションを開始する方法が提供されている。
- 管理者権限のコマンドプロンプトを起動する
- プロファイルを指定する
wpr -addboot <profile> [-addboot <profile> ...] -filemode -recordtempto <temp folder path>
Ex. First leve triage と CPU をトレースする
wpr -addboot GeneralProfile -addboot CPU -filemode -recordtempto C:\temp
wpr -stopboot <save filename>
プロファイル情報の詳細を確認
メモリモードの場合
wpr -profiledetails <profile>
ファイルモードの場合
wpr -profiledetails <profile> -filemode
WPA (Windows Performance Analyzer)
WPR で収集したデータは WPA を使って分析できる。WPA についてはまた別の記事で書く予定。
参考
タスクマネージャーの見方 (Memory 編)
こちらのマイクロソフトの記事がとても分かりやすかった。
物理メモリ
- ハードウェア予約済み (Hardware Reserved)
BIOS または他の周辺機器のドライバーで使用するために予約されたメモリ- 使用中 (In Use)
プロセス、ドライバーまたは OS によって使用されているメモリ- 変更済み (Modified)
他の用途で使用する前にディスクへの書き込みが必要な内容が含まれているメモリ- スタンバイ (Standby)
キャッシュ データやアクティブに使用されていないコードが含まれているメモリ- 空き (Free)
現在、使用されておらず、プロセス、ドライバーまたは OS にメモリが必要になったとき最初に使用されるメモリ領域タスクマネージャーには「ハードウェア予約済み」と「使用中」の使用量は、そのまま表示されていますが、他の状態はまとめられて表示されています。「利用可能」は、[スタンバイ + 空き] であり、「キャッシュ済み」は、[変更済み + スタンバイ] になります。
System Commit Limit
- システムコミット制限は「物理メモリ量 + 現在のページファイルサイズ」の合計値。タスクマネージャーの「コミット済み」のスラッシュの右側で確認できる。
- より詳細な情報は Process Explorer の [View] - [System Information] で確認できる
Paged Pool, Non-paged Pool
- カーネルモードヒープ (システムメモリプール) には、ページプールと、非ページプールがある
- ページプールはページイン、ページアウトが可能。非ページプールは常に物理メモリに存在することが保証されている
- タスクマネージャーのページプールと非ページプールには、空き容量と割り当て済み領域の両方が含まれる
- ページプールの最大サイズは、Windows8.1 以降の 64-bit システムの場合 15.5TB か、システムコミット制限のどちらか小さい方
- 非ページプールの最大サイズは、Windows8.1 以降の 64-bit システムの場合、物理メモリ量か 16TB のどちらか小さい方
物理メモリ量の制限
参考
Window Handle の作成で例外発生!
"Error Creating Window Handle" 例外に遭遇した場合、特定のアプリでの現象なのか、システム全体での現象なのかによって話が変わってくる。
以下では、特定のアプリでの現象という想定で話を進める。
まずは、タスクマネージャーの「詳細」画面で、「ハンドル数」「ユーザーオブジェクト」「GDIオブジェクト」の列を追加し、該当プロセスの値を確認する。
Object Categories
システムには 3 つのカテゴリのオブジェクトが用意されている。
1. User Object:ウィンドウ管理をサポート
2. GDI (Graphics Device Interface) Object:グラフィックスをサポート
3. Kernel Object:メモリ管理、プロセス実行、およびプロセス間通信 (IPC) をサポート
プロセスのハンドルテーブルは Kernel Object のみに使用され、User Object や GDI Object には使用されない
User Object 数が上限に達していないか
- User Object はほとんどが デスクトップヒープ から割り当てられる
- 既定ではプロセス毎に 10,000 個の上限がある
- この上限は以下のレジストリで変更できるが、上限に達した理由を調査する必要がある
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\USERProcessHandleQuota
GDI Object 数が上限に達していないか
- GDI Object はデスクトップヒープからではなく セッションプール から割り当てられる
- 既定ではプロセスごとに 10,000 個の上限がある
- この上限は以下のレジストリで変更できるが、上限に達した理由を調査する必要がある
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\GDIProcessHandleQuota
ハンドルの上限に達した?
ハンドルテーブルエントリ
- プロセスのハンドルテーブルエントリが枯渇する可能性は極めて低い
- Windows が 32-bit, 64-bit 版どちらであっても、エントリ数は 約1,600万個 もあり基本的には無制限と考えられる
- 特定の時点で 1万個を超えるハンドルを開いているプロセスは設計がよくないか、ハンドルリークが発生しているかのいずれか
ハンドルテーブルを格納するために必要なメモリ量
- ハンドルテーブルを格納するのに必要なメモリは ページプール から割り当てられる
- 最大数のハンドル(約1,600万個)を格納するのに必要なメモリ量は、64-bit Windows の場合 256MB
オブジェクトの種類ごとのハンドル数
Sysinternals の handle
ユーティリティを使うと、開かれているオブジェクトの種類ごとのハンドル数とその合計値を確認できる。
handle -s [-p <process name | pid>]
参考
NTFS 代替ストリーム
代替ストリーム
インターネットからダウンロードしたファイルや、メールの添付ファイルを NTFS 上に保存すると、ファイルに 代替ストリーム (Alternate Data Stream: ADS) が追加されます。ファイルのプロパティを開くと以下のようなセキュリティメッセージが表示されます。
ファイルに ADS が付いているかどうかは dir /r
で確認できます。
:Zone.Identifier:$DATA というのがそれです。
また、more < ファイル名:Zone.Identifier
で ADS の内容を確認できます。
notepad ファイル名:Zone.Identifier
とかでもよいです。
zip ファイルを展開する場合の注意点
zip ファイルに ADS が付いている場合、Windows 標準の「展開」で解凍すると展開されたファイルにも ADS が付与されます。圧縮・解凍ソフト(Lhaplus や 7zip など) で解凍すると、展開したファイルには ADS が付与されないケースがあります。Lhaplus では付与されませんでした。
参考サイト
Windows Kernel Debug のやり方
ローカルシステムのデバッグ
LiveKd を利用すると、ローカルシステムのカーネルメモリのスナップショットをメモリダンプとして作成し、そのダンプファイルにデバッガ―をアタッチしてデバッグができる。
livekd -k "C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\windbg.exe"
- LiveKd は Sysinternals のツール。Microsft Store から Sysinternals をインストールできる。
- OS をデバッグモードに切り替えて再起動する必要がない。ちなみに、デバッグモードに切り替えるのは
msconfig
などでできる。 - デバッグ対象のコンピュータで進行中の挙動はアタッチしているスナップショットには反映されないので注意。デバッガ―を終了する際に再実行するか聞かれるので「y」を入力するとスナップショットがリフレッシュされる。
- windbg は Windows SDK をダウンロードし、Debugging Tools for Windows のみインストールすればよい。
参考サイト
COMってなんだ
COM (Common Object Model) について調べたり実験したりして分かったことを書きます。
想像で書いている箇所もあり、間違いも多くあると思いますのでご注意ください。
調べれば調べるほどどこまでも奥深く沼だなーという気分になりました。
以下は、x64 PC 前提で書きます。
- COM ってなんだ
- COM 開発の流れ
- COM コンポートをレジストリに登録する
- .Net から COMコンポーネントを利用する
- COM サーバー
- 64-bit OS で 32-bit COM を使う方法
- 参考サイト
- よさそうな本 (中古だけど)
COM ってなんだ
- インターフェースと実装の分離
- COM はインターフェイスベースの開発を前提に作られている環境
- COM はプログラミング言語に依存しないフレームワーク
- インターフェース定義に専用言語 (IDL) を使う。真の COM プログラマは IDL から作業を開始する。
- COM ではそれぞれの GUID に固有の名前を付けている
- IID (Interface-ID) - インターフェース属性の GUID
- CLSID - coclass の GUID
- LIBID - library の GUID
- IUnknown インターフェイスの QueryInterface というメソッドで、COM オブジェクトがある特定のインターフェイスを実装しているかどうかを調べることができる
- インターフェイスは絶対に変更できないので、 バージョンアップするにはインターフェイスを新しく定義することになる
COM 開発の流れ
IDL でインターフェース・COMクラスを定義する
- IDL ファイル
*.idl
にインターフェイス、COM クラスを定義(どのクラスがどのインターフェイスを持つか定義)する。 - ILD を開発環境に取り込むには、Visual Studio や Platform SDK に付属する IDL コンパイラ
MIDL.exe
で IDL ファイルをビルドする。COM サーバ、COM クラインアントを C++ 以外の言語で開発する場合は、IDL ファイルでlibrary
属性を設定し、IDL コンパイラでビルドした時にタイプライブラリファイルが出力されるようにする。タイプライブラリは IDL をバイナリトークンにしたもの。
COM サーバ、COM クライアントを C++ で実装する場合
COM サーバ、COM クライアントを C++ 以外の言語で実装する場合
COM サーバの実装
- 参照にタイプライブラリを追加する。
- インターフェイスを実装する。
- ビルドして COM DLL を作る。
COM クライアントの実装
- VB の場合は
CreateObject()
の引数に ProgID を指定する。COM の GUID は長くて使いにくいので COM 利用側はプログラム識別子 (ProgID)
という別名を利用する。 - ProgID がレジストリなどを利用して CLSID に変換され、CLSID を利用して DLL がロードされ、COM オブジェクトが生成される。
- COM オブジェクトを利用してクライアント側を実装する。
COM コンポートをレジストリに登録する
COMクライアントからCOMコンポーネント(COMサーバー)を利用するには事前にレジストリに登録が必要です。レジストリへの登録となるので管理者権限での実行が必要です。
COM がアンマネージコードで作成されている場合
[32bit COM の場合]
C:\Windows\SysWOW64\regsvr32 <COM コンポーネント>
[64bit COM の場合]
C:\Windows\System32\regsvr32 <COM コンポーネント>
regsvr32.exe を実行すると COM コンポーネントがロードされ、DllRegisterServer()
関数が呼び出される。通常、この関数の中にレジストリへの登録処理が書かれている。登録を解除するには regsvr32 /u <COM コンポーネント>
を実行する。この場合は、DllUnregisterServer()
関数が呼ばれるのでこの中にレジストリの登録を解除する処理が書かれている。
COM がマネージコードで作成されている場合
[32bit COM の場合]
C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm <COM コンポーネント>
[64bit COM の場合]
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\RegAsm <COM コンポーネント>
.Net から COMコンポーネントを利用する
- 参照に
*.tlb (Type Library)
を追加します。事前にレジストリに COM コンポーネントが登録されていないとエラーになります(実験結果からこう理解していますが怪しいです・・・) - 参照を追加するとメタデータを含む
相互運用機能アセンブリ(Interop Assembly)
が自動生成されます。Tlbimp.exe
ツールが生成している?COM オブジェクトのメンバーへの参照は相互運用アセンブリにルーティングされてから、実際の COM オブジェクトに転送されます。 COM オブジェクトからの応答は相互運用アセンブリにルーティングされてから、.NET Framework アプリケーションに転送されます。
COM相互運用
COM サーバー
- COM サーバーには
インプロセスサーバー
とアウトオブプロセスサーバー
の 2種類がある。 - インプロセスサーバーは DLL として実装され、クライアントと同じプロセス空間で実行される。そのためクライアントと bitness は同じである必要がある。クライアントは、COM インターフェースへの直接呼び出しを使ってインプロセスサーバーと通信する。
- アウトオブプロセスサーバーは EXE として実装される。ローカルコンピューターまたはリモートコンピューター上に存在できる。別プロセスとの通信なので bitness は異なっていても問題ない。
64-bit OS で 32-bit COM を使う方法
リモートプロシージャコール (RPC) は問題なく 32-bit と 64-bit の境界を越えることができるため、COM コンポーネントをアウトオブプロセスで利用している場合は、64-bit Windows に移行するときに問題を引き起こすことはない。
COM コンポーネントをインプロセスからアウトオブプロセスに移行する方法
方法1:プロジェクトタイプをインプロセスからアウトオブプロセスに変換する
- 関連するソースコードが手元にある必要がある
- Visual Studio のプロジェクトの設定でサーバーのタイプを DLL から EXE に変更してビルドする
- この方法の利点は 64-bit クライアント側は全く変更する必要がないこと
方法2: COM+ を利用する
- COM+ をプロキシメカニズムとして利用する
- COM コンポーネントを COM+ に登録する。要調査:Windows ツール の「コンポーネントサービス」から登録するのかな?
- この方法の利点は、クライアントか COM コンポーネントかどちらかのソースコードの変更だけでよいこと
方法3: dllhost をサロゲートホストとして使う
- dllhost は 32-bit クライアントと 64-bit DLL の間の呼び出しを中継するラッパーとして機能する
- レジストリ設定が必要
方法4: アプトオブプロセスの COM Wrapper を DLL に追加する
参考サイト
- Component Object Model - Wikipedia
- MS COM プログラミングの基本 (上)
- MS COM プログラミングの基本 (中)
- MS COM プログラミングの基本 (下)
- COM プログラミング入門
- 単純な COM コンポーネントの作成
- COM/ActiveXの解説ページ
- EternalWindows COM
- COM講座
- ChalkTalk CLR – COMのすべて
- C++でATLを使わずにレジストリフリーのCOMサーバーを作成してWSHから利用する方法
- COM ベース アプリケーションの開発
- MS ガイド (COM)
- Access x86 COM from x64 .NET
- Registering the COM+ .dll
- Using 32bit COMponents in 64bit environment
よさそうな本 (中古だけど)
MSIアンインストールの詳細ログ出力コマンド
詳細ログを出力してアンインストールする
msiexec /x <ProductCode> /l*vx! log.txt
インストールオプション
/i <Product.msi> - インストール /x <Product.msi | ProductCode> - アンインストール
ログオプション
/l[*vx!] <LogFile>:詳細出力 v - 詳細出力 x - 詳細デバッグ情報 ! - 各行をログにフラッシュ * - v オプションと x オプションを除くすべての情報をログに記録します