(1)定义
每个进程都有一个与它关联的进程块(environment block),这是在地址空间内分配的一块内存。
(a)[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment]
对应的是系统环境变量的系统变量
(b)[HKEY_CURRENT_USER\Environment]
对应的是系统的环境变量的用户变量
注:当在注册表内修改了环境变得的数据时,必须重启机器时使其更改生效。
假如CreateFile一个文件,当参数传递的文件路径不是绝对路径时,系统只能在当前驱动器和目录下查找该文件。
系统在内部跟踪记录着一个进程的当前驱动器和目录。由于这种信息是以进程为单位来维护的,所以若进程内的一个线程更改了当前驱动器和目录,则进程内的所有线程的驱动器和目录都改变了。
使用如下函数来获取一个当前进程的驱动器和目录(WinDef.h文件中定义MAX_PATH为目录名称和驱动名称的最大的字符数):
要开打的应用程序的名字和参数。第一种设置方式:可以指定lpAppliationName为NULL,在lpCommandLine 以使用完整的应用程序名称加参数的形式来传递。第二种指定方式:lpAppliationName传递应用程序的路径,lpCommandLine传递参数。
注:lpCommandLine的参数类型为LPTSTR,也就是说不能讲一个字符串常量传递进去。进程内部会修改这个参数变量,在结束时还会修改回来。必须将其参数保存到临时存储区再以参数方式传递。
(5。2) lpProcessAttributes和lpThreadAttributes
创建一个新的进程,系统必须创建一个进程内核对象和一个线程内核对象,父进程有机会将内核对象的安全数据关联到这两个内核对象上。若都传递NULL,则系统将设置为内核对象分配默认的安全描述符属性。
(5。3)bInheritHandles
若为TRUE:则子进程会继承父进程中所有可以被继承的对象(不仅仅是内核对象???)的句柄。
若为FALSE:则子进程不会继承父进程的任何对象的句柄。
(5。4)dwCreationFlags
影响了新进程创建方式的标志。一般使用最多的是CREATE_SUSPENDED,表示创建完成后将子进程先挂起,直到父进程调用ResumeThread();函数使其进程的主线程开始执行。
(5。5)lpEnvironment
表示新进程要使用的字符串环境变量,此函数为NULL时,子进程将继承其父进程使用的环境变量。和使用GetEnvironmentStrings()函数返回的地址作为参数是相同的。
(5。6)lpCurrentDirectory
设置子进程的驱动器和目录。此参数为NULL时,子进程使用和父进程的相同的驱动器和目录。若不为NULL,则必须是一个包含驱动器符号的以'\0'结束的字符串。(必须包含一个驱动器号)
(5。7)lpStartupInfo
该结构参数用于指定新进程的主窗口的特性。
一般使用默认,使用如下初始化即可:
(5。8)lpProcessInformation(输出参数)
在createProcess函数返回之前,会填充这个结构体。包含子进程的进程和主线程的句柄、进程和主线程的ID。
可以使用如下相关的方法获取句柄和ID:
(6。1)主线程的入口函数返回(强烈推荐方式)
该过程会非常完美的将进程结束掉,主要做:(a)所有的C++的对象会调用析构函数来销毁c++对象。(b)正确释放线程栈使用的内存。(c)递减所有使用的内核对象的使用计数。(d)设置退出码。
(6。2)进程中的一个线程调用函数ExitProcess()函数。(避免使用)
调用ExitProcess和ExitThread此类函数会直接导致进程或者线程的终止。对于操作系统而言没什么问题,进程或者线程的所有操作系统资源都会被清理。但是C/C++程序不能进行正确的清理工作。
(6。3)另一个进程中的线程调用TreatmentProcess()函数。(避免使用)
被终止的进程得不到自己要被 终止的通知---应用程序不能正确的清理,也不能阻止他自己被强行终止(除非通过正常的安装机制)。进程无法将它在内存的信息刷新到磁盘上。
进程的终止以后不会泄露任何东西。操作系统在进程终止以后会进行彻底的清理,确保不会泄露任何操作系统的资源。
(6。4)进程中的所有线程都“自然死亡”。(几乎从来不会出现。)
进程内的所有的线程都终止了,返回码为最后一个终止线程的退出码。
(7)创建子进程
(8。1)在vs里面设置应用程序使用系统管理员权限运行,设置方法如下:
工程---属性---配置属性---链接器---清单文件--UAC设置级别 设置为:RequireAdministrator
(8。2)在运行时,手动提升进程的权限,使用的函数是ShellExecuteEx(),如下介绍:
仅仅看个例子吧,不是太清楚。
win95、win98版本及以上提供的函数:
EnumProcesses();下面是MSDN上的例子:
进程定义成一个正在运行的程序的一个实例,创建进程后会生成一个内核对象,操作系统使用它来管理进程,也是用它来保存进程统计信息的地方。
另外,进程的创建,操作系统会为它分配一块独立的地址空间,包含所有可执行的文件或DLL模块代码或者数据,还包括动态内存分配,例如:线程堆栈和堆的分配。
(2)进程实例句柄(HMODULE和HINSTANCE是一回事)
GetModuleFileName();//获取模块的文件路径,首参数为NULL时,获取的是当前进程的文件所在的路径
GetModuleHandle();//获取模块的句柄,参数为NULL时,获取当前进程的文件的句柄
(3)进程的环境变量
每个进程都有一个与它关联的进程块(environment block),这是在地址空间内分配的一块内存。
LPTCH lptch = GetEnvironmentStrings(); //获取完整的环境块
FreeEnvironmentStrings(lptch);//使用环境块完成后需要释放
windows系统登录以后,会检查注册表中的两个注册表项来获得初始的环境变量:
(a)[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment]
对应的是系统环境变量的系统变量
(b)[HKEY_CURRENT_USER\Environment]
对应的是系统的环境变量的用户变量
注:当在注册表内修改了环境变得的数据时,必须重启机器时使其更改生效。
有的应用程序(资源管理器,任务管理器,控制面板)可以在其主窗口收到WM_SETTINGCHANGE消息的时候,更新他们的环境块。使用如下的命令:
SendMessage(HWND_BROADCAST,WM_SETTINGCHANGE,0,(LPARAM)TEXT("Environment"));
//判断一个环境变量是否存在,使用如下函数:
DWORD WINAPI GetEnvironmentVariable(
__in LPCTSTR lpName,
__out LPTSTR lpBuffer,
__in DWORD nSize
);
//使用如下函数来设置一个环境变量的值,若值设为NULL,则在环境块中删除此变量。
BOOL WINAPI SetEnvironmentVariable(
__in LPCTSTR lpName,
__in LPCTSTR lpValue
);
//对于%XXXX%形式的字符换,使用如下函数展开:
DWORD WINAPI ExpandEnvironmentStrings(
__in LPCTSTR lpSrc,
__out LPTSTR lpDst,
__in DWORD nSize
);
(4)进程当前所在的驱动器和目录
假如CreateFile一个文件,当参数传递的文件路径不是绝对路径时,系统只能在当前驱动器和目录下查找该文件。
系统在内部跟踪记录着一个进程的当前驱动器和目录。由于这种信息是以进程为单位来维护的,所以若进程内的一个线程更改了当前驱动器和目录,则进程内的所有线程的驱动器和目录都改变了。
使用如下函数来获取一个当前进程的驱动器和目录(WinDef.h文件中定义MAX_PATH为目录名称和驱动名称的最大的字符数):
DWORD WINAPI GetCurrentDirectory(
__in DWORD nBufferLength,
__out LPTSTR lpBuffer
);
//使用如下的函数来设置:
BOOL WINAPI SetCurrentDirectory(
__in LPCTSTR lpPathName
);//参数包括驱动器符号和目录
(5)创建进程函数:
BOOL WINAPI CreateProcess(
__in LPCTSTR lpApplicationName,
__in_out LPTSTR lpCommandLine,
__in LPSECURITY_ATTRIBUTES lpProcessAttributes,
__in LPSECURITY_ATTRIBUTES lpThreadAttributes,
__in BOOL bInheritHandles,
__in DWORD dwCreationFlags,
__in LPVOID lpEnvironment,
__in LPCTSTR lpCurrentDirectory,
__in LPSTARTUPINFO lpStartupInfo,
__out LPPROCESS_INFORMATION lpProcessInformation
);
(5。1)lpApplicationName和lpCommandLine
要开打的应用程序的名字和参数。第一种设置方式:可以指定lpAppliationName为NULL,在lpCommandLine 以使用完整的应用程序名称加参数的形式来传递。第二种指定方式:lpAppliationName传递应用程序的路径,lpCommandLine传递参数。
注:lpCommandLine的参数类型为LPTSTR,也就是说不能讲一个字符串常量传递进去。进程内部会修改这个参数变量,在结束时还会修改回来。必须将其参数保存到临时存储区再以参数方式传递。
(5。2) lpProcessAttributes和lpThreadAttributes
创建一个新的进程,系统必须创建一个进程内核对象和一个线程内核对象,父进程有机会将内核对象的安全数据关联到这两个内核对象上。若都传递NULL,则系统将设置为内核对象分配默认的安全描述符属性。
(5。3)bInheritHandles
若为TRUE:则子进程会继承父进程中所有可以被继承的对象(不仅仅是内核对象???)的句柄。
若为FALSE:则子进程不会继承父进程的任何对象的句柄。
(5。4)dwCreationFlags
影响了新进程创建方式的标志。一般使用最多的是CREATE_SUSPENDED,表示创建完成后将子进程先挂起,直到父进程调用ResumeThread();函数使其进程的主线程开始执行。
(5。5)lpEnvironment
表示新进程要使用的字符串环境变量,此函数为NULL时,子进程将继承其父进程使用的环境变量。和使用GetEnvironmentStrings()函数返回的地址作为参数是相同的。
(5。6)lpCurrentDirectory
设置子进程的驱动器和目录。此参数为NULL时,子进程使用和父进程的相同的驱动器和目录。若不为NULL,则必须是一个包含驱动器符号的以'\0'结束的字符串。(必须包含一个驱动器号)
(5。7)lpStartupInfo
该结构参数用于指定新进程的主窗口的特性。
一般使用默认,使用如下初始化即可:
STARTUPINFO si;
ZeroMemory(&si,sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
(5。8)lpProcessInformation(输出参数)
在createProcess函数返回之前,会填充这个结构体。包含子进程的进程和主线程的句柄、进程和主线程的ID。
可以使用如下相关的方法获取句柄和ID:
GetCurrentProcessId(); //获取当前的进程的ID
GetCurrentThreadId();//获取当前的线程的ID
GetProcessId();//通过句柄获取进程的ID
GetProcessIdOfThread();//通过线程的ID获取所在进程的ID
Toolhelp32ReadProcessMemory();//没使用过这个,用的时候再去了解
(6)终止进程
(6。1)主线程的入口函数返回(强烈推荐方式)
该过程会非常完美的将进程结束掉,主要做:(a)所有的C++的对象会调用析构函数来销毁c++对象。(b)正确释放线程栈使用的内存。(c)递减所有使用的内核对象的使用计数。(d)设置退出码。
(6。2)进程中的一个线程调用函数ExitProcess()函数。(避免使用)
调用ExitProcess和ExitThread此类函数会直接导致进程或者线程的终止。对于操作系统而言没什么问题,进程或者线程的所有操作系统资源都会被清理。但是C/C++程序不能进行正确的清理工作。
(6。3)另一个进程中的线程调用TreatmentProcess()函数。(避免使用)
被终止的进程得不到自己要被 终止的通知---应用程序不能正确的清理,也不能阻止他自己被强行终止(除非通过正常的安装机制)。进程无法将它在内存的信息刷新到磁盘上。
进程的终止以后不会泄露任何东西。操作系统在进程终止以后会进行彻底的清理,确保不会泄露任何操作系统的资源。
(6。4)进程中的所有线程都“自然死亡”。(几乎从来不会出现。)
进程内的所有的线程都终止了,返回码为最后一个终止线程的退出码。
(7)创建子进程
//创建子进程
PROCESS_INFORMATION pi;
DWORD dwExitCode;
BOOL bSuccess = CreateProcess(....,pi);
if (bSuccess)
{
//
CloseHandle(pi.dwThreadId);
WaitForSingleObject(pi.dwProcessId,INFINITE);
GetExitCodeProcess(pi.dwProcessId,dwExitCode);
CloseHandle(pi.dwProcessId);
}
//创建子进程后,不再与父进程联系,则创建后关闭子进程的进程句柄和主线程句柄
PROCESS_INFORMATION pi;
DWORD dwExitCode;
BOOL bSuccess = CreateProcess(....,pi);
if (bSuccess)
{
CloseHandle(pi.dwThreadId);
CloseHandle(pi.dwProcessId);
}
(8)进程运行权限(vista以上的版本的系统)
(8。1)在vs里面设置应用程序使用系统管理员权限运行,设置方法如下:
工程---属性---配置属性---链接器---清单文件--UAC设置级别 设置为:RequireAdministrator
(8。2)在运行时,手动提升进程的权限,使用的函数是ShellExecuteEx(),如下介绍:
//此函数参数的结构体内的成员:
LPCTSTR lpVerb; //设置为“runas”
LPCTSTR lpFile; //设置为运行的进程的路径名称
//此时,运行时,会弹出权限访问的窗口。
(8。3)当前权限上下文
仅仅看个例子吧,不是太清楚。
BOOL GetProcessElevation(TOKEN_ELEVATION_TYPE *pElevationType,
BOOL *pIsAdmin)
{
HANDLE hTocken = NULL;
DWORD dwSize;
//获取当前进程令牌
if (!OpenProcessToken(GetCurrentProcess(),TOKEN_QUERY,&hTocken))
{
return FALSE;
}
BOOL bResult = FALSE;
//检索提升的类型信息
if(GetTokenInformation(hTocken,TokenElevationType,pElevationType,sizeof(TOKEN_ELEVATION_TYPE),&dwSize))
{
//创建sid对应管理员组
BYTE adminSID[SECURITY_MAX_SID_SIZE];
dwSize = sizeof(adminSID);
CreateWellKnownSid(WinBuiltinAdministratorsSid,NULL,&adminSID,&dwSize);
CString csLog;
csLog.Format(_T("*pElevationType;%d"),*pElevationType);
AfxMessageBox(csLog);
if (*pElevationType == TokenElevationTypeLimited)
{
//得到连接令牌的句柄(will have one if we are lua)
HANDLE hUnfilteredToken = NULL;
GetTokenInformation(hTocken,TokenLinkedToken,(void*)&hUnfilteredToken,sizeof(HANDLE),&dwSize);
//检查这个原始的令牌是否包含admin 的SID
if(CheckTokenMembership(hUnfilteredToken,&adminSID,pIsAdmin))
{
bResult = TRUE;
}
//不要忘记关闭这个hUnfilteredToken句柄
CloseHandle(hUnfilteredToken);
}else
{
*pIsAdmin = IsUserAnAdmin();
bResult = TRUE;
}
}
//不要忘记关闭进程的令牌
CloseHandle(hTocken);
return (bResult);
}
void CMFC_DIAloGDlg::OnBnClickedButton4()
{
// TODO: 在此添加控件通知处理程序代码
BOOL bAdmin = FALSE;
TOKEN_ELEVATION_TYPE tet;
GetProcessElevation(&tet ,&bAdmin);
if (bAdmin)
{
AfxMessageBox(_T("使用的是管理员权限运行"));
}else
{
AfxMessageBox(_T("使用的非管理员权限运行"));
}
CString csLog;
csLog.Format(_T("tet:%d bAdmin:%d "),tet,bAdmin);
AfxMessageBox(csLog);
}
(9)枚举系统中正在运行的进程
win95、win98版本及以上提供的函数:
Process32First();
Process32Next();
windows NT 提供的函数:
EnumProcesses();下面是MSDN上的例子:
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <psapi.h>
#pragma comment(lib,"Psapi.lib")
void PrintProcessNameAndID( DWORD processID )
{
TCHAR szProcessName[MAX_PATH] = TEXT("<unknown>");
// Get a handle to the process.
HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION |
PROCESS_VM_READ,
FALSE, processID );
// Get the process name.
if (NULL != hProcess )
{
HMODULE hMod;
DWORD cbNeeded;
if ( EnumProcessModules( hProcess, &hMod, sizeof(hMod), &cbNeeded) )
{
GetModuleBaseName( hProcess, hMod, szProcessName,
sizeof(szProcessName)/sizeof(TCHAR) );
}
}
// Print the process name and identifier.
CString cslog;
cslog.Format( TEXT("%s (PID: %u)\n"), szProcessName, processID );
OutputDebugString(cslog);
CloseHandle( hProcess );
}
void CMFC_DIAloGDlg::OnBnClickedButton4()
{
// Get the list of process identifiers.
DWORD aProcesses[1024], cbNeeded, cProcesses;
unsigned int i;
if ( !EnumProcesses( aProcesses, sizeof(aProcesses), &cbNeeded ) )
return;
// Calculate how many process identifiers were returned.
cProcesses = cbNeeded / sizeof(DWORD);
// Print the name and process identifier for each process.
for ( i = 0; i < cProcesses; i++ )
{
if( aProcesses[i] != 0 )
{
PrintProcessNameAndID( aProcesses[i] );
}
}
}