この記事では、PPxのPPx Text Moduleに自作のコマンドを追加する方法を紹介します。
また、公式HELPにもPPx Moduleについてが書かれているので合わせてご覧ください。
自作のコマンドを追加する手順
1.実装
PPx Text ModuleのPPXTEXT.Cを開きます。
ここでは単純に「Hello」という文字列を返すだけのコマンド「hello」を作成してみます。
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」というコマンドが用意されているのでこちらを利用します。
※自身の設定が消えないように、適用前にバックアップを取るか、追加取り込みをしてください
KC_main = { ; PPcメイン窓
^H ,*commandhash %"commandhash"%{commandname%}
}
commandhashを計算するショートカットキーを登録しておき、「hello」を計算します。
この時の「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ファイルも用意します。
LIBRARY PPXTEXT
EXPORTS
ModuleEntry @1
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 に
E:\path\to\PPXTXSRC> nmake
3.実行
生成されたPPXTEXT.DLLをPPxディレクトリへコピーし、以下の設定を書いてみます。
※自身の設定が消えないように、適用前にバックアップを取るか、追加取り込みをしてください
KC_main = { ; PPcメイン窓
^T ,%OB echo %*hello()
}
上記のコマンドを実行し、「Hello」と表示されれば成功です。
PPx Text Moduleのソース解説
以下は内部の話です、コマンド追加手順とは無関係ですので読み飛ばしても構いません。
まずは、PPx Text Moduleの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を検索すると
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を指したポインタであり、
// Z の初期化 -----------------------------------------------------------------
void ZInit(EXECSTRUCT *Z)
{
省略
Z->dst = Z->edit.EdBottom = Z->DstBuf;
省略
}
Z->DstBufは[CMDLINESIZE * 2]の配列
typedef struct {
省略
TCHAR DstBuf[CMDLINESIZE * 2]; // 変換後の内容を保存する一時領域
} EXECSTRUCT;
CMDLINESIZEは1024なので
#define CMDLINESIZE 0x400 // コマンドラインの大きさ
resultstringはANSI、UNICODE共に2048バイト分書き込める事になります、それ以上書き込もうとするとセグフォ(Segmentation fault)が発生します。
ただし、*2にしてるのは安全マージンを持たせているため、実際に書き込む際はCMDLINESIZE分だけ書き込むようにします。