第4章 进程 I

4.1.1 进程实例句柄

加载到进程地址空间的每一个可执行文件或者DLL文件都被赋予了一个独一无二的实例句柄。可执行文件的实例被昂做(w)WinMain函数的第一个参数hInstanceExe传入。在需要加载资源的函数调用中,一般都要提供此句柄的值。例如,为了从可执行文件的映像中能够加载一个图标资源,就需要调用下面的函数:

HICON LoadIcon(HINSTANCE hInstance, PCTSTR pszIcon);

此函数的第一个参数指出哪个文件包含了需要加载的资源。许多应用程序都会将(w)WinMain的hInstance参数保存在一个全局变量中,是其很容易被可执行文件的所有代码访问。

 

Platfrom SDK文档指出,有的函数需要一个HMODULE类型的参数。下面的GetModuleFileName函数就是一个例子:

DWORD GetModuleFileName(

HMOUDLE hInstance,

PTSTR pszPath,

DWORD cchPath)

--------------------------------------------------------------------------------------

说明,事实上,HMOUDLE和HINSTANCE完全是一回事。

--------------------------------------------------------------------------------------

 

(w)WinMain的hInstance参数的实际值是一个内存基地址: 系统将可执行文件的映像加载到进程地址空间中的这个位置。例如,假如系统打开可执行文嘉,并将它的内容加载到地址0x004000000,则hinstantcExe参数值为0x004000000.

可执行文件的映像具体加载到哪一个基地址,是由连接器决定的。不同的连接器使用不同的默认基地址。由于历史原因,visual studio链接器使用的默认基地址是0x004000000,这是在运行windows 98时,可执行文件的映像能加载到的最低的一个地址。使用Microsoft链接器的/BASE:address链接器开关,可以更改在将应用程序加载到哪个基地址。

为了知道一个可执行文件或者DLL文件被加载到进程地址空间额文件,可以使用如下所有GetModuleHandle函数来返回一个句柄/基地址:

HMODULE GetMoudleHandle(PCTSTR pszModule);

调用这个函数时,要传递一个以0为终止符的字符串,他指定了已在主调进程的地址空间中加载的一个可执行文件或者DLL文件的名称。如果系统找到了指定的可执行文件或者DLL文件名字,GetMoudleHandle就会返回可执行文件/DLL文件映像加载到的基地址。如果没找到,系统将会返回NULL.另一个用法是pszMoudle参数传入NULL,这样可以返回主调进程的可执行文件的基地址。如果我们的代码在一个DLL中,那么可以利用两种方法来了解代码证在什么模块中运行。第一个方法就是利用链接器提供的伪变量_ImageBase, 他指向当前正在运行的模块的基地址。如前所述,这个c运行库启动代码在动用我们的(w)Winmain函数时所做的事情。第二个方法就是嗲用GetMoudleHandleEx,将GET_MOUDLE_HANDLE_EX_FLAG_FROM_ADDRESS作为他的第一个参数,将当前函数的地址作为第二个参数。最后一个参数是一个指向HMOUDLE的指针,GetMoudleHnadleEx会用传入函数所在DLL的基地址来填写该指针。

记住GetmoudleHandle函数的两大重要特征。首先,它只检查主调进程的地址空间。如果主调进程没有任何通用对话框函数,那么一旦调用GetMoudleHandle,并iang其传递ComDlg32,就会导致返回NULL--即使它也许已经被加载到其他进程的地址空间。其次,调用GetModuleHandle并向其传递NULL值,会返回进程地址空间中的可执行文件的基地址。所以,即使调用GetMoudleHandle(NULL)的代码是一个在DLL文件中,返回值仍是可执行文件的基地址,而非DLL文件的基地址。

 

4.1.2 进程前一个实例的句柄

如前所述,c/c++运行库启动代码总是向(w)WinMain的hPrevInstane参数传递NULL,该参数用于16位windows系统,因而仍然将其保留为(w)winmain的一个参数,目的只是方便我们移植16位的windows应用程序。绝对不要在自己的代码中引用这个参数。

 

4.1.3 进程的命令行

系统在创建一个新进程时,会传一个命令行给他。这个命令行几乎中总是非空的: 至少,用于创建新进程的可执行文件的名称是命令行上的第一个标记token。不过,在后面讨论CreateProcess函数时候,我们会知道进程能接收只由一个字符构成的命令行,即用于终止字符串的0.c运行库的启动代码开始执行一个GUI应用程序是,会调用windows函数GetCommandLine来获取进程的完整命令行,忽略可执行文件的名称,然后将指向命令行甚于部分的一个指针传给winmain的pszCmdLine参数。

应用程序可以通过自己选择的任何一种方式来解析和解释命令行字符串。我们实际上可以写数据到pszCmdLine参数所指向的内存缓存区,但在任何情况下,写入缓存区的时候都不应该越界。就我个人而言,我始终把他当作一个只读的缓存区。如果想对命令行进程改动,我首先会想命令行缓存区复制到我的应用程序的一个本地缓存区,在对在即的本地缓存区进行修改。

我们可以效仿c运行库的例子,通过调用GetCommandLine函数来获取一个指向进程完整命令行的指针,如,PTSTR GetCommandLine();

该函数放回一个缓存区指针,缓存区中包含完整的命令行。注意,它返回的总是一个缓存区的地址。这个不应该向pszCmdLine写入数据的另一个理由;它指向同一个缓存区,修改它之后,就没办法知道原来的命令行是什么。虽然Microsoft反对继续使用全局变量_argc和_argv,但是,应用程序仍然可以使用他们来访问对象命令行的每个标记。利用ShellAPI.h文件中声明并由Shell32.dll导出的函数CommandLineToArgvw,即可将任何Unicode字符串分解成单独的标记

PWSTR* CommanLineToArgvw( PWSTR pszCmdLine, int* pNumargs)

此函数在内部分配内存,许多应用程序不会释放这块内存-它们依靠操作系统在进程终止时释放。这个完全可以接受。如果要自己释放内存,需要调用HeapFree。

int nNumargs;

PWSTR* ppargv = CommandLineToArgvW(GetCommandLineW(), &nNumArgs);

// free the memory block

HeapFree(GetProcessHeap(), 0, ppargv);

 

4.1.4 进程的环境变量

每个进程都有一个与它关联的环境块(environment block), 这是在进程地址空间内分配的一块内存。

访问环境块的方式。第一种调用GetEnviromentStrings函数来获取完整的环境块。如果不需要了,应该调用FreeEnviromentStrings函数来释放它。第二种方式是CUI专业。他用过应用程序main入口点函数所接收的TCHAR* env[]参数来实现。不同于GetEnviromentStrings返回的值,env是一个字符串指针数组,每个指针都指向一个不同的环境变量。

扩展环境变量字符串用ExpandEnviromentStrings函数

添加,删除或者修改一个变量的值,使用SetEnviromentVariable函数。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值