4. 进程(一) -> Windows核心编程【第五版】
程序员文章站
2022-03-13 23:51:32
...
1. GetModuleHandle
HMODULE GetModuleHandle(PCTSTR pszModule);
- 该函数返回一个执行文件或DLL文件被加载到进程地址空间的位置,是一个句柄/基地址;
- 调用这个函数时,要传递一个以0为终止符的字符串, 它指定了已在主调进程的地址空间中加载的一个可执行文件或DLL文件的名称。
- 如果找到指定的可执行文件或DLL文件名称,
GetModuleHandle
就会返回可执行文件/DLL文件映像加载到的基地址; - 若没有找到,系统将返回
NULL
获取主调进程的可执行文件的基地址的方法
-
GetModuleHandle
的pszModule参数传入NULL值; - 使用链接器提供的伪变量
_ImageBase
,该值指向当前正在运行的模块的基地址 调用
GetModuleHandleEx
, 第一个参数为GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
,第二个参数为当前函数的地址,最后一个参数为指向HMODULE
的指针。GetModuleHandleEx
会用传入函数(第二个参数)所在DLL的基地址来填写指针(第三个参数)演示代码
#include <Windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <tchar.h>
extern "C" const IMAGE_DOS_HEADER __ImageBase;
void DumpModel()
{
HMODULE hModule = GetModuleHandle(NULL);
_tprintf(TEXT("with GetModuleHandle(NULL) = 0x%x\r\n"), hModule);
hModule = NULL;
GetModuleHandleEx(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
(PCTSTR)DumpModel,
&hModule);
_tprintf(TEXT("with GetModuleHandleEx = 0x%x\r\n"), hModule);
_tprintf(TEXT("with ImageBase = 0x%x\r\n"), (HINSTANCE)&__ImageBase);
}
//int APIENTRY _tWinMain(HINSTANCE hInstanceExe, HINSTANCE, PTSTR pszCmdLine, int nCmdShow)
int _tmain(int argc, TCHAR *arcv[])
{
DumpModel();
return(0);
}
2. (w)WinMain的hPreInstance参数
实际上就是历史的参数,为了移植而保留
- C/C++ Windows的入口点函数(GUI应用程序):
-
入口点函数的第二个参数(
hPrevInstance
)总是传递NULL,是因为该参数是用于16位Windows系统,为了方便移植16位Windows应用程序,所以才保留的一个参数,在自己的代码中绝对不要引用该参数, 所以(w)WinMain函数定义可以用如下两种:- 第一种,没有指定参数名,所以编译器不会报告一个参数没有被引用到的警告。
- 第二种是利用
UNREFERENCED_PARAMETER宏
来消除警告
-
入口点函数的第二个参数(
//////////////////////第一种
int WINAPI _tWinMain(
HINSTANCE hInstanceExe,
HINSTANCE,
PSTR pszCmdLine,
int nCmdShow);
//////////////////////第二种
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
}
PS: WINAPI 和 APIENTRY实际上都是__stdcall
的宏定义, 关于 __stdcall
的介绍, 见文章 WINAPI和APIENTRY,C/C++函数调用的方式, 函数名字修饰规则
3. 获取环境变量
使用GetEnvironmentStrings函数
来获取完整的环境快
- 如果不再需要
GetEnvironmentStrings
函数返回的内存块,应调用FreeEnvironmentStrings
函数来释放它
#include <Windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <tchar.h>
#include <string.h>
#include <WinNT.h>
#include <StrSafe.h>
void DumpEnvStrings()
{
PTSTR pEnvBlock = GetEnvironmentStrings();
// 将环境变量字符串解析成如下格式
// =::=::\
// =...
// var=value\0
// ...
// var=value\0\0
// 有一些字符串以'='开始
// 解析成如下格式
// {0} =::=::\
// {1} =C:=C:\Windows\System32
// {2} =ExitCode=00000000
//
PTSTR pszCurrent = pEnvBlock;
TCHAR szName[MAX_PATH];
TCHAR szValue[MAX_PATH];
size_t current = 0;
HRESULT hr = S_OK;
PTSTR pszPos = NULL;
while (pszCurrent != NULL)
{
if (*pszCurrent != TEXT('='))
{
pszPos = _tcschr(pszCurrent, TEXT('='));
++pszPos;
size_t cbNameLength = (size_t)pszPos - (size_t)pszCurrent - sizeof(TCHAR);
hr = StringCbCopyN(szName, MAX_PATH*sizeof(TCHAR), pszCurrent, cbNameLength);
if (FAILED(hr))
{
break;
}
hr = StringCchCopyN(szValue, MAX_PATH, pszPos, _tcslen(pszPos)+1);
if (SUCCEEDED(hr))
{
_tprintf(TEXT("{%u} %s=%s\r\n"), current, szName, szValue);
}
else if (hr == STRSAFE_E_INSUFFICIENT_BUFFER)
{
_tprintf(TEXT("{%u} %s=%s..\r\n"), current, szName, szValue);
}
else
{
_tprintf(TEXT("{%u} %s=???\r\n"), current, szName);
break;
}
}
else
{
_tprintf(TEXT("{%u} %s\r\n"), current, pszCurrent);
}
++current;
while (*pszCurrent != TEXT('\0'))
++pszCurrent;
++pszCurrent;
if (*pszCurrent == TEXT('\0'))
break;
}
FreeEnvironmentStrings(pEnvBlock);
}
int _tmain(int argc, TCHAR *argv[])
{
DumpEnvStrings();
return(0);
}
使用main入口点函数所接收的THCAR *env[]
void DumpEnvVariable(PTSTR pEnvBlock[])
{
int current = 0;
PTSTR *pElement = (PTSTR *)pEnvBlock;
PTSTR pCurrent = NULL;
while (pElement != NULL)
{
pCurrent = (PTSTR)(*pElement);
if (pCurrent == NULL)
{
// 没有更多环境变量了
pElement == NULL;
}
else
{
_tprintf(TEXT("{%u} %s\r\n"), current, pCurrent);
++current;
++pElement;
}
}
}
int _tmain(int argc, TCHAR *argv[], TCHAR *env[])
{
DumpEnvVariable(env);
return(0);
}
- 上述结果均如下:
4. 进程的亲缘性
- 一般来说,进程中的线程可以在主计算机中的任何一个CPU上执行, 但是一个进程的线程可能被强制在可用CPU的子集上运行。这称为进程的亲缘性。
- 子进程继承了父进程的亲缘性。
5. 进程的错误模式
- 这是与每个进程相关联的是一组标志,用于告诉系统,进程对严重的错误应该如何作出反映,
- 包括磁盘介质故障、未处理的异常情况、文件查找失败和数据没有对齐等。
- 进程可以告诉系统如何处理每一种错误。方法是调用
SetErrorMode
函数:
UINT SetErrorMode(UINT fuErrorMode);
-
fuErrorMode
参数是下表的任何标志按位用OR连接在一起的组合。- 默认情况下,子进程继承父进程的错误模式标志。换句话说,如果一个进程的
SEM_NOGPFAULTERRORBOX
标志已经打开,并且生成了一个子进程,该子进程也拥有这个打开的标志。但是,子进程并没有得到这一情况的通知,它可能尚未编写以便处理GP故障的错误。如果GP故障发生在子进程的某个线程中,该子进程就会终止运行,而不通知用户。父进程可以防止子进程继承它的错误模式, 方法是在调用CreateProcess
时设定CREATE_DEFAULT_ERROR_MODE
标志
- 默认情况下,子进程继承父进程的错误模式标志。换句话说,如果一个进程的
上一篇: PHP判断客户端的浏览器类型
下一篇: php中的多种界面跳转方式