hmPerl.dllのソースの要点解説

概要

hmPerl.dllのソースの要点解説となります。
秀丸エディタ v8.66で追加された各種APIや機能は、どのように利用すれば良いのか、その理解に役立ててください。

hidemaruexe_export系

まずは、「秀丸本体からexportされている関数群」をdllで利用可能とするため、インポートしています。
いくつかの関数はラップ関数を用意した方がいいので、そのようにしています。

  • hidemaruexe_export.h

    ソースファイル自体にコメントが豊富についています。

    この中で極めて重要な働きをする関数があります。
    それが「Hidemaru_GetDllFuncCalledType」です。

    Hidemaru_GetDllFuncCalledType関数の多数の働き

    • ①どのように「loaddll」されたのか

      「loaddll文」なのか、「loaddll関数で何か値と束縛されたのか」を取得することが可能です。
      この情報があれば、dllは自分自身に対して秀丸マクロを発行できるため、
      ユーザーのマクロ記述に頼らない自由な自立情報伝達が可能となるのです。

    • ②適切な方法で関数が呼ばれたか

      関数の「返り値の型」や「引数の型」を取得をもとに、「関数の呼び方」が想定どおりのものか、チェックが可能です。
      ありがちな不正な呼び出しを防止することが可能です。

    • ③関数オーバーロード

      関数が呼ばれた際に、引数が「数値」か「文字列」か判断可能なので、
      「数値」でも「文字列」でもどちらでも機能する関数を作成することが可能です。

    • ④可変引数の関数

      引数として渡された数を取得できますので、可変的な引数を持つ関数を作成することが可能です。
      vsprintf関連の情報を与える必要もありません。

    • ⑤typeof … 秀丸上のとある変数やシンボルの自動型チェック

      関数へと引数を渡した後に、その型を判別できるわけですから、
      dllからは本来見えない、「秀丸マクロのシンボルの型」を自動判別することが可能です。

    hmPerlでは、Perl自体が記述できますので、④以外の全ての機能を利用しています。

  • hidemaruexe_export.cpp

    GetModuleHandle(NULL)などを利用して、一気にHidemaru.exeのハンドルを取得しても良いのですが、
    一応の情報を管理するものとして、Hidemaru.exeのフルパス取得 ⇒ ハンドル取得 ⇒ Export関数を取得、
    というように、手順を追って値を保持していっています。

    「Hidemaru_GetCursorPosUnicode」は複数箇所で登場するため「Hidemaru_GetCursorPos」という形で、このクラスでラップしています。
    他の関数もソース中に何回も出現するようであれば、この「CHidemaruExeExport」でラップ関数を作成するのが良いでしょう。

self_dll_info系

  • self_dll_info.h

    hmPerl.dllそのものの情報の保持です。
    ハンドルとdll自体のフルパスを保持しています。

    「iSelfBindedType」という珍しい値が保持されています。
    このdllが秀丸マクロからどのような形でloaddllされたのかの情報です。

    なぜこのような情報が必要なのか?

    「秀丸マクロ」と「hmPerl.dll」の間で「自動的に値をやり取りする」ためです。

    ① loaddll hidemarudir + @"hmPerl.dll";
    

    というdllのロード方法と、

    ② #PL = loaddll(hidemarudir + @"hmPerl.dll");
    

    という2種類のdllのロードの仕方があります。

    秀丸マクロから、hmPerl.dllへと値を伝達するためには、
    hmPerl.dllは、dll自身で秀丸マクロを発行し、dllfuncwやdllfuncstrwなどを利用して、
    自分自身へと値を渡す必要があります。

    この時に、一体hmPerl.dllが「①」か「②」か「呼ばれた形式」に合わせて、 マクロを発行する必要があり、
    ②の場合は、その#PLの中身の数値も把握する必要があります。

    当サイトの管理人は、この値を「dllの束縛タイプ」及び「dllの束縛値」と呼んでおり、
    いわゆるdllのハンドル(メモリアドレス)の値とは「異なる」秀丸独自の管理値です。

    このことは極めて重要なことで、理解があやふやではいけません。
    C++のdll内部から秀丸マクロを実行するためには、必ずこの点の理解が欠かせません。

  • self_dll_info.cpp

    「Hidemaru_GetDllFuncCalledType(-1)」によってloaddllのタイプを得ることができます。

dllfunc_interface系

  • dllfunc_interface.cpp

    この中で注目すべきは、「DoString」関数のみです。

    • 関数の使用法の正当性のチェック

      Hidemaru_GetDllFuncCalledType(0)を利用して返り値の型の正しさをチェック、
      Hidemaru_GetDllFuncCalledType(1)を利用して、引数の型の正しさをチェック、
      これらにより、DoString関数の迂闊な使用方法の間違いを防止しています。
      (主としてdllruncwの「w」が抜けることが多いのでそのチェックの意味合いが強いでしょう)

    • Perl実行時にエラーが発生した時の情報補強

      Evalのエラーは、実際の場所を少しつかみにくくなる傾向があるため、
      わかりやすい表示にしています。

perlez_engine系

  • perlez_engine.h

    ここはPerlEzエンジン独特なところです。
    C++11の機能によって、具体的な型の記述が不要となったので、記述がスッキリしたと言えるでしょう。

  • perlez_engine.cpp

    PerlEz.dllというdllの読み込み、及び、そのdll内にある関数ポインタを取得しています。
    又、「プロパティ的なものへの文字列でのアクセス方法の記述」がPerlでどのようにするのが理想的なのか、
    なかなか結論を見いだせなかったため、hmPerl.pmという形で各自がカスタム可能とするスタイルにしました。

perlez_magical_scalar系

ここもActivePerlのPerlEz独特な処理となっています。

  • 古さを感じさせる仕組み

    2016年現在の主流は、組み込み言語を使う時には、
    ベースとなる「C++やC#のコンパイル系言語」 ⇔ 「組み込みScript言語」において、
    「変数・関数・クラス・インスタンス・メソッド」等を問わず、
    お互いに「ほぼその構造のまま」やり取りできるのが主流ですが、

    PerlEzでは古風にも

    • 「グローバルシンボルに対して、Set/Getのフック関数」
    • 「その場でScript文をEvaluate」
    • 「関数をシンボルで名指しで指定してパラメタを与えて実行」

    と言った形に集約されています。

  • perlez_magical_scalar.h

    • フックするためのGet/Setの共通関数
    • 「hmPerl.pm」へと出している各関数が宣言されています。
  • perlez_magical_scalar.cpp

    • CPerlEzMagicalScalar::Hm::Macro::Get::VarSimbol()

      秀丸上の「変数やシンボル(filenameやx)」の値を、hmPerlのC++層へと読み込み、最終的にはPerl層へと返す処理となっています。
      ・最初に、hmPerl.dll自体に用意してある、引数の型を問わない「SetDynamicVar」という関数に対して、変数を引数として渡します。
      ・対象の値が「数値」か「文字列」かに応じて処理を分けて、最終的にはPerl層へと返しています。

    • SetDynamicVar

      「dllfunc_interface_internal.cpp」に定義されています。

      引数が「const void* dynamic_value」となっており、「数値」でも「文字列」でも一端は、引数を受け取ります。
      「Hidemaru_GetDllFuncCalledType(1)」によって、その型を調べ、情報を格納しておきます。
      この情報を利用することで、スクリプト言語に相応しい「型を自動的に吸収する処理」を実現しています。

    • CPerlEzMagicalScalar::Hm::Edit::Set::TotalText(...)
    • CPerlEzMagicalScalar::Hm::Edit::Set::SelectedText(...)
    • CPerlEzMagicalScalar::Hm::Edit::Set::LineText(...)

      これら3つの関数は処理がほとんど同じです。

      「PushStrVar」関数によって、一端hmPerl.dll内へと値をストックし、
      EvalMacroでhmPerl.dll自身のPopStrVar関数を実行することで、値をhmPerl⇒マクロ側へと吐き出しています。

      「insert dllfuncstrw(...)」といったように、insert の引数として関数の返り値をダイレクトに指定することで、
      「秀丸マクロの変数のメモリ上限」にひっかからないようにすることが出来ます。

    • CPerlEzMagicalScalar::Hm::Macro::Set::VarValue(...)

      Perl側から指定された秀丸マクロ上の変数シンボルに、値を設定する処理です。

      幸い、書き込み可能な変数の場合は、秀丸マクロは、「#***」と先頭に「#」があれば数値、
      「$****」と先頭に「$」があれば、文字列ですので、それによって処理を分けています。

      「Push***Var」関数によって、一端hmPerl.dll内へと値をストックし、
      EvalMacroでhmPerl.dll自身の「Pop***Var」関数を実行することで、値をhmPerl⇒マクロ側へと吐き出す、というわけです。