Windows Programming/多任務
外觀
進程
[編輯]檢查是否有同名的.exe進程
[編輯]有些應用場合,不允許一個.exe被同時有多個進程實例。因此,在啟動一個.exe時,要先判斷沒有同名的.exe已經運行了。示例代碼如下:
PROCESSENTRY32 entry;
HANDLE snapshot;
snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,NULL);
if( snapshot == INVALID_HANDLE_VALUE )
{
TRACE_ERROR(TRUE,"Error CreateToolhelp32Snapshot");
return FALSE;
}
if (Process32First(snapshot, &entry))
{
while(Process32Next(snapshot, &entry))
{
if (!strcmp(entry.szExeFile,szApplication)) //szApplication不含路径名
{
/// TRACE_INFO_FORMAT1(FALSE,"MEP software is already running (%s)",szFile);
CloseHandle(snapshot);
return TRUE;
}
}
}
else
{
TRACE_ERROR(TRUE,"Error Process32First");
CloseHandle(snapshot);
return FALSE;
}
CloseHandle(snapshot);
創建進程
[編輯]過程如下:
STARTUPINFO stStart = { sizeof(si) };
PROCESS_INFORMATION stInfo;
GetStartupInfo( &stStart );
if (!CreateProcess(szApplicationFile,NULL,NULL,NULL,FALSE,0,NULL,szCurrentDirectory,&stStart,&stInfo))
{
TRACE_ERROR_FORMAT1(TRUE,"Error while launching MEP software (%s)",szFile);
return FALSE;
}
else
{
TRACE_INFO_FORMAT1(FALSE,"MEP software launched (%s)",szFile);
return TRUE;
}
其中,dwCreationFlags用於標識標誌,以便用於規定如何來創建新進程。可以取值為:
- EBUG_PROCESS 父進程想要調試子進程和子進程將來生成的任何進程。當任何子進程(被調試進程)中發生某些事件時,將情況通知父進程。
- DEBUG_ONLY_THIS_PROCESS 與DEBUG_PROCESS標誌相類似,調試程序只被告知緊靠父進程的子進程中發生的特定事件。
- CREATE_SUSPENDED 新進程被創建,但是,它的主線程則被掛起。
- DETACHED_PROCESS 阻止基於CUI的進程對它的父進程的控制台窗口的訪問,並告訴系統將它的輸出發送到新的控制台窗口。
- CREATE_NEW_CONSOLE 為新進程創建一個新控制台窗口。如果同時設定CREATE_NEW_CONSOLE和DETACHED_PROCESS標誌,就會產生一個錯誤。
- CREATE_NO_WINDOW 不為應用程序創建任何控制台窗口。
- CREATE_NEW_PROCESS_GROUP 修改用戶在按下Ctrl+C或Ctrl+Break鍵時得到通知的進程列表。
- CREATE_DEFAULT_ERROR_MODE 不繼承父進程使用的錯誤模式。
- CREATE_SEPARATE_WOW_VDM 只能當你在Windows2000上運行16位Windows應用程序時使用。告訴系統創建一個單獨的DOS虛擬機(VDM),並且在該VDM中運行16位Windows應用程序。
- CREATE_SHARED_WOW_VDM 只能當你在Windows2000上運行16位Windows應用程序時使用。在系統的共享VDM中運行16位Windows應用程序。
- CREATE_UNICODE_ENVIRONMENT 告訴系統,子進程的環境塊應該包含Unicode字符。按照默認設置,進程的環境塊包含的是ANSI字符串。
- CREATE_FORCEDOS 強制系統運行嵌入16位OS/2應用程序的MOS-DOS應用程序。
- CREATE_BREAKAWAY_FROM_JOB 使作業中的進程生成一個與作業相關聯的新進程
- IDLE_PRIORITY_CLASS、BELOW_NORMAL_PRIORITY_CLASS、NORMAL_PRIORITY_CLASS、ABOVE_NORMAL_PRIORITY_CLASS、HIGH_PRIORITY_CLASS、REALTIME_PRIORITY_CLASS 空閒、低於正常、正常、高於正常、高實時,對於大多數應用程序來說不應該設定優先級類。
對於結構:
typedef struct _STARTUPINFO
{
DWORD cb; //包含STARTUPINFO结构中的字节数.如果Microsoft将来扩展该结构,它可用作版本控制手段.应用程序必须将cb初始化为sizeof ( STARTUPINFO )
PSTR lpReserved; //保留。必须初始化为N U L L
PSTR lpDesktop; //用于标识启动应用程序所在的桌面的名字。如果该桌面存在,新进程便与指定的桌面相关联。如果桌面不存在,便创建一个带有默认属性的桌面,并使用为新进程指定的名字。 如果lpDesktop是NULL(这是最常见的情况 ),那么该进程将与当前桌面相关联
PSTR lpTitle; //用于设定控制台窗口的名称。如果l p Ti t l e 是N U L L ,则可执行文件的名字将用作窗口名
DWORD dwX; //用于设定应用程序窗口在屏幕上应该放置的位置的x 和y 坐标(以像素为单位)。
DWORD dwY; //只有当子进程用CW_USEDEFAULT作为CreateWindow的x参数来创建它的第一个重叠窗口时, 才使用这两个坐标。若是创建控制台窗口的应用程序,这些成员用于指明控制台窗口的左上角
DWORD dwXSize; //用于设定应用程序窗口的宽度和长度(以像素为单位)
DWORD dwYSize; // 只有当子进程将CW_USEDEFAULT 用作CreateWindow 的nWidth参数来创建它的第一个重叠窗口时,才使用这些值。若是创建控制台窗口的应用程序,这些成员将用于指明控制台窗口的宽度
DWORD dwXCountChars; //用于设定子应用程序的控制台窗口的宽度和高度(以字符为单位)
DWORD dwYCountChars;
DWORD dwFillAttribute; //用于设定子应用程序的控制台窗口使用的文本和背景颜色
DWORD dwFlags; //请参见下一段的说明
WORD wShowWindow; //用于设定如果子应用程序初次调用的ShowWindow 将SW_SHOWDEFAULT 作为 nCmdShow 参数传递时,该应用程序的第一个重叠窗口应该如何出现。本成员可以是通常用于ShowWindow 函数的任何一个SW_*标识符
WORD cbReserved2; //保留。必须被初始化为0
PBYTE lpReserved2; //保留。必须被初始化为NULL
HANDLE hStdInput; //用于设定供控制台输入和输出用的缓存的句柄。按照默认设置,hStdInput 用于标识键盘缓存,hStdOutput 和hStdError用于标识控制台窗口的缓存
HANDLE hStdOutput;
HANDLE hStdError;
} STARTUPINFO, *LPSTARTUPINFO;
dwFlags可以取值:
- STARTF_USESIZE // 使用dwXSize 和dwYSize 成員
- STARTF_USESHOWWINDOW //使用wShowWindow 成員
- STARTF_USEPOSITION //使用dwX 和dwY 成員
- STARTF_USECOUNTCHARS //使用dwXCountChars 和dwYCount Chars 成員
- STARTF_USEFILLATTRIBUTE //使用dwFillAttribute 成員
- STARTF_USESTDHANDLES //使用hStdInput 、hStdOutput 和hStdError 成員
- STARTF_RUN_FULLSCREEN //強制在x86 計算機上運行的控制台應用程序以全屏幕方式啟動運行
- STARTF_FORCEONFEEDBACK
- STARTF_FORCEOFFFEEDBACK
作業:管理進程
[編輯]線程
[編輯]線程擁有的用戶對象是窗口與鈎子。
創建線程
[編輯]- CreateThread——Windows的API函數。應該僅限於工作者線程。線程函數定義為:DWORD WINAPI _yourThreadFun(LPVOID pParameter)。該函數創建的線程的內核對象的引用計數初始值為2,因為該線程與創建該線程時返回的句柄各索引了該內核對象。棧初始化時,壓入兩個參數,即RtlUserThreadStart函數的兩個參數,而這個RtlUserThreadStart函數是從內核態轉入用戶態後該線程第一個被執行的函數。
- _beginthreadex——MS對C Runtime庫的擴展SDK函數。針對C Runtime庫做了一些初始化的工作,以保證C Runtime庫工作正常;然後,調用CreateThread真正創建線程。 千萬不要使用_beginthread與_endthread。
- AfxBeginThread——MFC中創建GUI線程的MFC全局函數。首先創建了相應的CWinThread對象,然後調用CWinThread::CreateThread,在CWinThread::CreateThread中,完成了對線程對象的初始化工作,然後,調用_beginthreadex創建線程。簡化了操作或讓線程能夠響應消息,即可用於界面線程,也可以用於工作者線程。線程函數定義為:UINT _yourThreadFun(LPVOID pParam)
傳遞參數
[編輯]引用自己的線程內核對象
[編輯]- GetCurrentThread():返回偽句柄,即不會增加句柄的引用計數,也不會在當前進程中創建該句柄。
- DuplicateHandle():把偽句柄(pseudo)轉變為真句柄(real)。使用完後,應該CloseHandle。
結束線程
[編輯]線程結束後,線程內核對象的狀態變為被觸發(signaled),所有等待該線程的線程被觸發。線程終止運行,線程內核對象的引用計數減1,但不會自動釋放除非引用計數為0;因為可能有其他線程在等待該線程內核對象。可以用GetExitCodeThread來查看線程是否退出。
- 線程函數返回:推薦使用。因此最佳實踐是用一個標誌量表示是否需要結束線程,線程函數每次工作循環開始時判斷此標誌量從而實現完美退出。
- ExitThread:實際上這是背後缺省使用。顯式調用將不會析構C++對象與資源。清理線程的棧。
- TerminateThread:直接殺掉線程。異步函數。被殺死線程收不到「被殺」通知,線程不能正確清理資源,也不能阻止自己被殺。不會清理釋放該線程的棧。DLL不能收到線程終止通知。
- 進程終止:避免使用。因為主線程的入口點返回時,C啟動代碼將調用ExitProcess函數,這導致了所有在運行的子線程被結束;因此應該明確處理好每個子線程的終止。
線程局部存儲
[編輯]優先級
[編輯]調試
[編輯]判斷線程是否已經退出
[編輯]//判断
bool IsThreadExit(HANDLE hThread)
{
bool bRet = false;
DWORD dwExitCode;
if(GetExitCodeThread(hThread, &dwExitCode))
{
if(dwExitCode != STILL_ACTIVE)
bRet = true;
}
else
{
//error
err = GetLastError();
throw err;
}
return bRet;
}