青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

旅途

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

縱談進程枚舉

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

代碼下載:說明 ProcessSpy.zip

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

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

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

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

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

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

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

圖1 演示程序之用戶界面

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

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

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

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

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

    // 遍歷拍下來的所有進程
    if (Process32First(hProcessSnap, &pe32))
    {
        do
        {
            if (pe32.th32ProcessID && strcmp(pe32.szExeFile, "System"))
            {
                // 保存進程的名字、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)
{
    // 給指定的進程調(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;
}

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

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

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

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

我們的演示程序提供了完整的代碼實現(xiàn):
BOOL CPSApiSpy::BuildProcessList(void)
{
    // 枚舉獲得系統(tǒng)中的所有進程的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++)
    {
        // 保存進程的PID
        CProcessItem  processItem;
        processItem.SetProcessId(processes[i]);

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

BOOL CPSApiSpy::BuildModuleList(CProcessItem& inProcess)
{
    // 根據(jù)PID打開該進程,獲得一個進程操作句柄
    HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
        FALSE, inProcess.GetProcessId());
    if (hProcess)
    {
        HMODULE modules[1024];
        DWORD   needed;
        // 枚舉當(dāng)前進程調(diào)用的所有模塊
        if (EnumProcessModules(hProcess, modules, sizeof(modules), &needed))
        {
            char szName[MAX_PATH] = "";
            inProcess.CleanupModuleList();
            DWORD actualModuleCount = needed / sizeof(DWORD);
            // 獲得各個模塊文件的全路徑
            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中實現(xiàn)。程序開發(fā)中,我們需要包含頭文件Psapi.h,連接庫文件Psapi.lib。這些文件在安裝了微軟的Platform SDK后就可獲得。

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

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

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

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

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

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

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

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

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

我們的演示程序提供了完整的代碼實現(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)
{
    // 步驟一:從特定的注冊表路徑下獲取系統(tǒng)中所有的對象、計數(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;
    // 獲得裝載所有計數(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”的對象以及名為“ID Process”的計數(shù)器
        // 獲取它們的索引值(因為對象、計數(shù)器在性能數(shù)據(jù)中是靠索引來標識的)
        p = (LPSTR) buffer;
        while (*p)
        {
            if (p > (LPSTR) buffer)
            {
                for (p2 = p - 2; _istdigit(*p2); p2--)
                    ;
            }

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

        // 步驟三:獲取進程對象的所有性能數(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ù)塊開頭以四個字符“PERF”標識
            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;
            }

            // 如果緩沖不夠大,擴大緩沖后再試
            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);
        // 步驟四:在進程對象數(shù)據(jù)的計數(shù)器定義部分尋找“ID Process”計數(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++;
        }
        
        // 步驟五:遍歷所有實例,獲取實例的名字(即進程名)以及PID
        TCHAR  szProcessName[MAX_PATH];
        pInst = (PPERF_INSTANCE_DEFINITION) ((DWORD)pObj + pObj->DefinitionLength);
        for (i = 0; i < (DWORD)pObj->NumInstances; i++)
        {
            // 獲取進程名
            p  = (LPSTR) ((DWORD)pInst + pInst->NameOffset);
            rt = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)p, -1, szProcessName,
                sizeof(szProcessName), NULL, NULL);
            // 獲取進程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;
}

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

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

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

我們的演示程序提供了完整的代碼實現(xiàn):
BOOL CPDHSpy::BuildProcessList(void)
{
    LPTSTR      szCounterListBuffer     = NULL;
    DWORD       dwCounterListSize       = 0;
    LPTSTR      szInstanceListBuffer    = NULL;
    DWORD       dwInstanceListSize      = 0;
    
    BOOL pass = FALSE;
    // 第一次調(diào)用PdhEnumObjectItems以獲取需要的列表長度
    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”對象的所有計數(shù)器和實例
        pdhStatus = PdhEnumObjectItems(NULL, NULL, TEXT("Process"),
            szCounterListBuffer, &dwCounterListSize, szInstanceListBuffer,
            &dwInstanceListSize, PERF_DETAIL_WIZARD, 0);    
        if (pdhStatus == ERROR_SUCCESS)
        {
            pass = TRUE;
            LPTSTR  pInst = szInstanceListBuffer;
            // 獲得每個實例名,也就是進程名
            for (; *pInst != 0;    pInst += lstrlen(pInst) + 1)
            {
                if (strcmp(pInst, "System") && strcmp(pInst, "Idle") &&
                    strcmp(pInst, "_Total"))
                {
                    CProcessItem  processItem;
                    // 獲得進程的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)
{
    // 打開一個查詢對象
    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)前實例的“ID Process”計數(shù)器的值
    DWORD                  ctrType;
    PDH_FMT_COUNTERVALUE   fmtValue;
    pdhStatus = PdhGetFormattedCounterValue(hCounter, PDH_FMT_LONG,
        &ctrType, &fmtValue);

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

    return fmtValue.longValue;
}

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

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

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

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

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

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

正文完

附件:

  • 說明 ProcessSpy.zip
  • 說明 AppUI.JPG
  • 說明 PDF1.jpg
  • 說明 PDF2.jpg
  • 說明 PDF3.jpg
  • 說明 APPClass.jpg

  • posted on 2007-07-22 02:00 旅途 閱讀(997) 評論(1)  編輯 收藏 引用 所屬分類: 深入windows

    青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲一区二区在线播放| 制服丝袜激情欧洲亚洲| 欧美第一黄网免费网站| 欧美高清hd18日本| 欧美激情精品久久久久久变态| 激情亚洲成人| 亚洲欧洲av一区二区三区久久| 日韩一区二区精品葵司在线| 久久久激情视频| 亚洲一级一区| 亚洲美女在线一区| 久久国产精彩视频| 亚洲少妇诱惑| 久久影视精品| 欧美一级片久久久久久久| 香蕉久久夜色精品| 欧美亚洲免费在线| 宅男精品视频| 免费日韩av| 亚洲一区视频在线| 欧美视频一区二区三区| 中日韩美女免费视频网址在线观看| 亚洲国产精品嫩草影院| 免费看成人av| 亚洲免费电影在线| 99国产精品一区| 国产精品av免费在线观看| 亚洲一区二区伦理| 亚洲制服少妇| 黄色精品一区二区| 亚洲第一精品在线| 欧美精品一区二区三区久久久竹菊 | 亚洲影视在线| 亚洲一区精品电影| 国产一区日韩一区| 亚洲承认在线| 国产精品国产a级| 久久人人爽人人爽爽久久| 女人香蕉久久**毛片精品| 一区二区欧美日韩视频| 亚洲综合色自拍一区| 尹人成人综合网| 亚洲精品综合在线| 国产一区二区三区免费观看| 欧美激情bt| 国产精品毛片在线| 欧美成人在线影院| 国产精品成人va在线观看| 欧美综合国产| 欧美激情一二三区| 久久久综合香蕉尹人综合网| 欧美精品自拍| 久久亚洲精品一区| 国产精品久久国产愉拍 | 国产资源精品在线观看| 欧美国产视频日韩| 国产欧美日韩亚洲| 亚洲欧洲日本专区| 国产亚洲欧洲| 日韩一二三区视频| 在线观看av一区| 亚洲午夜精品一区二区| 亚洲国产天堂久久国产91| 亚洲制服av| 一区二区三区视频在线| 久久久久久午夜| 午夜久久一区| 欧美日韩爆操| 蜜桃久久精品一区二区| 国产欧美日韩精品a在线观看| 欧美护士18xxxxhd| 午夜精品一区二区三区在线| 亚洲精品视频免费在线观看| 欧美一区影院| 午夜视频久久久| 欧美午夜不卡视频| 亚洲激情欧美| 亚洲国产精品久久久| 久久av老司机精品网站导航| 亚洲欧美激情视频在线观看一区二区三区 | 艳女tv在线观看国产一区| 狠狠干综合网| 欧美亚洲三区| 欧美一区亚洲二区| 国产精品久久久久久亚洲调教 | 奶水喷射视频一区| 国产午夜亚洲精品羞羞网站| 亚洲无限av看| 亚洲男人第一网站| 欧美亚洲成人精品| 亚洲图片在线观看| 午夜欧美精品久久久久久久| 国产精品九九| 亚洲欧美一区二区三区在线| 香蕉久久一区二区不卡无毒影院 | 亚洲国产美女精品久久久久∴| 在线观看一区二区精品视频| 欧美亚洲三区| 久久在线观看视频| 欧美精品亚洲精品| 亚洲黄色小视频| 99香蕉国产精品偷在线观看| 猛干欧美女孩| 亚洲开发第一视频在线播放| 在线中文字幕一区| 国产精品观看| 久久se精品一区精品二区| 久久综合99re88久久爱| 在线观看亚洲一区| 欧美另类一区| 亚洲一区二区欧美| 久久夜色撩人精品| 91久久精品国产91久久性色tv| 欧美成人精品福利| 99www免费人成精品| 午夜精品免费视频| 狠狠爱综合网| 欧美日韩999| 欧美一区二区三区婷婷月色| 免播放器亚洲| 正在播放亚洲一区| 国产日韩在线一区| 欧美v亚洲v综合ⅴ国产v| 这里只有精品视频| 久久一日本道色综合久久| 91久久午夜| 国产精品成人在线| 美女精品在线| 午夜欧美视频| 亚洲成在人线av| 国产婷婷色一区二区三区| 可以免费看不卡的av网站| aa亚洲婷婷| 免费一级欧美在线大片| 亚洲天堂av电影| 在线观看成人小视频| 欧美视频久久| 六月婷婷久久| 午夜一区二区三区在线观看| 亚洲黄色av| 久久综合五月| 亚洲欧美中文在线视频| 亚洲国产裸拍裸体视频在线观看乱了中文 | 亚洲天堂久久| 欧美激情精品久久久久久大尺度| 亚洲欧美激情视频| 日韩一区二区福利| 影音先锋一区| 国产日韩欧美亚洲一区| 欧美日韩日日骚| 久久综合久久综合久久| 午夜精品免费在线| 一本色道久久综合亚洲精品不卡 | 国产精品久久久久久妇女6080| 久久精品视频网| 亚洲视频在线观看免费| 亚洲欧洲一区二区天堂久久| 久久尤物电影视频在线观看| 亚洲免费在线观看| 99视频国产精品免费观看| 亚洲成人在线观看视频| 国产一区二区三区av电影| 国产精品白丝黑袜喷水久久久| 欧美精品123区| 欧美阿v一级看视频| 久久久伊人欧美| 久久精品91| 欧美一级淫片aaaaaaa视频| 亚洲一二三区精品| 中日韩高清电影网| 一区二区三区视频观看| 亚洲免费激情| 日韩视频专区| 中文日韩电影网站| 这里是久久伊人| 亚洲自拍电影| 欧美亚洲三级| 久久九九精品99国产精品| 久久国内精品自在自线400部| 午夜视频精品| 久久精品免费电影| 久热re这里精品视频在线6| 狼狼综合久久久久综合网| 蜜桃av一区二区在线观看| 裸体一区二区| 欧美精品 日韩| 国产精品99一区二区| 国产精品自在线| 韩日精品中文字幕| 亚洲国产毛片完整版 | 亚洲日本一区二区三区| 欧美激情二区三区| 亚洲人体1000| 一区二区三欧美| 亚洲欧美日韩国产综合| 欧美一站二站| 久久综合国产精品| 欧美日韩蜜桃| 国产视频在线观看一区二区三区| 国产自产2019最新不卡|