)VC枚舉系統當前進程

來源:互聯網
上載者:User

 

四種方法實現VC枚舉系統當前進程

    在Windows 2000以上的MS作業系統,通過Windows的工作管理員可以列出當前系統的所有活動進程(1所示),在Windows XP中,更是在控制台下增加了一條Tasklist命令,讓系統下的所有進程無所遁行(2所示)。這一切是怎麼實現的呢?

引用侯捷大師在《深入淺出MFC》的一句話,“知其然而不知其所以然,真不是個好辦法”。既然如此,我們乾脆自己動手,自己通過編程來實現吧,這樣很有成就感哦!以下所有代碼均在Windows XP SP1+VC6.0 sp6編譯環境下通過。Ok,Let’ go!

方法一

第一種方法是大家比較熟悉的通過ToolHelp Service提供的API函數來實現。這裡用到了3個關鍵的函數:CreateToolhelp32Snapshot(),Process32First()和Process32Next()。下面給出了關於這三個函數的原形和參數說明;

HANDLE WINAPI CreateToolhelp32Snapshot(
  DWORD dwFlags, //系統快照要查看的資訊類型
  DWORD th32ProcessID      //值0表示當前進程
);
BOOL WINAPI Process32First(
  HANDLE hSnapshot,        //CreateToolhelp32Snapshot()建立的快照控制代碼
  LPPROCESSENTRY32 lppe  //指向進程入口結構
);
BOOL WINAPI Process32Next(
  HANDLE hSnapshot,        //這裡參數同Process32First
  LPPROCESSENTRY32 lppe  //同上
);

首先使用CreateToolhelp32Snapshot()建立系統快照控制代碼(hprocess是我們聲明用來儲存建立的快照控制代碼):

hProcess=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
然後調用Process32First()獲得系統快照中的第一個進程資訊(Report是BOOL型作為判斷系統快照中下一條進程記錄):

 report=Process32First(hProcess,pinfo);
接著用一個迴圈調用來遍曆系統中所有啟動並執行進程:

 while(report)
 {
 hModule=CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,pinfo->th32ProcessID);
  Module32First(hModule, minfo); 
  GetShortPathName(minfo->szExePath,shortpath,256);
  printf("%s --- %s/n",pinfo->szExeFile,shortpath);
  report=Process32Next(hProcess, pinfo);   
 }

筆者曾通過對Pstools工具包裡的Pslist.exe反編譯,發現該工具用的就是這種方法。如果你查詢MSDN,可以找到一個比這個功能更加完善的來源程式。

方法二

 第二種方法也很常見,通過MSDN就可以找到例子代碼,它是通過Psapi.dll提供的API函數EnumProcesses和EnumProcessModules來實現。有一點要說明的是,Visual Studio提供的SDK包裡沒有提供相應的Psapi.h和與之相對應的匯入庫,筆者當時就很納悶,MSDN裡的例子居然編譯不通,後來才發現,編譯時間根本找不到“psapi.h”。呵呵,還好,MSDN至少告訴我們他們都包含Psapi.dll裡,用VC內建的Depend工具一查,果然。這樣就好辦了,我們可以自己找到這些函數入口地址。

小知識:C也好C++也好,一般的函數名本質上都是一個地址,在Win32的API裡,它是指向函數所在Dll模組裡函數實現的入口地址。

下面是第二種方法的實現過程:首先,先把Psapi.dll裡要用到函數都定義好,方便後面顯示調用。

//在psaipi.dll中的函數EnumProcesses用來枚舉進程 
typedef BOOL (_stdcall *ENUMPROCESSES)(  //注意這裡要指明呼叫慣例為-stdcall
 DWORD* pProcessIds,  //指向進程ID數組鏈  
 DWORD cb,    //ID數組的大小,用位元組計數
 DWORD* pBytesReturned);   //返回的位元組
//在psapi.dll中的函數EnumProcessModules用來枚舉進程模組
typedef BOOL (_stdcall *ENUMPROCESSMODULES)(
 HANDLE hProcess,   //進程控制代碼
 HMODULE* lphModule, //指向模組控制代碼數組鏈
 DWORD cb,    //模組控制代碼數組大小,位元組計數
 LPDWORD lpcbNeeded);   //儲存所有模組控制代碼所需的位元組數
//在psapi.dll中的函數GetModuleFileNameEx獲得進程模組名
typedef DWORD (_stdcall *GETMODULEFILENAMEEX)(
 HANDLE hProcess,   //進程控制代碼
 HMODULE hModule,   //進程控制代碼
 LPTSTR lpFilename,   //存放模組全路徑名
 DWORD nSize    //lpFilename緩衝區大小,字元計算
);

好,通過每個定義的函數前的注釋你可以清楚看到這些原函數名,然後當然要載入包含這些函數模組Psapi.dll啦:
 hPsDll = LoadLibrary("PSAPI.DLL");
接著,通過Dll入口,我們尋找每個函數地址:

pEnumProcesses  = 
(ENUMPROCESSES)GetProcAddress(hPsDll, "EnumProcesses");
pEnumProcessModules  =
(ENUMPROCESSMODULES)GetProcAddress(hPsDll, "EnumProcessModules");
pGetModuleFileNameEx  =
   (GETMODULEFILENAMEEX)GetProcAddress(hPsDll, "GetModuleFileNameExA");
注意第三個函數名GetModuleFileNameExA,在Dll裡有以A和W結尾區分函數,A指採用的是ANSI字串方式,W則是UNICODE方式。於是,我們可以用下面的語句枚舉進程:
pEnumProcesses(processid, sizeof(processid), &needed);
 processcount=needed/sizeof(DWORD);
 
 for (i=0;i<processcount;i++)
 {
  //開啟進程
  hProcess=OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,
false, processid[i]);
  if (hProcess)
  {
   pEnumProcessModules(hProcess, &hModule, sizeof(hModule), &needed);
   pGetModuleFileNameEx(hProcess, hModule, path, sizeof(path));
   GetShortPathName(path,path,256);
   itoa(processid[i],temp,10);
   printf("%s --- %s/n",path,temp);
  }
  else
   printf("Failed!!!/n");
 }
 當然,在Google上一搜尋,可以找到不少提供的Psapi.h標頭檔和對應的Psapi.lib庫,這樣,你就可以更方便用這種方法了。

方法三

 也許你會說前兩種方法全世界都知道了,沒什麼大不了的!呵呵,那麼筆者現在介紹的第三種方法,你就未必知道了。本方法利用了Windows NT/2000下終端服務API函數WTSOpenServer()和WTSEnumerateProcess()來實現,這兩個函數都定義在Wtsapi32.dll裡。具體的關於終端服務方面的知識,大家可以查詢MSDN。
首先,我們來顯示申明這兩個函數原形:
typedef HANDLE (_stdcall *WTSOPENSERVER)(
LPTSTR pServerName //NetBios指定的終端服務名,如果我們查看本地終端所有進程資訊我們可以通過在控制台命令列下用nbtstat –an來擷取本機NetBios名。3所示。
);  

 
 
圖3

typedef BOOL (_stdcall *WTSENUMERATEPROCESSES)(
  HANDLE hServer, //WTSOpenServer返回的控制代碼 
  DWORD Reserved,  //保留值, 0
  DWORD Version,   //指定枚舉要求的版本, 必須為1
  PWTS_PROCESS_INFO* ppProcessInfo, //這個參數是關鍵,存放我們要的進程名和進程id
  DWORD* pCount   //用來存放ppProcessInfo裡WTS_PROCESS_INFO結構的數量指標
);

和前面一樣,要先裝載Wtsapi32.dll模組,擷取關鍵函數地址:

hWtsApi32 = LoadLibrary("wtsapi32.dll");
pWtsOpenServer 
  = (WTSOPENSERVER)GetProcAddress(hWtsApi32, "WTSOpenServerA");
pWtsEnumerateProcesses 
= (WTSENUMERATEPROCESSES)GetProcAddress(hWtsApi32,"WTSEnumerateProcessesA");

通過Argv[1]給終端服務名(這裡我們賦本機NetBios名)賦一個值並開啟這項服務:

char* szServerName = argv[1];
hWtsServer = pWtsOpenServer(szServerName);

然後開始遍曆終端伺服器上的所有進程,這裡我們是指原生所有進程
if(!pWtsEnumerateProcesses(hWtsServer,
       0,
       1,
       &pWtspi,
       &dwCount))
 {
  printf("enum processes error: %d/n", GetLastError());
  return;
 };
 
 for(int i=0; i<dwCount; i++)
 {
  printf("ps_Id: %d/t/tps_name: %s/n", pWtspi[i].ProcessId, pWtspi[i].pProcessName);
 }

怎麼樣,酷吧,跟前面兩種方法效果一樣!

方法四

 最後筆者介紹一種最少人用的方法,本方法是從“幻影旅團”論壇上看到的。後面給出的原始碼也是從他們那拷貝過來的。呵呵,這種方法也提供給我們一種很好的思路。

 這第四種方法利用了Native Api的NtQuerySystemInformation函數來實現。同樣沒有該函數的匯入庫,也要自己定義原形。整個實現不難,可是有點煩,因為在該函數參數結構上,筆者通查MSDN查了很久才找到這些相關的結構,下面我們來看看這個方法的實現吧。
先是自訂函數原形:

typedef NTSTATUS (__stdcall *PZWQUERYSYSTEMINFORMATION) 
                 (IN SYSTEM_INFORMATION_CLASS SystemInformationClass,  
                  IN OUT PVOID SystemInformation,  
                  IN ULONG SystemInformationLength,  
                  OUT PULONG ReturnLength);
然後,還是和前面一樣,要找到ZwQuerySystemInformation在Ntdll.dll模組裡的入口地址:

hModule = GetModuleHandle((LPCTSTR)"ntdll.dll");
pZwQuerySystemInformation =(PZWQUERYSYSTEMINFORMATION)
GetProcAddress(hModule, (LPCTSTR)"ZwQuerySystemInformation");
獲得指向進程資訊數組鏈的第一條進程資訊:
pSystemProcessInformation = (SYSTEM_PROCESS_INFORMATION *)pvProcessList;

和方法一方法二一樣,在獲得第一條進程資訊後,開始迴圈遍曆,列出其餘的進程:
while (TRUE) 
    { 
        if (pSystemProcessInformation->NextEntryDelta == 0) //如果是最後一條,則終止迴圈。
            break;                                   

        pSystemProcessInformation=(SYSTEM_PROCESS_INFORMATION*)((PCHAR)pSystemProcessInformation+ pSystemProcessInformation->NextEntryDelta); 
        pProcessName = (char *)malloc(pSystemProcessInformation->ProcessName.Length + 2); 
}

特別注意的是,這裡SYSTEM_PROCESS_INFORMATION結構裡進程名的類型為UNICODE_STRING, 而UNICODE_STRING裡的成員Buffer定義的是Unicode類型。
typedef struct _SYSTEM_PROCESS_INFORMATION  
{  
    DWORD NextEntryDelta;  
    DWORD dThreadCount;  
    DWORD dReserved01;  
    DWORD dReserved02;  
    DWORD dReserved03;  
    DWORD dReserved04;  
    DWORD dReserved05;  
    DWORD dReserved06;  
    FILETIME ftCreateTime; /* relative to 01-01-1601 */  
    FILETIME ftUserTime; /* 100 nsec units */  
    FILETIME ftKernelTime; /* 100 nsec units */  
    UNICODE_STRING ProcessName;      //這就是進程名
    DWORD BasePriority;  
    DWORD dUniqueProcessId;            //進程ID
    DWORD dParentProcessID;  
    DWORD dHandleCount;  
    DWORD dReserved07;  
    DWORD dReserved08;  
    DWORD VmCounters;  
    DWORD dCommitCharge;  
    PVOID ThreadInfos[1]; 
} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;

typedef struct _UNICODE_STRING { 
  USHORT Length; 
  USHORT MaximumLength; 
  PWSTR  Buffer;                 //注意,這裡為Unicode類型
} UNICODE_STRING, *PUNICODE_STRING;

所以,最後我們要調用WideCharToMultiByte()來還原成ANSI字串以便輸出:
WideCharToMultiByte(CP_ACP,  
                      0, 
                    pSystemProcessInformation->ProcessName.Buffer, 
                    pSystemProcessInformation->ProcessName.Length + 1, 
                    pProcessName, 
                    pSystemProcessInformation->ProcessName.Length + 1, 
                    NULL, 
                    NULL);
具體每個參數大家可以參照MSDN,很簡單的。以上四種方法的完整原始碼見光碟片。

 從這裡看出,查看系統當前活動進程的實現也不過如此,況且,筆者還沒有給出第五種方法(可以通過WMI的COM介面函數CoCreateInstance(),ConnectServer(),ExecQuery()等函數來實現,留給讀者自己練習),呵呵,好玩吧?居然有那麼多種方法。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.