変奏現実

パソコンやMMORPGのことなどを思いつくまま・・・記載されている会社名・製品名・システム名などは、各社の商標、または登録商標です。

この画面は、簡易表示です

パソコン

[三国英雄の夜明け]久々・・・

アップデートのおかげでずいぶんと戦力は強化できてるけど、

上を見上げるとキリがない

上を見れば三分の1くらいなので寧ろ後退している。

下位英雄を充実させているだけで先頭は500万目前のまま。

もう少しなんだけど打つ手がない

鎮魂の塔の王の魂もなかなかLv40になってくれない。

40になれば40になれば・・・

砂盤演義は31章に進んだけど入口で停滞中。

全く勝てる気がしない

※朱雀が全回復技を使ってるけどどう対処したら良いのか全く判らないので力押し。

ま、サボってるせいなんだけどね。(笑

サボってる間に自動的に資源が溜まるので遊ぶのは楽なんだけど

イベントの褒章が貰えないし、

強化が進まなかったりする。

洗練で少し上げようとしたら1万下がった。orz



[Javascript]漢字読み仮名辞書とローマ字変換

VBAではバリバリと日本語の変数を使うけど

JavaScriptでは日本語変数は使えないので、よく打ち間違えるからVisualStudioCodeのヘルプは大助かり

しかし、Excelで仕様書を書く時は何もヘルプ機能が無いのでAll JavaScriptで作ってみた。これならダウンロードすれば安心。(なハズ。

漢字読み仮名辞書とローマ字変換

  • ローマ字変換と漢字読み仮名変換はほぼ似てるのでクラス化
    • 対象テキストと辞書のエントリィ(ひらかなとか漢字とか)の両方の先頭部分を比較し一致箇所が一番長いものを拾い出し、辞書の読み(ローマ字とかひらがな)に差し替える。
    • 基本的に辞書はJSONのまま使用。
  • ローマ字変換と漢字ひら仮名変換の2オブジェクトを作成。
    • 辞書はそれぞれ別途用意。
    • 変数名で使うセパレータ(_)は、漢字読み仮名変換のみ指定。
    • 接頭語、接尾語指定は、漢字読み仮名変換のみ指定。
    • 例外的な記述の仕方はtralsrateAfterをオーバーロード。

使い方1:漢字の読みを登録しよう。

  1. 漢字欄に「新幹線」と入力
  2. ひらかな欄に「しんかんせん」と
  3. 【単語の読みを辞書に登録】ボタンを押す

使い方2:辞書に登録されたか確認しよう。

  1. 【辞書⇒クリップボード】ボタンを押す
  2. テキストエディタで貼り付け
  3. 多分、「新幹線\tしんかんせん\r\n」と出るハズ

使い方3:辞書を更新しよう

  1. テキストエディタを「新幹線\tしんかんせんwwww\r\n」に書き換える
  2. 全文をコピー
  3. 【クリップボード⇒辞書差替】ボタンを押す
  4. 漢字欄が「新幹線」なのを確認する
  5. 【漢字⇒ひらかな】ボタンを押す
  6. ひらがな欄に「しんかんせんwwww」と出るハズ

使い方4:ローマ字に変換しよう。

  1. ひらがな欄が「しんかんせんwwww」なのを確認する
  2. 【ひらがな⇒ローマ字】ボタンを押す
  3. ローマ字(スネークケース)欄に「shinkansenwww」と出るハズ
    • ※ローマ字変換処理は「知らない文字はそのまま出力する様になっている」

使い方5:辞書に登録した漢字を組み合わせて使ってみよう

  1. 漢字欄を「新幹線新幹線」と書き換える
  2. 【漢字⇒ひらかな】ボタンを押す
  3. ひらがな欄が「しんかんせん_しんかんせん」になる。
  4. 【ひらがな⇒ローマ字】ボタンを押す
  5. ローマ字(スネークケース)欄に「shinkansen_shinkansen」と出るハズ
    • ※登録した漢字と登録した漢字の間に_が入る。(仕様
  6. キャメルケース欄に「shinkansenShinkansen」と出るハズ
  7. コンスタントケース欄に「SHINKANSEN_SHINKANSEN」と出るハズ
  8. パスカルケース欄に「ShinkansenShinkansen」と出るハズ
  9. チェインケース欄に「shinkansen-shinkansen」と出るハズ

使い方6:「~1」とか「~2」とか、ありがちな変数名がどう変換されるかな?

  1. 漢字欄を「新幹線1」と書き換える
  2. 【漢字⇒ひらかな】ボタンを押す
  3. ひらがな欄が「しんかんせん1」になる。※辞書に未登録な文字の前後に[ _ ]は入らない。
  4. 【ひらがな⇒ローマ字】ボタンを押す
  5. ローマ字(大文字)欄に「SHINKANSEN1」、ローマ字(小文字)欄に「shinkansen1」と出るハズ
    • ※登録した漢字と登録した漢字の間に_が入る。(仕様

使い方7:辞書の登録

  1. 辞書ファイルを用意
  2. 全文をコピー
  3. 【クリップボード⇒辞書差替】ボタンをクリック

使い方8:複数の辞書を登録

  1. 1つ目の辞書は使い方7の通りに【クリップボード⇒辞書差替】ボタンで取込む
  2. 2つ目以降は【クリップボード⇒辞書追加】ボタンで取込む

使い方9:辞書を消そう。※ここ重要。

  1. F12でDevTools画面を開く
  2. 画面上の「アプリケーション」タグを選択
  3. 画面左の「ストレージ」の中の「LocalStrage」をクリックする
  4. 画面中央の「キー」の一覧に「kanjiYomi」を選択
  5. 右クリックして「削除」を選択

使い方は以上です。

ひらがな欄にカタカナを入力してもローマ字に変換できる。(ハズ

変数名中の「ロック」や「ファイル」等の英単語にするものは変則的な使い方ですが・・・

  • 漢字:「ロック」
  • ひらかな:「lock」

の様にして【単語の読みを辞書に登録】して【ひらかな⇒ローマ字】すると良いでしょう。

ローマ字にしたくない変数名中の「の」は

  • 漢字:「の」
  • ひらかな:「」

とすると出来るけど、「のはら」が「はら」になったり予想外のことが起きるでしょう。

新XXXXは「shinXXXX」にしたい場合は

  • 漢字:「新」
  • ひらかな:「しん@」

と読みに@を付けると読み仮名変換時に[ _ ]が付かない様にしたから

ローマ字にするとshinxxxxになるはず。

xxx年はxxxNenにしたい場合

  • 漢字:「年」
  • ひらかな:「@ねん」

と読みの先頭に@を付けると読み仮名変換時に[ _ ]が付かない様にしたから

◇◆◇◆◇◆◇◆

【単語の読みを辞書に登録】や【クリップボード⇒辞書差替】を登録するとLocalStrageに取り合えず保存するので

適当に【辞書⇒クリップボード】して辞書をバックアップすれば安全だろう。

LocalStrageはJSON式データで保存してるけど、

クリップボードは「漢字1\t読み仮名1\r\n」「漢字2\t読み仮名2\r\n」「漢字3\t読み仮名3\r\n」・・・形式なので、

多分、EXCEL⇔クリッップボード⇔画面で利用もできる。(知らんけど

なんでこんなのを作ったのかと云えば

Excelに日本語名とローマ字の対応表を作りXLOOKUPで引けばできることですが、

「xx処理yy関連zz対応www2」の様な日本語名がいっぱいあると登録するのに疲れてくるので

「xx処理」「yy部」「zz対応」「www」を一度登録すれば、

「aa処理yy関連zz関連www2」なら「aa処理」だけ登録すればいいものが欲しかった。(ダケ

それにしても・・・

3人居れば皆ローマ字はバラバラになるのは・・・(実に恐ろしい

試しに

IME用辞書のTEXT形式のものを見つけたので

置換前:^([^\t]+)\t([^\t]+)\t([^\t]+)$

置換後:\2\t\1

で正規表現ONで置換させて、うまく取り込める様だ。

しかし、LocalStrageの制約があるので、5MBを越える様な漢字の読みは登録できない。

ps.TEXTAREAタグに変えた

変数名の列を範囲指定して貼ってみると楽しいかもしれない。

  1. とりあえず、単漢読み仮名だけでも自動的にしたいなら
  2. 文化庁の「常用漢字一覧表」のPDFをダウンロード
  3. MS-WORDに強引に読ませる
  4. 全文をクリップボードに入れる
  5. テキストエディタに貼り付ける
  6. 雰囲気を見ながら
  7. 「^[\r\n]+」で、空行を削除 ※114行減る
  8. 「^\s(\S)\(\s+\)\s+」「\1\t」で、103行調整。
  9. 「^\s.*\r\n」で、タブや空白等で始まる行を削除 ※2249行減る
  10. 「^(\S+)\s+(\S+)\s+(.*)$」「\1\t\2」で、漢字と読みだけ残す ※1958行更新
  11. 「\(\S\)」で、(惡)などの旧字を削除 ※251行更新
  12. 「^漢字\t音訓\r\n」で、表タイトルを削除 ※115行削除
  13. 「^←→[^\r]*\r\n」で、削除 ※8行削除
  14. 微調整が必要なのが以下の14行。
    • 梅雨(つゆ),部屋(へや),今年(ことし), 昨日(きのう), お母(かあ)さん
    • 勺 シャク尺 シャク, 升 ショウ少 ショウ, 昭 ショウ宵 ショウ, 畝 せ瀬 せ, 浦 ホ補 ホ, 匁 もんめ夜 ヤ
    • 「人数」は, 陳 チン 陳列
    • 弁(/辨/瓣/辯/) ベン
  15. 全文コピって【クリップボード⇒辞書】ボタンを押す

と、してみたら良いかもしれない。

常用漢字⇒ジョウヨウカン_ジ⇒jou_you_kan_ji な具合

多分うまくできたと思うので、単漢字変換表サンプルのリンクをサンプルの画面に貼っておく。



[EXCEL]ネストした表の拾い出し

こんなネストした表から(nnn)形式の番号とその後のテキストを拾い出すとしたら・・・

ネストした表

VBAではなくセルの数式で頑張ると

(nnn)を拾い出す簡易な数式
(nnn)の後のテキストを拾い出す簡易な数式

な感じだろうか・・・

一応6行目の様なケースも考慮してSEARCH関数で(nnn)な何かを判定している。

手元のEXCELは数式に改行コードが入るので、図の様に改行できて見やすい。けど、手元のPCのCPUは古いけどi9-9900でメモリ32GBだから不自由なく使えてるダケかもしれない。よくあるハズレPCアンド旧EXCELな状況ではそうもいかない。

多分、重い数式の説明やらヘルプ機能が邪魔で手に負えなくなるから、sakuraエディタで最終行を複製し1行範囲でセル座標を置換して貼り付けた方がマシな気がする。

FINDやSEARCHの検索対象を範囲指定してスピンさせる範囲分列を消費させる方法もあるけど、CONCATでかき集めないといけないし、悪質なくらいネストしている表の場合は列が足りなくなる恐れがあるので複数行な数式の方がマシな気がする。

でも行方向にスピルさせれば・・・

長すぎる番号を拾い出す数式
長すぎる番号の後のテキストを拾い出す数式

数式を行数分コピペしなくていいから便利かもしれない。

※範囲が1行分足りなくなった時に、数式の6を7に一斉置換するためにsakuraエディタが必須かも。

もし10ネストとかになると数式の長さに引っかかったりしそうだし、やっぱり編集も面倒!

「長いIF数式」をLAMBDA関数で括り、セル名の参照範囲に割り当ててみよう。

長い数式をセル名付きのLAMBDA式化
短くなった番号を拾い出す数式
短くなった番号の後のテキストを拾い出す数式

範囲パラメータを指定すると自動的にスピルされてしまうので、4つの番号取得を一つにまとめる方法が無いのかな?

複数列でまとめた数式をLAMBDA化してみよう

複数列からxxxな式を追加してみた

セル名の範囲をシートにすると「シート名!セル名」になってしまうのでセル名の範囲をブックに変更。

セルの数式の方も

もっと短くなった番号を拾い出す数式
もっと短くなった番号の後のテキストを拾い出す数式

とまれ、ここまで短くなれば十分かな。

ネストが深くなったら、

  1. 「複数列からxxx」の参照範囲のLAMBDAのパラメータと式を増やす
  2. セルの数式もパラメータ増やす

で何とかなりそう。

「複数列のxxx」セル名でBYCOL関数を使ってみる

ん-。やはり列範囲を指定した時点でスピルされてしまう。

うまく行かないや(笑

ここまで数式で頑張らなくても

シートからCSVに吐き出して、エディタでTAB(¥t)を一斉削除してシートに戻せばよいのは、秘密です。



[三国英雄の夜明け]第五回統合大型アップデート

10月14日にアップデートが実装されました。

タイトルは多分誤字
謎過ぎる

領地内に鎮魂の塔が追加され、

読みにくいけど「鎮魂の塔」

10階まであるけど、とりあえずは4日ごとに順に解放らしい。

一階の方々

絵はある程度、攻略済みのもので、最初は●●●で、武力や戦力を指定値まで上げると●●になる。

英雄の武力を11も上げるには

星の神紋とか

武力+5

強化した宝物とか

倚天剣

を他から融通して条件クリアし、クリア判定を済んだ後に元に戻せばいい。

武力の場合は魂玉とか獣霊を融通すれば良さげ。

但し、「二人の英雄の合計の武力」が条件の時は、+22(二人分)だから条件自体がギリギリだったり、ちゃんと元の英雄に装備を戻せるかちょっと心配だったりするので、スクショを撮ったり事前準備はしっかりやっておいた方が良さそう。

そして、更に指定のアイテムを貢ぐと●●●、また更に別のアイテムを貢ぐと●●●になる。

食料や銀銭なら都合しやすいが米酒や拝帖はストックが切れてると溜まるまで数日かかるかも。

3個足りない
試練完成(2日後)

これをクリアすると強化リストにご対面。

★★5段階強化★★★

条件のアイテムは、征戦「古代の夢魔」(告知では「永久の悪夢」)で、

征戦のメニュー

あみだくじの様な経路を進め、時にはミニゲームで対戦し、終焉条件(ラスボス対戦)をクリアすると貰える「夢境の塵」で、商店「夢獏見世」でアイテム「古の回響き」や「却数の棘」等に変えることになるので、5段階クリアは長丁場になりそうです。

1段進めたら●●●と色が変わったので、段階ごとに色は変化しそう。

●●●

古代の夢魔は地獄道の他に5個くらいあった様なので、進むたびに厳しくなるんだろうなぁ。

あ、途中で拾った呪われてそうなアイテムは捨ててしまいたいけど

ヤバそうなアイテム

レベル5デス的な状況もあるかもしれないから、取っておくか・・・



[三国英雄の夜明け]残されたもの(のびしろ)

幕府

レベルアップの度に2%づつPC対戦時に戦力がアップする応援システム。※レベル48で110%アップ。

但し、各府英雄の評価合計が低いと十分に戦力がアップされないので全員S評価にするのがお勧め。

押し職を集中して上げる方が無難。

定計

戦計でレベル4に上げると英雄に装備できる。

戦計

百貨市場で各計を3★以上に上げると

図の説明の様に全体または一部の英雄の戦力等が強化される。

4★になれば英雄に装備すると良いこともある。

魂玉

竜金探索でアイテムを収集し英雄に装備させパワーアップ。魂玉を合成して品質アップするとモリモリ強くなるが、品質が上がるほど合成に必要な魂玉の数が増えるので、合成すると全体の戦力は下がり気味。余ったら合成するのがいい。

シーズン(9日間イベント)

任務(全22)を達成する度にアイテムを貰えるので、ほどほどにやっている。

部隊が全滅するとポイントを多く貰えるので食料がいっぱい必要。

襄陽争奪戦(8日間隔)

任務達成や時間制のランキングに応じてアイテムが貰えるので、ほどほどにやっている。

部隊が全滅するとポイントを多く貰えるので食料がいっぱい必要。

襄陽の奪還に成功すると産業のうち3か所で黄金50が拾え(10回/日)、年度報酬も多めに入る。

異民族侵入

ランダムに発生する。報酬は少ないけど、取らないのも勿体ない。

黄巾の乱

襄陽争奪戦の日を除き、毎日10、11、18、19時に発生。※20時に大群が来襲する時もある

報酬は少ないけど、取らないのも勿体ない。

その他(未着手)

乱戦

乱戦商店で欠片の購入に使えるアイテムをゲットできる。参戦料(黄金500~1000。官邸レベルで変動?)が高いし、参加は時間制なのでやっていない。

乱戦商店

兵力を強化する欠片を売っている。

英雄の塚

課金は力也

過去に競売した「神」英雄を入手できる。図の通り課金必須。

ps.2022/9/19

ここを見た訳では無いだろうけれど、鯖統合情報が出た。

領地に鎮魂の塔という修行ストーリーが追加される様だが、課金必須なら「俺には関係ない」感。

そうでなくても、まだ砂盤演義を30章攻略中なので早すぎ感。

新年ぐらいに・・・と予告されていたバージョンアップの前倒しなのかな?

ps.2022/9/24

19.5million

平均すると4~5万/日だけど、戦計アップを含むので、その他の増強ペースが下がっている気がする。

課金勢に引き離され過ぎ。

それと、昨日(9/23)の午後8時くらいつい先ほどまでYoutobeももっさり、ゲーム画面で「ロード中…」が長すぎ!ブラウザを開き直す事x100回?いったい何があったんだろう?Nuro光とかに変えても無駄なんだろうけどね。

ps.2022/9/25

今日も夜から回線が重い。

対戦とは云え、反射神経がほとんど要らないスマホ・ゲームなのに支障が出る程度に酷い。

手元から一番遠いルータを再起動しても変化なし。

手元に一番近いハブをちょっと電源を外して再起動。お、症状は軽くなった。

※「ロード中…」が出たままになる状態から、5~10秒で消える程度に改善。

「あのロ編入投票」が始まったので妨害がきたのか?と思ってたw

昼間は大丈夫だったんだけど?

夜8時を回るとお眠なハブなのか?

やってないものに「究極・強化」があった。

あまりの低確率で「眼中」から外れて久しい。

けど、偶然+8になった。

今まで使えなかった上位アイテムを溶かしまくって・・・

なせか28%で成功

+13まで進む。

統率99だから+1欲しいなぁ

ps.2022.10.9

やっと2千万

爵位条件の1つが未達成に戻っていたので、アップデートの前に達成するようにスキルを調整したら、戦力2千万達成、長かったなぁ。

10月末にアップデートがあるから、そこで今後の進路を考えよう。

ps.2022.10.12

アップデートが前倒しに14:00までメンテらしい。

前倒しアップデート?

ps.2022.10.13

今日はサーバのメンテのみ、アップデートは明日らしい。



[Windows]TypeLibraryってドコにあるの?

色々調べてもラチが空かないので、OLEのインタフェースを書いているハズのタイプライブラリィを調べてみる。

まずはタイプライブラリィを登録するにはRegSvr32.exeを使うので、レジストリィを探してみる。

HKEY_CLASSES_ROOT\TypeLib
HKEY_CLASSES_ROOT\WOW6432Node\TypeLib
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\WOW6432Node\TypeLib
HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Classes\TypeLib

この5か所に登録したプログラム(exeとかdll)のフルパスを登録しているらしい。

WOW6432Nodeは64ビット版Windowsだけだろう。パスこそ違えど、登録内容は同じみたいなので、一般ユーザで見れそうなトコを使えば良さそうだ。HKEY_CLASSES_ROOT\TypeLibが無難かな?

この配下に、

{CLSID}
  {バージョン1} @={TypeLibraryのバージョン1の名前}
    0
      win32 @={バージョン1の32ビット版のプログラムファイルのフルパス名}
      win64 @={バージョン1の64ビット版のプログラムファイルのフルパス名}
  ・・・
  {バージョンn} @={TypeLibraryのバージョンnの名前}
    0
      win32 @={バージョンnの32ビット版のプログラムファイルのフルパス名}
      win64 @={バージョンnの64ビット版のプログラムファイルのフルパス名}

な構成で、プログラムファイルのフルパス名だけが保存されていた。

※Windowsのバージョンごとにこの構成も違うのかもしれないけどね。

ファイルのありかは解ったけど、具体的なインタフェース(メソッドとかプロパティ等)を取得する方法は不明。またTypeLibの直下がクラス識別子 (CLSID)なので名前で検索しようとすると時間がかかりそうだから、一旦ヒットしたらINIファイルにでも保存して再利用した方がいいだろう。

とりあえず、ネットで探してみたら、この辺にやこのあたりに情報があった。

OLEView.EXEを管理者権限で開けば詳しく調べられるし見つけた内容はIDLファイルで保存できる

※管理者権限で開く必要がる。

CLSIDFromProgIDを使って名前からCLSIDを調べられるらしいが、正しい名前を知らないとダメだろうからOLEView.EXEで事前に正確な名前を判っていないと難しそう。

見つからなかったら、全検索して部分一致した名前を列挙するようなヘルプ機能が欲しい。

一応、TypeInfoを大元の情報を探してみる。

ただ、インタフェースで階層化してるトコまで見えてしまうので、

徹底的に調べ尽くしつくしかなく、最後に纏めて目の前のクラスから使えるメソッドを列挙するようにしないと使いにくい。

IDLファイルからADODB.ConnectionがOpenSchemaメソッドを持っていると判るまで

// Connectionを見つけた_Connectionが親らしい
    coclass Connection {
        [default] interface _Connection;
        [default, source] dispinterface ConnectionEvents;
    };
  ・・・
// _Connectionを見つけたConnection15が親らしい
    interface _Connection : Connection15 {
        [id(0x00000015), helpcontext(0x0012c8b8)]
        HRESULT Cancel();
    };
    ・・・
// Connection15を見つけた。これが本体らしい
    interface Connection15 : _ADO {
        [id(00000000), propget, helpcontext(0x0012c918)]
        HRESULT ConnectionString([out, retval] BSTR* pbstr);   // Getterらしい
        [id(00000000), propput, helpcontext(0x0012c918)]
        HRESULT ConnectionString([in] BSTR pbstr);       // Setterらしい
        ・・・
// OpenSchemaを発見
        [id(0x00000013), helpcontext(0x0012c8d8)]
        HRESULT OpenSchema(
                        [in] SchemaEnum Schema, 
                        [in, optional] VARIANT Restrictions, 
                        [in, optional] VARIANT SchemaID, 
                        [out, retval] _Recordset** pprset);
        }

この場合、Connectionの本体はConnection15らしいから、クラスのテンプレを作るならメソッドを列挙すれば良さそう。

このあたりのソースを改造しoleaut32.dllのLoadTypeLibでComTypes.TYPEKIND.TKIND_DISPATCHをチェックする様に

                    if (ta.typekind == ComTypes.TYPEKIND.TKIND_DISPATCH)
                    {
                        char[] numChars = { '0', '1', '2', '3', '5', '6', '7', '8', '9' };
                        if (sname.IndexOf("_") != 0 && sname.IndexOf("Events") == -1 && sname.IndexOfAny(numChars) == -1)
                        {
                            Console.WriteLine("DISPATCH {0} cFuncs:{1}, cImplTypes:{2}, cVars:{3}", sname, ta.cFuncs, ta.cImplTypes, ta.cVars);
                            int href;
                            info.GetRefTypeOfImplType(-1, out href); // -1は、謎仕様
                            ComTypes.ITypeInfo info2;
                            info.GetRefTypeInfo(href, out info2);
                            info2.GetTypeAttr(out ppta);
                            ComTypes.TYPEATTR ta2 = (ComTypes.TYPEATTR)Marshal.PtrToStructure(ppta, typeof(ComTypes.TYPEATTR));
                            if (ta2.typekind == ComTypes.TYPEKIND.TKIND_INTERFACE)
                            {
                                int con2;
                                string sname2, doc2, hlp2;
                                info2.GetDocumentation(-1, out sname2, out doc2, out con2, out hlp2);
                                Console.WriteLine("INTERFACE {0} cFuncs:{1}, cImplTypes:{2}, cVars:{3}", sname2, ta2.cFuncs, ta2.cImplTypes, ta2.cVars);
                                for (int j = 0; j < ta.cImplTypes; j++)
                                {
                                }
                            }
                        }
                    }

GetRefTypeOfImplTypeの謎仕様は偶然ココで見つけた。

Pythonのソースだけど、これが参考になるかもしれない。tlbparser.py のdef parse_typeinfo

MSのページでは

[in] index

ハンドルが返される実装された型のインデックス。有効な範囲は 0 からTYPEATTR 構造体のcImplTypesフィールドまでです。

となっているのに-1を渡すと得られるインデックスをGetRefTypeInfoに渡すと、その型が引き継いでいるインタフェース(typekind が ComTypes.TYPEKIND.TKIND_INTERFACE)な情報を得られる。

DISPATCH Properties cFuncs:11, cImplTypes:1, cVars:0
DISPATCH Property cFuncs:13, cImplTypes:1, cVars:0
DISPATCH Error cFuncs:14, cImplTypes:1, cVars:0
DISPATCH Errors cFuncs:12, cImplTypes:1, cVars:0
DISPATCH Fields cFuncs:17, cImplTypes:1, cVars:0
DISPATCH Field cFuncs:29, cImplTypes:1, cVars:0
DISPATCH Parameters cFuncs:13, cImplTypes:1, cVars:0
※名前に_や番号やEventsが付いていたものは除外

とインターフェースの情報の数が得られたので一歩前進かな?



[C#]Type.FindMembersとか

Type.GetType({型名})で取得できるタイプの中身を調べるType.FindMembersで見つけたサンプルをちょっこっと加工したもの。

// ToSearch フィルター
public static bool ToSearch(MemberInfo objMemberInfo, Object? objSearch)
{
    // Compare the name of the member function with the filter criteria.
    return true; // 常にtrueなのでFilterはnull以外なら何でもいい。
}
// FindMembers:MemberTypes.xxxx
public static void GetMemberTypes(Type t, MemberTypes mt, List<string> list)
{
    string filter = "*"; // 実はnull以外なら何でもいい。
    var mems = t.FindMembers(mt,
        BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance,
        new MemberFilter(ToSearch),
        filter);
    if (mems.Length > 0)
    {
        for (int j = 0; j < mems.Length; j++)
        {
            list.Add($"{t} {mt}'s supported: {mems[j].ToString()}.");
        }
    }
    else
    {
        // list.Add($"{t} does not implement the {mt}'s {filter}.");
    }
}
// GetMemberTypesAll
public static void GetMemberTypesAll(Type t, List<string>list)
{
    if (!listA.Exists(x => x == t.FullName))
    {
        listA.Add(t.FullName);
        GetMemberTypes(t, MemberTypes.Constructor,list);
        GetMemberTypes(t, MemberTypes.Custom, list);
        GetMemberTypes(t, MemberTypes.Event, list);
        GetMemberTypes(t, MemberTypes.Field, list);
        GetMemberTypes(t, MemberTypes.Method, list);
        GetMemberTypes(t, MemberTypes.NestedType, list);
        GetMemberTypes(t, MemberTypes.Property, list);
        GetMemberTypes(t, MemberTypes.TypeInfo, list);
        //以下はオマケ。
        GetTypeInterfaces(t, list);
        GetMembers(t, list);
    }
}
// FindInterfaces
public static List<string> listA = new();
public static void GetTypeInterfaces(Type t, List<string> list)
{
    string filter = "*";
    TypeFilter myFilter = new TypeFilter(ToSearch);
    Type[] myInterfaces = t.FindInterfaces(myFilter, "*");
    if (myInterfaces.Length > 0)
    {
        list.Add($"{t} implements the interface {filter}.");
        for (int j = 0; j < myInterfaces.Length; j++)
        {
            list.Add($"> Interfaces supported: {myInterfaces[j]}.");
            //
            string iTypename = myInterfaces[j].ToString();
            Type? t2 = Type.GetType(iTypename);
            if (t2 != null)
            {
                if (!listA.Exists(x => x == t2.FullName))
                {
                    List<string> list2 = new List<string>();
                    GetMemberTypesAll(t2, list2);
                    list.AddRange(list2);
                    listA.Add(t2.FullName);
                }
            }
        }
    }
    else
    {
        // list.Add($"{t} does not implement the interface {filter}.");
    }
}
// GetMembers
public static void GetMembers(Type t, List<string> list)
{
    // Get the MemberInfo array.
    MemberInfo[] members = t.GetMembers();
    // Get and display the name and the MemberType for each member.
    list.Add($"Members of {t.Name} ({members.Length})");
    foreach (var member in members)
    {
        MemberTypes memberType = member.MemberType;
        list.Add($"> {member.Name}: {memberType}");
    }
}

をこんな感じで呼び出す。

// 「コンソール・アプリ(>NET Framework)」のC#は、newの型省略表記やNull容認mの指定等が未対応の為
// 「コンソール・アプリ」でプロジェクトを作成。
// プロジェクトへのCOM参照: Microsoft ActiveX Data Object 6.1 Library
// using System.Reflection;

// コンパイラが変数の型宣言に伴う処理を行うことで
// Type.GetType("ADODB.Connection")で情報を得られる様にする。
public static ADODB.Connection dummy = null;
public static ADODB.Recordset? recordset = null;
public static ADODB.Connection? connection = null;
public static ADODB.Field? field = null;
//
public static void Main(string[] args)
{
    Type? t = Type.GetType("ADODB.Connection");
    List<string> list = new ();
    GetMemberTypesAll(t, list);
    //結果表示
    Console.Write("\r\n" + string.Join("\r\n", list.ToArray()) + "\r\n");
}

実行してみると、

ADODB.Connection implements the interface *.
> Interfaces supported: ADODB._Connection.
ADODB._Connection Method's supported: System.String get_ConnectionString().
ADODB._Connection Method's supported: Void set_ConnectionString(System.String).
ADODB._Connection Property's supported: System.String ConnectionString.
ADODB._Connection implements the interface *.
> Interfaces supported: ADODB.Connection15.
ADODB.Connection15 Method's supported: System.String get_ConnectionString().
ADODB.Connection15 Method's supported: Void set_ConnectionString(System.String).
ADODB.Connection15 Property's supported: System.String ConnectionString.
ADODB.Connection15 implements the interface *.
> Interfaces supported: ADODB._ADO.
Members of _ADO (0)
Members of Connection15 (3)
> get_ConnectionString: Method
> set_ConnectionString: Method
> ConnectionString: Property
> Interfaces supported: ADODB._ADO.
Members of _Connection (3)
> get_ConnectionString: Method
> set_ConnectionString: Method
> ConnectionString: Property
> Interfaces supported: ADODB.Connection15.
> Interfaces supported: ADODB._ADO.
> Interfaces supported: ADODB.ConnectionEvents_Event.
Members of ConnectionEvents_Event (0)
Members of Connection (0)ts_Event (0)
Members of Connection (0)

ほとんど情報を得られなかった。


  • タグ:

[C#]Type.GetTypeFromProgID

64ビットの環境では、GetTypeFromProgIDで得たタイプはSystem.__ComObjectにラップされているらしく何も情報が取得できないので、PowerShellのGet-Memberを借りてプロパティやメソッドの情報を得るしかない様に思えたが・・・

プロジェクトにADODBの参照を追加した時点で、

ADODB.Connection cn = null;

と書けるようになるのだから、”ADODB.xxxx”の大半の名前の問題が解消されているハズ。

実際に上をコードすると、

Type.GetTypeFromProgIDの結果はそのままだけど、

Type?t = Type.GetType("ADODB.Connection");

で、それっぽいタイプが得られる。

ADODB.Fieldは、Type.GetTypeFromProgIDでTypeを得られないので、ソースに変数を宣言しないと全く取得できなかった。

public static ADODB.Field? field = null;

これで、プロパティとかメソッドが見つかると思い、FindMembersを使っても大空振り。

ADODB.Connectionのツールチップにはinterface ADODB.Connection。

更にツールチップの逆アセンブル?リンクから

ADODB._Connectionが大元のインタフェースらしい

しかし、FindMembersを使っても ConnectionString くらいしか情報が得られない。

一方、FindInterfacesは、ADODB._Connection、ADODB.Connection15、ADODB._ADO、ADODB.ConnectionEvents_Eventを見つけてくるので、

FindMembersとFindInterfacesを組み合わせて、タイプを検索するC#コードを拡張していく様にすると、良い事があるかもしれない。

何度も何度も何度も・・・ビルドし直すのは面倒なんでやらないけど


  • タグ:

[C#]warning CS8602: null 参照の可能性があるものの逆参照です。

よく判らない警告が出る。

JsonArray arr = jsonNode.AsArray();
string[] paramArray = new string[arr.Count];
ArrayList list = new ArrayList();
for (int i = 0; i < arr.Count; i++)
{
    list.Add(arr[i].ToString()); ※「warning CS8602: null 参照の可能性があるものの逆参照です。」が発生する。
}
return paramArray;

正解は、

JsonArray arr = jsonNode.AsArray();
string[] paramArray = new string[arr.Count];
ArrayList list = new ArrayList();
for (int i = 0; i < arr.Count; i++)
{
    var ji = arr[i];
    list.Add((ji != null)?ji.ToString():"");
}
return paramArray;

arr[i] は何の型かは不定だから、var宣言の変数に格納するしか手段はない。

そう明示的に書けば、CS8602も大人しくなる。

が・・・

普通の型の配列子にはしつこい。

関数(... string?[]? paramArray) {
・・・
object?[] param = new object?[] { null, null, null, null};
if (paramArray != null)
{
    param[0] = paramArray[0].Replace("%EXEPATH%", Resource.GetExePath());
}
t.InvokeMember(methodName,
    BindingFlags.InvokeMethod,
    null,
    obj,
    param);

は、

関数(... string?[]? paramArray) {
・・・
object?[] param = new object?[] { null, null, null, null};
if (paramArray != null)
{
    var p0 = paramArray[0];
    if (p0 != null)
    {
        p0 = p0.Replace("%EXEPATH%", Resource.GetExePath());
    }
    param[0] = p0;
}
t.InvokeMember(methodName,
    BindingFlags.InvokeMethod,
    null,
    obj,
    param);


[C#(v7) + Power Shell(v7)]ActiveXObjectのメンバーの情報を取得したい

64ビット環境では、

Activator.CreateInstance(t)

の結果が全てラッパーなクラス(System.__ComObject)になるので、progIdのインタフェースの内容を調べる方法が無い。(らしい

Power Shell でActiveXObjectの変数を作ると

> [System.Environment]::Is64BitProcess
True ※ 64ビット版で動作中(らしい
> $ac = New-Object -ComObject ADODB.Connection
> Get-Member -InputObject $ac > ADODB.Connection.txt

Get-Member でインタフェースの内容がテキストで出力される。

   TypeName: System.__ComObject#{00001550-0000-0010-8000-00aa006d2ea4}
Name              MemberType Definition                                        
----              ---------- ----------                                        
BeginTrans        Method     int BeginTrans ()                                 
Cancel            Method     void Cancel ()                                    
Close             Method     void Close ()                                     
CommitTrans       Method     void CommitTrans ()                               
Execute           Method     _Recordset Execute (string, Variant, int)         
Open              Method     void Open (string, string, string, int)           
OpenSchema        Method     _Recordset OpenSchema (SchemaEnum, Variant, Var...
RollbackTrans     Method     void RollbackTrans ()                             
Attributes        Property   int Attributes () {get} {set}                     
CommandTimeout    Property   int CommandTimeout () {get} {set}                 
ConnectionString  Property   string ConnectionString () {get} {set}            
ConnectionTimeout Property   int ConnectionTimeout () {get} {set}              
CursorLocation    Property   CursorLocationEnum CursorLocation () {get} {set}  
DefaultDatabase   Property   string DefaultDatabase () {get} {set}             
Errors            Property   Errors Errors () {get}                            
IsolationLevel    Property   IsolationLevelEnum IsolationLevel () {get} {set}  
Mode              Property   ConnectModeEnum Mode () {get} {set}               
Properties        Property   Properties Properties () {get}                    
Provider          Property   string Provider () {get} {set}                    
State             Property   int State () {get}                                
Version           Property   string Version () {get}                           

参考:https://docs.microsoft.com/ja-jp/powershell/scripting/samples/creating-.net-and-com-objects–new-object-?view=powershell-7.2&viewFallbackFrom=powershell-6

の「WScript.Shell によるデスクトップ ショートカットの作成」の項

この記事、作成したショートカットの内容を保存するにはSaveメソッドを使います。という内容。

統合開発環境(IDE)でソースを書けば、自動的にメソッドの候補がリストアップされるのが当たり前だけど、PowerShellの窓は窓だけそんなモノはない。

そこで窓の中でメソッドを調べる方法を優しく伝授したかったらしい。(良い人じゃないですか!

$lnk | Get-Member

さて、C#からPowerShellを呼び出すには、

普通にWindows Storeからインストすると、コマンドラインのみ許可なインストに。

普通にダウンロードしてPowerShellをインストール。

おや? InvalidProgramException ex

どうやら、32ビットのDLLらしい。

仕方なく、PowerShellVer7の64ビット版をインストール。

やっと動き出す。

using (var invoker = new RunspaceInvoke())
{
    string source = @"$ac = New-Object -ComObjec " + progID + @" ; Get-Member -InputObject $ac";
    var results = invoker.Invoke(source, new object[] { });
    foreach (var result in results)    {
        Console.Write(result);
    }
}

古いVer5ではInvokeしまくればよかったのに、

今のVer7では

// PowerShellオブジェクトを作成
PowerShell ps = PowerShell.Create();
// 実行するコマンドを追加
ps.AddCommand("New-Object")
    .AddParameter("-ComObject", progId);
// コマンドを呼び出す
var results1 = ps.Invoke();
//ps.AddCommand("Get-Member -InputObject $ac");
ps.AddStatement().AddCommand("Get-Member")
    .AddParameter("-InputObject", results1);
// コマンドを呼び出す
var results2 = ps.Invoke();

かと思ったけど、AddScriptを使えばPowerShellの窓で打った内容をそのまま使えるらしい。

PowerShell ps = PowerShell.Create();
// 実行するコマンドを追加
ps.AddScript("$ac = New-Object -ComObject ADODB.Connection");
ps.AddScript("Get-Member -InputObject $ac");
var results = ps.Invoke();

しかし、参照するDLLが多い。

吐き出される InvalidProgramException を読みつつ参照DLLを追加していくと

今回使用分だけなのに、このザマ。

しかも、ローカルにコピーを指定しても、PowerShellをアンスコすると動かなくなる。

あげくに、結果がSystem.__ComObjectの内容だった。

★ADODB.Connection
☆start
System.__ComObject
void Add(psobject item), void ICollection[psobject].Add(psobject item), int IList.Add(System.Object value)
void Clear(), void ICollection[psobject].Clear(), void IList.Clear()
bool Contains(psobject item), bool ICollection[psobject].Contains(psobject item), bool IList.Contains(System.Object value)
void CopyTo(psobject[] array, int index), void ICollection[psobject].CopyTo(psobject[] array, int arrayIndex), void ICollection.CopyTo(array array, int index)
bool Equals(System.Object obj)
System.Collections.Generic.IEnumerator[psobject] GetEnumerator(), System.Collections.Generic.IEnumerator[psobject] IEnumerable[psobject].GetEnumerator(), System.Collections.IEnumerator IEnumerable.GetEnumerator()
int GetHashCode()
type GetType()
int IndexOf(psobject item), int IList[psobject].IndexOf(psobject item), int IList.IndexOf(System.Object value)
void Insert(int index, psobject item), void IList[psobject].Insert(int index, psobject item), void IList.Insert(int index, System.Object value)
bool Remove(psobject item), bool ICollection[psobject].Remove(psobject item), void IList.Remove(System.Object value)
void RemoveAt(int index), void IList[psobject].RemoveAt(int index), void IList.RemoveAt(int index)
string ToString()
psobject Item(int index) {get;set;}
int Count {get;}
bool IsFixedSize {get;}
bool IsReadOnly {get;}
bool IsSynchronized {get;}
System.Object SyncRoot {get;}
☆end

もうダメか?と思ったけど、先のaddScriptを使う方はうまくいった。

一旦Get-Memberの結果をC#に引き渡した時にラップされすぎしたのかもしれない。

★ADODB.Connection
☆start
int BeginTrans ()
void Cancel ()
void Close ()
void CommitTrans ()
_Recordset Execute (string CommandText, Variant RecordsAffected, int Options)
void Open (string ConnectionString, string UserID, string Password, int Options)
_Recordset OpenSchema (SchemaEnum Schema, Variant Restrictions, Variant SchemaID)
void RollbackTrans ()
int Attributes () {get} {set}
int CommandTimeout () {get} {set}
string ConnectionString () {get} {set}
int ConnectionTimeout () {get} {set}
CursorLocationEnum CursorLocation () {get} {set}
string DefaultDatabase () {get} {set}
Errors Errors () {get}
IsolationLevelEnum IsolationLevel () {get} {set}
ConnectModeEnum Mode () {get} {set}
Properties Properties () {get}
string Provider () {get} {set}
int State () {get}
string Version () {get}
☆end

Errorsとかコレクションの場合はCountプロパティがあればほぼ間違いない。

> Get-Member -InputObject $ac.Errors
   TypeName: System.__ComObject#{00000501-0000-0010-8000-00aa006d2ea4}
Name    MemberType            Definition
----    ----------            ----------
Clear   Method                void Clear ()
Refresh Method                void Refresh ()
Item    ParameterizedProperty Error Item (Variant) {get}
Count   Property              int Count () {get}

> Get-Member -InputObject $ac.Properties
   TypeName: System.__ComObject#{00000504-0000-0010-8000-00aa006d2ea4}
Name    MemberType            Definition
----    ----------            ----------
Refresh Method                void Refresh ()
Item    ParameterizedProperty Property Item (Variant) {get}
Count   Property              int Count () {get}

という感じでネストすればよさそう。

意外だったのが、ADODB.Fieldがレジストリィ未登録らしく失敗するので、これは特定のコマンドを叩いて調べることにする。

もう少しテンプレの作り方を工夫した方がよさそうだ。

いや、そもそも、GetMemberBinderをどうにかできれば、PowerShellを呼ばなくてもいいんだけどね。

internal class PowerShellExec
{
    public static void MakeActiveXTemplateJDFile(string docRoot, string[] classNames)
    {
        //  ActiveXObjectのテンプレ作成
        foreach (string className in classNames)
        {
            MakeActiveXObjectJsFile(docRoot, className);
        }
    }
    //  ActiveXObjectのテンプレ作成
    public static void MakeActiveXObjectJsFile(string docRoot, string className)
    {
        MemInfo[]? result = PowerShellExecute(className);
        // 複数行の場合がある
        className = className.Split("\r\n")[0];
        if (result == null)
            return;
        foreach (MemInfo p0 in result)
        {
            //Console.WriteLine(p0.ToString());
        }
        MemInfo[] p = result.Where(x => x.memberType == PSMemberTypes.Property && x.child == null).ToArray();
        MemInfo[] c = result.Where(x => x.memberType == PSMemberTypes.Property && x.child != null).ToArray();
        MemInfo[] m = result.Where(x => x.memberType == PSMemberTypes.Method).ToArray();
        //
        string temp = @"
//  " + className + @" クラス
class " + className.Replace(".", "_") + @" extends ActiveXObject {
    constructor() {
        // とりあえず定番
        super();
        this.className = this.constructor.name.replace('_', '.');
        // プロパティ
        // 名前リストからプロパティを作成
        this.propertyNames = [" + string.Join(", ", p.Select(x => "'" + x.name + "'")) + @"];
        this.makeProperties(this.propertyNames);
        // プロパティ(コレクション)
        // 名前リストからダミーのメソッド(コレクション)を作成
        this.propertyCollections = {
" + string.Join(@",
",
    c.Select(x1 => {
        MemInfo[] cp = x1.child.Where(x2 => x2.memberType == PSMemberTypes.Property).ToArray();
        MemInfo[] cm = x1.child.Where(x2 => x2.memberType == PSMemberTypes.Method).ToArray();
        return "            '" + x1.name + @"': {
" + "                'properties': [" + string.Join(", ", cp.Select(x3 => "'" + x3.name + "'")) + @"],
                'methods': [" + string.Join(", ", cm.Select(x3 => "'" + x3.name + "'")) + @"],
                'events': []
            }";
    }))
+ @"
        };
        this.makePropertiesCollection(this.propertyCollections);
        // メソッド
        // 名前リストからダミーのメソッドを作成
        this.methodNames = [" + string.Join(", ", m.Select(x => "'" + x.name + "'")) + @"];
        this.makeMethods(this.methodNames);
        // イベント
        // 名前リストからダミーのメソッドを作成
        this.eventNames = [];
        this.makeEvents(this.eventNames);
    }
}
";
        //
        string path = docRoot + className.Replace(".","_") + ".js";
        if (!File.Exists(path))
        {
            File.WriteAllText(path, temp);
        }
    }
    // 
    public static MemInfo[]? PowerShellExecute(string progInfo)
    {
        string progId="";
        try
        {
            List<MemInfo> rc = new();
            // PowerShellオブジェクトを作成
            // Powershell.Create("get-process").Invoke(); はもうできない。
            using (PowerShell ps = PowerShell.Create())
            {
                // 実行するコマンドを追加
                // $ac = New-Object -ComObject ADODB.Connection
                // Get-Member -InputObject $ac
                string[] infoA = progInfo.Split("\r\n");
                progId = infoA[0];
                //
                string[] cmdLines = new string[]{
                    "$ac = New-Object -ComObject " + progId,
                    "Get-Member -InputObject $ac"
                };
                // 
                if (infoA.Length > 1)
                {
                    // 先頭はprogId
                    progId = infoA[0];
                    // 以降はPowerShellコマンド
                    cmdLines = infoA.Skip(1).ToArray();
                }
                //
                foreach (string cmdLine in cmdLines)
                {
                    ps.AddScript(cmdLine);
                }
                var results = ps.Invoke();
                //
                foreach (var result in results)
                {
                    rc.Add(new MemInfo(ps, "$ac.", result));
                }
                //Console.WriteLine("☆end");
            }
            return rc.ToArray();
        }
        catch (BadImageFormatException ex)
        {
            Console.WriteLine("★★★★★★ " + progId + ":" + ex.GetType().FullName + "\r\n" + "64ビット用のPowerShellのDLLを使用してください。\r\n" + ex.Message + "★★★★★★");
        }
        catch (InvalidProgramException ex)
        {
            Console.WriteLine("★★★★★★ " + progId + ":" + ex.GetType().FullName + "\r\n" + "\r\n" + ex.Message + "★★★★★★");
        }
        catch (Exception ex)
        {
            Console.WriteLine("★★★★★★ " + progId + ":" + ex.GetType().FullName + "\r\n" + ex.Message + "★★★★★★");
        }
        return null;
    }
}
//
public class MemInfo
{
    public string name;
    public string? type; 
    public string defition;
    public PSMemberTypes memberType;
    public List<MemInfo>? child = null;
    //
    public MemInfo(PowerShell ps, string preCmd, PSObject m)
    {
        dynamic a = m.ImmediateBaseObject;
        this.name = a.Name;
        //
        string[] definition = a.Definition.Split(" ");
        this.type = definition[0];
        //
        this.defition = a.Definition;
        this.memberType = a.MemberType;
        //
        if (a.MemberType == PSMemberTypes.Property)
        {
            // Countプロパティを持つプロパティはネスト
            ps.AddScript("Get-Member -InputObject " + preCmd + definition[1] + " -Name Count");
            var results = ps.Invoke();
            if (results.Count > 0)
            {
                List<MemInfo> rc = new();
                ps.AddScript("Get-Member -InputObject " + preCmd + definition[1]);
                results = ps.Invoke();
                foreach (var result in results)
                {
                    rc.Add(new MemInfo(ps, preCmd + definition[1] + ".", result));
                }
                this.child = rc;
            }
        }
    }
    //
    public override string ToString()
    {
        return this.defition;
    }
    //
    public static string Right(string str, int len)
    {
        if (len < 0)
        {
            throw new ArgumentException("引数'len'は0以上でなければなりません。");
        }
        if (str == null)
        {
            return "";
        }
        if (str.Length <= len)
        {
            return str;
        }
        return str.Substring(str.Length - len, len);
    }
}

これを

string[] classNames = new string[] {
    "ADODB.Connection",
    @"ADODB.Field  ※← レジストリィ未登録なので変数$acを使用する特定のPowerShellコマンドを指定して作成する。
$ac = New-Object -ComObject ADODB.Recordset
$ac.Fields.Append(""fld1"", 129, 20)
Get-Member -InputObject $ac.Fields(0)",
    "ADODB.Recordset"
};
//string[] classNames = new string[] { "ADODB.Connection" };
PowerShellExec.MakeActiveXTemplateJDFile(docRoot, classNames);

な感じっで呼ぶと

ADODB_Connection.js

ADODB_Field.js

ADODB_Recordset.js

の3ファイルができる。

※コマンドの変数を$acに固定しているのは、プロパティをネストしていく際に使用する変数が$acだから

// new ActiveXObject("xxx")用
class ActiveXObject {
    constructor(className, json)
    {
        this.domain       = "localhost"; ←適宜修正
        this.port         = "8000"; ←適宜修正
        this.className    = className;
        this.objectID     = null;
        // 派生クラスの場合は再帰してしまうので、処理を省略
        if(typeof(className) !== 'undefined')
        {
            // classNameから当該クラスのJavaScript側クラスのオブジェクトを作成
            let cf = new Function('className', `return new ${className.replace('.', '_')}()`);
            let obj = undefined;
            try {
                obj = cf(className);
            } catch (ex) {
                throw new Error(`★new ActiveXObject("${className}")未定義クラス`);
            }
            // 初期設定値(json)があれば、設定する
            if (typeof (json) !== 'undefined') {
                obj['objectID'] = json['objectID'];
                obj.__setup__(json);
            }
            else
            // 無ければ, C#のサーバに問い合わせる。
            {
                // サーバー側にnewしたことを通知
                // obj.init();
                // 放置してもサーバ側のオブジェクトは解放されないので、オブジェクトが不要になったら、適宜termメソッドを呼び出す事
                // this.term();
            }
            return obj; // new の戻り値
        }
    }
    // 設定する
    __setup__(json) {
        // プロパティにコピー
        for (let key of this.propertyNames) {
            let jj = json[key]
            if (typeof(jj) !== "undefined") {
                this[`_${key}`] = json[key];
            }
        }
        // プロパティ(コレクション)にコピー
        for (let key in this.propertyCollections) {
            let jj = json[key]
            if (typeof(jj) !== "undefined") {
                if (typeof (this[`_${key}`]) === "undefined" || this[`_${key}`] === null) {
                    this[`_${key}`] = {};
                }
                for (let idx in jj) {
                    let k = jj[idx].Name;
                    this[`_${key}`][`${k}`] = jj[idx];
                }
                // this[key]['Item'] = this[key]
            }
        }
    }
    // サーバー側にnewしたことを通知
    async init() {
        let json = await ActiveXObject.staticSendMessage('POST', this.domain, this.port, `ActiveXObject/${this.className}`);
        ActiveXObject.staticInit(this, json);
    }
    // オブジェクトの解放
    async term() {
        return await ActiveXObject.staticSendMessage('DELETE', this.domain, this.port, `ActiveXObject/${this.objectID}`);
    }
    // サーバー側にnewしたことを通知
    static staticInit(obj, json)
    {
        // 必須
        if (typeof (json["objectID"]) === 'Nothing')
        {
            alert("objectIDが取得できません。");
        }
        obj.objectID = json["objectID"];
        if (typeof (obj.onobjectID) !== 'undefined')
        {
            obj.onobjectID();
        }
        obj.SetProperties(json['activeXobject']);
        //
    }
    // 送信
    static staticSendMessage(method, domain, port, page, data)
    {
        let promise = new Promise( (resolve, reject) => {
            try {
                let parserFunction = this.parserFunction;
                let xhr = new XMLHttpRequest();
                let url = encodeURI(`http://${domain}:${port}/${page}`);
                xhr.open(method, url, true);
                xhr.responseType = 'text';
                xhr.send(this.staticEncodeHTMLForm(data));
                xhr.onload = function(e) {
                    if (xhr.readyState == 4) {
                        if (xhr.status == 200 ) {
                            try {
                                let json = xhr.response;
                                json = JSON.parse(json, parserFunction);
                                resolve(json);
                                return;
                            } catch (ex) {
                                console.log(`'${ex.toString()}'\n url:http://${domain}:${port}/${url}\n json:'${xhr.response}'`);
                            }
                        }
                    }
                };
            }
            catch (ex) {
                reject(ex);
                return;
            }
        });
        return promise;
    }
    // データをBODYの形式に変換
    static staticEncodeHTMLForm(data) {
        if (typeof(data) === 'undefined' || data === null)
        {
            return null;
        }
        let a = [];
        for (let aa of data) {
            a.push(aa);
        }
        return "params=" + JSON.stringify(a);
    }
    //
    static staticMakeUrlParameters(arg) {
        let rc = [];
        for (let idx = 1; idx <= arg.length; idx++) {
            rc.add(`p${idx}=JSON.stringify(${arg[idx - 1]})`);
        }
        return rc.join('&');
    }
    //
    static staticParserFunction(k,v)
    {
        try
        {
            if (typeof(v) === "string" && ((v.indexOf("function") === 0) || (v.indexOf("async") === 0)))
            {
                return eval(`(${v})`);
            }
            else
            {
                return v;
            }
        } catch (ex) {
            console.log(ex.toString());
        }
    }
    //
    /**
     * 名前リストからプロパティを作成
     * 
     * @param {String[]} propertyNames   名前リスト
     */
    makeProperties(propertyNames) {
        for (let propName of propertyNames) {
            Object.defineProperty(this, propName, {
                get() {
                    return this[`_${propName}`];
                },
                set(value) {
                    this[`_${propName}`] = value;
                }
            });
        }
    }
    /**
     * 名前リストからプロパティ(コレクション)を作成
     * 
     * @param {String[]} propertyCollectionNames   名前リスト
     */
    makePropertiesCollection(propertyCollections) {
        for (let collectionName in propertyCollections) {
            if (typeof (this[collectionName]) === "undefined") {
                this[collectionName] = {};
            }
            let co = this[collectionName];
            // コレクション本体
            Object.defineProperty(this, collectionName, {
                get() {
                    return this[`_${collectionName}`];
                },
                set(value) {
                    this[`_${collectionName}`] = value;
                }
            });
            // コレクションの設定内容
            let collection = propertyCollections[collectionName];
            // コレクションのプロパティ
            // property
            for (let propertyName of collection.properties) {
                Object.defineProperty(co, propertyName, {
                    get() {
                        return co[`_${propertyName}`];
                    },
                    set(value) {
                        co[`_${propertyName}`] = value;
                    }
                });
            }
            // コレクションのメソッド
            for (let methodName of collection.methods) {
                if (typeof (co[methodName]) === "undefined")
                {
                    co[methodName] = function () {
                        let args = arguments;
                        if (this.objectID == null) {
                            this.onobjectID = async function () {
                                let rc = await co[methodName].apply(co, args);
                                resolve(rc);
                                return;
                            }
                        } else {
                            let path = `ActiveXObject/${this.objectID}/${collectionName}/${methodName}`;
                            return new Promise(async (resolve, reject) => {
                                let json = await ActiveXObject.staticSendMessage('PUT', this.domain, this.port, path, args);
                                // エラーがあれば返す。
                                if (typeof (json['error']) !== 'undefined') {
                                    reject(json['error']);
                                    return;
                                }
                                //
                                this.SetProperties(json['activeXObject']);
                                //
                                let rc = json['returnValue'];
                                if (rc != null && rc["__constructor_name__"]) {
                                    let className = rc["__constructor_name__"];
                                    delete rc["__constructor_name__"];
                                    let obj = new ActiveXObject(className, rc);
                                    resolve(obj);
                                    return;
                                }
                                resolve(rc);
                                return;
                            });
                        }
                    };
                }
            }
            // event 未定
            //
        }
    }
    /**
     * 名前リストからメソッドを作成
     * 
     * @param {String[]} methodNames   名前リスト
     */
    makeMethods(methodNames) {
        for (let methodName of methodNames) {
            this[methodName] = function () {
                let args = arguments;
                if (this.objectID == null) {
                    return new Promise(async (resolve, reject) => {
                        this.onobjectID = async function () {
                            let rc = await this[methodName].apply(this, args);
                            resolve(rc);
                            return;
                        }
                    });
                } else {
                    let path = `ActiveXObject/${this.objectID}/${methodName}`;
                    return new Promise(async (resolve, reject) => {
                        let json = await ActiveXObject.staticSendMessage('PUT', this.domain, this.port, path, args);
                        // エラーがあれば返す。
                        if (typeof (json['error']) !== 'undefined') {
                            reject(json['error']);
                            return;
                        }
                        //
                        this.SetProperties(json['activeXObject']);
                        //
                        let rc = json['returnValue'];
                        if (rc != null && rc["__constructor_name__"]) {
                            let className = rc["__constructor_name__"];
                            delete rc["__constructor_name__"];
                            let obj = new ActiveXObject(className, rc);
                            resolve(obj);
                            return;
                        }
                        resolve(rc);
                        return;
                    });
                }
            };
        }
    }
    /**
     * 名前リストからイベントを作成
     * 
     * @param {any} eventNames
     * @param {any} staticMakeUrlParameters
     */
    makeEvents(eventNames) {
        // イベントは、逐次作成
    }
    //
    staticMakeUrlParameters(args) {
        if (args.length == 0) {
            return "";
        }
        let rc = [];
        for (let [arg, index] of args) {
            rc.push(`param${index}:${arg}`);
        }
        return rc.join("&");
    }

    // SendMessageの戻り値の['activeXobject']からプロパティを設定
    // 更新するプロパティがあれば処理する
    SetProperties(jsonReturn) {
        // 何もなければ何もしない
        if (typeof (jsonReturn) === 'undefined') {
            return;
        }
        if (jsonReturn === null) {
            return;
        }
        //
        delete jsonReturn["__constructor_name__"];
        this.__setup__(jsonReturn);
    }
}



top