Ko-Window プログラミング、入門編 その2 「アプリケーションらしいプログラム」 今回はもうちょっと進んで、ファイル操作を含めたアプリケーションを作ってみま す。 ● Ko-Window でのファイル操作 Ko-Window でのファイル操作に特別なことはほとんど何もありません。C言語のラ イブラリをそのまま使ったファイルアクセスができます。 これは、引数として与えたファイルのサイズを表示するプログラム例です。 ---------------------------------------------------------------------------- #include #include int WindowHeapSize= 1024*8; /* <=== 重要! */ int FileSize= 0; char *FileName= NULL; fSize( fname ) /* ファイルサイズを求める */ char *fname; { FILE *fp; if( fname && (fp= fopen( fname, "rb" )) ){ int size; fseek( fp, 0, 2 ); size= ftell( fp ); fclose( fp ); return size; } return 0; } EventExec( wp, info ) WindowID wp; EventInfo *info; { DrawBuf dbuf[10], *dp= dbuf; char str[80]; switch( info->option ){ case EventOpen: FileSize= fSize( FileName ); WindowRedraw( wp ); return TRUE; case EventClose: WindowClose( wp ); WindowConnectionClose(); return TRUE; case EventRedraw: sprintf( str, "%d byte", FileSize ); DrawSetClear( dp++, 1 ); DrawSetSymbol( dp++, 4, 4, str, AttrDefault, 12 ); WindowDraw( wp, dbuf, dp-dbuf ); return TRUE; } return FALSE; } WindowMain( argc, argv ) char **argv; { int x= 10, y= 10; argc= AnalyzeArgs( argc, argv, &x, &y, NULL, NULL ); if( argc >= 2 ) FileName= *++argv; WindowTitleOpen( x, y, 120, 50, NULL, "smpl", Close|Push, EventExec ); } ---------------------------------------------------------------------------- ファイル名からファイルサイズを得るにはさまざまな方法がありますが、ここでは あえて fopen(), fseek(), ftell() という方法を取ってみました。 ●ファイル操作と HEAP 領域 さて、ファイル操作例のプログラムで注目して欲しいのは、4行目にある WindowHeapSize の部分です。この変数は特別な意味を持ち、そのアプリケーション で使う HEAP 領域のメモリサイズを決定します。 HEAP とは一言で行ってしまえば malloc() や calloc() でメモリ確保に使うため の領域です。 だから malloc() を使うプログラムでは必ずこの WindowHeapSize に予め HEAP 領 域の大きさを byte 単位で入れておかなければなりません。逆に malloc() を使わな いプログラムでは、この変数を 0 にしておくとそのアプリケーションが消費するメ モリを低く押えることができるわけです。 Command.win 上で ps コマンドを使うと、それぞれのプロセスでどれだけの HEAP を確保しているのかがわかるでしょう。 上の例のプログラムでは、malloc() は使ってないのに HEAP 領域を 8Kbyte も確 保しています。実は、fopen() 関数が内部で malloc() を呼び出しているからなの です。 fopen() でファイルアクセスを行うプログラムでは、同時に開くファイルの個数に 応じて HEAP 領域を設定して置かないと、fopen() で常に失敗してしまうことになり ます。ここは Ko-Window でファイル操作をする場合に引っ掛かりやすいところなの で注意して下さい。 XCv1-lib と XCv2-lib の場合は、「fopen() で同時に開く可能性のあるファイル の数×2Kbyte」くらいの HEAP 領域が必要になります。libc の場合は、×2Kbyte で はなく ×5Kbyte で計算して下さい。もちろん malloc() も使うならそれ以上必要で すから、かなり余裕を持って多目に設定しておいたほうが良いでしょう。 ちなみにこの WindowHeapSize という変数は、起動前に一度だけシステムで参照さ れるので、プログラム内で書き換えても意味がありません。必ずグローバル変数とし て初期値を定数で与えて下さい。 ●外部からファイル名を受け取る サンプルのプログラムでは、ファイル名は起動時にコマンドラインから与えなけれ ばなりませんでした。しかし一度起動した後はサイズを表示するだけであまり意味が ありません。 そこで画像ローダー等でよくみかけるように、起動後も kf 等からファイル名を受 け取れるようにしてみましょう。 EventExec() を以下のように修正します。 ---------------------------------------------------------------------------- EventExec( wp, info ) WindowID wp; EventInfo *info; { DrawBuf dbuf[10], *dp= dbuf; char str[80]; switch( info->option ){ case EventOpen: /* ここで EventUser を受け取ることができるよ、という宣言をする */ WindowSetEventAttr( wp, EventAttrDefault|EventUserON ); FileSize= fSize( FileName ); WindowRedraw( wp ); return TRUE; case EventClose: WindowClose( wp ); WindowConnectionClose(); return TRUE; case EventRedraw: sprintf( str, "%d byte", FileSize ); DrawSetClear( dp++, 1 ); DrawSetSymbol( dp++, 4, 4, str, AttrDefault, 12 ); WindowDraw( wp, dbuf, dp-dbuf ); return TRUE; /* これが実際のファイル受け取り処理 */ case EventUser: switch( info->ComData ){ case UserStrings: /* kf等からのファイル名受取 */ FileSize= fSize( *(char**)info->ComBuffer ); break; case UserString: case UserPaste: /* 文字列も取り敢えずファイル名と見る */ FileSize= fSize( info->ComBuffer ); } WindowRedraw( wp ); return TRUE; } return FALSE; } ---------------------------------------------------------------------------- kf 等からファイル名を受け取った場合 UserEvent というイベントが発生します。 その UserEvent の種類が info->ComData に数値として入ります。実際のデータは info->ComBuffer というポインタにアドレスとして渡されます。 ファイル名が渡される場合は、常に UserStrings というユーザーデータタイプが 使われます。これは複数の文字列(ファイル名)を意味します。 例えば、file1.dat, file2.dat, file3.dat という 3 つのファイル名が送られて くる場合に info->ComBuffer の指している内容は以下のようになります。 char *fnamelist[4]= { "file1.dat", "file2.dat", "file3.dat", NULL }; info->ComData= UserStrings; info->ComBuffer= fnamelist; なお、上の例のプログラムでは、複数ファイル名が送られてきても処理できないの で、UserStrings で最初の1つ目のファイルしか見ていません。結構強引なポインタ 処理に見えますが、0個のファイル名は送られてこない(イベントにならない)という 暗黙のルールがあるためこれで大丈夫です。 もう1つ、例のプログラムでは UserString と UserPaste というのも使っています。 これらは共に、単一の文字列が送られてきたことを示すデータタイプです。kf では ファイル名が1つでも必ず UserStrings になりますが、これらも一応ファイル名とし て処理しておきます。こうすると、usend 等による外部からのコントロールが可能に なり(UserString)、また Clip & Paste で切り出してきたファイル名(UserPaste)でも 受理できるようになります。 ところで、説明では kf 等と書きましたが、このようにファイル名を他のウィンド ウへ送る機能を持ったシェル(ファインダー)には現在次のものがあります。 ・FINDER.win Ko-Window の初期のシステムに付属していたものです ・kf.win 多彩なメニュー登録でそれ自体で何でもできる強力なシェルです ・shell.win Macライクなアイコンでファイルを表現するタイプのものです これらはすべてファイル名転送に同じインターフェースを持っています。 (MicroFINDER というのもありましたが、構成や中身は FINDER.win と同じです) ●外部にデータを送る 外部からファイル名を受け取ったことで、かなりウィンドウアプリケーションらし くなりました。もっとも、ウィンドウ表示はそのままで寂しく、ぜんぜんそれらしく ないじゃないかという人がいるかもしれません。しかし、ウィンドウアプリケーショ ンによるメリットは、ハデなデコレーションではなく複数アプリケーションが同一の 場で自由な連係を保てることにあります。 その連係動作をもっと強めるために今度は逆に、他のウィンドウにデータを送るこ とを考えてみます。 EventExec() を再び以下のように修正します。 ---------------------------------------------------------------------------- EventExec( wp, info ) WindowID wp; EventInfo *info; { DrawBuf dbuf[10], *dp= dbuf; static char str[80]; /* メモリに残すために static に変更 */ switch( info->option ){ case EventOpen: WindowSetEventAttr( wp, EventAttrDefault|EventUserON ); FileSize= fSize( FileName ); WindowRedraw( wp ); return TRUE; case EventClose: WindowClose( wp ); WindowConnectionClose(); return TRUE; case EventRedraw: sprintf( str, "%d byte", FileSize ); DrawSetClear( dp++, 1 ); DrawSetSymbol( dp++, 4, 4, str, AttrDefault, 12 ); WindowDraw( wp, dbuf, dp-dbuf ); return TRUE; case EventUser: switch( info->ComData ){ case UserStrings: FileSize= fSize( *(char**)info->ComBuffer ); break; case UserString: case UserPaste: FileSize= fSize( info->ComBuffer ); } WindowRedraw( wp ); return TRUE; /* マウスの左ボタンでデータ転送をする */ case EventMouseSwitch: if( info->LeftON ){ /* データ送信は、たったのこの1行だけ! */ UserSendOperation( wp, info, UserString, str ); return TRUE; } return FALSE; } return FALSE; } ---------------------------------------------------------------------------- 今度の変更では、マウス左ボタンの処理を加えました。ウィンドウ内に表示してい るファイルサイズの文字列を、マウスの左ボタンでドラッグして、Command.win やエ ディタ等へ簡単に転送できるようになります。 処理している関数は UserSendOperation() これ1つきりです。ただしこの場合 corlib.a (もしくは libcor.a) が必要になりますので、コンパイル時にリンクして いない場合は追加しておいて下さい。 このデータ転送がついたことで、急にアプリケーションとしての幅が広がりました。 もっとも ファイル名 -> ファイルサイズ への変換くらいたいした事ないのかもしれ ませんが、これをベースにいろいろな応用を考えてみて下さい。 ここまでで結構プログラムが長く複雑になったように感じるかもしれませんが、よ くよくみると 80行足らずの C 言語のソースです。たったこれだけのコードで、この 入出力を含めた処理が記述できてしまうわけですから、Ko-Window ウィンドウプログ ラミングのシンプルさと分かりやすさを実感できるのではないでしょうか。 ●悩まないためにファイル操作へのヒント 「Ko-Window でのファイル操作に特別なことはほとんど何もありません」と冒頭へ書 きましたが、実はひとつだけ重要なことがあります。それは、 自分でオープンしたファイルは責任を持って自分でクローズする ということです。当たり前のようですが、Ko-Window のように多数のプログラムが共 存する世界では、極めて重要なことです。より詳しいことが file.doc に書いてある のでぜひ参照しておいて下さい。 -- 1994 9/01 初期公開版 1995 9/15 libc対応と変更のあった関数名の修正 小笠原博之 oga@dgw.yz.yamagata-u.ac.jp DenDenNET: DEN0006 COR.