戻る


EXM形式プログラミング入門                          津守 正樹 (Brahma)                               95/02/14                              改訂 02/22 0 はじめに  HP100/200LX(以下HP100LXと表記)の内蔵PIMは極めて優秀であり、広範 な分野をカバーします。しかし、内蔵PIMでもカバーできない業務が発生し た場合、どうすればよいのでしょうか。  幸いにして、HP100LXはIBM-PC/XT互換機であり、MS-DOS Ver5 を内蔵して います。そのため、DOSアプリケーションを作成することにより、任意の業 務を行うことが可能です。ただし、通常のDOSアプリケーションでは、内蔵 PIMの様にメモリの許す限り起動し自由に切り替える、ということができま せん。  そこで本稿では、内蔵PIMの形式であるEXM形式のプログラムの作り方につ いて解説します。本稿で基礎知識を得て、本稿で挙げている開発資料を読め ば内蔵PIMと同様の特徴を持ったプログラムを作成することが可能です。  但し、本稿発表時点ではHP95LX用の開発キットしか一般公開されていない 為、その範囲内での開発について解説します。  本稿は、C言語の知識を持ち、ある程度のプログラムを組むことができる 人を対象とします。また、一般的なHP100LXに関する知識(用語、操作)に ついても持っていることを前提とします。 1 EXM形式とは  EXM形式とはHP95/100/200LXの System Manager において動作するアプリ ケーションの形式です。ただし、EXM形式とは正式名称ではなく、正式には System Manager-Compliant Application と呼ばれます。本稿では、MS-DOS の一般的なプログラムの形式であるEXE形式と対比する為、及び表記の短さ と親しみやすさからEXM形式と表現します。  EXM形式の便利な点は、System Manager のタスクスイッチング機能を使用 できること、機能の共通化により実行形式のファイルサイズと実行時のメモ リの占有量が小さいこと、System Manager が提供するサービスを使用できる こと、などです。  逆に不便な点は、作成する上で守らなければいけない約束事がある、一般 的なC言語の関数で使用できないものがある、EXE形式からEXM形式への変換が 必要、メモリモデルは tiny か small モデルしか使えない、などです。 2 EXM形式作成上の留意点  一般的なDOSのプログラムを作成することに比べ、EXM形式のプログラムを 作成する場合は、以下の点に注意する必要があります。  ・メモリ管理   System Manager は独自のメモリ管理を行っていますので、従う必要が   あります。具体的には動的なメモリの確保は全て System Manager 提供   の関数を使用します。そのため、一般的なC関数で使えなくなるものが   あります。stdio.h関係がその例で、代替の関数が用意されています。  ・スタートアップコード   通常のC言語のスタートアップコードは使用できません。System   Manager用のスタートアップコードを使用します(95開発キットに含ま   れている)。このため、起動時の引数、環境変数は使えません。  ・初期化、終了処理   EXM形式プログラムの起動時と終了時には、m_init関数とm_fini関数を   コールする必要があります。exit関数等は使用できません。  ・イベントループ   他のEXM形式プログラムと協調動作を行うために、プログラム内部で   m_event関数やm_nevent関数を使ってイベント待ちを行う必要があり   ます。キーボード入力はイベントループに含めなければなりません。 3 EXM形式作成に必要なもの  以下のものを用意して下さい。  ・HP95LXソフト開発キット   FYHPPC LIB15 #22に登録されています。   特に、\tools\crt0.obj、\tools\csvc.obj、\headers\*.h は、コンパ   イル時に必要となりますので、適切なディレクトリに収納しておいて   下さい。  ・HP100LX開発資料   NORIさんが配布して下さっています。   EXE形式からEXM形式に変換するツールの e2m.exe の新版が含まれてお   り、新版はtlink対応になっています(但し古いバージョンのtlinkには   対応していません)。旧版は95開発キットに含まれています。  ・Cコンパイラ   TC++/BC++をCコンパイラとして使用した場合に対応していることは確認   済みです。95開発キットがそのまま使用できます。また、推奨コンパイ   ラは MS-C Ver.5.1以降です。それ以外のコンパイラについてはFYHPPC   8番会議室でお尋ね下さい。   筆者は TC++ Ver1.0 2ndEdition(98版)と MS-C Ver.6.00A(PS/55版)を   HP100LX上で使用しています。   コンパイラによっては、ヘッダーファイルやスタートアップコードの   修正が必要になります。  ・ホストマシン   必要ではありませんが、開発効率を上げるために高速なマシンで開発す   ることは有効です。PC98かIBM PC互換機を使用する人が多いようです。   コンパイラにも言えることですが、HP100LXがXT互換機だからといって   PC互換機にこだわる必要はありません。機種依存の関数(グラフィック、   BIOS、サウンド等)とスタートアップコードを除けば、PC98用のコンパ   イラでも使用可能なものが多いです。  ・HP95LXシステムマネージャドキュメント   FYHPPC LIB15 #46に登録されている、HP95LXの System Manager に関す   る資料です。   特に Chapter 7 と Chapter 8 は必読であり、Chapter 8 に関しては有   志の方々により和訳され、NORIさんが配布して下さっています   (上記のHP100LX開発資料に含まれています)。  ・HP95LXプログラミングガイド   FYHPPC LIB15 #47に登録されている、HP95LXのプログラミングに関する   資料です。   System Manager の内部処理について述べられており、EXM形式プログラ   ミングの参考になります。  注:HP95LXソフト開発キットとHP95LXシステムマネージャドキュメントは    HP/YHP社の好意により公開されているものでHP/YHP/Lotus社によるサ    ポートは行われていません。    NORIさんが配布して下さっている資料は、プログラム開発の意図    を持った方の為にボランティアで配布して下さっているものです。開    発の意図がない方はご遠慮下さい。 4 プログラムの実際  論より証拠ということで、実際にプログラムを走らせてみます。  見慣れない関数や構造体がありますが、なにも言わず、コンパイル及び 実行をしてみて下さい。  以下のプログラムはHP95LXソフト開発キットに含まれている SMHELLO を さらに簡単にした、SMHELLOT(SMHELLO Tiny)です。 ========================================================= SMHELLOT.C #include "interfac.h" #include "event.h" #include <string.h> void app_display( void ); void main( void ) { EVENT appevent; int done = 1; m_init(); /* System Manager への初期化宣言 */ app_display(); /* 画面表示 */ do { /* イベントループ */ m_event( &appevent ); /* イベントの取得 */ switch ( appevent.kind ) { /* イベント種類の判定 */ case E_KEY: /* キー入力発生 */ done = 0; break; } } while( done ); m_fini(); /* System Manager への終了宣言(戻らない) */ } void app_display( void ) { m_setmode( 1 ); /* テキストモードにセット */ m_disp( 3, 5, "hello, world", strlen( "hello, world" ), 0, 0 ); /* 文字の表示 */ } ==================================================================== =========================================================== MAKEFILE smhellot.exm : smhellot.exe e2m smhellot smhellot.exe : smhellot.obj tlink /m smhellot+a:\tc\lib\csvc+a:\tc\lib\crt0,,,a:\tc\lib\cs.lib smhellot.obj : smhellot.c a:\tc\bin\tcc -ms -c -Ia:\tc\headers smhellot.c ====================================================================  上記の二つのファイル(SMHELLOT.C,MAKEFILE)を任意のディレクトリに 入れ、MAKEFILEを自分のディレクトリ構成に合わせ修正します。あとは、 MAKEするだけで、SMHELLOT.OBJ,SMHELLOT.EXE,SMHELLOT.MAP, SMHELLOT.EXMが作成されます。但し、上記のMAKEFILEはTurboC用ですので、 開発環境に応じて変更して下さい。  SMHELLOT.EXMを登録して実行してみて下さい。キー入力により終了します。 5 プログラムの解説  各部分の解説をします。上記のソースには切り出しやすいように行番号 を付けませんでしたが、解説では行番号を使用します。  ・SMHELLOT.C    1行目 System Managerとのインターフェース用のインクルードファ       イル    2行目 イベントサービス用のインクルードファイル    9行目 EVENT構造体の確保 これにより発生したイベントの情報を       得る   12行目 System Managerへの初期化宣言 (*)   13行目 画面表示の為app_disp関数をコール   15行目 ここから22行でイベントループを形成   16行目 イベントの取得を行う (*)   17行目 EVENT構造体に含まれたイベントの種類により分岐   18行目 イベントがキー入力ならイベントループから抜ける(〜20行)   24行目 System Managerへの終了宣言 (*)   30行目 画面をテキストモードに設定 (*)   32行目 "hello, world"という文字列を出力 (*)   *の付いている行の関数は全て System Manager 提供の機能(System  Manager Service)を実現する関数です。   ・m_init関数     EXMプログラムの最初(mainエントリポイント)で必ず呼び出す必要     があります。   ・m_event関数     キー入力やプログラム終了などのイベントを取得します。イベント     が発生するか、タイムアウト(約0.5秒間)するまで、プログラムに     制御は戻りません。   ・m_fini関数     EXMプログラムの終了時には必ず呼び出す必要があります。この関数     呼び出し後は制御はプログラムに戻りません。exit関数やreturn関     数でEXMプログラムを終了することは禁止されています。   ・m_setmode関数     画面モードの設定を行います。ただし、設定できるのはHP95LX互換     モードのテキストモードとグラフィックモードだけです。   ・m_disp関数     文字列を画面に表示します。ただし、HP95LX互換モードでのみ動作     します。   このプログラムのように、入力待ちをするような場合は、全てイベント  ループに含めます。これにより、効率的(ユーザーにとって無理のない)  なタスクスイッチングを実現します。   当然のことではありますが、イベント待ち以外に待ちを作るべきでは  ありません。HP100LXがそのプログラムに独占されてしまうからです。  m_event(m_nevent)関数によるイベント待ちを実行しないと、他のプログ  ラムの起動はできませんし(hot key を認識しないため)、アラームも  機能しません。   またイベント待ちから次のイベント待ちに戻る時間が短い方が良いこと  は言うまでもありません。  ・MAKEFILE   MAPファイルを作成する必要がありますので、tlinkに /m オプションを  付けます(5行目)。crt0.obj は System Manager用スタートアップコー  ド、csvc.obj は System Manager インターフェースモジュールです。   e2m.exe は、EXEファイルとMAPファイルからEXM形式ファイルを作成  します(2行目)。 6 System Manager Service  System Manager Service は機能別に以下の19に分けられます。  ・イベントサービス (Event Services)  ・メニューサービス (Menu Services)  ・ファイルメニューサービス (File Menu Services)  ・スクリーンサービス (Screen Services)  ・編集サービス (Editing Services)  ・ファイルサービス (File Services)  ・プロセス管理サービス (Process Management Services)  ・クリップボードサービス (Clipboard Services)  ・サウンドサービス (Sound Services)  ・メモリ管理サービス (Memory Management Services)  ・日付/時間サービス (Date/Time Services)  ・印刷サービス (Printer Services)  ・設定サービス (Configuration Services)  ・通信サービス (Communications Services)  ・その他サービス (Miscellaneous Services)  ・リソースサービス (Resource Services) (*)  ・ヘルプサービス (Help Services) (*)  ・比較サービス (Collating Services)  ・123ブリッジサービス (1-2-3 Bridge Services)  SMHELLOT に使用した、m_init関数,m_fini関数はプロセス管理サービス m_event関数はイベントサービス、m_setmode関数,m_disp関数はスクリーン サービスに所属します。  各サービスの詳細は HP95LXシステムマネージャドキュメント Chapter 8 を参照して下さい。*の付いているサービスは内蔵PIM用であり、自作プロ グラムには使用できません。 7 イベント  SMHELLOT はEXM形式のプログラムですが、System Managerのプログラ ムらしくありません。実行してみると分かりますが、起動中に別のプロ グラムを起動して戻った場合、画面が表示されないからです。  System Manager はイベントとタスク、メモリの管理を行っていますが 個々のプログラムの画面表示の管理まではしてくれません。  別のプログラムが起動した後、プログラムが再びアクティブになった 際にはイベントが発生します。すなわち、 System Manager がEVENT構造 体によりイベントとして連絡してくれますので、アクティブになったプロ グラム自身が画面の再表示を行う必要があります。  この際、画面のイメージを保存しておくと高速に再表示することが可能 ですが、メモリを余計に消費しますのでそのようなことはしないことが推 奨されています。  では、SMHELLOT に再表示の機能をつけ加えます。17行目の下に以下の プログラムをつけ加えて下さい。 case E_ACTIV: app_display(); break;  プログラムが再度アクティブになった際には appevent.kind に E_ACTIV が代入されています。それを判定して再度画面の表示を行うわけ です。  イベントには以下の12種類が存在します。   ・E_ACTIV    プログラムがアクティブになった際に発生   ・E_ALARM_DAY  プログラムがアラームをセットする為のイベント   ・E_ALARM_EXP  設定したアラーム時刻になった際に発生   ・E_BREAK    Ctrl+Breakキーを押した際に発生   ・E_BRIDGE   ブリッジサービス要求時の123専用のイベント   ・E_DEACT    他のプログラムの起動により非アクティブ状態になる           際に発生   ・E_GROW    123への拡張要求   ・E_KEY     キー入力が発生した際に発生   ・E_NONE    イベント無し   ・E_SHRINK   123への縮小要求   ・E_TERM    プログラムが終了させられた際に発生   ・E_TIMECHANGE システムの日時が変更された際に発生  これらのイベントに応じた対応をプログラミングすることにより、効率的 に System Manager の機能を利用し、高機能かつ使いやすいプログラムを 実現します。  タスクスイッチングを行う上で重要なことは、E_DEACT,E_TERM の各イベ ントが発生した場合、次にイベント待ち(m_event(m_nevent)関数のコール) になってはじめて、非アクティブ化やプログラムの終了が行われる、という 点です。つまり、非アクティブ化やプログラムの終了が発生しても、一度は 制御がプログラムに戻されるということです。そのため、E_DEACT,E_TERM イベントの検出により、クリップボードの更新やファイルのクローズ等の 必要な処理を行うことができます。 8 イベントループを持たないEXM形式プログラム  一般的にはEXM形式プログラムはイベントループを持ちますが、待ちが 発生しないプログラム、あるいは他のアプリケーションを起動したくない プログラムなら持たないことも可能です。ただし、そのプログラムの実行中 はタスクスイッチングを行うことができません。  その例としてプログラムを一つ示します。クリップボードの読み書きと ファイルアクセス、DOSプログラムの起動を行っていますので、そちらも 参考にして下さい。 =========================================================== DOSCOM.C #define TFNAME "a:\\sys\\doscom.tmp" #define REDIRCT " > "TFNAME"\r" #include "interfac.h" #include "fileio.h" #include <string.h> void app_main(void); void main( void ) { m_init(); app_main(); m_fini(); } void app_main( void ) { char com_buf[128], rbuf[1024]; int index, len; FILE fd; com_buf[0] = '\0'; m_setmode( 1 ); if( m_open_cb() != 0 ) /* クリップボードのオープン */ return; if( m_rep_index((char far *)"TEXT", (int far *)&index, (unsigned *)&len) != 0 ) { /* TEXT形式のインデックスの取得 */ m_close_cb(); /* クリップボードのクローズ */ return; } m_cb_read( index, 0, rbuf, len ); /* クリップボードから読み出し */ m_close_cb(); rbuf[ len ] = '\0'; strncat( com_buf, rbuf, sizeof( com_buf )); strncat( com_buf, REDIRCT, sizeof( com_buf )); if( m_spawn( com_buf, strlen( com_buf ), 2, "Wait a minute." ) != 0 ) /* 文字列をCOMMAND.COMに渡す */ return; if( m_open( &fd, TFNAME, strlen( TFNAME ), 0, 0 ) != 0 ) /* ファイルのオープン */ return; if( m_open_cb() != 0 ) { m_close( &fd ); /* ファイルのクローズ */ return; } if( m_reset_cb( "DOSCOM" ) != 0 ) { /* クリップボードの内容のクリア */ m_close_cb(); m_close( &fd ); return; } m_new_rep( "TEXT" ); /* TEXT形式でデータを受け取るようクリップボードをセット */ m_read( &fd, rbuf, sizeof( rbuf ), &len ); /* ファイルから読み出し */ m_cb_write( rbuf, len ); /* クリップボードに書き込み */ m_fini_rep(); /* 現在の形式のデータの終了をクリップボードに通知 */ m_close_cb(); m_close( &fd ); } ====================================================================  特に説明は加えませんが、クリップボードの文字列をそのままコマンド として実行し、結果をファイル経由でクリップボードに収納しているプロ グラムです。System Manager上からDOSコマンドを実行する為のプログラム です。  ただし、データが日本語か否かの判定を行っていませんので、ペースト 時に文字化けを起こすことがあります。これは、クリップボードから取り 出したデータの最後が2バイトコード(日本語)の1バイト目になっている かをチェックすることにより回避可能です。  実行しDOSコマンドが終了すると必ずキー入力を求めますが、これは m_spawn関数の特性です。起動するプログラムがキー入力を受け付けない 性質のものなら、キーバッファに任意のキー入力をストアしておくことに より回避可能です(INT 16H)。  クリップボード使用上の注意点ですが、オープンしたままプログラムを 終了すると他のプログラムがクリップボードをオープンできなくなる為、 クリップボードが使えなくなります。また、ファイルをオープンしたまま プログラムを終了した場合は、他のEXM形式プログラムの動作に悪影響を 及ぼすことがあります。但し、ファイルのオープンとクローズは一対一で 対応するようにして下さい(クローズが多くても悪影響を及ぼします)。 9 開発キットの問題点  HP100LXでEXM形式プログラミングを行うにあたり、現在公開されている 開発キットにはいくつか問題があります。  これは、公開されているのがHP95LX用であるということに起因します。  ・画面モード   m_setmode関数はHP95LX用の画面モードにしか対応していません。   そのため、別の手段でCGAモードに変更しますが、その際いくつか   の関数が使用できなくなります。m_disp関数等です。  ・メニューサービス   HP95LXとHP100LXではメニューが変わっていますが、公開されているの   はHP95LX用ですので、HP95LX風のメニューしか使えません。  ・イベントサービス   m_event関数では特に不具合は確認されていませんが、m_nevent関数を   使用するとシステムマクロが正常に動作しなくなります。  この他にも、m_spawn関数がLotus123と併用できない等、いくつかの不具 合が存在します。  デベロッパー登録を行うとHP100LX向け開発資料とライブラリ類が手に入 りますが、現在は登録が中止されているとのことです。一般公開できるよう にNORIさんがHP/YHP社に働きかけて下さっています。 10 より知識を得るために  HP100LXでEXM形式プログラミングを行うにあたり、DOS Function Call や BIOS Call が使用できます(一部に制限がかかる場合があります)。  その為、一般に流通しているDOS/V関係の書籍も資料として有効です。  また、FYHPPCのLIBに登録されているプログラムの一部にはソースコード が添付されています。他人の書いたソースコードを読むことは大変参考に なりますので、お勧めします。 11 おわりに  本稿は筆者がHP100LXでプログラミングを始めて一ヶ月の時点で中本さん の要請に応じて書いた文章(FYHP MES16にアップ)を補足修正したものです。  当時、ここまでの知識が得られたのは、苦労してHP95/100LXの環境を整備 してこられた先輩方のおかげであり、感謝の念に絶えません。この場をお借 りして、深くお礼申し上げます。  あれから半年しか経っていないのですが、EXM形式のプログラミング技術 を取得された方も増え、数多くの魅力的なソフトウェアが公開されました。 隔世の感もありますが、本稿の原版が少しは貢献できたのではと自負して おります。  本稿がHP100LXでのプログラミングを始める方の手助けとなることを祈り 結びとさせていただきます。 12 付記  ・本稿はフリーソフトウェアと同等の扱いとします。  ・本稿から得た知識に対して対価を支払う必要はありませんが、その成果   をNIFTY-Serve FYHPPCで公開することを希望します(強制ではありませ   ん)。  ・本稿の著作権は筆者(津守正樹,Brahma)にあります。  ・個人レベルでの再配布は許可しますが、不特定多数への配布や転載に   関しては筆者に許可を得て下さい。また、再配布を行う場合は被配布者   に最新のバージョンを提供するよう心がけて下さい。  ・本稿は各種資料から得た知識、独自に解析した結果、どなたかから教え   て頂いた情報等を元に、筆者なりに理解している内容を記述したもの   です。内容の正確さ、不正確なことにより被る不利益に関しては、一切   保証しません。  ・本稿に関する質問、意見等はFYHPPC 8番会議室において受け付けます。   メイルによる質問も受け付けますが、的を得た質問の場合はFYHPPC 8番   会議室で引用させていただくことがありますので予めご了承下さい。 付録 HP100LX用開発キットを用いたプログラミング  本稿発表時点では一般公開されていませんが、HP100LX用開発キット (Developer's GuideとISVディスケット)を用いると、HP100/200LX搭載の System Managerに対応したEXM形式プログラムを作成することが可能です (但しHelpコンパイラが添付されていないためHelp機能は使えません)。  そして、中本さんが独自の開発キット(以下NKITと表記)を開発し公開 して下さったため、デベロッパー登録をしなくてもHP100/200LX用のAPIを 使用したプログラムの作成が可能になりました。  以下に、HP95LX用からの変更点とサンプルプログラムを付記します。 付1 HP95LX用からの変更点  プログラマから見て、以下の点が変更されました。  ・APIの変更   System Manager Service (System Manager API)がいくつか新設及び   廃止、変更されました。詳しくは後述します。  ・LHAPIの導入   メニュー、ファイルメニュー、スクリーン、編集の各サービスが廃止   され、LHAPI(Lotus Handheld Application Programming Interface)が   導入されました。LHAPIとはHP100LXに内蔵されたGUIルーチン群です。   LHAPIにより、容易にCUAに沿ったインターフェースを利用することが   できます。   LHAPIに関する詳細は、8番会議室で不定期連載中(本稿発表時点)の   LHAPI入門を参照して下さい。  ・アプリケーションDataブロックの移動   System ManagerはEXM形式プログラムのDataブロックを勝手に移動させ   てしまう為、farポインタのセグメントの扱いに注意する必要がありま   す。   実際にはHP95LX用開発キットでも同じ問題は存在していたのですが、   関数の引数の殆どがnearでしたので意識する必要があまりありません   でした。しかし、HP100LX用の関数はfarポインタを要求するものが多く   特にLHAPIでは必ず使用します。その為、対策が不可避となってきます。  その他にも、イベント管理等が変更されています。  HP純正の開発キットでは推奨コンパイラであるMS-C(Ver.6以降)に特有の 機能を使用していますので、他のコンパイラを使用するのが困難になって います。NKITでは、TurboCやLSI Cによる開発が可能です。 付2 関数の新設及び廃止  以下の関数が新設及び廃止されました。また、名称が同じでも機能や引数 が変更されたものもあります。  各新設関数の一行説明については、おおはたさん、中本さんの解析を参考 にさせていただいています。  ・イベントサービス → イベント管理サービス   廃止    ・m_event    ・m_nevent   新設    ・m_action      イベント要求(イベント取得等)を与えて実行する  ・メニューサービス   LHAPIに移行  ・ファイルメニューサービス   LHAPIに移行  ・スクリーンサービス   LHAPIに移行  ・編集サービス   LHAPIに移行  ・ファイルサービス → ファイルI/Oサービス   新設    ・m_card_changes      カード差替回数を取得する    ・m_flush      不明    ・m_get_drive_list      有効なドライブリストを取得する  ・プロセス管理サービス   廃止    ・m_init    ・(m_spawnarg)   新設    ・(m_appcount)      ロードされているアプリケーション数を取得する    ・m_get_TCB      TCB(Task Control Block)配列へのポインタを取得する    ・m_get_TCB_size      TCBへの登録最大数を取得する    ・m_init_app      システムマネージャーレベルの初期化処理を行う    ・m_keylock      ホットキーによるタスクスイッチを禁止する    ・m_keyunlock      ホットキーによるタスクスイッチを許可する  ・クリップボードサービス   廃止    ・m_rep_name   新設    ・m_get_cb_info      クリップボードの情報を取得する  ・サウンドサービス   新設    ・m_play      7オクターブの音楽を演奏する  ・メモリ管理サービス   新設    ・m_current_task      現在のタスク番号を返す    ・m_enable_app_key      ロックされたブルーキーを利用可能にする    ・m_free_static_block      確保したstatic領域を開放する    ・m_free_SVC_entry      S_SERVICEエントリーを開放する    ・m_get_active_DOS_size      不明(動作中のDOSアプリが使用しているメモリ量の取得?)    ・m_get_avail_123_mem      123メモリ確保関数で確保可能なサイズを取得する    ・m_get_avail_init_mem      アプリケーションスタート時の利用可能領域のサイズを取得する    ・m_get_avail_mem      拡張可能なnear領域のサイズを取得する    ・m_get_avail_static_mem      確保可能な最大のstatic領域のサイズを取得する    ・m_get_DOS_hot_key      不明(DOS起動キーを取得?)    ・m_get_DOS_part_size      不明(Setupで設定したDOSサイズを取得?)    ・m_get_far_block      System Managerが使用するfar領域を割り当てる    ・m_get_far_handle      farポインタへのnearハンドルを取得する    ・m_get_far_user_handle      ユーザー用のfarポインタへのnearハンドルを得る    ・m_get_static_block      staticブロックを確保する    ・m_get_static_size      不明(Setupで取得したstaticサイズを取得?)    ・m_get_static_used      不明(使用中のstatic領域のサイズを取得?)    ・m_get_SVC_entry      S_SERVICEへのエントリポイントを取得する?    ・m_load_client      リダイレクタをロードする    ・m_load_TSR      特別なTSRをstatic領域にロードする    ・m_locate_SVC      特定のS_SERVICEエントリへのポインタを取得する    ・m_lock_out_app_key      ブルーキーをロックしTCBを更新する    ・m_modify_hot_key      実行中のタスクのホットキーを変更する    ・m_QuickKeyCheck      イベントループ前に山越でキーを取得する    ・m_reg_far      間接farテーブルを登録する    ・m_set_DOS_hot_key      DOS起動キーを設定する    ・m_set_DOS_part_size      次に起動するDOSアプリのサイズを設定する    ・m_set_far_block      間接farブロックの領域を拡張、縮小、開放する    ・m_set_static_size      static領域のサイズを設定する    ・m_task_info      TCB情報を取得する    ・m_unload_client      リダイレクタを開放する  ・日付/時間サービス   廃止    ・m_alarm    ・(m_posttime)   新設    ・m_keybez      内蔵インターナショナルキーボードサポートをチェックする    ・m_nalarm      アラームを待ち行列に加えペンディング情報を取得する    ・m_nfalarm      条件に一致するアラームを検索する  ・印刷サービス   新設    ・m_write_printer_atb      プリンタへアトリビュートを送信する    ・m_trans_printer_atb      バッファへアトリビュートを収納する  ・設定サービス   日付/時間サービスに併合  ・通信サービス   廃止    ・ComAnswer    ・ComCommand    ・ComConfigure    ・ComDial    ・ComGet    ・ComGetModem    ・ComHangUp    ・ComSet    ・ComSetDtr    ・ComStatus   新設    ・ComAcqReceiveBytes      ポートの獲得と受信を行う    ・ComAcqSendBytes      ポートの獲得と送信を行う    ・ComAcquire      ポートの使用権を排他的に獲得する    ・ComGetParameters      回線設定を取得する    ・ComMdmClr      モデムラインをクリアする    ・ComMdmSet      モデムラインをセットする    ・ComMdmStatus      UARTのMCRとMSRを取得する    ・ComPower      通信ポートの電源オンオフ操作をアプリケーションに許可する    ・ComRelease      ポートの使用権を解放する    ・ComSetParameters      回線設定を設定する  ・その他サービス   廃止    ・drawbox    ・m_errmsg    ・message    ・message3    ・msg_off    ・showname   新設    ・m_disable_macros      マクロの実行・記録を禁止する    ・m_enable_macros      マクロの実行・記録を可能にする    ・m_get_sysver      System version IDを得る    ・m_playback_macro      マクロを再生する    ・m_ram_iv_info      RAMイメージベクタのアドレスとアクティブな数を取得する    ・m_start_record      動作中のマクロを停止し、マクロの記録を開始する    ・m_stop_record      現在記録中のマクロの記録を終了する    ・m_sys_rsrc_addr      システムリソースアドレスを含む文字列を取得する    ・m_update_apname_list      全ドライブのAPNAME.LSTを更新する  ・リソースサービス   新設    ・m_GetKeyLabel      スキャンコードから覚えやすい文字列を取得する    ・m_GetScanCode      覚えやすい文字列からスキャンコードを取得する    ・m_InitKeyLabels      キーラベルとスキャンコードのテーブルを初期化する  ・ヘルプサービス   廃止    ・m_help_display    ・m_help_init    ・m_help_key    ・m_help_term   新設    ・m_help2_display      ヘルプスクリーンデータを表示する    ・m_help2_getmetrics      ヘルプスクリーン座標を取得する    ・m_help2_init      ヘルプシステムを初期化する    ・m_help2_key      ヘルプシステムにキー入力を渡す    ・m_help2_term      ヘルプシステムを終了する  ・比較サービス   新設    ・m_colLicsChar      LICS文字をソートオーダーで比較する    ・m_colLicsStr      LICS文字列をソートオーダーで比較する  ・123ブリッジサービス   不明  ・API管理サービス(新設)    ・m_clear_api_error      直前のクラス/関数のエラーをクリアする    ・m_disable_fnct_tbl      関数テーブルで最初の無効にされていない関数を無効にする    ・m_enable_fnct_tbl      最後に無効にした関数を有効にする    ・m_get_api_error      直前にエラーを起こした関数IDを取得する    ・m_get_class_tbl      関数テーブルのアドレスを取得する    ・m_get_free_api_class      クラスグループを指定し登録可能なクラスIDを取得する    ・m_insert_fnct_tbl      ディスパッチ行列に関数テーブルを追加する    ・m_remove_fnct_tbl      ディスパッチ行列から関数テーブルを削除する    ・m_search_api_class      指定されたグループから検索文字列と一致するクラスを探す    ・m_valid_api_fnct      クラス/関数の状態を取得する  ・LHAPIサービス(新設)    ・ClearError      LHAPIBLOCK構造体のエラー番号とハンドラをクリアする    ・ClearRect      画面の一部を白く消去    ・DeactivateLHAPI      LHAPIに非アクティブになる旨を通知する    ・DrawChar      1文字を表示する    ・DrawFKeys      ファンクションキーを表示する    ・DrawNChars      最大数を指定してテキストを表示する    ・DrawText      テキストを表示する    ・GetCurrentFont      カレントに定義されたシステムフォントを取得する    ・GetDefaultFont      デフォルトフォントを取得する    ・GetError      エラー番号を取得する    ・GetErrorHandler      エラーハンドラーを取得する    ・GetFocus      フォーカスされたウィンドウへのポインタを取得する    ・GetLHAPIBlockPtr      LHAPIBLOCK構造体へのオフセットアドレスを取得する    ・GetLocalClipping      ローカルクリッピング領域を取得する    ・InitializeLHAPI      LHAPIBLOCK構造体の初期化を行う    ・IntersectLocalClipping      特定の矩形領域と交差するローカルクリッピング領域を定義する    ・Line      線を描画する    ・Outline      矩形を黒線で被う    ・Panel      パネルを表示する    ・ReactivateLHAPI      LHAPIBLOCK構造体の再構築する    ・Rectangle      色とフラグを指定して矩形を表示する    ・RegisterFont      定義された内から使用するフォントを設定する    ・ResetAlt      ALTキーリリース時のMENUキー入力メッセージ発生の防止する    ・ResetRepRule      低位の画素置換ルールをLHAPI定義のデフォルトにリセットする    ・ResetVideo      内部LHAPIビデオ状態変数をリセットする    ・SendAllMsg      メッセージを全ウィンドウに送る    ・SendFocusMsg      フォーカスされたウィンドウにメッセージを送る    ・SendMsg      特定のウィンドウにメッセージを送る    ・SetCurrentFont      カレントのシステムフォントを設定する    ・SetDefaultFont      デフォルトフォントを設定する    ・SetError      LHAPIBLOCK構造体のエラー番号を設定する    ・SetErrorHandler      エラーハンドラーを設定する    ・SetGlobalClipping      グローバルクリッピング領域を設定する    ・SetHelpFkey      アプリケーション内共通のヘルプキーを設定する    ・SetLocalClipping      ローカルクリッピング領域を設定する    ・SetMenuFont      メニューに使用するシステムフォントを設定する    ・SetRepRule      低位の画素置換ルールを設定する    ・ShadowBox      影付きのパネルを表示する    ・ShowSymbol      特殊シンボルを表示する    ・SubclassMsg      自分以外のクラスハンドラにメッセージを送る    ・UninitializeLHAPI      LHAPIに終了を通知する 付3 サンプルプログラム  4章で例示した SMHELLOT を改造したサンプルプログラムを以下に示し ます。LHAPIを使用しており、その定義の為に少々長くなっています。 ========================================================= LHHELLOT.C #if defined(NKIT) #include "lxapi.h" /* NKIT専用ヘッダ */ #else #include "cap2.h" /* LHAPI定義 */ #include "interfac.h" /* System Manager API定義 */ #include "event.h" /* EVENT構造体定義 */ #include "sysdefs.h" /* システム定数定義 */ #endif char far *msgNull = ""; /* ダミー */ char far *msgAppName = "Hello"; /* タイトル */ char far *msgHello = "hello, World"; /* 表示文字列 */ char far **StringTable[] = { &msgNull, &msgAppName, &msgHello, }; int far MainHandler( PLHWINDOW, WORD, WORD, WORD,...); BOOL Done; /* イベントループ脱出用フラグ */ LHAPIBLOCK LHAPIData; /* LHAPI管理領域 */ EVENT_NORM app_event; /* イベント管理用構造体 */ LHWINDOW MainView = { /* アプリケーション本体オブジェクト */ (PLHCLASS)MainHandler, 0, 0, 640, 200, &msgNull, 0, 0, 0, NULL, NO_FKEYS, NO_MENU, NO_HELP }; LHWINDOW MainTitle = { /* タイトルバー */ TitleBar, 0, 0, 0, 0, (PLHRES)&msgAppName, 0, 0, TITLE_NODATETIME|STYLE_NOFOCUS, NULL, NO_FKEYS, NO_MENU, NO_HELP }; #if defined(TURBOC) #define GetDataSeg() _DS #endif #if defined(LSIC) int _asm_(char *); #define GetDataSeg() _asm_(" mov ax,ds\n") #endif void FixupFarPtrs( void ) /* Dataブロック移動対策(注) */ { int i, dataseg; #if defined(MSC) _asm { mov ax,ds mov dataseg,ax } #else dataseg = GetDataSeg(); #endif for ( i = 0; i < countof( StringTable ); i++ ) *((( int *)( StringTable[i] )) + 1 ) = dataseg; } int far MainHandler( PLHWINDOW Wnd, WORD Message, WORD Data, WORD Extra,...) /* アプリケーション本体オブジェクトのハンドラ */ { if(( Message == DRAW ) && ( Data & DRAW_FRAME )) { /* 表示メッセージ */ /* CREATE,E_ACTIVE,E_REFRESH時に送られる */ ClearRect(Wnd->x,Wnd->y,Wnd->w,Wnd->h); /* 画面のクリア */ DrawText( 200, 80, msgHello, DRAW_NORMAL, FONT_DEFAULT ); /* 文字列の表示 */ } return SubclassMsg(Object, Wnd, Message, Data, Extra); /* 親クラスにメッセージを転送(親クラスの機能を継承) */ } void main( void ) { m_init_app( SYSTEM_MANAGER_VERSION ); /* System Managerへの初期化宣言 */ InitializeLHAPI( &LHAPIData ); /* LHAPIブロックの初期化 */ SetDefaultFont(FONT_LARGE); /* デフォルトフォントの設定 */ m_reg_app_name( msgAppName ); /* アプリケーション名の登録 */ SendMsg( &MainView, CREATE, CREATE_FOCUS, 0 ); /* アプリケーション本体オブジェクトの生成 */ SendMsg( &MainTitle, CREATE, CREATE_NORMAL, 0 ); /* タイトルの生成 */ Done = FALSE; /* DoneがTRUEでイベントループから脱出 */ while( !Done ) { /* イベント(ディスパッチ)ループ */ app_event.do_event = DO_EVENT; /* イベント取得を設定 */ m_action( &app_event ); /* イベント処理 */ switch( app_event.kind ) { /* イベントの種類で分岐 */ case E_ACTIV: /* 再アクティブ化要求 */ case E_REFRESH: /* 画面再描画要求 */ FixupFarPtrs(); /* Farポインタの修正 */ ReactivateLHAPI( &LHAPIData ); /* LHAPIにACTIVE化を通知 */ break; case E_DEACT: /* 非アクティブ化要求 */ DeactivateLHAPI(); /* LHAPIにDEACTIVE化を通知 */ break; case E_TERM: /* 強制終了要求 */ case E_BREAK: /* CTRL+BREAKを検出 */ case E_KEY: /* キー入力を検出(ブルーキーを除く) */ FixupFarPtrs(); /* Farポインタの修正 */ Done = TRUE; /* イベントループ脱出 */ break; } } m_fini(); /* System Managerへの終了宣言(戻らない) */ } ==================================================================== (注)   Dataブロックの移動は、再アクティブ化のときだけ発生するわけでは  ありません。動的なメモリの確保(アプリケーション自身、System  Managerによるものを問わず)の際には移動することが考えられます。   その為、上記のFarポインタの修正方法では問題が起きうるのですが  今回は単機能のサンプルプログラムということで考慮していません。  また、LHAPI入門のサンプルプログラム1でも同様になっています。 =========================================================== MAKE.MSC DSTNAME = lhhellot MSCDIR = a:\c700 OKITDIR = a:\dev\100exm LIBDIR = $(OKITDIR)\libs INCDIR = $(OKITDIR)\headers STACK_SIZE = 0x1000 COPTS = /DMSC CFLAGS = /AS /Ze /Oswl /Gs /G1 /DNDEBUG /DAPI2 /DOFFICIAL $(COPTS) LFLAGS = /CP:0xffff /NOI /NOE /NOD /MAP /SE:0x80 /ST:$(STACK_SIZE) SYS_LIBS = $(LIBDIR)\bb.lib $(LIBDIR)\mlhstub.lib $(LIBDIR)\caprea.lib $(DSTNAME).exm: $(DSTNAME).exe e2m $(DSTNAME) $(DSTNAME).exe: $(DSTNAME).obj $(SYS_LIBS) \(MSCDIR)\bin\link $(LFLAGS) @<< $(DSTNAME).obj,$(DSTNAME).exe,,$(SYS_LIBS); <<NOKEEP .c.obj: \(MSCDIR)\bin\cl /c /I$(INCDIR) $(CFLAGS) $*.c ==================================================================== =========================================================== MAKE.TCC DSTNAME = lhhellot TCDIR = a:\tc NKITDIR = a:\dev\nkit COPT = -1 -G -DNKIT -DTURBOC $(DSTNAME).exm : $(DSTNAME).exe e2m $(DSTNAME) $(DSTNAME).exe : $(DSTNAME).obj $(TCDIR)\bin\tlink /m $(NKITDIR)\tccstart+$(DSTNAME)\ +$(NKITDIR)\tcclib,$(DSTNAME),, $(DSTNAME).obj : $(DSTNAME).c $(TCDIR)\bin\tcc $(COPT) -ms -c -I$(NKITDIR) $(DSTNAME).c ==================================================================== =========================================================== MAKE.LSI DSTNAME = lhhellot LSICDIR = a:\lsic NKITDIR = a:\dev\nkit COPTS = -DNKIT -DLSIC $(DSTNAME).exm : $(DSTNAME).exe e2m $(DSTNAME) $(DSTNAME).exe : $(DSTNAME).obj $(LSICDIR)\bin\lld -M -o $(DSTNAME).exe $(NKITDIR)\lsistart.obj $(DSTNAME).obj $(NKITDIR)\lsilib.obj $(DSTNAME).obj : $(DSTNAME).c $(LSICDIR)\bin\lcc -c $(COPTS) -I$(NKITDIR) $(DSTNAME).c ====================================================================  LHHELLOT.Cは純正開発キットとNKITに対応しています。MAKE.MSCはMS-C用 (純正)、MAKE.TCCはTurboC用(NKIT)、MAKE.LSIはLSI C用(NKIT)です。  動作確認はそれぞれ、MS-C Ver6.00A(PS/55版)、TurboC++ Ver1.0 2ndEdition(98版)、LSI C-86 Ver3.30c 試食版、で行いました。  LHAPI固有の部分を除けば、関数が変わっただけの部分が多いため解説は 省きます。LHAPI固有の部分に関してはLHAPI入門を参照して下さい。  95LX用の開発と比べ、プログラムの流れで変わったのはイベント処理です。  従来のm_event(m_nevent)関数はSystem managerに制御を渡し、イベント の発生を待つタイプの関数でした。しかし、m_action関数はSystem Manager に制御を渡した後の動作を指定できます。  動作の指定はEVENT_NORM構造体のdo_eventメンバで行い、以下の種類が存 在します。  ・DO_EVENT    イベント待ちを行う  ・DO_YIELD    アプリケーションをasleep状態にする  ・DO_FINI    アプリケーションを終了させる  ・DO_NO_EVENT    イベント待ちをせずイベントを取得する  ・DO_NO_FINI    アプリケーションの終了を阻止する  ・DO_BRIDGE    123とのブリッジサービスを行う  ・DO_IC_INIT    タスク間通信(IC)を開始する  ・DO_IC_CLOSE    タスク間通信を終了する  ・DO_SWAP    ICセッションにある他のアプリケーションにスワップする  ・DO_LAUNCH    EXM形式プログラムを起動する  ・DO_EXEC    DOSプログラムを起動する  ・DO_DOS_CLOSE    起動中のDOSプログラムを強制終了する  ・DO_REFRESH    フォアグラウンドのアプリケーションにE_REFRESHイベントを送る  ・DO_SERVICE_COMPLETE    特別TSRの処理を終了する  ・DO_REQUEST_FOREGROUND    自タスクをフォアグラウンドにする  ・DO_CLOSE_APP    EXM形式プログラムを終了させる  ・DO_EXEC_FULL    DOSプログラムを完全コントロール下で起動する  ・DO_EXIT_SYSMGR    System Managerと総てのアプリケーションを終了させる  ・DO_MOVE_TO_END    自タスクを待ちキューの最後におく  これらの指定により、高度なマルチタスクプログラミングを実現します。 サンプルプログラムでは、do_eventメンバにDO_EVENTを代入してm_action 関数を実行しており、これは従来のm_event関数に該当します。

戻る

All contents and programs Copyright 1996,97,98 (C) by Masaki " Brahma " Tsumori all rights reserved.