![]() |
![]() |
[SDK32]Win32 でアプリケーションを列挙する方法最終更新日: 1998/02/18文書番号: J041632 |
この資料は以下について記述したものです。
概要
Win32 プログラミングで共通する問題の一つに、すべての "アプリケーション" を列挙することがあります。Windows NT 4.0 のタスク マネージャーがそのよい例です。Windows NT 4.0 のタスク マネージャは、2 種類の方法で "アプリケーション" をリストします。タスク マネージャの最初のタブでは、デスクトップ上にあるすべての "アプリケーション ウインドウ" をリストします。タスク マネージャの 2 つ目のタブでは、システム中のすべての "プロセス" をリストします。この資料では、この 2 種類のタスクを Windows 95 と Windows NT の両方で行う方法について詳しく説明します。 詳細
トップレベル ウインドウの列挙
プロセスの列挙と、デスクトップ上のトップレベル ウインドウの列挙を比較した場合、トップレベル ウインドウを列挙する方が比較的簡単です。Windows NT および Windows 95 プラットフォームでトップレベル ウインドウを列挙するには、EnumWindows() 関数を使用します。GetWindow() でウインドウのリストを作成しないようにしてください。Z オーダー変更によって混乱を招き、ウインドウを損失する可能性があるためです。 プロセスの列挙
システム中のプロセスのリスト作成は、ウインドウの列挙に比べると多少複雑です。これは、システム中のプロセスのリスト作成を行う API 関数が Windows 95 と Windows NT では根本的に異なっているためです。Windows 95 では、ToolHelp32 という API グループの関数を使用しなければいけません。Windows NT では、PSAPI.DLL の関数を使用します。この DLL は Platform SDK で入手することができます。この資料では、Windows 95 および Windows NT の両方の場合について説明します。また、Windows NT と Windows 95 の両方で動作する "EnumProcs()" という ラッパー関数のサンプルも紹介します。 CreateToolhelp32Snapshot(), Process32First(), and Process32Next().ToolHelp32 関数を使う最初のステップは、システム中の情報の "スナップショット" をとることです。これには CreateToolhelp32Snapshot() 関数を使用します。この関数を使うと、スナップショットに格納する情報のタイプを指定することができます。プロセス情報も必要なときは、TH32CS_SNAPPROCESS フラグを指定するようにしてください。この関数は HANDLE を返します。このハンドルを使い終わった後は、CloseHandle() に忘れずに呼び出すようにしてください。 次は、スナップショットからプロセスのリストを取得します。まず Process32First を 1 回呼び出し、続いて Process32Next を繰り返し呼び出します。Process32Next はスナップショット中のプロセス リストすべてに対して繰り返し呼び出します。Process32Next が FALSE を返すまで続けます。これらの関数は、どちらもパラメータとしてスナップショットを示すハンドルをとり、さらに PROCESSENTRY32 構造体を示すポインタもとります。 Process32First または Process32Next 呼び出しの後、PROCESSENTRY32 構造体にはシステム中の 1 つのプロセスに関する有益な情報が格納されます。プロセス ID は、この構造体の th32ProcessID メンバにあります。このプロセス ID を OpenProcess() API に渡すと、プロセスを示すハンドルを取得することができます。プロセスの実行可能ファイルとパスは、同じ構造体の szExeFile メンバに格納されています。その他の有益な情報も、この構造体に格納されています。 注意: Process32First() を呼び出す前に、dwSize メンバに sizeof(PROCESSENTRY32) をセットするのを忘れないで下さい。Windows NT および PSAPI.DLL: Windows NT でプロセスのリストを作成する場合は、PSAPI.DLL で提供する関数を使用します。PSAPI.DLL ファイルは Platform SDK で配布されます。Platform SDK は、http://www.microsoft.com/msdn/sdk より入手することができます。その他に必要な PSAPI.H および PSAPI.LIB も Platform SDK に含まれています。 PSAPI.DLL 中の関数を使用するには、PSAPI.LIB ファイルをプロジェクトに追加し、PSAPI.DLL 関数を呼び出すすべてのモジュールに PSAPI.H ファイルをインクルードします。PSAPI.DLL を使う実行可能ファイルでは、PSAPI.DLL を忘れずに配布するようにしてください。PSAPI.DLL は現在はオペレーティング システムでは配布していません。 ToolHelp32 関数同様、PSAPI.DLL にも便利な関数がいろいろと含まれています。ただし、今回はプロセス列挙に関連する次の関数のみを紹介します。 EnumProcesses(), EnumProcessModules(), GetModuleFileNameEx(), GetModuleBaseName().EnumProcesses() は、システム中のプロセス リストを作成するときに使用する最初の関数です。この関数の宣言は次のようになっています。 BOOL EnumProcesses( DWORD *lpidProcess, DWORD cb, DWORD *cbNeeded );EnumProcesses() は DWORD 配列( lpidProcess )と 配列のサイズ( cb )を示すポインタおよび DWORD を示すポインタをとり、返されるデータ (cbNeeded) の長さを返します。DWORD 配列は、システム中のプロセスのプロセス ID を格納します。この DWORD パラメータ( cbNeeded )を示すポインタは、使用されている配列のサイズを返します。次の計算処理では、いくつのプロセス ID が返されたかを計算します。 nReturned = cbNeeded / sizeof(DWORD)1 つ注意しなければいけないことがあります。ドキュメントでは、このとき返される DWORD を "cbNeeded" と呼んでいますが、実際には配列に渡された値のサイズを見積もる方法は存在していません。EnumProcesses() は、cb パラメータに渡した配列値のサイズを超える値は、cbNeeded には返しません。そのため EnumProcesses() を確実に成功させる唯一の方法として、cbNeeded = cb で返ると、より大きい配列を確保し、cbNeeded < cb となるように何度も同じ処理を繰り返します。 これでシステム中の各プロセス ID に対して配列が作成されました。目的がプロセスの名前を取得することであれば、まずここでハンドルを取得します。プロセス ID からハンドルを取得するときは、OpenProcess() を使用します。 ハンドルを取得したら、プロセスの "最初" のモジュールを取得する必要があります。あるプロセスの最初のモジュールを取得するには、パラメータを次のように指定して EnumProcessModules() を呼び出します。 EnumProcessModules( hProcess, &hModule, sizeof(hModule), &cbReturned );この呼び出しの結果、プロセスの最初のモジュールを示すハンドルが hModule 変数に格納されます。プロセスには名前がありませんが、プロセス中の最初のモジュールはそのプロセスの実行可能モジュールとなることができるということを覚えておいてください。 これで、プロセスの実行可能モジュールのパス名、モジュール名を取得するための準備が整いました。これには、hModule を使って GetModuleFileNameEx()、GetModuleBaseName() を呼び出します。どちらの関数も、プロセスを示すハンドル、モジュールを示すハンドル、さらに名前、バッファのサイズを返すバッファ ポインタをとります。 この処理を EnumProcesses() が返す各プロセス ID すべてに対して行います。その結果、Windows NT 上のプロセスのリストが完成します。 16 ビット プロセス: Windows 95 の 16 ビット アプリケーションは、ToolHelp32 に関する限りは 32 ビット アプリケーションとほぼ同等です。16 ビット アプリケーションにも、Win32 アプリケーション同様、プロセス ID があります。しかし Windows NT の場合は、状況は異なります。 Windows NT 上で実行される 16 ビット アプリケーションは、いわゆる仮想DOS マシン( VDM )で動作します。EnumProcesses はシステム中の 16 ビット アプリケーションを認識することができませんが、16 ビット EXE が実行されている 32 ビット NTVDM プロセスを返します。Windows NT 上で動作する 16 ビット アプリケーションを列挙するには、VDMEnumTaskWOWEx() という関数を使わなければいけません。ソース モジュールに VDMDBG.H をインクルードし、プロジェクトには VDMDBG.LIB ファイルをリンクしてください。これら 2 つのファイルは Platform SDK で出荷されています。 この関数の宣言は次のようになっています。 INT WINAPI VDMEnumTaskWOWEx( DWORD dwProcessId, TASKENUMPROCEX fp, LPARAM lparam );dwProcessId には、列挙したい 16 ビット タスクを含む NTVDM プロセスの ID が入ります。fp パラメータは、コールバック関数 "enum" を示すポインタです。また lparam パラメータはユーザー定義の lparam で、enum 関数へ渡されます。 "enum" 関数は、次のように定義してください。 BOOL WINAPI Enum16( DWORD dwThreadId, WORD hMod16, WORD hTask16, PSZ pszModName, PSZ pszFileName, LPARAM lpUserDefined );この関数は、VDMEnumTaskWOWEx() へ渡される NTVDM プロセスで動作する1つの 16 ビットタスクに対して1回呼ばれます。列挙を継続したい場合は FALSE を返し、列挙を終了したい場合は TRUE を返します。 注意: これは EnumWindows() とは逆なので注意してください。 サンプル コード
以下のサンプル コードは PSAPI.DLL と ToolHelp32 を "EnumProcs()" という 1 つの関数にカプセル化したものです。この関数は EnumWindows() と同様の働きをします。つまり関数へのポインタを使い、システム中の各プロセス毎に関数を繰り返し呼び出します。この関数の宣言は次のように行います。 BOOL WINAPI EnumProcs( PROCENUMPROC lpProc, LPARAM lParam );この関数を使う場合は、コールバック関数を次のように宣言してください。 BOOL CALLBACK Proc( DWORD dw, WORD w16, LPCSTR lpstr, LPARAM lParam );dw パラメータには ID が入り、w16 には 16 ビット タスク番号または 32 ビット プロセスの場合は 0 が入ります。( Windows 95 では常にゼロが入ります。) lpstr パラメータにはファイル名を示すポインタが入り、lParam はユーザー定義の lParam で EnumProcs() に渡されます。 EnumProcs() 関数は、明示的にリンクすることで ToolHelp32 および PSAPI.DLL 関数を使用します。通常の暗黙のリンクではないのでご注意ください。これは、EnumProcs() 関数を含むコードが、Windows NT と Windows 95 でバイナリ互換できるようにするためです。( ToolHelp32 関数を暗黙のリンクで使用すると、Windows NT では EXE のロードに失敗し実行することができません。) /********************* EnumProc.h *********************/ #include <windows.h> typedef BOOL (CALLBACK *PROCENUMPROC)( DWORD, WORD, LPSTR, LPARAM ) ; BOOL WINAPI EnumProcs( PROCENUMPROC lpProc, LPARAM lParam ) ; /********************* EnumProc.c (or .cpp) *********************/ #include "EnumProc.h" #include <tlhelp32.h> #include <vdmdbg.h> typedef struct { DWORD dwPID ; PROCENUMPROC lpProc ; DWORD lParam ; BOOL bEnd ; } EnumInfoStruct ; BOOL WINAPI Enum16( DWORD dwThreadId, WORD hMod16, WORD hTask16, PSZ pszModName, PSZ pszFileName, LPARAM lpUserDefined ) ; // EnumProcs 関数はコールバック関数を示すポインタをとります。 // このコールバック関数は、プロセス EXE のファイル名とプロセス ID を提供する // システム中の、プロセスごとに1回呼ばれます。 // コールバック関数の定義: // BOOL CALLBACK Proc( DWORD dw, LPCSTR lpstr, LPARAM lParam ) ; // // lpProc - コールバック ルーチンのアドレス。 // // lParam - コールバック ルーチンに渡されるユーザー定義の LPARAM 値。 // BOOL WINAPI EnumProcs( PROCENUMPROC lpProc, LPARAM lParam ) { OSVERSIONINFO osver ; HINSTANCE hInstLib ; HINSTANCE hInstLib2 ; HANDLE hSnapShot ; PROCESSENTRY32 procentry ; BOOL bFlag ; LPDWORD lpdwPIDs ; DWORD dwSize, dwSize2, dwIndex ; HMODULE hMod ; HANDLE hProcess ; char szFileName[ MAX_PATH ] ; EnumInfoStruct sInfo ; // ToolHelp 関数のポインタ。 HANDLE (WINAPI *lpfCreateToolhelp32Snapshot)(DWORD,DWORD) ; BOOL (WINAPI *lpfProcess32First)(HANDLE,LPPROCESSENTRY32) ; BOOL (WINAPI *lpfProcess32Next)(HANDLE,LPPROCESSENTRY32) ; // PSAPI 関数のポインタ。 BOOL (WINAPI *lpfEnumProcesses)( DWORD *, DWORD cb, DWORD * ); BOOL (WINAPI *lpfEnumProcessModules)( HANDLE, HMODULE *, DWORD, LPDWORD ); DWORD (WINAPI *lpfGetModuleFileNameEx)( HANDLE, HMODULE, LPTSTR, DWORD ); // VDMDBG 関数のポインタ。 INT (WINAPI *lpfVDMEnumTaskWOWEx)( DWORD, TASKENUMPROCEX fp, LPARAM );
// 実行されているのが Windows 95 か Windows NT かを確認します。 osver.dwOSVersionInfoSize = sizeof( osver ) ; if( !GetVersionEx( &osver ) ) { return FALSE ; } // Windows NT の場合: if( osver.dwPlatformId == VER_PLATFORM_WIN32_NT ) { // ライブラリをロードし、プロシージャを明示的に取得します。 // これは、PSAPI.DLL への参照を解決できないという理由で // Windows 95 でのロードが失敗しないようにするための配慮です。 hInstLib = LoadLibraryA( "PSAPI.DLL" ) ; if( hInstLib == NULL ) return FALSE ; hInstLib2 = LoadLibraryA( "VDMDBG.DLL" ) ; if( hInstLib2 == NULL ) return FALSE ; // プロシージャのアドレスを取得します。 lpfEnumProcesses = (BOOL(WINAPI *)(DWORD *,DWORD,DWORD*)) GetProcAddress( hInstLib, "EnumProcesses" ) ; lpfEnumProcessModules = (BOOL(WINAPI *)(HANDLE, HMODULE *, DWORD, LPDWORD)) GetProcAddress( hInstLib, "EnumProcessModules" ) ; lpfGetModuleFileNameEx =(DWORD (WINAPI *)(HANDLE, HMODULE, LPTSTR, DWORD )) GetProcAddress( hInstLib, "GetModuleFileNameExA" ) ; lpfVDMEnumTaskWOWEx =(INT(WINAPI *)( DWORD, TASKENUMPROCEX, LPARAM))GetProcAddress( hInstLib2, "VDMEnumTaskWOWEx" ); if( lpfEnumProcesses == NULL || lpfEnumProcessModules == NULL || lpfGetModuleFileNameEx == NULL || lpfVDMEnumTaskWOWEx == NULL) { FreeLibrary( hInstLib ) ; FreeLibrary( hInstLib2 ) ; return FALSE ; } // PSAPI 関数の EnumProcesses を呼び出して、現在システム中にある // すべての ProcID を取得します。 // 注意: ドキュメントには、EnumProcesses の第三パラメータは // cbNeeded であると書かれていますが、これはこの関数を 1度呼び出すと // 確保するバッファのサイズを確認することができ、2度目の呼び出しで // バッファにデータを格納することができるということを暗に意味してい // ます。 // この説明はこのケースには当てはまりません。CbNeeded パラメータは、 // 返された PID の数を返します。そのためバッファ サイズがゼロの // 場合は、cbNeeded はゼロを返します。 // 注意: "HeapAlloc" のループは、システム中のすべての PID に対して // 適切なサイズのバッファを確実に割り当てるための対策です。 dwSize2 = 256 * sizeof( DWORD ) ; lpdwPIDs = NULL ; do { if( lpdwPIDs ) { HeapFree( GetProcessHeap(), 0, lpdwPIDs ) ; dwSize2 *= 2 ; } lpdwPIDs = HeapAlloc( GetProcessHeap(), 0, dwSize2 ); if( lpdwPIDs == NULL ) { FreeLibrary( hInstLib ) ; FreeLibrary( hInstLib2 ) ; return FALSE ; } if( !lpfEnumProcesses( lpdwPIDs, dwSize2, &dwSize ) ) { HeapFree( GetProcessHeap(), 0, lpdwPIDs ) ; FreeLibrary( hInstLib ) ; FreeLibrary( hInstLib2 ) ; return FALSE ; } }while( dwSize == dwSize2 ) ; // 取得した ProcID の数は? dwSize /= sizeof( DWORD ) ; // 各 ProcID に対してループ。 for( dwIndex = 0 ; dwIndex < dwSize ; dwIndex++ ) { szFileName[0] = 0 ; // プロセスをオープンします。(セキュリティによってシステム中の // 各プロセスにアクセスできない可能性があります。) hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, lpdwPIDs[ dwIndex ] ) ; if( hProcess != NULL ) { // ここで EnumProcessModules を呼び出して、プロセス中の最初の // モジュールのみを取得します。これは、次にフル パス名を取得する // ための .EXE モジュールとなるためとても重要な処理です。 if( lpfEnumProcessModules( hProcess, &hMod, sizeof( hMod ), &dwSize2 ) ) { // フル パス名を取得します。 if( !lpfGetModuleFileNameEx( hProcess, hMod, szFileName, sizeof( szFileName ) ) ) { szFileName[0] = 0 ; } } CloseHandle( hProcess ) ; } // OpenProcess の成功の有無に関係なく、ProcID を使って enum 関数を // 呼ぶことができます。 if(!lpProc( lpdwPIDs[dwIndex], 0, szFileName, lParam)) break ; // NTVDM はありましたか? if( _stricmp( szFileName+(strlen(szFileName)-9), "NTVDM.EXE")==0) { // 16 ビット enum proc の情報を格納します。 sInfo.dwPID = lpdwPIDs[dwIndex] ; sInfo.lpProc = lpProc ; sInfo.lParam = lParam ; sInfo.bEnd = FALSE ; // 16 ビット スタッフを列挙します。 lpfVDMEnumTaskWOWEx( lpdwPIDs[dwIndex], (TASKENUMPROCEX) Enum16, (LPARAM) &sInfo); // メイン enum 関数は終了していますか? if(sInfo.bEnd) break ; } } HeapFree( GetProcessHeap(), 0, lpdwPIDs ) ; FreeLibrary( hInstLib2 ) ; // Windows 95 の場合: }else if( osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ) {
hInstLib = LoadLibraryA( "Kernel32.DLL" ) ; if( hInstLib == NULL ) return FALSE ; // プロシージャのアドレスを取得します。 // Kernel32 のこれらの関数を明示的にリンクしています。そうしないと // このコードを使用するモジュールは、 Kernel32 に Toolhelp32 を // 持たない Windows NT 上ではロードに失敗するためです。 lpfCreateToolhelp32Snapshot= (HANDLE(WINAPI *)(DWORD,DWORD)) GetProcAddress( hInstLib, "CreateToolhelp32Snapshot" ) ; lpfProcess32First= (BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32)) GetProcAddress( hInstLib, "Process32First" ) ; lpfProcess32Next= (BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32)) GetProcAddress( hInstLib, "Process32Next" ) ; if( lpfProcess32Next == NULL || lpfProcess32First == NULL || lpfCreateToolhelp32Snapshot == NULL ) { FreeLibrary( hInstLib ) ; return FALSE ; } // システム プロセスの Toolhelp スナップショットを示すハンドルを // 取得します。 hSnapShot = lpfCreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 ) ; if( hSnapShot == INVALID_HANDLE_VALUE ) { FreeLibrary( hInstLib ) ; return FALSE ; } // 最初のプロセスの情報を取得します。 procentry.dwSize = sizeof(PROCESSENTRY32) ; bFlag = lpfProcess32First( hSnapShot, &procentry ) ; // プロセスがある間、ループを繰り返します。 while( bFlag ) { // ファイル名と ProcID を使って enum 関数を呼び出します。 if(lpProc( procentry.th32ProcessID, 0, procentry.szExeFile, lParam )) { procentry.dwSize = sizeof(PROCESSENTRY32) ; bFlag = lpfProcess32Next( hSnapShot, &procentry ); }else bFlag = FALSE ; }
}else return FALSE ; // ライブラリを開放します。 FreeLibrary( hInstLib ) ; return TRUE ; } BOOL WINAPI Enum16( DWORD dwThreadId, WORD hMod16, WORD hTask16, PSZ pszModName, PSZ pszFileName, LPARAM lpUserDefined ) { BOOL bRet ; EnumInfoStruct *psInfo = (EnumInfoStruct *)lpUserDefined ; bRet = psInfo->lpProc( psInfo->dwPID, hTask16, pszFileName, psInfo->lParam ) ; if(!bRet) { psInfo->bEnd = TRUE ; } return !bRet; } |
|
|
© 2001 Microsoft Corporation. All rights reserved. Terms of Use. |