全体の概観と、設計上の重要な点を説明します
knowbugのプログラムは「サーバー」と「クライアント」の2つのプログラムからなります
サーバーはHSPランタイムと直接的に連携します。 ランタイムから情報を読み取ること、クライアントからの指示をランタイムに伝えることが目的です
クライアントはデバッグウィンドウを表示します。 サーバーからもらったデータをユーザーに表示すること、ユーザーの操作をサーバーに伝えることが目的です
通信
サーバー (ランタイム) <------> クライアント (デバッグウィンドウ)
ソースコードは src
ディレクトリにあります
src
knowbug_client
: クライアントの実装 (HSP3)knowbug_core
: サーバーの実装の主要部分 (C++)knowbug_dll
: サーバーの実装のDLL部分 (C++)
主要な実装は kc_*.hsp
にあります。(kc = knowbug_client)
クライアントの実装にデバッガ特有の部分は含まれていません。 普通のGUIアプリケーションです
kc_main.hsp
はアプリのエントリーポイント (入口、最初に実行される部分) です。
サーバーとの通信部分もここに含まれています。
kc_app.hsp
がサーバーとの通信に関する実装をもつ必要をなくし、UIの機能の実装に集中できるようにするためです
kc_app.hsp
は主要な機能の実装を含んでいます。
mod_*.hsp
という名前のファイルは汎用的な機能を提供するモジュールです。
詳細はファイル内のコメントを参照してください
サーバー側の実装は knowbug_dll
と knowbug_core
の2つのプロジェクトに分かれています
knowbug_dll
は、配布時に hsp3debug.dll
という名前になる、HSPのランタイムからロードされるDLLです。
アプリのエントリーポイント (入口) となる、以下の関数を公開します:
- DLLのロード時に呼ばれる関数 (
DllMain
) - HSPランタイムから呼ばれる関数 (
debugini
,debug_notice
)
クライアントを起動して通信すること、knowbugのデータを所有することも、ここの役目です
knowbug_core
は主要な機能の実装から汎用的なモジュールまですべて含みます
HSPのランタイムに関する情報は HspObjects
というオブジェクトに問い合わせて得る、というかたちにしています。
情報の対象となるエンティティ (例えば配列の要素) を指し示すのには HspObjectPath
を使います
HspObjectPath
(オブジェクトパス) は、HSPのオブジェクトへの到達経路を表します。
例えば「モジュール @
にある配列変数 a
の添字1の要素」は、次のようなかたちのパスで表します:
ルート → モジュール(`@`) → 静的変数(`a`) → 配列要素(1)
パスを使うのは次のような理由からです:
- 経路を遡ることで、オブジェクトの生存判定を確実に行える
- 配列の要素や命令のパラメータなどのオブジェクトは消滅することがあります
- 消滅したオブジェクトへのアクセスは実行時エラーにつながるおそれがあり、危険です
- 情報の取り方をknowbug側の都合に合わせて設計できる
- 情報を取得する部分と加工する部分を切り離せる
HspObjects
にパスを渡す、主な利用者は以下の2つです:
HspObjectWriter
: データを文字列表記に変換する。詳細ウィンドウに出てくるものHspObjectList
: オブジェクトの構造を表すリスト。リストビューに出てくるもの
hsx_*
はHSPランタイムの情報を読み取る処理をまとめたものになっています。
HSPランタイムの事情と、knowbug固有の機能を分離するために存在します
サーバーはランタイムからもらったオブジェクト (HSPCTX
や HSP3DEBUG
) からポインタをたどることで、ランタイムの情報にアクセスできます。
これには注意する点があります:
- HSPランタイム側のデータの持ちかたは、スクリプトを実行するときの効率や整合性を考慮して設計されています。
- 関連するデータが複数の構造体に分散して配置されていたり、一部の情報は計算で算出する必要があったりします
- ポインタの脱参照時のNULL検査や、配列アクセス時の境界検査が必要です
- メモリアクセス違反による実行時エラーが起こると、ランタイムごとクラッシュしてしまうことになります
hsxはランタイムのデータにアクセスして、必要な情報を取得するための関数を提供します。 これは以下の利点があります:
- 分かりやすい
- 情報を取得する際に、その情報を取得する関数を呼ぶだけなので分かりやすい
- データがどこにあるのか、どう計算するのか、ということを考えなくてよい
- 安全
- 関数の内部で必要な検査が行われる
エンコーディングごとに文字列のクラスを用意しています
- knowbugは複数のエンコーディングを扱う必要があります
shift_jis
: HSPの非UTF-8版のランタイムで使うUTF-8
: HSPのUTF-8版のランタイムと、knowbugのソースコードで使うUTF-16
: Windows APIで使う
- エンコーディングごとのクラスは、エンコーディングの混同を防ぐことと、エンコーディングの変換を簡略化してくれます
- 詳細は
encoding.h
のコメントを参照してください
サーバーはHSP3のランタイムプロセスによってロードされるDLLなので、ランタイムと同一のプロセス空間にいます。 クライアントはサーバーによって起動される、独立したプロセスです
ランタイムプロセス
+------------------+
| hsp3.exe | クライアントプロセス
| | +----------------+
| hsp3debug.dll <--+-----> | knowbug_client |
+------------------+ +----------------+
サーバーとクライアントは、ウィンドウメッセージを使って双方向的に通信します
以下のことについて知っておくとよいです
- プロセス
- 文字コード (文字列のエンコーディング)
- ウィンドウメッセージ、メッセージキュー
- HSPのインストールディレクトリにhspsdkディレクトリがあり、その下にある hsp3dll.txt, hsp3code.txt はHSPの内部構造を理解するにあたって非常に便利です
- OpenHSPのソースコードを必要に応じて読みます
- HspObjectPath の設計の振り返り #31
- HSPSDK ラッパーモジュールの再設計 (hsp_sdk_ext) #53
- サーバー(DLL)・クライアント(HSP)構成への移行 (v2) #70