• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            旅途

            如果想飛得高,就該把地平線忘掉

            縱談進(jìn)程枚舉

            轉(zhuǎn)自:http://www.blog.edu.cn/user2/33587/archives/2005/254906.shtml

            代碼下載:說(shuō)明 ProcessSpy.zip

            當(dāng)程序出現(xiàn)異常而失去響應(yīng),我們通常的做法是打開(kāi)Windows任務(wù)管理器強(qiáng)行將其“殺死”。Windows任務(wù)管理器是個(gè)好東西,它能顯示當(dāng)前系統(tǒng)中運(yùn)行的所有進(jìn)程,以及它們的實(shí)時(shí)性能參數(shù)。但是作為程序員,你知道這些功能是怎么實(shí)現(xiàn)的嗎?

            “這 有什么難的?!”你可能會(huì)說(shuō),“不就是調(diào)用那幾個(gè)進(jìn)程枚舉函數(shù)嘛!”是啊,單純實(shí)現(xiàn)Windows任務(wù)管理器類似的功能是不難。但是,你先別急,關(guān)于進(jìn)程 枚舉,可能你只知其一,不知其二;更何況,我們這里還有其三、其四。除此之外,我們這里還要增強(qiáng)功能,顯示與各個(gè)進(jìn)程相關(guān)聯(lián)的模塊(即DLL,動(dòng)態(tài)鏈接 庫(kù))信息。

            進(jìn)程與DLL的基礎(chǔ)知識(shí)
            大家知道,Windows 98/2000/XP都是多任務(wù)操作系統(tǒng)。所謂多任務(wù),就是系統(tǒng)中可以同時(shí)運(yùn)行多個(gè)進(jìn)程。而所謂進(jìn)程,就是應(yīng)用程序的運(yùn)行實(shí)例。通俗地講,進(jìn)程就是一個(gè)運(yùn)行起來(lái)的.EXE程序。

            系統(tǒng)中的進(jìn)程都用一個(gè)DWORD類型的數(shù)據(jù)來(lái)唯一標(biāo)識(shí),我們稱之為PID。即使同一個(gè)應(yīng)用程序運(yùn)行多個(gè)實(shí)例,它們的PID也是不一樣的。另外,進(jìn)程擁有自己私有的虛擬地址空間,進(jìn)程與進(jìn)程之間不會(huì)相互干擾;每個(gè)進(jìn)程都至少包含一條線程。

            那么,DLL與進(jìn)程又有什么關(guān)系呢?大家知道,自Windows誕生之日起,Windows操作系統(tǒng)就使用DLL來(lái)支持公共函數(shù)調(diào)用。DLL中實(shí)現(xiàn)的函數(shù)代碼不出現(xiàn)在.EXE文件中,但可以被各個(gè)進(jìn)程所使用。

            使用DLL的好處包括:
            1)    可以顯著地減小每個(gè)組件的大小(特別是對(duì)于一些大型軟件系統(tǒng))。
            2)    使升級(jí)更為簡(jiǎn)單。如果我們想要使用新版本的函數(shù),改變DLL中的函數(shù)后,只需重新編譯DLL項(xiàng)目,然后再連接使用該函數(shù)的各個(gè)應(yīng)用程序;而應(yīng)用程序本身不需要重新編譯。
            3)    便于功能模塊化,乃至開(kāi)發(fā)任務(wù)的團(tuán)隊(duì)協(xié)作。

            一般來(lái)說(shuō),一個(gè)進(jìn)程總是調(diào)用這個(gè)或那個(gè)DLL中的函數(shù)。進(jìn)程與DLL是一種依賴關(guān)系。在我們的演示程序中,我們不僅要做進(jìn)程枚舉,我們還要來(lái)揭示進(jìn)程與DLL的這種依賴關(guān)系。演示程序的用戶界面如下:

            圖1 演示程序之用戶界面

            好了,言歸正轉(zhuǎn),我們直奔主題。接下去,我們就來(lái)逐一介紹各種進(jìn)程枚舉方法。

            方法一:使用工具庫(kù)(Tool Help Library)函數(shù)
            這是一種歷史最悠久、也是最基本的方法(從Windows 95開(kāi)始就支持這種方法)。這些API函數(shù)中,最重要的當(dāng)屬CreateToolhelp32Snapshot,它的函數(shù)原型如下:
            HANDLE WINAPI CreateToolhelp32Snapshot(
              DWORD dwFlags,      
              DWORD th32ProcessID  
            );

            這 個(gè)函數(shù)的功能就是給系統(tǒng)拍“快照”。拍照的對(duì)象由參數(shù)dwFlags決定,比如dwFlags值為TH32CS_SNAPPROCESS表示對(duì)象為系統(tǒng)中 的所有進(jìn)程,值為TH32CS_SNAPMODULE表示對(duì)象為由th32ProcessID參數(shù)指定的進(jìn)程調(diào)用的所有模塊(也就是DLL)。

            當(dāng) 調(diào)用CreateToolhelp32Snapshot函數(shù)給指定的對(duì)象拍完快照之后,我們就可以使用Process32First、 Process32Next、Module32First、Module32Next等函數(shù)進(jìn)行“取片”工作了,就是遍歷剛才拍下來(lái)的所有進(jìn)程、進(jìn)程調(diào)用 的所有模塊。

            我們的演示程序提供了完整的代碼實(shí)現(xiàn):
            BOOL CToolHelpSpy::BuildProcessList(void)
            {
                // 給系統(tǒng)中所有進(jìn)程拍快照
                HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
                if (hProcessSnap == INVALID_HANDLE_VALUE)
                {
                    return FALSE;
                }
                
                PROCESSENTRY32 pe32 = {0};
                pe32.dwSize = sizeof(PROCESSENTRY32);

                // 遍歷拍下來(lái)的所有進(jìn)程
                if (Process32First(hProcessSnap, &pe32))
                {
                    do
                    {
                        if (pe32.th32ProcessID && strcmp(pe32.szExeFile, "System"))
                        {
                            // 保存進(jìn)程的名字、PID
                            CProcessItem  processItem;
                            processItem.SetProcessName(pe32.szExeFile);    
                            processItem.SetProcessId(pe32.th32ProcessID);
                            // 加入列表保存
                            mProcList.AddTail(processItem);
                        }
                    } while (Process32Next(hProcessSnap, &pe32));
                }
                CloseHandle(hProcessSnap);

                return TRUE;
            }

            BOOL CToolHelpSpy::BuildModuleList(CProcessItem& inProcess)
            {
                // 給指定的進(jìn)程調(diào)用的所有模塊拍快照
                HANDLE hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,
                    inProcess.GetProcessId());
                if (hModuleSnap == INVALID_HANDLE_VALUE)
                {
                    return FALSE;
                }

                MODULEENTRY32 me32 = {0};
                me32.dwSize = sizeof(MODULEENTRY32);
                
                inProcess.CleanupModuleList();
                // 遍歷所有模塊
                if (Module32First(hModuleSnap, &me32))
                {
                    do
                    {
                        // 保存模塊文件全路徑
                        inProcess.AddModuleItem(me32.szExePath);
                    } while (Module32Next(hModuleSnap, &me32));
                }
                CloseHandle(hModuleSnap);

                return TRUE;
            }

            注:工具庫(kù)函數(shù)在Kernel32.dll中實(shí)現(xiàn)。程序開(kāi)發(fā)中,我們需要包含頭文件Tlhelp32.h,連接庫(kù)文件Kernel32.lib。

            注: 我們這里使用自定義類CProcessItem來(lái)描述一個(gè)進(jìn)程,它保存了進(jìn)程的名字、PID等信息,另外還維持一個(gè)該進(jìn)程調(diào)用的所有模塊的列表。相應(yīng)地, 我們也使用一個(gè)自定義類CModuleItem來(lái)描述模塊,它保存模塊文件的全路徑、版本號(hào)、文件大小、說(shuō)明信息、所屬產(chǎn)品名等。(下同)

            方法二:使用PSAPI (Process Status API)函數(shù)
            這是一種Windows NT/2000下的方法。核心是使用EnumProcesses函數(shù)。它的原型如下:
            BOOL EnumProcesses(
              DWORD *lpidProcess,    // 用于保存所有進(jìn)程的PID的數(shù)組
              DWORD cb,                     // 上述數(shù)組的大小
              DWORD *cbNeeded        // PID數(shù)組中實(shí)際返回的(有效)字節(jié)數(shù)
            );

            當(dāng) 獲得系統(tǒng)中所有進(jìn)程的PID后,我們就可以使用OpenProcess函數(shù)打開(kāi)指定的進(jìn)程,再調(diào)用GetModuleBaseName獲得該進(jìn)程的名字, 調(diào)用EnumProcessModules枚舉該進(jìn)程調(diào)用的所有模塊,調(diào)用GetModuleFileNameEx獲得模塊文件的全路徑。

            我們的演示程序提供了完整的代碼實(shí)現(xiàn):
            BOOL CPSApiSpy::BuildProcessList(void)
            {
                // 枚舉獲得系統(tǒng)中的所有進(jìn)程的PID
                DWORD  processes[1024], needed;
                if (!EnumProcesses(processes, sizeof(processes), &needed))
                {
                    return FALSE;
                }

                char  szName[MAX_PATH]   = "";
                DWORD actualProcessCount = needed / sizeof(DWORD);
                for (DWORD i = 0; i < actualProcessCount; i++)
                {
                    // 保存進(jìn)程的PID
                    CProcessItem  processItem;
                    processItem.SetProcessId(processes[i]);

                    // 打開(kāi)當(dāng)前進(jìn)程以獲得進(jìn)程操作句柄
                    HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
                        FALSE, processes[i]);
                    if (hProcess)
                    {
                        HMODULE hModule;
                        DWORD   needed;
                        // 枚舉當(dāng)前進(jìn)程調(diào)用的所有模塊
                        if (EnumProcessModules(hProcess, &hModule, sizeof(hModule), &needed))
                        {
                            // 獲得并保存進(jìn)程的名字
                            GetModuleBaseName(hProcess, hModule, szName, sizeof(szName));
                            processItem.SetProcessName(szName);    
                            mProcList.AddTail(processItem);
                        }
                        CloseHandle(hProcess);
                    }
                }
                return TRUE;
            }

            BOOL CPSApiSpy::BuildModuleList(CProcessItem& inProcess)
            {
                // 根據(jù)PID打開(kāi)該進(jìn)程,獲得一個(gè)進(jìn)程操作句柄
                HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
                    FALSE, inProcess.GetProcessId());
                if (hProcess)
                {
                    HMODULE modules[1024];
                    DWORD   needed;
                    // 枚舉當(dāng)前進(jìn)程調(diào)用的所有模塊
                    if (EnumProcessModules(hProcess, modules, sizeof(modules), &needed))
                    {
                        char szName[MAX_PATH] = "";
                        inProcess.CleanupModuleList();
                        DWORD actualModuleCount = needed / sizeof(DWORD);
                        // 獲得各個(gè)模塊文件的全路徑
                        for (DWORD i = 1; i < actualModuleCount; i++)
                        {
                            GetModuleFileNameEx(hProcess, modules[i], szName, sizeof(szName));
                            inProcess.AddModuleItem(szName);
                        }
                    }
                    CloseHandle(hProcess);
                }
                
                return TRUE;
            }

            注:PSAPI函數(shù)在Psapi.dll中實(shí)現(xiàn)。程序開(kāi)發(fā)中,我們需要包含頭文件Psapi.h,連接庫(kù)文件Psapi.lib。這些文件在安裝了微軟的Platform SDK后就可獲得。

            方法三:利用系統(tǒng)收集的性能數(shù)據(jù)(Performance Data)
            這也是一種Windows NT/2000下的方法。首先,我們需要介紹一些關(guān)于性能監(jiān)視(Performance Monitoring)的背景知識(shí)。

            所 謂性能監(jiān)視,實(shí)際上是Windows NT/2000提供的一種系統(tǒng)功能,它能實(shí)時(shí)采集、分析系統(tǒng)內(nèi)的應(yīng)用程序、服務(wù)、驅(qū)動(dòng)程序等的性能數(shù)據(jù),以此來(lái)分析系統(tǒng)的瓶頸、監(jiān)視組件的表現(xiàn),最終幫助 用戶進(jìn)行系統(tǒng)的合理調(diào)配。這里,還要引入一個(gè)性能對(duì)象(Performance Object)的概念,即被監(jiān)視者。一般系統(tǒng)中的性能對(duì)象包括處理器(Processor)、進(jìn)程(Process)、線程(Thread)、網(wǎng)絡(luò)通訊 (如TCP、UDP、ICMP、IP等)、系統(tǒng)服務(wù)(如ACS/RSVP Service)等。(本文我們關(guān)心的是進(jìn)程,即名為“Process”的對(duì)象。)下面,我們給出系統(tǒng)性能數(shù)據(jù)的結(jié)構(gòu)參考圖:

            圖2 系統(tǒng)性能數(shù)據(jù)的結(jié)構(gòu)

            性 能對(duì)象有兩種:一種只支持一個(gè)實(shí)例,另一種支持多個(gè)實(shí)例。(我們關(guān)心的進(jìn)程對(duì)象支持多個(gè)實(shí)例,而每個(gè)實(shí)例對(duì)應(yīng)系統(tǒng)中的一個(gè)進(jìn)程。)一個(gè)對(duì)象可以有多個(gè)性能 指標(biāo);每個(gè)性能指標(biāo)都用一個(gè)計(jì)數(shù)器(Counter)來(lái)記錄。就進(jìn)程對(duì)象而言,它擁有的計(jì)數(shù)器種類包括ID Process(進(jìn)程的PID)、Thread Count(線程數(shù))、Priority Base(進(jìn)程優(yōu)先級(jí))、IO Read Bytes/sec(每秒IO讀取字節(jié)數(shù))、IO Writer Bytes/sec(每秒IO寫出字節(jié)數(shù))等。(本文我們只關(guān)心ID Process計(jì)數(shù)器的值。)

            支持單一實(shí)例的對(duì)象數(shù)據(jù)結(jié)構(gòu)如下(也就是圖2中各個(gè)對(duì)象數(shù)據(jù)塊的展開(kāi)形式):

            圖3 支持單一實(shí)例的對(duì)象數(shù)據(jù)結(jié)構(gòu)

            支持多實(shí)例的對(duì)象數(shù)據(jù)結(jié)構(gòu)如下(增加了各個(gè)實(shí)例的定義部分):

            圖4 支持多實(shí)例的對(duì)象數(shù)據(jù)結(jié)構(gòu)

            知 道了性能數(shù)據(jù)結(jié)構(gòu),接下去我們?cè)趺磥?lái)讀取它們呢?最基本的方法就是通過(guò)注冊(cè)表函數(shù),如RegOpenKeyEx、RegQueryValueEx、 RegCloseKey等。值得注意的是,這里雖然使用的是注冊(cè)表函數(shù),但性能數(shù)據(jù)并不存儲(chǔ)在注冊(cè)表數(shù)據(jù)庫(kù)中;讀取性能數(shù)據(jù)時(shí)調(diào)用函數(shù) RegOpenKeyEx,主鍵值應(yīng)該是HKEY_PERFORMANCE_DATA。而當(dāng)性能數(shù)據(jù)獲得之后,根據(jù)各部分?jǐn)?shù)據(jù)結(jié)構(gòu)的定義,計(jì)算偏移量,我 們就能獲取我們感興趣的數(shù)據(jù)了。

            我們的演示程序提供了完整的代碼實(shí)現(xiàn):
            #define INITIAL_SIZE        51200
            #define EXTEND_SIZE         25600
            #define REGKEY_PERF         _T("Software\\Microsoft\\Windows NT\\Currentversion\\Perflib")
            #define REGSUBKEY_COUNTERS  _T("Counters")
            #define PROCESS_COUNTER     _T("process")
            #define PROCESSID_COUNTER   _T("id process")

            BOOL CPerformanceSpy::BuildProcessList(void)
            {
                // 步驟一:從特定的注冊(cè)表路徑下獲取系統(tǒng)中所有的對(duì)象、計(jì)數(shù)器的名字
                LANGID lid = MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL);
                TCHAR  szSubKey[1024];
                _stprintf(szSubKey, _T("%s\\%03x"), REGKEY_PERF, lid);
                HKEY  hSubKey;
                DWORD rt = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szSubKey, 0,
                    KEY_READ, &hSubKey);
                if (rt != ERROR_SUCCESS)
                {
                    return FALSE;
                }

                DWORD  dwType = 0;
                DWORD  dwSize = 0;
                LPBYTE buffer = NULL;
                BOOL   pass = FALSE;
                // 獲得裝載所有計(jì)數(shù)器名字的緩沖大小
                rt = RegQueryValueEx(hSubKey, REGSUBKEY_COUNTERS, NULL,
                    &dwType, NULL, &dwSize);
                if (rt == ERROR_SUCCESS)
                {
                    buffer = (LPBYTE) malloc(dwSize);
                    memset(buffer, 0, dwSize);
                    rt = RegQueryValueEx(hSubKey, REGSUBKEY_COUNTERS, NULL,
                        &dwType, buffer, &dwSize);
                }

                LPSTR  p, p2;
                DWORD  dwProcessIdTitle;
                DWORD  dwProcessIdCounter;
                PPERF_DATA_BLOCK             pPerf;
                PPERF_OBJECT_TYPE            pObj;
                PPERF_INSTANCE_DEFINITION    pInst;
                PPERF_COUNTER_BLOCK          pCounter;
                PPERF_COUNTER_DEFINITION     pCounterDef;
                if (rt == ERROR_SUCCESS)
                {
                    pass = TRUE;
                    // 步驟二:查找名為“Process”的對(duì)象以及名為“ID Process”的計(jì)數(shù)器
                    // 獲取它們的索引值(因?yàn)閷?duì)象、計(jì)數(shù)器在性能數(shù)據(jù)中是靠索引來(lái)標(biāo)識(shí)的)
                    p = (LPSTR) buffer;
                    while (*p)
                    {
                        if (p > (LPSTR) buffer)
                        {
                            for (p2 = p - 2; _istdigit(*p2); p2--)
                                ;
                        }

                        if (_tcsicmp(p, PROCESS_COUNTER) == 0)
                        {
                            // 獲取“Process”對(duì)象的索引
                            for (p2 = p - 2; _istdigit(*p2); p2--)
                                ;
                            _tcscpy(szSubKey, p2+1);
                        }
                        else if (stricmp(p, PROCESSID_COUNTER) == 0)
                        {
                            // 獲取“ID Process”計(jì)數(shù)器的索引
                            for (p2 = p - 2; _istdigit(*p2); p2--)
                                ;
                            dwProcessIdTitle = atol(p2 + 1);
                        }
                        // Point to the next string
                        p += (_tcslen(p) + 1);
                    }

                    // 步驟三:獲取進(jìn)程對(duì)象的所有性能數(shù)據(jù)
                    free(buffer);
                    buffer = NULL;
                    dwSize = INITIAL_SIZE;
                    buffer = (LPBYTE) malloc(dwSize);
                    memset(buffer, 0, dwSize);
                    while (pass)
                    {
                        rt = RegQueryValueEx(HKEY_PERFORMANCE_DATA, szSubKey, NULL,
                            &dwType, buffer, &dwSize);
                        pPerf = (PPERF_DATA_BLOCK) buffer;
                        // 性能數(shù)據(jù)塊開(kāi)頭以四個(gè)字符“PERF”標(biāo)識(shí)
                        if ((rt == ERROR_SUCCESS) && (dwSize > 0) &&
                            pPerf->Signature[0] == (WCHAR)'P' &&
                            pPerf->Signature[1] == (WCHAR)'E' &&
                            pPerf->Signature[2] == (WCHAR)'R' &&
                            pPerf->Signature[3] == (WCHAR)'F')
                        {
                            break;
                        }

                        // 如果緩沖不夠大,擴(kuò)大緩沖后再試
                        if (rt == ERROR_MORE_DATA)
                        {
                            dwSize += EXTEND_SIZE;
                            buffer  = (LPBYTE) realloc(buffer, dwSize );
                            memset(buffer, 0, dwSize );
                        }
                        else
                        {
                            pass = FALSE;
                        }
                    }
                }

                if (pass)
                {
                    pObj = (PPERF_OBJECT_TYPE) ((DWORD)pPerf + pPerf->HeaderLength);
                    // 步驟四:在進(jìn)程對(duì)象數(shù)據(jù)的計(jì)數(shù)器定義部分尋找“ID Process”計(jì)數(shù)器
                    pCounterDef = (PPERF_COUNTER_DEFINITION) ((DWORD)pObj + pObj->HeaderLength);
                    for (DWORD i = 0; i < (DWORD)pObj->NumCounters; i++)
                    {
                        if (pCounterDef->CounterNameTitleIndex == dwProcessIdTitle)
                        {
                            dwProcessIdCounter = pCounterDef->CounterOffset;
                            break;
                        }
                        pCounterDef++;
                    }
                    
                    // 步驟五:遍歷所有實(shí)例,獲取實(shí)例的名字(即進(jìn)程名)以及PID
                    TCHAR  szProcessName[MAX_PATH];
                    pInst = (PPERF_INSTANCE_DEFINITION) ((DWORD)pObj + pObj->DefinitionLength);
                    for (i = 0; i < (DWORD)pObj->NumInstances; i++)
                    {
                        // 獲取進(jìn)程名
                        p  = (LPSTR) ((DWORD)pInst + pInst->NameOffset);
                        rt = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)p, -1, szProcessName,
                            sizeof(szProcessName), NULL, NULL);
                        // 獲取進(jìn)程PID
                        pCounter = (PPERF_COUNTER_BLOCK) ((DWORD)pInst + pInst->ByteLength);
                        DWORD processId = *((LPDWORD) ((DWORD)pCounter + dwProcessIdCounter));
                        if (strcmp(szProcessName, "System") && processId)
                        {
                            CProcessItem  processItem;
                            processItem.SetProcessId(processId);
                            processItem.SetProcessName(szProcessName);    
                            mProcList.AddTail(processItem);
                        }
                        // Point to the next process
                        pInst = (PPERF_INSTANCE_DEFINITION) ((DWORD)pCounter + pCounter->ByteLength);
                    }
                }

                if (buffer)
                {
                    free(buffer);
                    buffer = NULL;
                }
                RegCloseKey(hSubKey);
                RegCloseKey(HKEY_PERFORMANCE_DATA);
                return pass;
            }

            注:方法三用到的僅僅是注冊(cè)表操作函數(shù),而這些函數(shù)在advapi32.dll中實(shí)現(xiàn)。程序開(kāi)發(fā)中,我們需要包含頭文件winperf.h。另外,該方法中各個(gè)進(jìn)程所調(diào)用的模塊,仍然使用方法二的PSAPI函數(shù)獲得,這里就不再列出。

            方法四:使用PDH (Performance Data Helper)函數(shù)
            這種方法的底層實(shí)現(xiàn)跟方法三其實(shí)是一樣的。但我們看到,方法三實(shí)現(xiàn)起來(lái)非常繁瑣。為了簡(jiǎn)化應(yīng)用,PDH函數(shù)對(duì)方法三的實(shí)現(xiàn)進(jìn)行了一層封裝。我們這里的進(jìn)程枚舉,主要使用PdhEnumObjectItems函數(shù),它的函數(shù)原型如下:
            PDH_STATUS PdhEnumObjectItems(
              LPCTSTR szDataSource,                      // 數(shù)據(jù)源
              LPCTSTR szMachineName,                 // 機(jī)器名
              LPCTSTR szObjectName,                    // 對(duì)象名
              LPTSTR mszCounterList,                    // 計(jì)數(shù)器列表
              LPDWORD pcchCounterListLength,   // 計(jì)數(shù)器列表長(zhǎng)度
              LPTSTR mszInstanceList,                    // 實(shí)例列表
              LPDWORD pcchInstanceListLength,   // 實(shí)例列表長(zhǎng)度
              DWORD dwDetailLevel,                      // 獲取信息的級(jí)別
              DWORD dwFlags                                 // 保留為0
            );

            對(duì) 于每一個(gè)獲得的進(jìn)程實(shí)例,我們還要得到它的PID,也就是得到“ID Process”計(jì)數(shù)器的值。這時(shí),我們會(huì)用到其他的PDH函數(shù),包括:PdhOpenQuery、PdhAddCounter、 PdhCollectQueryData、PdhGetFormattedCounterValue、PdhCloseQuery等。

            我們的演示程序提供了完整的代碼實(shí)現(xiàn):
            BOOL CPDHSpy::BuildProcessList(void)
            {
                LPTSTR      szCounterListBuffer     = NULL;
                DWORD       dwCounterListSize       = 0;
                LPTSTR      szInstanceListBuffer    = NULL;
                DWORD       dwInstanceListSize      = 0;
                
                BOOL pass = FALSE;
                // 第一次調(diào)用PdhEnumObjectItems以獲取需要的列表長(zhǎng)度
                PDH_STATUS pdhStatus = PdhEnumObjectItems(NULL, NULL, TEXT("Process"),
                    szCounterListBuffer, &dwCounterListSize, szInstanceListBuffer,
                    &dwInstanceListSize, PERF_DETAIL_WIZARD, 0);
                if (pdhStatus == ERROR_SUCCESS)
                {
                    szCounterListBuffer  = (LPTSTR) malloc((dwCounterListSize * sizeof (TCHAR)));
                    szInstanceListBuffer = (LPTSTR) malloc((dwInstanceListSize * sizeof (TCHAR)));
                    // 第二次調(diào)用PdhEnumObjectItems
            // 獲得“Process”對(duì)象的所有計(jì)數(shù)器和實(shí)例
                    pdhStatus = PdhEnumObjectItems(NULL, NULL, TEXT("Process"),
                        szCounterListBuffer, &dwCounterListSize, szInstanceListBuffer,
                        &dwInstanceListSize, PERF_DETAIL_WIZARD, 0);    
                    if (pdhStatus == ERROR_SUCCESS)
                    {
                        pass = TRUE;
                        LPTSTR  pInst = szInstanceListBuffer;
                        // 獲得每個(gè)實(shí)例名,也就是進(jìn)程名
                        for (; *pInst != 0;    pInst += lstrlen(pInst) + 1)
                        {
                            if (strcmp(pInst, "System") && strcmp(pInst, "Idle") &&
                                strcmp(pInst, "_Total"))
                            {
                                CProcessItem  processItem;
                                // 獲得進(jìn)程的PID
                                processItem.SetProcessId(GetPIDCounterValue(pInst));
                                processItem.SetProcessName(pInst);    
                                mProcList.AddTail(processItem);
                            }
                        }
                    }
                }

                if (szCounterListBuffer != NULL)
                {
                    free(szCounterListBuffer);
                    szCounterListBuffer = NULL;
                }
                if (szInstanceListBuffer != NULL)
                {
                    free(szInstanceListBuffer);
                    szInstanceListBuffer = NULL;
                }
                return pass;
            }

            DWORD CPDHSpy::GetPIDCounterValue(LPTSTR inInstanceName)
            {
                // 打開(kāi)一個(gè)查詢對(duì)象
                HQUERY   hQuery   = NULL;
                PDH_STATUS pdhStatus = PdhOpenQuery (0, 0, &hQuery);

                HCOUNTER hCounter = NULL;
                char szPathBuffer[MAX_PATH];
                sprintf(szPathBuffer, "\\Process(%s)\\ID Process", inInstanceName);
                pdhStatus = PdhAddCounter(hQuery, szPathBuffer, 0, &hCounter);
                pdhStatus = PdhCollectQueryData(hQuery);

                // 獲得當(dāng)前實(shí)例的“ID Process”計(jì)數(shù)器的值
                DWORD                  ctrType;
                PDH_FMT_COUNTERVALUE   fmtValue;
                pdhStatus = PdhGetFormattedCounterValue(hCounter, PDH_FMT_LONG,
                    &ctrType, &fmtValue);

                // 關(guān)閉查詢對(duì)象
                pdhStatus = PdhCloseQuery (hQuery);

                return fmtValue.longValue;
            }

            注:PDH函數(shù)在Pdh.dll中實(shí)現(xiàn)。程序開(kāi)發(fā)中,我們需要包含頭文件Pdh.h,連接庫(kù)文件Pdh.lib。

            演示程序說(shuō)明
            我們的演示程序使用VC6.0開(kāi)發(fā)完成,是一個(gè)基于對(duì)話框的MFC程序。程序設(shè)計(jì)秉承OOP風(fēng)格,以及用戶界面(User Interface)與業(yè)務(wù)邏輯(Business Logic)分離的原則,結(jié)構(gòu)簡(jiǎn)單、條理清晰,相信大家很容易能夠讀懂代碼。

            由于本文總共介紹了四種進(jìn)程枚舉的方法,我們?cè)O(shè)計(jì)了如下一個(gè)邏輯控制類繼承結(jié)構(gòu):

            圖5 演示程序邏輯控制類結(jié)構(gòu)

            另外,演示程序?qū)τ谶M(jìn)程調(diào)用的模塊采用了延后枚舉(Lazy Enumerating)的策略,即在程序啟動(dòng)的時(shí)候并沒(méi)有將所有進(jìn)程調(diào)用的模塊都枚舉好,而僅在需要的時(shí)候進(jìn)行。這樣可以顯著節(jié)省程序啟動(dòng)的時(shí)間。

            寫在最后
            進(jìn)程隱藏(與其相對(duì)的就是進(jìn)程枚舉)一直是一個(gè)很熱門的話題,思路有很多,其中有一種就是攔截系統(tǒng)API函數(shù)EnumProcesses的調(diào)用。通讀本文后,你覺(jué)得這種思路可行嗎?或者你有了其他新的想法!這些都是筆者寫作此文的初衷。

            正文完

            附件:

          1. 說(shuō)明 ProcessSpy.zip
          2. 說(shuō)明 AppUI.JPG
          3. 說(shuō)明 PDF1.jpg
          4. 說(shuō)明 PDF2.jpg
          5. 說(shuō)明 PDF3.jpg
          6. 說(shuō)明 APPClass.jpg

          7. posted on 2007-07-22 02:00 旅途 閱讀(975) 評(píng)論(1)  編輯 收藏 引用 所屬分類: 深入windows

            国産精品久久久久久久| 久久久久香蕉视频| 久久99国产乱子伦精品免费| 欧美牲交A欧牲交aⅴ久久| 97久久精品午夜一区二区| 国产精品热久久毛片| 综合久久一区二区三区 | 一本色道久久88加勒比—综合| 久久国产成人午夜aⅴ影院| 97精品伊人久久久大香线蕉| 国产国产成人精品久久| 久久无码av三级| 伊人久久无码中文字幕| 岛国搬运www久久| 婷婷伊人久久大香线蕉AV| 国产亚洲美女精品久久久| 色婷婷综合久久久久中文| 久久se精品一区二区影院| 久久久国产精品网站| 亚洲国产另类久久久精品小说| 久久久久亚洲AV无码专区网站| 国产精品99久久99久久久| 久久精品卫校国产小美女| 久久久久亚洲精品男人的天堂| 久久精品国产一区| 久久综合精品国产二区无码| 久久久精品久久久久特色影视| 久久97精品久久久久久久不卡| 亚洲综合精品香蕉久久网| 大香伊人久久精品一区二区| 久久久久无码国产精品不卡| 久久99精品九九九久久婷婷| 国产—久久香蕉国产线看观看| 久久青青草原国产精品免费 | 韩国三级中文字幕hd久久精品| 久久精品www人人爽人人| 日韩久久久久久中文人妻| 三上悠亚久久精品| 亚洲精品蜜桃久久久久久| 日韩av无码久久精品免费| 亚洲AV无码1区2区久久|