ckw 改造版のパッチ

改造版 ckw の修正版のバイナリとソースを公開で公開されているソースを改造した。

起動時にコンソールがちらつくのが気になるので、以下のように修正した。

static void __hide_alloc_console()
{
    /*
     * Open Console Window
     * hack StartupInfo.wShowWindow flag
     */
    DWORD*  pflags = (DWORD*) 0x00020068; /* private memory */
    WORD*   pshow  = (WORD*)  0x0002006C;
    DWORD*  px     = (DWORD*) (0x00020068 - sizeof(DWORD) * 7);
    DWORD*  py     = (DWORD*) (0x00020068 - sizeof(DWORD) * 6);

    DWORD   backup_flags = *pflags;
    WORD    backup_show  = *pshow;
    DWORD   backup_x = *px;
    DWORD   backup_y = *py;

    STARTUPINFO si;
    GetStartupInfo(&si);

    /* check */
    if(si.dwFlags == backup_flags && si.wShowWindow == backup_show && si.dwX == backup_x && si.dwY == backup_y) {
        *pflags |= STARTF_USESHOWWINDOW | STARTF_USEPOSITION;
        *pshow  = SW_HIDE;
        *px = 99999; // 見えない場所で開かせる
        *py = 99999; // 見えない場所で開かせる
    }

    AllocConsole();

    /* restore */
    *pflags = backup_flags;
    *pshow  = backup_show;
    *px = backup_x;
    *py = backup_y;
}
while((gConWnd = GetConsoleWindow()) == NULL) {
    Sleep(10);
}
ShowWindow(gConWnd, SW_SHOWNA); // アクティブにしない
while (!IsWindowVisible(gConWnd)) {
    Sleep(10);
}


これで起動が結構すっきりするはず。


本当はタスクバーに一瞬コンソールウィンドウが表示されるのを、以下のコードで抑制したかったんだけど、コンソールウィンドウが特殊だからか、そもそもこんなことは許されないのか、実現できなかった。

SetWindowLong(gConWnd, GWL_EXSTYLE, GetWindowLong(gConWnd, GWL_EXSTYLE) | WS_EX_TOOLWINDOW);
SetWindowPos(gConWnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);

とりあえず GaucheMosh、MzScheme、GHCi で動作確認ができた。

ここからは余談。

ckw の改造に着手したのは、Gauche のウィンドウズ版を快適に操作したい、というのがモチベーション。gosh.exe を起動すると、コンソールが表示されるので、そのコンソールに外部アプリからメッセージを送って、外部から入力したことにしたいな、というのが事の始まり。Spy++ でクリップボードからの貼り付け時のメッセージを覗いてやろうとしたら、コンソールはスパイできない仕様のようだ。

じゃあ仕方が無いので標準入出力をパイプする gosh のフロントエンドを作るか、と思って VC++ でプロジェクトを作って実証してみたんだけど、スクラッチで作るのはとても大変ということがわかった。入出力の肩代わりは簡単なんだけど、編集や IME 制御が面倒くさい。

「そうだ、Tera Term を使ってみてはどうだろう。ターミナルとしての完成度はかなり高いし、パイプ機能を組み込むだけだったら割と簡単かも?」と思って Tera Term のソースコードを落っことしてきて中身を見てみたら、当然といえば当然なんだけど結構複雑。元々の作りがポートとソケットへの接続を前提にしていて、そこにパイプ機能を突っ込もうとすると、構造体のメンバを増やしたり、いろんな箇所に判定文を入れたりしなきゃならなくて、全体を把握して修正を入れるには時間がかかりすぎてしまう。

「じゃあ Cygterm を改造して GaucheCygwin 版を使うということにしようか」と Cygterm のソースコードも解析。GCC が新しいのかオプションが廃止され -mno-cygwin オプションがなくなり cygwin1.dll が必要になったりしたけどなんとかビルドはできた。動作したところでさて Cygterm のソースコードに改造を施そうかと思ってきちんとソースを読んでみたら、README にはウィンドウのないアプリだと書いてあったが本当にウィンドウを作っていない。てっきり表示してないだけだと思っていたので当てが外れてしまった。メッセージを送る相手がいないじゃないか。Cygwin 上で Gauche をビルドまでしたのに。

むむむむむ、と思ってググってみたら見つけたのが ckw だ。ソース量はかなり小さくて、全体を把握しやすい。しかもこのアプリの目的はコンソールのフロントエンドなので、標準で gosh のフロントエンドになりうる。起動時のウィンドウのちらつきがちょっと気になるけど後はこれにメッセージを受け取る実装を追加すればよさそうだ(←いまここ)。

そうそう、秀丸 Ver.8 で外部アプリを起動して、その結果の標準出力を表示させることができるようになったんだけど、これだと 1 回 1 回プロセスの起動しなおしになってしまうので、例えば

(define (add a b) (+ a b))
(add 1 2)

を無理やり

gosh -e"(define (add a b) (+ a b))" -E"print (add 1 2)" -Eexit

という感じで評価させることができなくもないんだけど、使い勝手が悪いので、秀丸から gosh のフロントエンドにメッセージを飛ばしたい、というわけだ。

Scheme の勉強をしたいのに、まずはツールを作らなきゃならないところが Emacs ユーザではない悲しさというかなんというか。