
この記事では、Windowsエクスプローラーを展開した経験、具体的には「パワーユーザーメニュー」と呼ばれるコンテキストメニューについてお話します。 古い[スタート]メニューのプレゼンテーションが本当に必要だとは言えませんが、それでも仕事に必要な基本機能への迅速かつ構造化されたアクセスを可能にしたいと思っています。 パワーユーザーメニューは、次の2つの方法で呼び出すことができます。1. [スタート]ボタンを右クリックします。 2. Windowsキー+ Xのキーの組み合わせを押します。マイクロソフトはこのメニューを編集する機能を提供していますが、この機能は非常に制限されており、メニュー階層、アイコン付きアイテムを作成できず、ショートカットのみをサポートします。 説明した機能を実装するために、Windows Explorerプロセスへのdllインジェクションを実行し、コンテキストメニューの操作を制御するapi呼び出しをインターセプトします。 実験的なオペレーティングシステムとして、Windows 8.1 x64を使用します。
それでは、Windowsエクスプローラーのアドレススペースにdllを挿入できるようにする手順から始めましょう。 使用するインジェクション方法は「コードケーブdllインジェクション」と呼ばれ、事前に準備されたマシンコードを選択したプロセスのアドレス空間にインジェクションします。 このマシンコードは、必要なライブラリを使用してLoadLibraryにAPI呼び出しを行い、制御をアプリケーションに返します。
void InjectDLLx64( LPPROCESS_INFORMATION ppi, LPCTSTR dll ) { CONTEXT threadContext; DWORD length; LPVOID memBuf; DWORD64 loadLibApi; union { PBYTE cC; PDWORD64 cP; } ip; #define CODESIZE 92 static BYTE code[CODESIZE+SIZE_T(MAX_PATH)] = { 0,0,0,0,0,0,0,0,
マシンコードはCPUレジスタを保存し、プログラム実行段階で定義されたAPI呼び出しLoadLibraryを使用して必要なライブラリをロードし、レジスタの内容を復元して制御を返します。 当然、注入時には、プロセスは一時停止状態になっている必要があります。
dllの実装を実行するアプリケーションの他の関数のコードは、あまり関心がないので考慮しません。
コンテキストメニューを展開できるようにするには、そのハンドルを取得する必要があります。 メニューを表示するには、TrackPopupMenu関数を使用します。プロトタイプを検討してください。
int WINAPI TrackPopupMenu( _In_ HMENU hMenu, _In_ UINT uFlags, _In_ int x, _In_ int y, _In_ int nReserved, _In_ HWND hWnd, _In_opt_ const RECT *prcRect)
ご覧のとおり、メニューを所有するHWNDウィンドウとメニュー自体のハンドルもあります。 ただし、インターセプトを実装する前に、呼び出されたときにこの関数が受け取るパラメーターを見てみましょう。 API Monitorアプリケーションを使用します。 メーカーのウェブサイト
API Monitorからダウンロードできます。 APIモニターで関数のブレークポイントを構成した後、パワーユーザーメニューを開き、次のようなウィンドウを取得しようとします。

呼び出しから、エクスプローラーがTPM_RETURNCMDフラグを使用してコンテキストメニューを開くことは明らかです。つまり、選択したアイテムを決定するWM_COMMANDタイプのメッセージを検索する必要はありません。 ユーザーが指定した要素は、TrackPopupMenu関数自体によって返されます。ユーザーが何も選択していない場合は0が返されます。
API呼び出しのインターセプトを整理するには、
ミニフックライブラリを使用し
ます 。 ただし、オリジナルでは、Boostをプルします。 Boostにバインドされていないバージョンは、記事の付録に記載されています。
以下は、インターセプトされた関数のコードです。
int WINAPI HookedTrackPopupMenu( _In_ HMENU hMenu, _In_ UINT uFlags, _In_ int x, _In_ int y, _In_ int nReserved, _In_ HWND hWnd, _In_opt_ const RECT *prcRect) { WCHAR className[250]; int command; GetClassName(hWnd,className,250); int cpount = GetMenuItemCount(hMenu); if(wcscmp(L"ImmersiveSwitchList",className) == 0 && !isInizialized) { HMENU hsubMenu = CreatePopupMenu(); InsertMenu(hsubMenu, 0, MF_BYPOSITION | MF_STRING, 23, L"Item"); InsertMenu(hMenu, 0, MF_BYPOSITION | MF_POPUP , (UINT_PTR)hsubMenu, L"Group"); isInizialized = true; } command = originalTrackMenu(hMenu, uFlags, x, y, nReserved, hWnd, prcRect); switch (command) { case 23 : { MessageBoxA(hWnd, "Test", "Test", MB_OK+MB_ICONINFORMATION); return 0; break; } default: { break; } } return command; }
ここでわかるように、コンテキストメニューが必要な場所、つまりImmersiveSwitchListクラスのあるウィンドウで呼び出されることを確認します。 ウィンドウクラスの値は、Visual Studioに付属のSpy ++ユーティリティを使用して設定されました。 次に、コンテキストメニューを展開し、元の出力関数を呼び出して、操作の結果を待ちます。 メニュー項目を選択すると、MessageBoxがトリガーされます。 次のスクリーンショットは、変更されたパワーユーザーメニューの外観を示しています。

おわりに
dllインジェクションとAPI関数のインターセプトを使用して、Windowsエクスプローラーのコンテキストメニューを変更する可能性を検討しました。 同様に、Windowsエクスプローラーまたは他のプロセスのコンテキストでメニューをインターセプトできます。 ただし、TPM_RETURNCMDフラグなしでメニューが呼び出される場合、作成した要素の選択が正しく処理され、既存の機能の操作が中断されないように、親ウィンドウのウィンドウプロシージャも展開する必要があります。 これは、SetWindowLongPtr関数APIを使用して実装し、拡張関数にポインターを渡し、親ウィンドウプロシージャに制御を戻すことも忘れないでください。
この記事のソースはVisual Studio 2012で作成され、
DllInject.zipで入手できます。
英語のファイルシステムレベルでのパワーユーザーメニューの編集に関する記事へのリンクもあり
ます。Windows8のWinKey + Xパワーユーザーメニューにシャットダウン、再起動オプションを追加します。PSシステムプログラミングの専門家ではないため、不正確な場合があります。