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⇒マクロ側へと吐き出す、というわけです。