进程实例句柄
加载到进程地址空间的每一个可执行文件或者DLL文件都被赋予了一个独一无二的实例句柄。可执行文件的实例被当作(w)WinMain函数的第一个参数hInstanceExe传入。在需要加载资源的函数调用中,一般都要提供此句柄的值。例如,为了从可执行文件的映像中加载一个图标资源,就需要调用下面这个函数:
HICON LoadIcon (
HINSTANCE
hInstance,
PCTSTR
pszIcon);
LoadIcon函数的第一个参数指出了哪个文件(可执行文件或DLL文件)包含了想要加载的资源。许多应用程序会将(w)WinMain函数的hInstanceExe参数保存在一个全局变量中,使其可以很容易被可以执行文件的所有代码访问到。
Platform
SDK文档指出,有的函数需要一个HMODULE类型的参数。下面的GetModuleFileName函数就是一个例子:
DWORD
GetModuleFileName (
HMODULE
hInstMOdule,
PTSTR
pszPath,
DWORD
cchPath );
说明 事实上,HMODULE和HINSTANCE完全是一回事。如果某个函数的文档指出需要一个HMODULE参数,我们可以传入一个HInstance,反之亦然。之所以会有两种数据类型,是由于在16位windows中,HMODULE和HINSTANCE表示不同类型的数据。
(w)WinMain的hInstanceExe参数的实际值是一个内存基地址;系统将可执行文件的映像加载到进程空间中的这个位置。例如,假如系统打开可执行文件,并将它的内容加载到地址0x00400000,则(w)WinMain的hInstanceExe参数值就是0x00400000。
可执行文件的映像具体加载到哪一个基地址,是由链接器决定的。不同的链接器使用不同的默认基地址。由于历史原因,VS链接器使用的默认基地址是0x00400000,这是在运行windows 98时,可执行文件的映像能加载到的最低的一个地址。使用Microsoft链接器的/BASE:address链接器开关,可以更改应用程序加载到哪个基地址。
为了知道一个可执行文件或DLL文件加载到进程地址空间的什么位置,可以使用如下所示的GetModuleHandle函数来返回一个句柄/基地址:
HMODULE GetModuleHandle(PCTSTR pszModule);
调用这个函数时,要传递一个0-end字符串,它指定了已在主调进程的地址空间中加载的一个可执行文件或DLL文件的名称。如果系统找到了指定的文件,GetModuleHandle会返回可执行文件/DLL文件加载到的基地址。如果没有找到文件则返回NULL。GetModuleHandle的另一个用法是传入NULL,则它返回主调进程可执行文件的基地址。如果我们的代码在一个DLL中,那么可以利用两种方法来了解代码正在什么模块中运行。第一个办法是利用链接器提供的伪变量__ImageBase,它指向当前正在运行模块的基地址。如前所述,这是C运行库的启动代码在调用我们的(w)WinMain函数时所做的事情。
第二种方式是调用GetModuleHandleEx,将GET_MODULE_HANDLE_EX_
FLAG_FROME_ADDRESS作为它的第一个参数,将当前函数地址作为第二个参数。最后一个参数是指向HMODULE的指针,GetModuleHandleEx会用传入函数(第二个参数)所在DLL的基地址来填写该指针。下面代码演示了这几种用法:
extern "C" const IMAGE_DOS_HEADER __ImageBase;
void DumpModule(){
HMODULE hModule =
GetModuleHandle(NULL);
_tprintf(TEXT("with GetModuleHandle(NULL) = 0x%x\r\n"),hModule);
_tprintf(TEXT("with __ImageBase = 0x%x\r\n"),(HINSTANCE)&__ImageBase);
hModule = NULL;
GetModuleHandleEx(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
(PCTSTR)DumpModule,
&hModule);
_tprintf(TEXT("with GetModuleHandleEx(...) = 0x%x\r\n"),hModule);
}
int _tmain(int argc, _TCHAR*
argv[])
{
DumpModule();
return 0;
记住GetModuleHandle函数的两个重要特征。首先,它只检查主调用进程的地址空间。如果主调进程地址空间没有用到任何通用对话框函数,那么一旦调用GetModuleHandle,并向其中传递ComDlg32,就会导致返回NULL——即使ComDlg32也许已经加载到其他进程的地址空间了。其次,调用GetModuleHandle并传NULL,会返回进程地址空间中的可执行文件的基地址。所以即使调用GetModuleHandle(NULL)的代码在一个DLL中,返回值仍是可执行文件的基地址,而不是DLL的基地址。
进程前一个实例的句柄
C/C++运行库总是向(w)WinMain的hPrevInstance参数传递NULL。该参数用于16位windows系统,因而仍然将其保留为(w)WinMain的一个参数,目的只是为了我们移植16位windows程序。绝对不要在自己的代码中引用这个参数。因此,最好像下面这样写自己的(w)WinMain函数:
int WINAPI
_tWinMain (
HINSTANCE hInstanceExe,
HINSTANCE,
PSTR pszCmdLine,
Int nCmdShow);
由于没有为第二个参数指定参数名,所以编译器不会报告一个“参数没有被引用到”的警告。
进程的命令行
系统的创建一个新进程的时候,会传一个命令行给它。这个命令行几乎总是非空的;至少,用于创建新进程的可执行文件的名称是命令行上的第一个标记(Token)。不过在调用CreateProcess函数的时候能接受只由一个字符构成的命令行,即用于终止字符的0。C运行库的启动代码开始执行一个GUI程序的时候,会调用windows函数GetCommandLIne来获取进程的完整命令行,忽略可执行文件的名称,然后将指向命令行剩余部分的一个指针传给WinMain的pszCmdLine参数。
应用程序可以通过自己选择的任何一种方式来分析和解释命令行字符串。甚至我们可以写数据到pszCmdLine指向的内存缓冲区,但在任何情况下,我们都不应该写入,最好把它当作一个只读缓冲区对待。如果要对命令行进行改动,最好首先把命令行缓冲区复制到应用程序的一个本地缓冲区,然后再对本地缓冲区进行修改。
我们也可以效仿C运行库的做法,调用GetCommandLIne函数来获取一个指向进程完整命令行的指针:
PTSTR GetCommandLine();
该函数返回一个缓冲区指针,缓冲区中包含完整的命令行(包括可执行文件的完整路径名)。注意GetCommandLine总是返回同一个缓冲区的地址。这是不应该向pszCmdLine写入的另一个理由:它指向同一个缓冲区,修改它之后,我们就没办法知道原来的命令行是什么了。
虽然Microsoft反对继续使用全局变量__argc和__argv(或__wargv),但是应用程序仍然可以利用它们来访问命令行的每个标记。利用ShellAPI.h文件中声明并由Shell32.dll导出的函数CommandLineToArgW即可将任何Unicode字符串分解成单独的标记:
PWSTR* CommandLineToArgW (
PWSTR
pszCmdLine,
int*
pNUmArgs);
这个函数只有Unicode版本。参数pszCmdLine指向一个命令行字符串。这通常是GetCommandLineW函数的返回值。pNUmArgs会被设为命令行中实参的数目。CommandLineToArgW返回的是一个Unicode字符串指针数组的地址。
CommandLineToArgW在内部分配内存。许多应用程序不会释放这部分内存——它们依靠操作系统在进程终止时释放这块内存。这是完全可以接受的,不过,如果自己想释放内存,正确的做法是调用HeapFree:
int pNumArgs;
PWSTR *ppArgv =
CommandLineToArgvW(GetCommandLineW(), &pNumArgs);
if(*ppArgv[1] == L"x"){
//...
}
HeapFree(GetProcessHeap(),0,ppArgv);
分享到:
相关推荐
进程创建成功后,你可以通过进程ID和句柄进行后续操作,例如等待进程结束(`WaitForSingleObject`)、获取进程退出码(`GetExitCodeProcess`)等。此外,为了确保资源的有效管理,别忘了在适当的时候关闭不再使用的...
3. **进程终止**:一旦有了进程句柄,就可以调用TerminateProcess函数,向目标进程发送一个信号,使其立即停止执行并释放其占用的所有资源。需要注意的是,这种方法通常是强制性的,可能导致数据丢失,因此在非必要...
进程是操作系统中执行的程序实例,每个进程都有一个唯一的标识符——进程句柄。在Windows中,可以通过GetCurrentProcess() API函数获取当前进程的句柄,该句柄可以用于与进程对象交互,例如查询进程的优先级。...
`CreateProcess()`函数接收多个参数,如应用程序名称、命令行参数、进程和线程的安全属性、句柄继承标志、创建标志、环境变量、当前目录、启动信息结构和进程信息结构。这些参数用于定义新进程的属性和行为。 在...
### 知识点:隐藏自己的进程——通过进程注入DLL实现 #### 一、概述 在计算机安全领域,隐藏进程是一项关键技术,它可以帮助恶意软件或黑客工具躲避系统的检测和防御机制。其中一种常用的方法是通过DLL注入来实现...
Windows API函数`OpenProcess`用于获取进程句柄,`GetProcessTimes`获取进程运行时间,`TerminateProcess`用于结束进程。这需要具备一定的编程基础,但可以实现高度定制化的监控需求。 三、注意事项 - 结束进程前,...
1. 进程的查找:源码中可能会有查找特定进程的代码,这通常需要用到`EnumProcesses`和`OpenProcess`函数,通过遍历系统中所有的进程,获取目标进程的句柄。 2. 权限控制:由于涉及到对进程的控制,源码中可能会涉及...
这个函数接受进程句柄和最大等待时间作为参数。 #### 实验步骤与结果 1. **单进程运行**:在单进程模式下,两个进程按顺序执行。记录下总的执行时间。 2. **多进程运行**:将两个进程设置为并发执行。同样记录总...
【操作系统实验报告——进程的创建】 本实验报告主要围绕“进程的创建”这一主题展开,旨在让学生通过实际操作,理解并掌握操作系统中进程的概念、创建方法以及资源分配的基本原理。实验选用的操作系统环境是EOS。 ...
1. **实时进程信息**:ProcessExplorer不仅显示正在运行的进程,还包括每个进程的详细信息,如PID(进程ID)、线程、句柄数、内存使用情况、CPU占用率等,这使得用户可以更精确地了解系统的负载状态。 2. **高度可...
1. **确认进程合法性**:首先确认该进程是否为正常的Windows服务进程。可以通过查看控制面板中的“服务”来确认这一点。 2. **查看资源使用情况**:使用Process Explorer6查看该进程的CPU和内存占用情况,判断是否...
其中`hProcess`参数是通过`OpenProcess()`函数或其他等效方法获得的进程句柄,必须包含`PROCESS_TERMINATE`权限。`uExitCode`参数是进程退出时返回的代码,但关于其具体影响,官方文档表述模糊。 #### 任务管理器与...
在这个实验中,我们通过编写两个小程序——`mycp.c` 和 `mycat.c`,来深入理解这些基本概念。 `mycp.c` 是一个简单的文件复制程序,它使用原始的文件系统调用(如 `open()`, `read()`, `write()`, `close()`)来...
- 使用命令行工具`cl.exe`编译程序,命令格式为`C:\>CL 1-1.cpp`。 - 确保包含`#pragma comment(lib, "user32.lib")`指令,以正确链接用户界面库。 #### 3.2 进程句柄的获取与使用 - **进程句柄的作用**:操作...
操作系统实验报告——进程的创建 本实验主要围绕操作系统中的进程管理展开,重点在于理解和实践进程的创建过程。实验目标是通过使用操作系统的API函数,如`CreateProcess`,来创建和管理进程,深入理解进程与程序的...
在实际使用中,用户可以通过命令行参数或者图形界面与WindowUtil.exe交互,输入窗口句柄或其他标识符来查询相关信息。开发者则可以利用提供的API或SDK,将WindowUtil的功能集成到自己的应用中,提升软件的诊断和调试...
该函数接收多个参数,分别用于指定新进程的可执行文件名称、命令行参数、安全属性、环境变量、当前目录以及启动信息等。通过调用`CreateProcess`,`Explorer.exe`能够以指定的参数创建一个新的进程,从而启动目标...