VC++
PPx

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

More than 1 year has passed since last update.

この記事では、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分だけ書き込むようにします。