茵蒂克絲
博主很想詳細的介紹一下進程神馬的,、內(nèi)存神馬的,,但是真的整理起來發(fā)現(xiàn)要做到讓有C基礎的學生能很好理解,一看就懂還是感覺很有難度,。也許是博主水平還不夠吧,。
作為90后程序員,博主還是不走尋常路吧,,到了進程這里開始,,我們反過來學一下,順便補一補一些基礎知識,。
上一節(jié)文件編程的時候,,有說過文件流的狀態(tài),當我們開始操作一個文件,,這個文件的打開關閉都由程序控制,,而其中的內(nèi)容是可以動態(tài)增減的,這樣的情況類似水流一般,,我們把這樣的文件狀態(tài)稱作文件流,。
那么實際上,用戶的輸入,、輸出這一類的“緩存”也就是這樣的狀態(tài),,通常是叫做輸入流和輸出流,除了這兩個還有一個錯誤流,。C語言中的標準庫(stdio.h)中有定義這三個常用的流,,也即stdin(標準輸入流)、stdout(標準輸出流)和stderr(標準錯誤流),。如果大家仔細深究 stdio.h 的頭文件會發(fā)現(xiàn),,這些流的定義跟文件的定義實際上是同樣的。
好了,,回到終點,,那么既然這些東西都已經(jīng)是 “流” 的狀態(tài)了,那么也就意味著我們不需要去打開什么文件,,來生成什么文件流,,直接可以用文件操作的函數(shù)來操作這些輸入輸出流。
例如:
1 2 | scanf ( "%d" , &i);
實際上就是 fscanf (stdin, "%d" , &i); 的簡寫而已,。
|
PS:這實際上應該算是C語言的基礎知識,,不能理解為什么大學都不教(博主觀點)
CMD 工具集
那么,開始有講過的,,windows編程其實就是去查API,然后用API,。這一節(jié)講進程,,我們就通過編寫一個學習用的cmd工具集來一步步走向進程編程,。
首先我們要從輸入流來讀取用戶的輸入,這一操作在標準庫中可以使用 fgets 函數(shù)來實現(xiàn),,調(diào)用 windows API 的話我們就用 ReadFile 就可以了,。
1 2 3 4 5 6 7 | BOOL WINAPI ReadFile(
_In_ HANDLE hFile, // 讀取的文件句柄
_Out_ LPVOID lpBuffer, // 保存讀取緩沖字符數(shù)組
_In_ DWORD nNumberOfBytesToRead, // 緩沖數(shù)組的大小
_Out_opt_ LPDWORD lpNumberOfBytesRead, // 實際讀出的大小
_Inout_opt_ LPOVERLAPPED lpOverlapped // 異步IO文件結構體
);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | #include <Windows.h>;
#include <stdio.h>;
void welcome();
int main()
{
char Command_str[MAX_PATH];
DWORD Command_len;
HANDLE hConsoleInput;
// 獲取輸出流的句柄
hConsoleInput = GetStdHandle(STD_INPUT_HANDLE);
// 輸出歡迎信息
welcome();
while (1)
{
// 清空命令字符串
memset (&Command_str, 0, MAX_PATH);
// 輸出提示符
printf ( "\nLscmd>;" );
// 讀取輸入流
ReadFile(
hConsoleInput, // 文件句柄
Command_str, // 獲取內(nèi)容的緩沖字符數(shù)組
MAX_PATH, // 緩沖數(shù)組大小
&Command_len, // 實際讀出的大小
NULL);
printf ( "接收到命令:[%s]" , Command_str);
}
}
void welcome()
{
printf ( "Lellansin's CMD Tool [版本 0.0.1]\n" );
printf ( "學習自制 (c) www. 歡迎交流\n" );
}
|
可以簡單的看到,我們的輸入都有獲取到,,并且輸出的時候還連帶我們輸?shù)幕剀嚕〒Q行符)
定制兩個簡單的命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | #include <Windows.h>;
#include <stdio.h>;
#include <string.h>; // for strcmp
#include <stdlib.h>; // for exit
void welcome();
void command_switch( char *cmd_str);
int main()
{
char Command_str[MAX_PATH];
DWORD Command_len;
HANDLE hConsoleInput;
// 獲取輸出流的句柄
hConsoleInput = GetStdHandle(STD_INPUT_HANDLE);
// 輸出歡迎信息
welcome();
while (1)
{
// 清空命令字符串
memset (&Command_str, 0, MAX_PATH);
// 輸出提示符
printf ( "\nLscmd>;" );
// 讀取輸入流
ReadFile(
hConsoleInput, // 文件句柄
Command_str, // 獲取內(nèi)容的緩沖字符數(shù)組
MAX_PATH, // 緩沖數(shù)組大小
&Command_len, // 實際讀出的大小
NULL);
command_switch(Command_str);
}
}
void command_switch( char *cmd_str)
{
char cmd_tmp[MAX_PATH]={0};
char *pstr = cmd_str, *ptmp = cmd_tmp;
// 一直賦值到換行之前
while (*pstr != '\r' && *pstr != '\n' )
{
*ptmp++ = *pstr++;
}
// printf("收到命令:[%s]\n", cmd_tmp);
// 判斷命令
if ( strcmp (cmd_tmp, "hi" ) == 0 )
{
printf ( "你好~" );
} else if ( strcmp ( cmd_tmp, "exit" ) == 0 )
{
exit (0);
} else
{
printf ( "Error: 命令未找到\n" );
}
}
void welcome()
{
printf ( "Lellansin's CMD Tool [版本 0.0.1]\n" );
printf ( "學習自制 (c) www. 歡迎交流\n" );
}
|
創(chuàng)建進程
那么到現(xiàn)在為止,,我們的cmd工具已經(jīng)有了一個基本的雛形,接下來要做的就是調(diào)用我們原來寫的命令,。
有的同學可能會想到直接用 stdlib.h 里的 system 函數(shù)來解析命令,,這個是可以的。但是這個函數(shù)是C標準庫的,,并不是windows系統(tǒng)的API,,它的效率是非常的低的。而且,,這里是在在講的是 windows 編程所以讀者請不要偷懶哦,。
首先,我們來了解一下進程的概念,。當一個程序運行起來的時候,,操作系統(tǒng)一定要為這個程序(Program)創(chuàng)建一個進程(Process),以方便管理,。有些同學也知道每個程序跑起來之后都被分配了一個進程ID,,實際上在進程調(diào)度的時候,操作系統(tǒng)還會為我們的程序分配進程的一些列事務:資源,、虛擬內(nèi)存地址空間,、系統(tǒng)調(diào)用接口、優(yōu)先級,、環(huán)境變量等等,。
所以進程實際上可以理解成一個運行起來的程序,就如進程的英文原本的意思(Process)一樣這是個運行過程,。每個運行起來的程序都要被操作系統(tǒng)安排進程(過程)的來管理,。
如果你能想象這一過程,那么就不難理解,,想要運行一個程序必須要創(chuàng)建一個進程,。所以,這里我們就需要為我們原本所寫的程序創(chuàng)建一個進程,,就可以調(diào)用了,。
1 2 3 4 5 6 7 8 9 10 11 12 | BOOL WINAPI CreateProcess(
_In_opt_ LPCTSTR lpApplicationName, // 啟動程序路徑
_Inout_opt_ LPTSTR lpCommandLine, // 啟動程序的命令行代碼
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, // 進程屬性
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, // 線程屬性
_In_ BOOL bInheritHandles, // 是否繼承句柄
_In_ DWORD dwCreationFlags, // 標識和優(yōu)先級
_In_opt_ LPVOID lpEnvironment, // 環(huán)境變量設置
_In_opt_ LPCTSTR lpCurrentDirectory, // 當前目錄設置
_In_ LPSTARTUPINFO lpStartupInfo, // 啟動信息設置
_Out_ LPPROCESS_INFORMATION lpProcessInformation // 新進程的信息
);
|
MSDN 文檔: CreateProcess function
返回值
如果函數(shù)執(zhí)行成功,返回非零值,。如果函數(shù)執(zhí)行失敗,,返回零,,可以使用GetLastError函數(shù)獲得錯誤的附加信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | #include <Windows.h>;
#include <stdio.h>;
#include <string.h>; // for strcmp
#include <stdlib.h>; // for exit
void welcome();
void command_switch( char *cmd_str);
BOOL CreateChildProcess( char *cmd_str);
int main()
{
char Command_str[MAX_PATH];
DWORD Command_len;
HANDLE hConsoleInput;
// 獲取輸出流的句柄
hConsoleInput = GetStdHandle(STD_INPUT_HANDLE);
// 輸出歡迎信息
welcome();
while (1)
{
// 清空命令字符串
ZeroMemory(&Command_str, MAX_PATH);
// 輸出提示符
printf ( "\nLscmd>;" );
// 讀取輸入流
ReadFile(
hConsoleInput, // 文件句柄
Command_str, // 獲取內(nèi)容的緩沖字符數(shù)組
MAX_PATH, // 緩沖數(shù)組大小
&Command_len, // 實際讀出的大小
NULL);
command_switch(Command_str);
}
}
void command_switch( char *cmd_str)
{
char *pstr = cmd_str;
// 遍歷到換行之前
while (*pstr != '\r' && *pstr != '\n' )
{
*pstr++;
}
// 覆蓋換行
*pstr = '\0' ;
// printf("收到命令:[%s]\n", cmd_str);
// 判斷命令
if ( strcmp (cmd_str, "hi" ) == 0 )
{
printf ( "你好~ 歡迎使用 Lellansin 的cmd工具\n" );
} else if ( strcmp ( cmd_str, "exit" ) == 0 )
{
exit (0);
} else
{
// 創(chuàng)建子進程
CreateChildProcess(cmd_str);
}
}
BOOL CreateChildProcess( char *cmd_str)
{
STARTUPINFO start_info;
PROCESS_INFORMATION process_info;
BOOL flag;
// 將啟動信息結構清零 ( 相當于 memset 0, 不過效率更高 )
ZeroMemory( &start_info, sizeof (start_info) );
// 設置結構大小,,cb屬性應為結構的大小
start_info.cb = sizeof (start_info);
// 將進程信息結構清零
ZeroMemory( &process_info, sizeof (process_info) );
flag = CreateProcess(
NULL, // 不傳程序路徑, 使用命令行
cmd_str, // 命令行命令
NULL, // 不繼承進程句柄(默認)
NULL, // 不繼承線程句柄(默認)
FALSE, // 不繼承句柄(默認)
0, // 沒有創(chuàng)建標志(默認)
NULL, // 使用默認環(huán)境變量
NULL, // 使用父進程的目錄
&start_info, // STARTUPINFO 結構
&process_info ); // PROCESS_INFORMATION 保存相關信息
if ( !flag )
{
// 創(chuàng)建失敗
printf ( "Error: 命令未找到 (%d).\n" , GetLastError() );
return 0;
}
// 等待子進程結束
// 使用到了通過 PROCESS_INFORMATION 結構體獲取子進程的句柄 hProcess
WaitForSingleObject( process_info.hProcess, INFINITE );
// 關閉進程句柄和線程句柄
CloseHandle( process_info.hProcess );
CloseHandle( process_info.hThread );
return 1;
}
void welcome()
{
printf ( "Lellansin's CMD Tool [版本 0.0.1]\n" );
printf ( "學習自制 (c) www. 歡迎交流\n" );
}
|
查看進程
CreateToolhelp32Snapshot
獲取當前的系統(tǒng)快照
1 2 3 4 | HANDLE WINAPI CreateToolhelp32Snapshot(
_In_ DWORD dwFlags,
_In_ DWORD th32ProcessID
);
|
dwFlags [輸入?yún)?shù)]
指明所需的系統(tǒng)快照,。該參數(shù)可以是如下列表中的一個或多個值。
Value | Meaning |
---|
TH32CS_INHERIT (0x80000000) | 表明快照 (snapshot) 句柄是可以被繼承 (inheritable) ,。 |
TH32CS_SNAPALL | 包含所有系統(tǒng)中的所有進程和線程,,加上指定進程中堆和模塊的信息( 通過th32ProcessID特別指明的進程id,如果為0則無),。相當于指定 TH32CS_SNAPHEAPLIST, TH32CS_SNAPMODULE, TH32CS_SNAPPROCESS, 和 TH32CS_SNAPTHREAD 通過或運算('|') 聯(lián)合使用 |
TH32CS_SNAPHEAPLIST (0x00000001) | 快照中包含通過 th32ProcessID 指定進程的所有堆 (heaps) 信息,。 想要列舉堆的信息,請查詢 Heap32ListFirst,。 |
TH32CS_SNAPMODULE (0x00000008) | 快照中包含通過 th32ProcessID 指定進程的所有modules信息,。 想要列舉modules信息,請查詢 Module32First ,。 如果函數(shù)失敗報錯ERROR_BAD_LENGTH,,請重試此函數(shù)直到成功。 |
TH32CS_SNAPMODULE32 (0x00000010) | 快照中包含所有通過 th32ProcessID 指定進程的32位模塊(modules)信息(通過64位調(diào)用也是返回32位),。 該flag可以與 TH32CS_SNAPMODULE 或者 TH32CS_SNAPALL 聯(lián)合使用,。如果函數(shù)失敗報錯 ERROR_BAD_LENGTH ,請重試此函數(shù)直到成功,。 |
TH32CS_SNAPPROCESS (0x00000002) | 快照中包含系統(tǒng)中的所有進程 (processes) 信息,。 想要列舉 processes 信息,請查詢 Process32First,。 |
TH32CS_SNAPTHREAD (0x00000004) | 快照中包含系統(tǒng)中的所有線程 (threads) 信息,。想要列舉 threads 信息, 請查詢 Thread32First,。 確認是否屬于某個指定的進程,,可以在列舉線程信息的時候,拿進程標識與 THREADENTRY32 結構體的 th32OwnerProcessID 成員相比較來判斷 ,。 |
th32ProcessID [輸入?yún)?shù)]
如果為0則獲取所有進程的快照,,如果不為零則獲取該進程id的信息
返回值
成功則返回該快照的句柄
遍歷進程信息
1 2 3 4 | BOOL WINAPI Process32First(
_In_ HANDLE hSnapshot,
_Inout_ LPPROCESSENTRY32 lppe
);
|
1 2 3 4 | BOOL WINAPI Process32Next(
_In_ HANDLE hSnapshot,
_Out_ LPPROCESSENTRY32 lppe
);
|
與文件目錄類似, Process32First 用于獲取第一個進程的信息,, Process32Next 用于獲取下一個進程的信息,。參數(shù)一 hSnapshot 是指獲取的進程快照,參數(shù)二 lppe 則是一個指向 PROCESSENTRY32 結構體的指針(LP + PROCESSENTRY32),。
PROCESSENTRY32 結構體
系統(tǒng)進程快照中某一個進程信息的具體結構
1 2 3 4 5 6 7 8 9 10 11 12 13 | typedef struct tagPROCESSENTRY32
{
DWORD dwSize;
DWORD cntUsage;
DWORD th32ProcessID; // this process
ULONG_PTR th32DefaultHeapID;
DWORD th32ModuleID; // associated exe
DWORD cntThreads;
DWORD th32ParentProcessID; // this process's parent process
LONG pcPriClassBase; // Base priority of process's threads
DWORD dwFlags;
CHAR szExeFile[MAX_PATH]; // Path
} PROCESSENTRY32;
|
MSDN 文檔 :
CreateToolhelp32Snapshot
Process32First
Process32Next
PROCESSENTRY32 結構體
ps 查看進程列表
再來新建一個項目名字叫做 ps ,,記得要是空項目隨后添加如下代碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | #include <Windows.h>
#include <stdio.h>
#include <TlHelp32.h>
/*
TlHelp32.h for
PROCESSENTRY32
CreateToolhelp32Snapshot()
Process32First()
Process32Next()
*/
int main( int argc, char const *argv[])
{
HANDLE hSnapshot;
HANDLE hProcess;
PROCESSENTRY32 pe32;
// 獲取進程快照
hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
if ( hSnapshot == INVALID_HANDLE_VALUE )
{
printf ( "CreateToolhelp32Snapshot (of processes) 失敗" );
return ;
}
// 設置輸入?yún)?shù),結構的大小
pe32.dwSize = sizeof ( PROCESSENTRY32 );
// 開始列舉進程信息
if ( !Process32First( hSnapshot, &pe32 ) )
{
printf ( "Process32First() 失敗" );
CloseHandle( hSnapshot ); // 關閉句柄
return ;
}
printf ( "進程ID\t父進程\t線程數(shù)\t優(yōu)先級\t進程名" ); // 基本優(yōu)先級
do {
// 打印進程相關信息
printf ( "\n%u" , pe32.th32ProcessID ); // 進程id
printf ( "\t%u" , pe32.th32ParentProcessID ); // 父進程id
printf ( "\t%d" , pe32.cntThreads ); // 線程數(shù)
printf ( "\t%d" , pe32.pcPriClassBase ); // 基本優(yōu)先級
printf ( "\t%s" , pe32.szExeFile ); // 進程名
} while ( Process32Next( hSnapshot, &pe32 ) );
CloseHandle( hSnapshot ); //關閉句柄
return ;
}
|
因為是在命令行的環(huán)境下運行,,所以直接F7生成exe即可,,不需要直接執(zhí)行,。如果是拿lscmd來做實驗的話,,程序?qū)懞昧诉€要找到exe復制到path目錄下,,感覺有點麻煩,為了方便大家也可以直接將程序的生成目錄,,改成博主開始使用的 F:\mytools目錄 (Visual Studio 如何設置生成目錄)
測試數(shù)據(jù):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | Lellansin's CMD Tool [版本 0.0.1]
學習自制 (c) www. 歡迎交流
Lscmd>ps
進程ID 父進程 線程數(shù) 優(yōu)先級 進程名
0 0 2 0 [System Process]
4 0 106 8 System
292 4 2 11 smss.exe
392 380 49 8 avgrsa.exe
432 392 10 8 avgcsrva.exe
640 632 10 13 csrss.exe
704 632 3 13 wininit.exe
720 696 13 13 csrss.exe
764 696 3 13 winlogon.exe
812 704 10 9 services.exe
824 704 7 9 lsass.exe
832 704 11 8 lsm.exe
940 812 10 8 svchost.exe
1020 812 9 8 svchost.exe
724 812 19 8 svchost.exe
888 812 22 8 svchost.exe
1732 812 33 8 avgwdsvc.exe
1816 812 4 8 sqlwriter.exe
1852 812 6 8 svchost.exe
1924 812 4 8 vmware-usbarbitrator64.exe
1976 812 6 8 vmnat.exe
...... 省略N條 ......
7008 1676 3 8 notepad++.exe
6204 1500 11 6 chrome.exe
6600 1500 11 6 chrome.exe
6628 1500 11 6 chrome.exe
6920 1500 11 6 chrome.exe
7716 3800 10 8 MSBuild.exe
5980 724 5 8 audiodg.exe
4132 1500 11 8 chrome.exe
5048 1500 11 8 chrome.exe
4976 2052 6 8 vcpkgsrv.exe
8072 7196 5 8 mspdbsrv.exe
4724 3800 8 8 vcpkgsrv.exe
3844 720 5 8 conhost.exe
7124 3400 1 8 ps.exe
Lscmd>
|
終止進程
OpenProcess 獲取進程句柄
1 2 3 4 5 | HANDLE WINAPI OpenProcess(
_In_ DWORD dwDesiredAccess,
_In_ BOOL bInheritHandle,
_In_ DWORD dwProcessId
);
|
參數(shù)
dwDesiredAccess [輸入?yún)?shù)]
進程句柄對該進程的訪問權限,。詳見進程訪問權限。
bInheritHandle [輸入?yún)?shù)]
句柄是否繼承(填寫 TRUE 或者 FALSE),,如果繼承(TRUE)那么如果該進程創(chuàng)建子進程的時候這個句柄也會被繼承到子進程,。
dwProcessId [輸入?yún)?shù)]
將要打開(open)的進程ID。
如果指定進程是系統(tǒng)進程 (0x00000000),,該函數(shù)會失敗并且最后的錯誤會是ERROR_INVALID_PARAMETER,。如果指定進程是系統(tǒng)空閑進程(原文:Idle process 博主備注:一種內(nèi)存管理進程,準確的來講名字叫做 System Idle Process)或者某個子系統(tǒng)進程(原文:CSRSS processes 博主備注:準確的說是 Client Server Runtime Process 任務管理器里可以看到它 csrss.exe),,該函數(shù)會失敗并且最后的錯誤會是ERROR_ACCESS_DENIED 因為其訪問限制會阻止用戶級別(user-level)的代碼獲取其句柄,。
返回值
如果函數(shù)成功,返回值為指定進程的句柄,。
如果函數(shù)失敗,,返回值為NULL??梢允褂?GetLastError 函數(shù)獲得錯誤的附加信息,。
MSDN: OpenProcess
TerminateProcess
終止(Terminate) + 進程(Process) = 終止進程(TerminateProcess)
1 2 3 4 | BOOL WINAPI TerminateProcess(
_In_ HANDLE hProcess,
_In_ UINT uExitCode
);
|
參數(shù)一 hProcess [輸入?yún)?shù)]
待終止的進程句柄。該句柄必須擁有 PROCESS_TERMINATE (進程終止) 的權限,。更多信息,,請查看進程的安全與訪問權限。
參數(shù)二 uExitCode [輸入?yún)?shù)]
設置通過使用該方法退出的進程與線程的退出碼,?(exit code 嘛,,博主覺得返回值更好理解一些)。 使用GetExitCodeProcess函數(shù)可以可以獲取到進程退出時返回的值,。使用GetExitCodeThread函數(shù)可以獲取到線程退出時返回的值,。
返回值
函數(shù)執(zhí)行成功,返回值為非零,。
函數(shù)執(zhí)行失敗,,返回值為零。更多信息可以通過GetLastError函數(shù)獲取,。
kill 終止進程
新建一個空項目名為 kill ,,隨后代碼如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | #include <windows.h>
#include <stdio.h>
void help();
int main( int argc, char const *argv[])
{
int ProcessID;
HANDLE hProcess;
// 如果只有一個參數(shù)
if (argc == 1)
{
help();
return 0;
}
// 如果有兩個參數(shù)
if (argc == 2)
{
// 獲取進程id
ProcessID = atoi (argv[1]);
// 獲取進程句柄
hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, ( DWORD )ProcessID );
// 終止進程
TerminateProcess(hProcess, 0);
}
}
void help()
{
printf ( "終止進程\n" );
printf ( "kill <進程id>\n" );
}
|
使用演示:
打開兩個lscmd,第一個執(zhí)行
1 2 3 4 5 6 7 8 | Lellansin's CMD Tool [版本 0.0.1]
學習自制 (c) www. 歡迎交流
Lscmd>cmd.exe
Microsoft Windows [版本 6.1.7601]
版權所有 (c) 2009 Microsoft Corporation,。保留所有權利,。
C:\Users\Lellansin>
|
執(zhí)行cmd.exe之后發(fā)現(xiàn)程序切換到了cmd之下,,這個時候我們在打開第二個 lscmd ,(注:如果你的 lscmd 放在path路徑之下的話,,博主的也就是F:\mytools,,可以直接 win+r 調(diào)出運行,輸入 lscmd 回車即可調(diào)用出來)
在我們的第二個CMD工具集中使用ps查看進程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | Lellansin's CMD Tool [版本 0.0.1]
學習自制 (c) www. 歡迎交流
Lscmd>ps
進程ID 父進程 線程數(shù) 優(yōu)先級 進程名
... 其余省略 ...
1676 1956 36 8 explorer.exe
7904 1676 1 8 lscmd.exe
5844 7904 1 8 cmd.exe
4180 1676 1 8 lscmd.exe
5568 4180 1 8 ps.exe
#通過觀察進程列表可以發(fā)現(xiàn)我們使用lscmd打開的cmd.exe
#其進程id是5844,,那么我們用新鮮的kill.exe來試試它
Lscmd>kill 5844
#運行之后發(fā)現(xiàn)第一個lscmd中的cmd.exe退出來了,,牛刀小試、程序ok
|
更多改進
1.首先工具集本身還缺乏一些過多的指令,,比如我們常用的cd(切換目錄)命令,,我們可以繼續(xù)自定義一些命令
2.實際上關于一個進程我們還可以再獲取更多的信息,也即我們的ps命令可以修改一下多加一個參數(shù),。形如: ps 1676 這樣調(diào)用時則列舉出進程id為1676的搜有信息,,這個信息可以有很多,包括其進程具體的:
- 線程信息(Thread32First,、Thread32Next)
- 模塊信息(Module32First,、Moudle32Next)
- 堆信息(Heap32ListFirst、Heap32Next)
- 內(nèi)存使用情況(GetProcessMemoryInfo)
3.kernel32.dll是windows的核心DLL,,很多內(nèi)核級別的API都需要從其中導出,,其實上述的例子中關于查看進程信息的大部分介紹的都是該DLL導出的函數(shù)(如果上一個問題又解決的,仔細查看模塊信息會可以找到到程序調(diào)用掉用的每一個DLL),,不過不是在Windows API中而是在Tool help API中,,需要引用的是 Tlhelp32.h (不是Windows.h)
關于進程一些信息操作,除了kernel32.dll中的Tool help API還有一個 PS API (從Psapi.dll中導出頭文件為 psapi.h ),,大家有興趣的可以搜搜看,,然后嘗試用其中的函數(shù)來改寫。
4.kill 命令需要我們通過一個進程的id來殺死它,,有的時候找一個進程的id有些麻煩,,不過找名字卻很容易所以你可以考慮改寫這個程序,讓它可以通過名稱來(etProcessIdByName函數(shù))終止一個進程,。
5.設置與獲取環(huán)境變量( GetEnvironmentStrings,GetEnvironmentVariable 和 SetEnvironmentVariable等)屬于支線部分,,大家可以研究使用這個API來跳過設置PATH系統(tǒng)環(huán)境變量,直接弄一個程序內(nèi)部的環(huán)境變量就可以了,,這樣程序的可移植性更高,。
小結
任何一個程序,想要運行那么必須為這個進程分配空間并且分配一個唯一的進程標識(進程ID),,在上面的例子中我們有看到這樣的額一串數(shù)據(jù):
1 2 3 4 5 6 | 進程ID 父進程 線程數(shù) 優(yōu)先級 進程名
1676 1956 36 8 explorer.exe
7904 1676 1 8 lscmd.exe
5844 7904 1 8 cmd.exe
4180 1676 1 8 lscmd.exe
5568 4180 1 8 ps.exe
|
在其中一個lscmd中打開cmd,,我們很直觀的就能看到cmd.exe的父進程就是該lscmd.exe。
仔細看我們可以發(fā)現(xiàn),我打開了兩個lscmd程序,,而這個兩個進程的父進程則是explorer.exe(1676)即我們的桌面,,不論是通過win+r調(diào)用還是雙擊打開,實際上都需要explorer為其新建一個進程來運行我們打開的程序,。
概念上理解之后,,剩下的就是熟悉API了,各位可以參照上面的 "更多改進" 來編寫一些程序提高API的熟練度
相關函數(shù)
大部分與進程或線程有關的函數(shù)可以在MSND的 Process and Thread Functions 中被找到,。小部分,,例如 PS API 中的一些函數(shù)就找不到了,。