昨日の続き。LoadLibrary16() を使って、krnl386.exe を読み込み、GetProcAddress16() を使って、GlobalDosAlloc() (Win16 API) のアドレスを取得して、QT_Thunk() 経由でそれをコールして、下位 1M 領域のメモリを割り当てて、さらに、VxDCall() で DPMI 0300h - Simulate Real Mode Interrupt を呼び出し、それを介して INT 1Ch - Function 00h を発行したところ、無事現在時刻を取得することができた。
次は、Win32 app から、INT 21h - Function 7143h を呼び出せるか試してみたい。Win9x の場合、Win32 API ではディレクトリのタイムスタンプを設定することができないのだが、Win16 app や DOS プログラムからならば、INT 21h - Function 7143h を呼び出すことでそれができる。また NT 系ならば、CreateFile() と SetFileTime() で Win32 app でもディレクトリのタイムスタンプを設定できる。これで、Win9x 上でも Win32 app 単独でディレクトリのタイムスタンプを設定することができるようになるはず・・・。
関連する情報を探していたら、[mfc 12119] Re: Shift+ 再起動の実現のしかたは? というものを見つけた。Win9x で高速再起動を実行する API は Win16 からしか呼び出せないわけだが、それを Win32 から呼び出してしまうという方法だ。それと、その補足。ただし thunk32.lib をリンクしてしまうと、NT 系では起動できなくなってしまうはず。9x/NT 両用にするためには、QT_Thunk() は、LoadLibrary(), GetProcAddress() を使って呼び出す必要がある。
すぐそばには、morry 氏の [mfc 12069] Re: WindowsでDISKBIOSをシステムコールする方法 という投稿もあった。
よく考えたら、この方法を使えば Win32 app からの Win16 API や 割り込み (V86 モード) の呼び出しは、したい放題ということではないか。前述の高速再起動の他に GetFreeSystemResources() といった API も、この方法で使えるようになるわけだ。
何と、サンクに関しては、「Microsoft Windows 95プログラマーズガイド」にしっかり載っているじゃないか。全然気が付かなかった。ちなみにこの本、定価 \11,500 なのだが、古本屋で \800 で手に入れた。また、LoadLibrary16(), GetProcAddress16(), QT_Thunk() に関しては、「Windows 95 内部解析」に少し書かれていた。この本も古本屋で手に入れたのだが、せっかく買ったのにほとんど読んでいない。ところで「Windows 95 内部解析」のような本で WinNT/2k/XP について解説した本はないのだろうか。最近この手の OS の内部に踏み込んで解説している類の本を全然見かけないような気がするのだが・・・。
Vector では、タイムスタンプを操作するソフトだけで1つのカテゴリーが作られていた。こんなにたくさんあるとは。果たしてこの中に Win9x でディレクトリのタイムスタンプを設定できるソフトはあるのだろうか。
QT_Thunkを用いたMSCDEXによる制御法 というものを見つけた。サンクコンパイラを使わずにサンクを行う方法のようだ。非公開 API の LoadLibrary16(), GetProcAddress16() などを使って、16bit DLL のサブルーチンのアドレスを取得し、QT_Thunk() を使って、その 16bit サブルーチンをコールするというのが大まかな方法のようだ。
LoadLibrary16() でググってみたところ、Wine のソースの一部が引っかかった。非公開のものも含めて、kernel32.dll がエクスポートしている関数名が載っていてなかなか興味深い。この前のシステム環境変数を取得するための非公開 API は、GetWin16DOSEnv() という名前で載っていた。自分としては、GetSystemEnvironmentStrings() の方がふさわしい名前だと思うのだが。
Win16 API の仕様を知りたいと思って、API の名前でググってみても全然情報が出てこない。かなり前に MSDN から入手した Win3.1 用 DDK の CD-ROM を漁ってみたところ、VC++ 1.5 も含まれており、その中に Win16 API のリファレンスもあった。WIN31WH.HLP というファイルがそのリファレンスだったが、サイズはわずか 7MB しかなかった。今の MSDN Library とはサイズが 2〜3桁違う。
Morry's Un'Gramming Page (Internet Archive) では、Win32 アプリで下位 1M 領域にメモリを確保する方法を3種類試して、いずれも失敗していたが、Win32 アプリ単独で下位 1M 領域にメモリを確保する方法を思いついた。前述の非公開 API を使って、krnl386.exe を読み込み、GlobalDosAlloc() のアドレスを取得し、QT_Thunk() を使ってそれを呼び出せばよいのではないだろうか。
早速試してみたところ、どうやらうまくいっているようだ。次は、これで確保したメモリ領域を使って、VxDCall() 経由で INT 1Ch - Function 00h を発行できるか試してみるとするか。
hrtimer.sys について調べていたら、UNDOCUMENTED 9801/9821 Vol.2 の memsys.txt の 0000:04F1〜04F3h に少し説明があった。関連項目に INT 1Ch - Function 80h,81h というものが載っていたが、手元の資料にはそれに関する説明がない。ググっても見あたらなかったので自分で調べてみた。どうやら以下のような仕様になっているものと思われる。hrtimer.sys をインストールしていなくても使えるのかどうかの確認はしていない。
INT 1Ch - Function 80h 分類 TIMER BIOS 名前 ハイレゾタイマの読み出し 対象 カレンダ時計割り込み搭載機種 入力 AH=80h 出力 CX:DX=経過時間(単位1/32秒) * 000000〜2A2FFFhの数値で表される。 解説 o 今日の日付になってからの経過時間を、1/32秒単位で取得する。 関連 INT 1Ch - Function 00h INT 1Ch - Function 81h 0000:04F1〜04F3h INT 1Ch - Function 81h 分類 TIMER BIOS 名前 ハイレゾタイマの設定 対象 カレンダ時計割り込み搭載機種 入力 AH=81h CX:DX=経過時間(単位1/32秒) * 000000〜2A2FFFhの数値で表される。 解説 o 今日の日付になってからの経過時間を、1/32秒単位で設定する。 関連 INT 1Ch - Function 01h INT 1Ch - Function 80h 0000:04F1〜04F3h
Win98 DDK の中にも thunk.exe が入っていた。すっかり存在を忘れていた。どういう訳か WinME DDK の中には入っていないようだ。
Platform SDK の中を探すと thunk.exe が見つかった。Platform SDK をインストールするのが面倒だったので、単体で動くのかどうか試してみることにした。「実例で学ぶWin32API活用術」のサンクを使ったサンプルプログラムを thunk.exe で処理してみた。
Microsoft (R) Thunk Compiler Version 1.8 for Windows 95. May 11 1995 13:16:19 Copyright (c) Microsoft Corp 1988-19104. All rights reserved.
というメッセージが表示されて、無事アセンブラソースが生成された。1988-19104 という表示が笑える。Win16 の開発環境は整えていないので、そこから先は試していない。
ActivePerl 5.8.6.811 が出ていた。use encoding "cp932"; で日本語ファイル名が扱えなくなる問題はそのままだった。
何となく GetEnvironmentStrings でググってみた。いくつかおもしろそうなページが見つかった。そこからリンクされているページにもおもしろそうなところがいくつかあった。さすがに、Win9x でシステム環境変数を変更する方法に関する情報は見つからなかった。
cab32.dll が利用している cab ファイルを操作するための MS 製のライブラリがどこで手に入るのか以前から疑問に思っていたが、ようやく分かった。Microsoft Cabinet SDK または、Microsoft Cabinet Software Development Kit から手に入る。プログラム開発に必要なのは、この中に含まれる fci.h, fdi.h, fci.lib, fdi.lib のようだ。これらのファイルは、Platform SDK や、VS.NET 2003 にも含まれていた。
cab ファイルの解凍だけならば、libmspack というフリーのライブラリでもできるようだ。そのサイトのトップに片仮名で「カイザー」と書いてあるのがかなり謎である。
「vxd le ヘッダ」でググってみたら、「新世紀 びじゅある/コマンドライン アナライザ」なんてものを見つけた。ソフト自体は公開されていないが、解析サービスをやっているようだ。
SpringM の解析中、関数の入り口の
xor eax,eax push ebp push offset C0046c9c3 push fs:[eax] mov fs:[eax],esp
といったコードがなにをやっているのか分からず不思議に思っていたのだが、構造化例外ハンドラ (SEH) のためのコードだそうだ。SEH については、A Crash Course on theDepths of Win32 Structured Exception Handling, MSJ January 1997 にかなり詳しく書いてあるようなのだが・・・あまりに長くて読む気がしない。
SpringM で zip ファイルを見ると、SpringM 起動後最初に開いた zip ファイルの中の先頭ファイルのタイムスタンプだけが実際より1時間早い時間が表示されるという現象が発生した。原因は SpringM なのか、axzip.spi なのか、偽 UnZip32.DLL なのか、それとも 7-zip32.dll なのか、切り分けに苦労したが、どうやら axzip.spi 0.08 の問題ようだ。undll.exe で 偽 UnZip32.DLL を呼び出した場合にはタイムスタンプは正常に表示された。axzip.spi を呼び出すテストプログラムを書いて試したところ、初回の呼び出し時だけ、先頭ファイルのタイムスタンプが1時間ずれることが確認できた。
Win9x では、PC-98 / AT 問わずに、重い処理を長時間行うと、Windows 上の時計が次第に遅れていくことがある。しかし、そのような場合でも WD で表示される時間はなぜか全く遅れていない。DOS コマンドなら遅れないのかと思って、time コマンドで時間を表示したところ、Windows プログラムと同じく遅れている。debug コマンドから、int 1ch, ah=00h (PC-98 Timer BIOS, Get Date and Time) を実行してみたところ、取得できた時間は全く遅れていなかった。Timer BIOS から時刻を取得して、Windows の時間を設定し直すプログラムを作ってみるのも技術的におもしろいかもしれない。(実用性からいえば、SNTP などで定期的に時刻を修正する方がよほど有用だが。) Win32 プログラムからどのようにして割り込みを発行すればよいのかが悩ましい問題だ。Win32 から Win16 へサンクダウンして Win16 DLL から割り込みを発行するのが最も一般的な手法だが、サンクは使ったことがない。Windows 用の 16bit コンパイラが必要という点も問題だ。VxD から割り込みを発行するのは有りだろうか。ところで、AT 互換機でも int 1ah, ah=02h/04h で取得した時間は遅れていないのだろうか。
Win9x 上で割り込みを発行したときの処理については、Morry's Un'Gramming Page (Internet Archive) にいろいろと書いてある。
SpringM で axzip.spi + unzip32.dll で zip ファイルの解凍を行うと、ファイルのタイムスタンプが 9時間ずれるのが以前から気になっていた。偽UnZip32.DLL というものを見つけたので、試してみることにした。これは、unzip32.dll と同じインターフェイスで 7-zip32.dll を使えるようにするためのラッパーだそうだ。偽UnZip32.DLL を使った状態で lview で zip ファイルを見ると、タイムスタンプは相変わらず 9時間ずれたままだったが、実際にファイルを解凍してみるとタイムスタンプのずれは発生しなかった。オリジナル版 unzip32.dll はもういらないな。
AntiZIP(ZIP撲滅委員会)というページを発見。(というか、偽UnZip32.DLL はここで知った。)
Pt's amagramme v0.00 Files ← DOS 関連のおもしろいプログラムがいろいろと公開されている。多くのプログラムが DOS 用のプログラムでありながら、プロテクトモードを駆使したプログラムであるという点がすごい。cppb は便利そうだと以前から思っているのだが、まだ使っていない。LEMM や、その関連プログラムも試してみたいと思っているが、まだ試したことはない。LLFN や lep などは、特に怪しげな感じがしておもしろそうだ。
昨日の続き。何とか TightVNC 経由で PC-98 上の IME が on/off できるようになった。AT 互換機側で [半角/全角] または、Ctrl + [変換] を押せば、IME が on/off できる。手抜きのため、Alt + [半角/全角] では on/off できないが、たいした問題ではないだろう。
サーバが AT 互換機でクライアントが PC-98 の場合も、今のままだと IME の on/off ができないはずだが、自分自身は必要としていないので、そこまでやるつもりはない。
AT 互換機の [半角/全角] キーは、動作がちょっと特殊なようだ。普通のキーの場合、例えばスペースキーを1回押したときにプログラムに対して送られてくるメッセージは以下のようになる。
KEYDOWN Key:20h, Scan:39h, RptCnt:1, Ext:0, Contxt:0, PrevStat:0, Stat:0 KEYUP Key:20h, Scan:39h, RptCnt:1, Ext:0, Contxt:0, PrevStat:1, Stat:1
このように、普通のキーでは、まず KEYDOWN が送られてきて、その後 KEYUP が送られてくる。一方 [半/全] キーを1回押したときのログは以下のようになった。
KEYUP Key:f3h, Scan:29h, RptCnt:1, Ext:0, Contxt:0, PrevStat:1, Stat:1 KEYDOWN Key:f4h, Scan:29h, RptCnt:1, Ext:0, Contxt:0, PrevStat:0, Stat:0
普通のキーとは、KEYDOWN と KEYUP の順序が逆になっている。さらに、送られてくるキーコードが f3h と f4h の2種類がある。もう一度 [半/全] キーを押すと、今度は以下のメッセージが送られてきた。
KEYUP Key:f4h, Scan:29h, RptCnt:1, Ext:0, Contxt:0, PrevStat:1, Stat:1 KEYDOWN Key:f3h, Scan:29h, RptCnt:1, Ext:0, Contxt:0, PrevStat:0, Stat:0
先ほどとはキーコードの順序が逆になっている。ちなみに、キーコード f3h は VK_DBE_SBCSCHAR、f4h は VK_DBE_DBCSCHAR として IME.H には定義されている。
要するに、プログラム側から見ると、全角モードに移るときには、VK_DBE_SBCSCHAR キーが離されて代わりに VK_DBE_DBCSCHAR キーが押され、逆に半角モードに移るときには、VK_DBE_DBCSCHAR キーが離されて代わりに VK_DBE_SBCSCHAR キーが押されることになるようだ。(DBCS: Double Byte Character Set, SBCS: Single Byte Character Set ?)
最終版にするなどと公言しておきながら、再びいじってみた。今回は ResoureHacker で、いくつかのダイアログボックスにアクセスキーを追加、ついでに Enter や Esc で閉じることのできなかったダイアログボックスをそれらのキーで閉じれるようにしてみた。他にも何ヶ所かアクセスキーを追加したいところはあるのだが、残りは逆アセンブルリストを見ながらいじらないといけないような部分なので、そこまでやるつもりはない。
TightVNC 漢字キー対応版 や RealVNC日本語版 を PC-98 の Win9x でサーバとして使うと、AT 互換機をクライアントとしたときに、キーボードから IME を ON にできないのが以前から気になっていた。TightVNC のサーバを PC-98 に対応させてみることにした。
まずは改造前に普通にコンパイルしてみたのだが、うまくコンパイルできない。VC7.1 では、zlib のコンパイルの段階でなんだかよく分からないエラーが出た。VC7.1 は止めて、VC6 でコンパイルしようとしたら、プロジェクト設定ファイルの WinVNC.dsp が読み込めないとのエラーが出た。仕方がないので、WinVNC.dsp を1から作り直した。いろいろプロジェクトの設定をいじって何とかコンパイルできるようになった。
PC-98 に対応させるためのコードを書いてみたがうまくいかない。AT 互換機と PC-98 では、IME を on/off させるためのキーコードがどう違っているのかがよく分からなかったので、入力されたキーコードを表示させるためのプログラムを作って、AT 互換機と、PC-98、さらに VNC 経由の PC-98 で動作を確認してみた。キーコードやスキャンコードを表示するソフトなら既にかなりたくさんあるようだが、どのソフトなら必要な情報を表示してくれるか吟味しているよりは、自分で作ってしまった方が早いと思って作ってしまった。
キーコード表示プログラムの実行結果を基に、TightVNC を改造。PC-98 で IME が on にできるようになったが、少し動作がおかしい。Ctrl キーが押されたままになっている。
改造後の ijexp32 が落ちてしまう原因を調べてみた。msvcp50.dll などでは、Import Lookup Table と Import Address Table の内容が異なっていた。Import Lookup Table がある場合はそれを参照し、無い場合には Import Address Table を参照するようにしたところ、msvcp50.dll でも springm.exe でもインポート情報が正しく表示されるようになった。
ijexp32 を VC7.1 (= VS.NET 2003) でもコンパイルしてみた。いくつかエラーが出た。例えば以下の部分では、std::vector<_Ty>::iterator から 'const void *' に変換できないとのエラーが出た。
vector<TCHAR> vecBuff(nLen); nLen = ::GetWindowText(hwndMsg, vecBuff.begin(), nLen);
STL はまるで理解していないが、以下のように書き換えたところ、エラーは出なくなった。
nLen = ::GetWindowText(hwndMsg, &vecBuff.front(), nLen);
このように書き換えたものは、VC6 でも問題なくコンパイルできた。
他には、StdAfx.cpp のコンパイル時に、
WINVER not defined. Defaulting to 0x0501 (Windows XP and Windows .NET Server)
というメッセージが表示されたのが少し気になる。このままでは、VC7.1 でコンパイルしたものは WinXP 以降でしか動かないということだろうか。
Win9x では、システムが使用中のファイルを置き換えるには、wininit.ini を使う。一方、WinNT 系では MoveFileEx() という API を使って同じことを行うことができる。その情報がどこに記録されているのか調べてみた。レジストリの HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations に REG_MULTI_SZ 型で記録されていることが分かった。実際に記録されている様子を見ると、
\??\C:\WINDOWS\System32\ShellExt\SET82.tmp !\??\C:\WINDOWS\System32\ShellExt\ijexp32.dll
という文字列が書かれていた。1行目が元のファイル名、2行目が置き換え先のファイル名のようだ。
スタートメニューの「ファイル名を指定して実行」からプログラムを実行した場合には環境変数の展開がどう行われるのか試してみた。結果はコマンドプロンプトから実行した場合と全く同じで %%path%% と入力しても環境変数の展開を抑制することはできなかった。
I.j Shell Property Sheets Export/Import 32 (ijexp32) では、Delphi などで作られたプログラムのインポート情報が表示できないのが以前から気になっていた。ijexp32 にはソースが同梱されていたことに気付いて、改造してみることにした。
ijexp32 は、VC5 で書かれているが、VC6 でコンパイルしたところ、いくつかエラーが出た。reinterpret_cast<DWORD> という部分をコメントアウトしたところ、無事コンパイルできた。元々の値が DWORD なのに、わざわざ reinterpret_cast<DWORD> で DWORD にキャストしようとしていたのが問題だったようである。
PE の構造は相変わらずよく分かっていないが、http://www.interq.or.jp/chubu/r6/reasm/PE_FORMAT/intro.html を参考にして、springm.exe とそれ以外の .exe ファイルを見比べて、インポート情報が表示されない原因を探してみた。
Delphi 製のプログラムは、インポート ディレクトリ テーブルの内容が少し違っていた。Delphi 製のプログラムは、Import Lookup Table RVA の値が 0 になっており、Import Address Table RVA しか使わないようになっていた。ijexp32 では、Import Lookup Table RVA を参照していたので、インポート情報が表示されなくなっていた。Import Lookup Table と Import Address Table の内容は基本的に同じようなので、Import Address Table を参照するように変更してみたところ、springm.exe のインポート情報も表示されるようになった。
改造後の ijexp32 を使って、msvcp50.dll や msvcp60.dll のインポート情報を表示させようとしたら、エラーが発生して、プロパティを表示していたプログラムが落ちてしまった。困った。
VC6 でリモートデバッグを試してみた。「おサル最強の味方!リモートデバッグ」を参考にした。MSVCMON.EXE がどこにあるのか分からなかったが、C:\Program Files\Microsoft Visual Studio\Common\MSDev98\Bin\ にあった。
複数の OS でデバッグを行いたいときには結構便利そうだ。
WinNT 系のコマンドプロンプトは環境変数の展開に問題があることに気付いた。コマンドラインから %path% と入力すると、これは環境変数 path の中身に置き換えられる。もし %path% という文字列をそのままプログラムに渡したいときには、Win9x では %%path%% というように % を2つ重ねると良い。しかし WinNT 系では、%%path%% と入力しても、%path% の部分が展開されてしまう。いろいろ試してみたが、%path% という文字列を展開せずにそのまま渡す方法が見つからなかった。どうやらこの動作は、ExpandEnvironmentStrings() API の動作と全く同じようだ。ExpandEnvironmentStrings() の方は(呼び出すプログラムの側で何とかすればよいので)まだいいとしても、コマンドプロンプトから %path% といった文字列をプログラムに渡すことができないのは欠陥ではなかろうか。
コマンドプロンプトから、command.com を起動した上で試してみても、%path% という文字列を渡すことはできなかった。Cygwin の bash など別のシェルを使えば %path% といった文字列を渡すことはできる。
kernel32.dll を調べてみようと思って、I.j Shell Property Sheets Export/Import 32 (ijexp32) と diswin に掛けてみたが、どちらも、序数 (ordinal) でしかエクスポートされていない API に関しては何も情報を表示してくれなかった。そこで、XDA (XDAWIN) を使ってみたところ、そのような API が存在することを表示してくれた。Win98SE と WinXP の kernel32.dll について調べてみると、Win98SE では ordinal 34 は隠し API だったが、WinXP では BuildCommDCBAndTimeoutsW() という全く別の API だった。
昨日調べたことを実際に試してみた。まず、以下の内容を sysenv.def という名前で保存し、
LIBRARY KERNEL32 EXPORTS GetSystemEnvironmentStrings@0 @34
以下のコマンドを実行すると、
LIB -machine:i386 -def:sysenv.def
sysenv.lib というインポートライブラリができる。これをリンクすると、kernel32.dll ordinal 34 (0x22) を GetSystemEnvironmentStrings() という名前で呼び出すことができる。Win98SE でこれを実行してみたところ、システム環境変数領域が取得できることが確認できた。環境変数は MS-DOS と全く同じ形式で格納されているようだった。GetSystemEnvironmentStrings() が返してきたアドレスから 0x10 を引いたアドレスには MCB (Memory Control Block) もあり、MCB を読むことで環境変数領域のサイズを取得することもできた。さらに、システム環境変数領域を書き換えたところ、システム環境変数が書き換わったことを確認できた。
SpringM の解析・パッチ当ての際には、dispe, diswin, MASM 7.10, VC6 のデバッガといったかなり単純なツールを使ったが、プログラムの解析用にはいろいろと便利なツールもあるようだ。Digital Travesia 〜 でじたる とらべしあ 〜には、いろいろとツールや情報がある。
ふと、Windows でグローバルな環境変数を自分のプログラムから設定するにはどうすればいいのだろうということを考えてみた。WinNT 系では、環境変数はレジストリに記録されているのだろう。調べてみると、以下の場所に記録されていることが分かった。
HKEY_CURRENT_USER\Environment HKEY_CURRENT_USER\Volatile Environment HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment (HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Session Manager\Environment)
問題は Win9x 系である。Win9x の CD-ROM には、winset.exe というグローバルな環境変数を変更するためのツールが入っている。winset.exe を調べてみると、kernel32.dll の ordinal 34 (0x22) にリンクしていた。どうやら隠し API のようである。
自作のプログラムで、こういう隠し API を呼び出したいときは特殊なテクニックが必要だったはず。そういえば、Morry's Un'Gramming Page に情報があったはず・・・と思って行ってみると、何と閉鎖していた。非常に残念である。Internet Archive からダウンロードできる分はダウンロードしておいた。HTML ファイルは全てダウンロードできたが、いくつかの GIF ファイルと、LZH ファイルがダウンロードできなかった。LZH ファイルは何年か前にいくつかダウンロードしてあったような気がするので探してみるか。画像がないのはちょっと問題だ。誰か保存していた人はいないだろうか。
結局目的の内容は、「VxDCall の概要」(Internet Archive) に書かれていた。
Jikes を、--enable-source15 を指定してコンパイルし直してみた。J2SE 5.0 で拡張された構文について説明してある本も一冊買ってみた。
Jikes を使ってみたところ、日本語の文字化けが発生した。-encoding オプションを指定してみたところ、このビルドでは -encoding オプションは使えないとのエラーが表示された。最新のものでは -encoding が使えるのではないかと期待していたが、1.22 でも -encoding は有効になっていなかったということだ。
日本語が使えないのは不便なので、Windows向け Jikesコンパイルメモを見ながら、-encoding オプションが使える、Jikes をコンパイルしてみることにした。このページでは、コンパイル環境として MinGW を使っているが、これだけのために、MinGW を導入するのは面倒なので、Cygwin の mingw を使ってコンパイルすることにした。
まずは、libiconv のコンパイルである。cygwin1.dll が無くても動作するようにするため、-mno-cygwin オプションを指定している。インストールは、make install ではなく、手動で行っている。
$ tar xvzf libiconv-1.9.2.tar.gz $ cd libiconv-1.9.2 $ gunzip -c ../libiconv-1.9.2-cp932.patch.gz | patch -p1 $ patch -p1 < ../libiconv-1.9.2-cp932-MS932-patch-1.diff $ CC='gcc -mno-cygwin' CXX='g++ -mno-cygwin' ./configure --enable-static $ make $ cp -p include/iconv.h /usr/include/mingw/ $ cp -p lib/.libs/libiconv.a /usr/lib/mingw/
次は、Jikes のコンパイルである。
$ CXX='g++ -mno-cygwin' ./configure $ make $ strip src/jikes.exe
というようにしてコンパイルしたところ、Jikes の実行時に、
*** Semantic Warning: I/O warning: "No such file or directory" while trying to open C.
というエラーが出てしまった。なかなか原因が分からなかったが、-bootclasspath C:\jdk1.5\jre\lib\rt.jar というオプションに原因があった。パスの区切り文字が ';' ではなく、':' になってしまっていたため、C と \jdk1.5\jre\lib\rt.jar が別のパスとして扱われていたのである。結局、以下のようにやり直した。
$ CXX='g++ -mno-cygwin' acx_cv_pathname_style_dos='yes' ./configure $ make $ strip src/jikes.exe
これでとりあえず -encoding オプションが使えるようになったが、Shift_JIS のソースをコンパイルするたびに毎回 -encoding MS932 を指定しないと行けないのは面倒だ。-encoding オプションを指定しないときには、MS932 がデフォルトのエンコーディングとなるように変更してみることにした。
jikesapi.cpp に、
JikesOption::JikesOption() : bootclasspath(NULL), extdirs(NULL), classpath(NULL), sourcepath(NULL), directory(NULL), encoding(NULL), nowrite(false), deprecation(false), optimize(false), verbose(false), depend(false), old_classpath_search_order(false), help(false), version(false), g(SOURCE | LINES), source(UNKNOWN), target(UNKNOWN), tolerance(DEFAULT)
という部分がある。encoding(NULL), を encoding("MS932"), に書き換えて再コンパイルしたところ、無指定で Shift_JIS のソースがコンパイルできるようになった。
configure の際に、--enable-source15 を指定すると、J2SE 5.0 の新しい構文の一部が使えるようになるようだ。しかし、まだ実験的なもののようだ。
Java でテキストビューアを作ってみようと思って、JEditorPane を使ったサンプルプログラムを動かしてみたのだが、J2SE 5.0 だと
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException at javax.swing.text.html.CSS$FontSize.getValue(Unknown Source) : : at java.awt.EventDispatchThread.run(Unknown Source)
という例外が発生してしまう。J2SE 1.4 だと問題なく動くのだが。なんでじゃー! ぬるぽ。
全部自前で描画してしまえば動作は軽くなりそうだが、コーディングは大変だ。さてどうするか。
diskio ルーチンを使って、EXDAP512 をちょっと調べてみた。どうやら普通の FAT16 フォーマットのようだ。セクタ 0 を読んで、BPB を取得することもできたし、FAT を読んで、クラスタチェーンを取得することもできた。
Susie Plug-in の使い方についてもう少し調査。DIB の扱い方を知りたかったのだが、Windowsプログラミング研究室にいろいろと書いてある。AWT Native Interface を使って、Java から Susie Plug-in を使ってみようと考えているところである。
Susie Plug-in の使い方・作り方について少し調べてみようと思った。Susie Plug-inプログラミングがよくまとまっているようだ。
Design Wave Magazine 1月号 には Xilinx の FPGA 基板が付いているということで、とりあえず1冊確保。今までの Altera の CPLD 基板や FPGA 基板を全く使っていないというのに・・・。
コンフィギュレーションには Parallel Cable IV が必要らしいが、純正品を使わずに何とかする方法はあるのだろうか。ヒューマンデータなどを見れば何かありそうだが。
rwfd や ipl98 で使っているダイレクトディスクアクセスルーチン diskio を少し改良。これを使って、Win9x の FAT32 ドライブでも正しいクラスタサイズを取得するためのルーチンを書いてみた。JNI を使って、Java アプリに組み込んでみるのもおもしろいかもしれない。
クラスタサイズ取得ルーチンを書いている途中で、MSDN ライブラリの GetDiskFreeSpace() の日本語版の説明で完全に間違っている部分を発見。lpRootPathName の説明で、「一方、"C:" のようなドライブ指定の最後に円記号を付けることはできません。」と書かれているが、実際は、C:\ のように円記号を付けなければならない。
Linux-NTFS のドキュメントに NTFS に関する結構詳しい説明があった。
久しぶりに Jikes を使ってみたら、JDK をバージョンアップしたせいで動かなかった。jikes のサイトへ行ってみると、Ver.1.22 が公開されていた。Jikes をバージョンアップしたところ、無事、JDK 5.0 でも動作するようになった。ところで、Jikes は、JDK 5.0 で導入された新しい構文には対応しているのだろうか。
SWT を使った開発の際に、Sleak というものを使うとリソースリークのチェックができるらしい。SWT を使ったプログラムを書いたことはまだないが、機会があれば試してみたい。
Eclipse 3.0.1 をインストールしてみた。
Perl 5.8.4 の日本語の扱いはやっぱり何かおかしい。s/// を使って置換を行うと、どういう訳か置換が行われた行だけ日本語が Unicode に化けてしまう。encoding の指定の仕方をいろいろ変えてみたがどうもうまくいかない。そのスクリプトでは Shift_JIS で扱うと問題が起こるのは、@ を @ に置換する部分だけだったので、結局、
s/\@/@/g;
を
s/(?<![\x81-\x9f\xe0-\xfc])\@/@/g;
に書き換えて、ファイル入出力時には文字コードの変換は一切行わないようにした。(?<!pattern) という表記については perlre に載っている。
「任意のフォルダから右クリックでCygwinのプロンプトを開く方法」というものを見つけた。同じようなことは今月号の UNIX MAGAZINE にも載っていた。Explorer を使うことはほとんど無いので、これを使うこともほとんど無いだろうが、一応メモ代わりに書いておこう。
実は SpringM の現在のディレクトリで Cygwin を起動したいと思うことはたまにある。よく考えれば SpringM に Cygwin を起動するコマンドを登録してしまえば良さそうである。前述のページを見ると、/etc/profile の中の cd "$HOME" という行をコメントアウトすることで、Cygwin を起動するときにホームディレクトリに移動してしまわないように変更している。ただ、そのままだと通常起動時にホームディレクトリへ移動しなくなってしまうので、cygwin.bat の中で、chdir %HOME% とすることでホームディレクトリへ移動している。しかし、Windows 側で HOME 環境変数を設定しておく必要があるというのが個人的にはどうも気に入らない。
Cygwin (bash) が起動すると、/etc/profile が読み込まれ、cd "$HOME" が実行されることで、ホームディレクトリへ移動するわけだから、そこから直前のディレクトリへ戻ることができれば、SpringM (or Explorer) の開いていたディレクトリへ行けることになる。1つ前のディレクトリに簡単に戻る方法はないのかと調べてみると、cd - で戻れることが分かった。今までこんな簡単なコマンドを知らなかったとは・・・orz。
結局、SpringM のユーザー定義機能に、
C:\cygwin\bin\cko.exe -geometry 80x40 +si -e /bin/bash --login -i
というコマンドを登録して、さらにキーカスタマイズで、これに Y を割り当てた。これで、SpringM からは Y を打つだけで Cygwin が起動できるようになった。SpringM で開いていたディレクトリに移動するには、cd - と入力すればよい。これは便利だ。
ByteBlaster II の互換ケーブルを製作している人がいるのを発見。(ByteBlaster2ダウンロードケーブルの製作)
SpringM のパッチの開発終了宣言を出したというのにバグを発見。1.50k7 で k4 のバグを修正したところ、今度は、ディレクトリ名の表示が途中で切れてしまう。さてどうやって直すか。
Format 関数の呼び出し部分を見直していたのだが・・・うーん、もしかしてメモリリークしてたのかも? 一応タスクマネージャで確認した限りではメモリの使用量が増えているようには見えなかったのだが。Delphi の文字列の扱いはよく分からん。
もう一度タスクマネージャでメモリの使用量を確認してみた。SpringM を長時間起動していると使用量が少しずつ増加していく。オリジナル版ではどうかと確認してみると、こちらもやはり長時間使っていると使用量が少しずつ増加していく。パッチを当てたのとは別の部分でメモリリークを起こしている可能性がありそうだ。しかし今までずっと SpringM を使ってきて、メモリリークで問題が起きた記憶はないので、特に気にするほどのことではあるまい。
なんとか、表示のバグ修正を完了。メモリリーク(?)はよく分からないので放置。今度こそパッチの開発は終了かな? 結局 SpringM のパッチの作成には 10日以上掛けたことになるが、おそらくソースがあれば半日程度の作業だろう。はっきり言って作業効率は最悪だ。しかしこれで SpringM をまだしばらく現役で使える。SpringM 1.50k8 が使えなくなる頃までには自作のファイラーを作り上げたいところである。
おおっと、1.50k4 で 4GiB 以上のファイルサイズの表示に対応させた際に少しミスってた。ファイル名が長いと、ファイル名がファイルサイズの表示部分にまではみ出してしまう。
また SpringM を改造。タイムスタンプの表示をいじって、年を4桁表示できるようになった。ついでに秒も表示するようにしてみた。元のプログラムは、strcat のような関数を使って、表示する文字列を組み立てていたのだが、それを Delphi の Format 関数を使って文字列を組み立てるように変更してみた。はじめは、アセンブラレベルでの Format 関数の呼び出し方が分からずに苦労した。
今更気付いたが、ハングアップの日々を書き始めてから 6年も経った。先月の HTML ファイルは、30kB を超えていて、今までで一番大きい。最近は、おもしろいサイトを見つけるとブックマーク代わりに書くことも多い。
SpringM のパッチを試してくれた方が感想を書いてくれている。そこにあるような不具合には遭遇した記憶がないので、残念ながら対策を施すことはないだろう。さらに、lview を .rar に対応などのレベルになるとパッチで何とかするのは無理である。パッチで何とかできるのは、表示などの内部処理に深く関わらない部分がせいぜいである。実は、今回パッチを作ろうという気になったのは、NT 系で空ディレクトリが削除できないのが、設定の変更だけで対策可能だということが分かったからである。SpringM の大きな2つの問題点のうちの1つが解消できたので、もう1つも何とかしたいと思ったというわけである。
SpringM ではタイムスタンプの変更が 2秒単位でしかできないのが少し気になっていた。調べてみたところ、FileTimeToDosDateTime() を使って、タイムスタンプを一度 DOS 形式に変換して扱っていることが判明。せめて SYSTEMTIME 形式で扱っていれば良かったのだが、これでは 1秒単位に変更するのは無理だ。
「ハングアップの日々」をアンテナに登録している人がいるようだ。
http://i-know.jp/iteb/?did=MTtZ1FoXJzzqY4Pi&t=1100683382
FAT32 の構造は広く知られていて、MS 自身によって仕様書も公開されている。しかし、NTFS については仕様が公開されていないため、情報が少ない。復元など NTFS に対応しているソフトも存在するので、NTFS を解析して情報を公開している人はいないかと探してみることにした。
NTFS.com というサイトがあった。NTFS 関連のツールを作っている会社(?)のサイトらしい。NTFS の BPB の構造が載っているが、これは自分自身かなり前から知っている。肝心の Master File Table (MFT) はどうなのだろうと思って見てみると、概要しか書かれていなかった。残念。
NTFS の構造ではないが、NTFS のセキュリティ機能と落とし穴は、なかなか興味深い。NTFS では、副次ストリームというものを扱えるということを初めて知った。(これを含めて IPA ISEC セキュア・プログラミング講座には、ためになる情報がいろいろある。) これからのファイラーでは、副次ストリームも扱えるようになるとおもしろいかもしれない。
副次ストリームなど、NTFS を扱うプログラムを作る上で有用そうな情報があるサイトをいくつかメモしておく。
http://www.microsoft.com/japan/msdn/windows/windows2000/ntfs5.asp
http://www.port139.co.jp/csir_ntfsads.htm
http://damedame.monyo.com/ml/archive/200/220.html
http://damedame.monyo.com/ml/archive/200/221.html
http://www4.org1.com/~kitt/cgi/ctb/ctb.cgi?db=sound&id=697&mode=raall
今日も SpringM を改造。ファイルサイズを3桁区切りで表示できるようになった。今までは、数百 MB 以上のファイルを扱うときには、サイズが一目では分からずわざわざ桁数を数えたりしていたが、これでサイズが一目で把握できるようになりますます使いやすくなった。ついでに、年を4桁表示に変えてみようと思ったのだが、こちらはどうもうまくいかなかった。年を 100 で割って余りを算出している部分があったのだが、そこを書き換えて年をそのまま使うようにしてみたのだが、表示は2桁のままになっている。
後はマークファイルサイズが 4GiB 以上でも正しく表示できるようにしたいところである。MPEG2 ファイルを数個マークしただけで、表示が狂ってしまうのはやはり使いづらい。ただ、こちらは問題がいくつもの関数にまたがっているので、対策はかなり大変である。
先月の spam メールの集計結果。着信拒否になった spam が少なくとも 5355通。着信拒否にならなかった spam が 437通。そのうち Spam Mail Killer で削除できた spam は 422通(96.6%)。
4GiB 以上のファイルサイズの場合は、すべて 4,294,967,295 bytes として表示するようにしてみた。しかしこうすると、実際にはファイルサイズが変化しているのに、表示されているファイルサイズには一切変化が現れていないという状態が起こりうる。実際に試してみると大いに違和感を感じた。結局、ファイルサイズの表示も 4GiB 以上に対応させることにした。