一歩進んだ Rexx

Classic REXX (REstructured eXtended eXecutor)
File #04

アイコンを取り出してみよー -- 2000.12.10

はにゃ〜〜ん。Workplace shellオブジェクトで困ってる人がいたら, 実体を見せずに忍び寄り こっそり教えちゃう白い影。って, 影が白い訳あるかー, みたいな。
そんなこんなで, 今回, 抽象オブジェクトからアイコンを得るってヤツにょ。

まずは, ソレの使い方から。

/* 抽象オブジェクトからアイコンを得るサンプル。  */
/* 試しに, '<WP_DESKTOP>' にある 'IconVV.exe' を */
/* 探し出して, アイコンをファイルに出力してみる  */
options 'ETmode'
True = 1; False = 0;

call RxFuncAdd 'SysLoadFuncs', 'rexxutil', 'SysLoadFuncs'
call SysLoadFuncs

ObjID = '<WP_DESKTOP>'
hObj = SysIni('User', 'PM_Workplace:Location', ObjID)

/* 探したいプログラムを指定する。パス名は付けない。 */
ahEXE = ProgObjPath(hObj, 'IconVV.exe', 'Handle')
say 'ハンドル:' c2x(reverse(left(hObj, 2)))'→' ahEXE

bin = SysIni('User', 'PM_Abstract:Icons', ahEXE)
if bin == 'ERROR:' then do
   say 'exeファイルの中に含まれてるから, 分かんない。(T-T)'
   exit
end

iconFile = '$$temp.ICO'
call charout iconFile, bin, 1
call charout iconFile

exit
内容はコメントにある通りで, Desktopにプログラムがあるとして, その実際のファイル名を指定するってーと, ソレを探し出してハンドルを表示するって処理。
んで, そのあと 'PM_Abstract:Icons'を使ってアイコンを取るって訳だもん。

エラー処理にも書かれている通り, *.exeファイルの中に入っているアイコンのばやい, 抽象オブジェクトつってもアイコンを取り出したりはできない。 どーすんのかっつーと, *.exeファイルのヘッダーをアレして取り出すってことになって, ソレはもー大変。 もしかすっと こーゆーのは Toolkitに書かれているかも。探してみてちょ。

それから, ProgObjPath なんだけど, コレは第三引き数に 'H'とか 'Handle'とか指定すっとハンドルを, そーでなければ 絶対パスを返しちゃう, そーゆーやつ。 探したいファイル名を第二引き数に指定しとくことで, ソレと比較する訳だね。

ProgObjPath: procedure expose driv. node. True False
parse arg hObj +2, str, m +1
str = translate(str)
Inif = 'User'
Key = strip(c2x(reverse(hObj)), 'L', 0)
val = SysIni(Inif, 'PM_Abstract:FldrContent', Key)
if val == 'ERROR:' then return ''
do i = 1 to length(val) by 4
   hnd = d2x(c2d(reverse(substr(val, i, 4))))   /* 前ゼロを取るため (strip()でも可) */
   obj = SysIni(Inif, 'PM_Abstract:Objects', hnd)
   p = pos('WPProgramRef', obj)
   if p > 0 then do
      parse var obj =(p)'04000b00'x+6 txt +2
      if RxWPFileSystem(txt, 'txt') then
         if translate(FileSpec('N', txt)) == str then
            if translate(m) \== 'H'
               then return txt
               else return hnd
   end
end
return ''

処理内容は, 第一引き数で指定したハンドルのフォルダーにある, 抽象オブジェクトのコンテンツを一つ一つ調べて, 同じファイル名があるかどうか調べてるだけ。
その抽象オブジェクトの実体を指すハンドルを, RxWPFileSystemの前の行の parseで取り出してる訳だね。 んで, 処理内容を詳しく解説するってーと ・・

  1. "WPProgramRef" てゆー文字列があるかどーか調べて,
  2. その文字列の位置以降の 0x04000b00 のバイナリパターンを探し当てて,
  3. そっから 6バイト進んで, そのあとの 2バイトを取出す。

さて, アイコンが exeの中に含まれていて どーしても取り出せないってばやいの対処方法。
ソレは ・・ ソレは ・・・ アイコンをビミョーに編集する。 するとソレが 'PM_Abstract:Icons'になるだよ。ソレで取り出せるって訳。ちょっちインチキっぽいけどね。(^^)

実のところ コレって, アイコンを得る方法は? の回答のつもりなんだけど, でも ホントは, APIを知りたいはずだと思うんで, 全く答えになったいない可能性大。 ま, こーゆー方法もあるってことでアレしてちょ。 それから Warpのページにも解説してたりするにょ。


ORexx環境での Classic Rexx -- '99.3.8

ふと, アレして 〆(^^) のプログラムを ORexxの環境にして動かしてみた。
白龍のMemoPad編集とか, いくらなんでも動かないだろう・・。 と, 思ったらあっけなく動いた。あれ?, 予想と違う。二重呼び出しとか使いまくってんのに?

そんなに互換性があるのなら認識 改めなきゃ。そー思ったのもつかの間。それ以外の大概のプログラムが動かない (T_T

「拒絶反応・・, くっ, この私を拒絶するというのかっ?」

と, ボケかましていても解決には向かわないんで調べてみることに。
そこで分かったこと。

* 変なコードが入っていると動きもしない。
例えば PageBreak の 0x0c とかが入っていると, 実行以前にエラーになった。 こーゆー部分はコメントの内側にしか書いちゃいけないらしい。
* options に指定するのは大文字でなくてはならない。
そうでないと有効にならない。options translate('EXmode') とかするとよいかも。でも仕様には大文字になるように書いてあるのに〜 (T_T
* options 'EXMODE' 状態での parse に問題あり。
このパターンは失敗するパターン→ parse value '全角{x}' with '{'str'}'
処理を分ければオッケ→ parse value '全角{x}' with '{'tmp; parse var tmp str'}';
どーゆー規則でこーなっているか分かったけど, でも分かった所でどーしょーもない (T_T
* datatype('', 'B') は 1(つまり true)になる。
Classic Rexxでは ''が trueになるのは 'X'の指定だけなんだけど, ORexxでは ''は, 'X'の指定だけでなく 'B'の指定も trueになる。

まだ, 他にもあるかもしんない。でも EAも巨大になるし遅いし, やっぱ Classic一筋がよいかも。 そー思う 〆(^^) であったのこと。

※ 他にもあることを御存じの場合, お知らせ下さい。(まぁ, ここにまとめておきたいだけなんだけど (^^; )


ソース行 -- '99.3.1

今回は, ソースですかっ? そーっす。(←バカ)

えと, parse 命令 にも source ってーのがあるけど, そーではなくって関数を取り上げる。 動いているそのプログラム自身の sourceを取り出す事ができるとゆーヤツだ。

これはもうインタプリタならではの芸当で, コンパイラにはこれは無理ってものだ。 ORexx とかでも, 中間言語(のよーなもの)に落としてしまっては, これは使えない。 もちろん Rexxコンパイラーでコンパイルしたもの(?)でも, これは使えないだろう。

例えば, 自分自身を栗鼠とするにはー, じゃなくってリスト。

/* source print program */
do i = 1 to sourceline()
   say sourceline(i)
end
exit
むーん, 何だかばかばかしいプログラム。別にいいんだけど。

いったい, これが何の役に立つのか。それはヒミツだ。ふっふーん, 分かる人には分かるってもんさっ。
ある所ではインストールプログラムに・・って, ゆってんじゃん。

月の始まりなので今回は軽く (←それは関係ないと思うんだけど)。そりでは (^o^)/~~


WorkplaceのFileSystemのハンドルの一覧 -- '98.12.27

まるで前回の続きのような, ハンドルのダンプ・プログラム (←"のような"? (^^;)
例えば何か別の言語でアレするとしても, 確認しながら動かすと色々と便利。 でも, だからって『日本語で』とか『英語で』とかゆーのは無理。(←なにゆってんだか(^^;)

/* Workplace handle   [dump program] */
options 'ETmode'
True = 1; False = 0;

call RxFuncAdd 'SysLoadFuncs', 'rexxutil', 'SysLoadFuncs'
call SysLoadFuncs

ESlist = 'idx'
idx = ''
call RxWPFileSystemInit

say '=== DRIV information ==='
do i = 1 to driv.0
   v = driv.i
   say c2x(left(v, 16)) substr(v, 17)   /* 前半を16進表示と, 後半はそのまま */
end

say '=== NODE information ==='
do i = 1 to length(idx) by 2
   n = substr(idx, i, 2)
   v = node.n
   say c2x(left(v, 28)) '('substr(v, 29)')'   /* 前半を16進表示と, 後半はそのまま */
end
exit

この後ろにでも, RxWPFileSystemInit:からの処理を付けると, 簡単にソレを表示する事ができるってもんさー。はっは, って忘れ物。この改造がないとダンプできない。

   when str == 'NODE' then do
      parse var val =(p) +4 node +28 -2 ln +1 +1 nodename '0'x
      n = substr(node, 3, 2)/*;if symbol('node.n') \= 'LIT' then say nodename;*/
      idx = idx''n   /* ←ココ */
      node.n = node''nodename
      p = p +32 +c2d(ln) +1
   end

で, 他の言語でアレするばやい, ここまでは同等のことができるってもんだけど, 関数 RxWPFileSystemを実装ってのはちょいとつらい。
検索するには, nodeの 3Byte目から 2Byteの部分の一覧を作っといて, 一つ一つ調べるとか, 高速化するならハッシュでも用意するとか, 二分検索するとか・・ ちょっち手間かも。

Rexx系の言語では, これを連想配列(っぽいもの?)で扱うと簡単。これは AWKとか, Perl(の機能の上のヤツ) にも同等の機能がある (Perlは使った事ないんだけどね(^^)。

しまった, これ『小物ソフト』ネタに使えたんじゃないのか? (^^)\(バキ☆)


WPFileSystem(たぶん) の解説 -- '98.12.24

おお, お待たせだったらしい (^-^)
・・で, 前回のが判りにくかったようなので, 『深く静かに説明しよう』モード。

OS2SYS.INI ファイルを INIファイルビュワー(?)で見ると, その中にひときわ大きいものを目にすることができよー。 そー, これこそが WPFileSystemのデータベースなのだー。

図-1
アプリケーション名キー名
PM_Workplace:Handles0BLOCK1Val(28645)
PM_Workplace:ActiveHandlesHandlesAppNameVal(22)=PM_Workplace:Handles0^@
PM_Workplace:Handles1BLOCK1Val(28602)

なぜ でっけーのが2つあるかを, 先に説明しよう。 この二つの間に, 文字列を含むのが登録されている。それは片方のデータベースのアプリケーション名になっている。 つまり, そーゆーことなのだ。(←どーゆーことだ)
このデータベースを更新するときは, 新しいデータベースを作成しといて, 指し示している PM_Workplace:ActiveHandlesを切り換える。そーゆー具合にして成長していくわけなのじゃ。 (まるで見てきたようにゆってるけど, 見た訳じゃない。見れるもんでもないけど)

で, なぜ BLOCK1とゆーよーに "1"なのかとゆーと, この例では BLOCK1までだけど, 環境によっては BLOCK2までとか, それ以上とか・・ 3以上は まだお目にかかった事がないけど。 こりは (確認していないけどたぶん)1つのエントリーあたり 64KByteという制限があるためだろー。 データベースを得る方法をまとめると,

  1. PM_Workplace:ActiveHandles, HandlesAppName によってアプリケーション名を得る
  2. そのアプリケーション名, BLOCKn でデータベースを取り込む。 n は 1から順で, エラーになるまで取り込み, 連結する。

あー, 何だか今回も眺め, ちがう 長め。
(↑)の方法で取得したものを, こんどはいろいろアレする訳で・・。 この先頭には, 未確認の 4Byteがついてる。こりゃー無視。 そして, その次に現れるのは "DRIV"か "NODE"の 4文字の文字列。 これ以外を見た事はないんで, (適当だけど)この 2種類しかないんだろうたぶん。きっと。

で, これがずっと続く。もうそれは延々と・・ ってゆー訳じゃなくて, データが終わるまで (^^;
図-2
=== Handle dump V1.0 ===

AppName=PM_Workplace:Handles1 Block=1
Hed=95BA0000

 DRIV EC4C6C000000000015901023D8486C00 [A:]
0100FE940000000000000000000000004C496C00A4A9770101000200 (A:)

 DRIV E4FF6C00000000001554BDA7AC4C6C00 [D:]
01003BC3000000004C496C0000000000204D6C00644C6C0001000200 (D:)
010068CC3BC3000000000000484B6C0098496C00D8486C0001000C00 (INSTTEMP.EPF)
01006A883BC30000084B6C00D84B6C00904B6C00084B6C0001000800 (OS2IMAGE)
01001C046A880000C04A6C0000000000484B6C00C04A6C0001000200 (FI)
01001E7E1C040000E4496C0000000000084B6C00784A6C0001000800 (BONUSPAK)
01008B371E7E000098496C00784A6C002C4A6C0098496C0001000800 (IBMWORKS)
01002F4E8B3700000000000000000000E4496C004C496C0000000C00 (IBMWDESK.CMD)
0100E13E1E7E00002C4A6C0000000000C04A6C002C4A6C0001000800 (FAXWORKS)
01006318E13E00000000000000000000784A6C00E4496C0000000C00 (FAXWORKS.EXE)
0100EBE03BC30000904B6C00644C6C001C4C6C00904B6C0001000400 (INST)
01002AAFEBE000000000000000000000D84B6C00484B6C0001000800 (J_POCKET)
0100B35B3BC300001C4C6C0000000000AC4C6C001C4C6C0001000800 (J_POCKET)
010069DCB35B00000000000000000000644C6C00D84B6C0000000A00 (README.CMD)
(↑)こりは 〆(^^)の持つダンプツールによるもの。やっぱ図があると違うねっ。うひょっ。

図(2)の 3 Byte目から 0x3BC3が入っているねっ。これは "D:"を指すハンドルといえよー。 その次の 2 Byteは 0x0000なので, この上には何も存在しないことを意味している。
例えば, 下側の (J_POCKET)は, ハンドル 0xB35B, 上位には 0x3BC3 つまり "D:" ・・とゆーことは root直下っつーことにる。
上側の (J_POCKET)は, ハンドル 0x2AAF, その上位に "INST", その上位に "D:"。

ノード情報の後ろ側。最後の 2 Byteは上で説明したので, その, さらに前の 2Byte。 これはファイルかどうかを意味している。ってとこ。

このデータベースの情報は必ずしも, 実際の物と一致しているわけではない。 Workplaceを介さずに, たとえばコマンドプロンプトで rd xxx とかしても, このデータベースからは削除されない。いや, もしかしたらこれは増加していく一方なのかもしれない。
これが BLOCK2, 3と, どんどん増えると読み込みも(つまり起動も)時間がかかるし, 稼働中の処理速度も遅くなる。これは必要最小限にすべき情報だと 〆(^^)は思うことがないこともない。 んじゃ, どーすんのさ, これ。
そー, これは・・・

まあ, 中身見なくても INIファイルがでかくなったら考えもんだな。

Workplaceより先に 〆(^^)が壊れかけている気がする。・・崩れているのかも。


WPFileSystem(たぶん) -- '98.12.15

今回, お待たせ(なのか?)のハンドルからファイル名を ってゆーアレ。 WPFileSystemのヤツ (たぶん(^^;; )。 んー, 今回ちょっと眺め, いや長め。いやー あっはっはー。
インターフェースは WPToolsに合わせてある。 きしょー, こいつめー, にくいねーっ。

使い方は, これ→ Rc = RxWPFileSystem(handle, 'txt')
ハンドルを指定する時は '#'から始まると文字列で, そうでないものから始まると・・。
で, ファイル名は, この場合 txtとゆー変数に入る。コーテーションでくくって文字列で渡すことに注意 (うわ, まじめ)。 フォルダーのばやいは最後に'\'が付いてくる。 関数の戻り値は Rc ... 成功(=True:1)/失敗(=False:0) ってゆー感じ。

RxWPFileSystem: parse arg , RxWPFileSystem; return RxWPFileSystemCore(arg(1));
RxWPFileSystemCore: procedure expose driv. node. True False (RxWPFileSystem)
parse arg hObj
if length(hObj) > 2 & left(hObj, 1) == '#' then
   hObj = reverse(x2c(substr(hObj, 2)))
if \datatype(driv.0, 'W') then call RxWPFileSystemInit
if symbol('node.hObj') == 'LIT' then return False
res = ''
fDir = c2d(substr(node.hObj, 25, 1))
do while hObj \== '0000'x
   res = substr(node.hObj, 29)'\'res
   hObj = substr(node.hObj, 5, 2)
end
call value RxWPFileSystem, left(res, length(res) -\fDir)
return True

なぜ 2つに分かれているのか。それは・・・ あぁ思い出した。 (↓)こっちは iniファイルから読みだす処理専用。

RxWPFileSystemInit: procedure expose driv. node. (ESlist)
Inif = 'System'
Appl = SysIni(Inif, 'PM_Workplace:ActiveHandles', 'HandlesAppName')
if Appl == 'ERROR:' then return
val = ''
do i = 1
   blk = SysIni(Inif, Appl, 'BLOCK'i)
   if blk == 'ERROR:' then leave
   val = val''blk
end
cnt = 0
p = 5
do while p <= length(val)
   str = substr(val, p, 4)
   select
      when str == 'DRIV' then do
         parse var val =(p) +4 driv +16 drivname '0'x
         cnt = cnt +1
         driv.cnt = driv''drivname
         p = p +20 +length(drivname) +1
      end
      when str == 'NODE' then do
         parse var val =(p) +4 node +28 -2 ln +1 +1 nodename '0'x
         n = substr(node, 3, 2)/*;if symbol('node.n') \= 'LIT' then say nodename;*/
         node.n = node''nodename
         p = p +32 +c2d(ln) +1
      end
   otherwise say 'Error String='s
   end
end
driv.0 = cnt
return

元々は RxTT16.INFに記述されていたもの。でもどっから入手したかわかんない。うはは。
少し前, RxTT28.INFが Hobbesに上がっているとゆー情報を宇野さんとこから得た。 あれ, だいぶ前だったかな? まあ, いいや。
16とか 28とかはバージョンらしい。RxTT28.INFにも全く同じ内容が記述されていた。

ちょびっと追記。これ, /2マガにも入ってた。(↓)辿りかた。-- 補足 '98.12.18

でもねー, ここの部分の処理って少し変だったんだよねー。 だから改造の手がかなり入っちゃってる。新規に作ったのと変わんないくらい。 だからって, どーってゆーこともないんだけどね (^^;

あうっち。忘れてた。プログラムの先頭にでも True = 1; False = 0; が, それと handleを文字列として指定する時は '#hhhh'ってゆー感じで・・。うじゃ (^o^)/~~
深く静かに解説しよう


Copyright (C) 1998-2004 Rexx使いの織華
email: ori@drive.co.jp