search
LoginSignup
0
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

Paper Plane xUI Advent Calendar 2016 Day 10

posted at

updated at

Paper Plane xUI(PPx)のText Moduleに自作のコマンドを追加する

この記事では、PPxのPPx Text Moduleに自作のコマンドを追加する方法を紹介します。
また、公式HELPにもPPx Moduleについてが書かれているので合わせてご覧ください。

自作のコマンドを追加する手順

1.実装

PPx Text ModuleのPPXTEXT.Cを開きます。
ここでは単純に「Hello」という文字列を返すだけのコマンド「hello」を作成してみます。

PPXTEXT.C
BOOL PPXAPI ModuleEntry(PPXAPPINFOW *ppxa,DWORD cmdID,PPXMODULEPARAM pxs)
{
    if ( cmdID == PPXMEVENT_FUNCTION ){
        省略
        if ( ((pxs.command->commandhash == 0) || (pxs.command->commandhash == 0xc914d34f)) && !wcscmp(pxs.command->commandname,L"HELLO")){
            wcscpy(pxs.command->resultstring, L"Hello");
            return TRUE;
        }
        省略
    }
}

2016/12/14:commandhashについてを修正

commandhashの計算はPPxの「PPD_CMD.C:GetModuleNameHash」関数が行っています、自前で計算してもいいのですが、「*commandhash」というコマンドが用意されているのでこちらを利用します。

※自身の設定が消えないように、適用前にバックアップを取るか、追加取り込みをしてください

PPx.CFG
KC_main = { ; PPcメイン窓
^H  ,*commandhash %"commandhash"%{commandname%}
}

commandhashを計算するショートカットキーを登録しておき、「hello」を計算します。

commandhash.png

この時の「0xc914d34f」が、helloコマンド(大文字のHELLO)のcommandhashになります。
また、この他にもppxcmds.plのNameHashでも調べる事ができます。

2.ビルド

保存後、PPx Text Moduleをビルドします。
その際、makefileをVC++でビルド設定へ変更しています。
※僕の場合は、VC++2015 x86 Native Build Tools Command Promptを利用しています。

2016/12/13追記
また、別途defファイルも用意します。

PPXTEXT.DEF
LIBRARY PPXTEXT
EXPORTS
  ModuleEntry @1
MAKEFILE
UseDebug = 0    # 1 なら debug 指定
UseBCC = 0  # 1 なら BCC32 , 0 なら CL
UseIlink = 0    # 1 なら ilink32(BCC 5.5以降) を使用
VCTOOLKIT = 1   # VC TOOL KIT を使うなら 1 に、VS2008なら 0 に
cmd
E:\path\to\PPXTXSRC> nmake  

3.実行

生成されたPPXTEXT.DLLをPPxディレクトリへコピーし、以下の設定を書いてみます。

※自身の設定が消えないように、適用前にバックアップを取るか、追加取り込みをしてください

PPx.CFG
KC_main = { ; PPcメイン窓
^T  ,%OB echo %*hello()
}

上記のコマンドを実行し、「Hello」と表示されれば成功です。

ppx_module.png

PPx Text Moduleのソース解説

以下は内部の話です、コマンド追加手順とは無関係ですので読み飛ばしても構いません。

まずは、PPx Text ModuleのPPXTEXT.Cを開きます

PPXTEXT.C
BOOL PPXAPI ModuleEntry(PPXAPPINFOW *ppxa,DWORD cmdID,PPXMODULEPARAM pxs)
{
    if ( cmdID == PPXMEVENT_FUNCTION ){
        // ====================================================================
        if (省略 "NOWDATETIME")){
            省略
        }
        if ( ((pxs.command->commandhash == 0) || (pxs.command->commandhash == 0xf75d5755)) && !wcscmp(pxs.command->commandname,L"CLIPPEDTEXT")){
            int len = 0;

            if ( OpenClipboard( ppxa->hWnd ) != FALSE ){
                HGLOBAL hG;
                WCHAR *text;

                hG = GetClipboardData(CF_UNICODETEXT);
                text = GlobalLock(hG);
                len = wcslen(text);
                if ( len > (CMDLINESIZE - 16) ) len = CMDLINESIZE - 16;
                memcpy( pxs.command->resultstring,text,len * sizeof(WCHAR) );
                GlobalUnlock(text);
                CloseClipboard();
            }
            pxs.command->resultstring[len] = '\0';
            return TRUE;
        }

    ...
}

既存コマンドの「CLIPPEDTEXT」を例に解説します。
ここで、大事なのは「!wcscmp(pxs.command->commandname,L"CLIPPEDTEXT")」「commandhash == 0xf75d5755」「pxs.command->resultstring」の三つです、それぞれ順を追って説明します。

!wcscmp(pxs.command->commandname,L"CLIPPEDTEXT")

これは見た目で分かる通り、「*clippedtext」コマンドの大文字との一致チェックです、処理までは追えてませんが、pxs.command->commandnameには呼び出したコマンドを大文字化した文字列が入っています。

commandhash == 0xf75d5755

2016/12/14:commandhashについてを修正

これは"CLIPPEDTEXT"から生成されたハッシュ値です、予めpxs.xxx->commandhashを調べることでコマンド名の全体の検索速度が上がります。

pxs.command->resultstring

こちらは戻り値です、PPx Text Module側では単にメモリに書き込んでいますが、書き込める量は幾つでしょう、もう少し探ってみます。

PPx側のPPD_MLD.Cを検索すると

PPD_MDL.C
void FunctionModule(EXECSTRUCT *Z)
{
    省略
#ifndef UNICODE
        WCHAR 省略,destW[CMDLINESIZE];
        #define DESTBUF destW
#else
        #define DESTBUF Z->dst
#endif
    省略
    function.resultstring = DESTBUF;
}

という処理を見かけます、ANSI時はWCHARの[CMDLINESIZE]配列ですね。
ここでUNICODE時のDESTBUFをさらに探ります、Z->dstはZ->DstBufを指したポインタであり、

PPD_CMD.C
// Z の初期化 -----------------------------------------------------------------
void ZInit(EXECSTRUCT *Z)
{
    省略
    Z->dst = Z->edit.EdBottom = Z->DstBuf;
    省略
}

Z->DstBufは[CMDLINESIZE * 2]の配列

PPD_DEF.H
typedef struct {
    省略
    TCHAR DstBuf[CMDLINESIZE * 2];  // 変換後の内容を保存する一時領域
} EXECSTRUCT;

CMDLINESIZEは1024なので

PPCOMMON.H
#define CMDLINESIZE 0x400       // コマンドラインの大きさ

resultstringはANSI、UNICODE共に2048バイト分書き込める事になります、それ以上書き込もうとするとセグフォ(Segmentation fault)が発生します。

ただし、*2にしてるのは安全マージンを持たせているため、実際に書き込む際はCMDLINESIZE分だけ書き込むようにします。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
0
Help us understand the problem. What are the problem?