进程的入口函数、环境变量和当前目录
2014-12-01 16:32
197 查看
《windows核心编程》第四章--进程
好长的一章呀~~~!内容基本有五点:1.Win32程序的基本内容;2.CreateProcess函数;3.关于子进程;4.如何终止一个进程;5.用户权限;
以及利用toolhelp编写的类似于任务管理器的一个小工具。
下面是一些笔记:
首先,我们应该知道PC中跑的一个程序,也就是一个进程可以被分为两个部分即内核对象和地址空间,内核对象自不必说,地址空间是程序加载可执行文件和DLL模块以及动态内存分配的地方!
在WIN32中,我们的程序可以被分为两类GUI和CUI程序,尽管他们的界限很模糊,但是他们对应的主函数却不同!
编译器通过判定子系统开关/SUBSYSTEM判断程序了入口函数,两种类型的唯一区别是:当从_tmain函数启动时,系统会为你的程序创建一个控制台,换句话说,当程序启动之后,这两总类型的程序其实差别不大!!
关于windows是如何调用程序的主函数的,这里书中劈头盖脸说了好多,但是我没怎么看懂!!!具体说来有可能是以下这个过程:
首先连接器会查询/SUBSYSTEM开关,根据子系统类型插入一个函数来启动C/C++运行库(这个运行库,在这本书中屡屡出现,但是作者丝毫没有要介绍一下的意思 !!!!)
C/C++运行库的启动函数是这样来选择的:
上面这四个函数完成事大概有这些:
1.初始化C/C++运行库,这样我们才能调用运行库中的函数(比如free,malloc什么的!!),初始化运行库的全局变量(这里面包含环境变量和命令行)
2.初始化程序的全局变量和一些静态的变量
3.这一点最重要:获取程序的命令行和环境变量!最后程序,调用我们定义的main函数!!!!
main函数先返回到运行库,运行库调用exit函数,这个函数析构全局对象和静态对象并且调用ExitProcess退出!!!因此,我们可以发现,一旦主线程退出,整个程序就升天了,而且不留一点痕迹,可以说火化的很干净!同时我们可以发现,ExitProcess是exit调用的,如果直接调用它全局的对象无法正确析构。
下面我讨论一下HINSTANCE,环境变量,以及命令行
HINSTANCE也就是进程实例句柄,书中提到HMODULE和他是一回事,俩个类型可以混用!!
这个参数是WinMain函数的第一个参数,他表示一个内存基地址(就是EXE文件加载到进程地址空间的位置!!)这个地址我们可以在连接器中设定(/BASE:address),
vs2010中我们可以设定为加载到固定地址(默认0X00400000),或者随机地址!
下面总结了获取hIns的几种方法:
1.保存成全局变量;
2.通过窗口句柄获得
3.通过窗口过程消息参数lParam获得
4.对于函数GetModuleHandle我们如果传入NULL函数返回的就是相应的hIns
5.连接器的临时变量__ImageBase表示当前运行模块的基地址
WinMain函数中还有一个参数hPreInstance,我们一般不用,在定义函数时,我们可以只声明他,但是不定义它的变量名!!
进程的命令行
我们可以用GetCommandLine函数我们完整的命令行!C/C++运行库正是如此,之后对于GUI程序,可执行文件的名称被忽略,剩下的信息被传递个参数pszCmdLine,对于CUI程序,argv中获得的命令行是完整的,argv[0]指向可执行文件的名称!
我们可以调用函数CommandLineToArgvW分解从GetCommandLine得到的命令行串,这个函数在函数内部动态分配内存,因此使用完之后要调用HeapFree释放内存空间。
进程的环境变量
正如书中所说,每一个进程都有一个以=::=::\开头的环境块,我们可以预计这个标记之后紧接的就是格式为VarName1=VarValue1\0的环境变量!!(书中有一个点没描述清楚,=::=::\之后紧跟一个\0)!
下面的函数输出环境变量!
注意,函数GetEnvironmentStrings返回的内存是内部分配的,需要用专门的函数FreeEnvironmentStrings释放!!
同时,我们还应该留意,对于环境变量来说,空格也算一个字符!!
下面几个函数能让我操纵程序的环境变量
当前所在驱动器目录
这个很好理解,GetCurrentDirectory函数和SetCurrentDirectory函数很方便能获得或者设定进程的当前所在驱动器目录(这里提一下一个常量MAX_PATH,这个值是当前驱动器目录的最大字符数!)!一般情况下当前所在驱动器目录的默认值就是EXE文件所在的那个文件夹!
除了这个当前所在驱动器,硬盘里还有其他的驱动器,驱动器这些驱动器也有一个当前目录!我们把这些目录信息保存在环境变量里,叫做进程的当前目录!
举个例子:我们在C: \ LQ \ test.exe,当我运行test时,默认条件下当前所在的驱动器和目录就是C: \ LQ \。但是,我还可以为我的每个驱动器指定一个当前目录,我可以把它保存在环境块里!
比如这样:
=D:=D: \ LBN
=E:=E: \ CLP
那么上面这个的东西有什么用呢?我们知道:程序要打开一个文件,会按照下面的目录搜索
1。EXE文件所在的目录;2。进程的当前目录;3。Windows系统目录;4.Windows目录.;5。Path中列出的目录;
如果我们可以直接访问进程的当前目录!比如我要打开D: \ LBN中的Read.txt。我只要这样OpenFile(D:Read.txt),程序会直接在D: \ LBN路径下寻找目录!
我们应该使用_chdir来更改当前目录(<direct.h>),这个函数会修改或者保存当前目录到环境变量!调要GetFullPathName可以获得任意一个驱动器的当前目录!
好长的一章呀~~~!内容基本有五点:1.Win32程序的基本内容;2.CreateProcess函数;3.关于子进程;4.如何终止一个进程;5.用户权限;
以及利用toolhelp编写的类似于任务管理器的一个小工具。
下面是一些笔记:
首先,我们应该知道PC中跑的一个程序,也就是一个进程可以被分为两个部分即内核对象和地址空间,内核对象自不必说,地址空间是程序加载可执行文件和DLL模块以及动态内存分配的地方!
在WIN32中,我们的程序可以被分为两类GUI和CUI程序,尽管他们的界限很模糊,但是他们对应的主函数却不同!
int _tmain(int argc,TCHAR** argv,TCHAR** envp);//控制台程序 int WINAPI _tWinMain(HINSTANCE hIns,HINSTANCE,PTSTR pszCmdLine,int nCmdShow)//窗口程序
编译器通过判定子系统开关/SUBSYSTEM判断程序了入口函数,两种类型的唯一区别是:当从_tmain函数启动时,系统会为你的程序创建一个控制台,换句话说,当程序启动之后,这两总类型的程序其实差别不大!!
关于windows是如何调用程序的主函数的,这里书中劈头盖脸说了好多,但是我没怎么看懂!!!具体说来有可能是以下这个过程:
首先连接器会查询/SUBSYSTEM开关,根据子系统类型插入一个函数来启动C/C++运行库(这个运行库,在这本书中屡屡出现,但是作者丝毫没有要介绍一下的意思 !!!!)
C/C++运行库的启动函数是这样来选择的:
/subsystem:"console" /entry:"mainCRTStartup" (ANSI) /subsystem:"console" /entry:"wmainCRTStartuup" (UNICODE)
/subsystem:"windows" /entry:"WinMain" (ANSI) /sbusystem:"windows" /entry:"wWinMain" (UINCODE)系统不会直接调用我们定义的main函数,而调用上面四个函数之一,由他们来调用。
上面这四个函数完成事大概有这些:
1.初始化C/C++运行库,这样我们才能调用运行库中的函数(比如free,malloc什么的!!),初始化运行库的全局变量(这里面包含环境变量和命令行)
2.初始化程序的全局变量和一些静态的变量
3.这一点最重要:获取程序的命令行和环境变量!最后程序,调用我们定义的main函数!!!!
main函数先返回到运行库,运行库调用exit函数,这个函数析构全局对象和静态对象并且调用ExitProcess退出!!!因此,我们可以发现,一旦主线程退出,整个程序就升天了,而且不留一点痕迹,可以说火化的很干净!同时我们可以发现,ExitProcess是exit调用的,如果直接调用它全局的对象无法正确析构。
下面我讨论一下HINSTANCE,环境变量,以及命令行
HINSTANCE也就是进程实例句柄,书中提到HMODULE和他是一回事,俩个类型可以混用!!
这个参数是WinMain函数的第一个参数,他表示一个内存基地址(就是EXE文件加载到进程地址空间的位置!!)这个地址我们可以在连接器中设定(/BASE:address),
vs2010中我们可以设定为加载到固定地址(默认0X00400000),或者随机地址!
下面总结了获取hIns的几种方法:
1.保存成全局变量;
2.通过窗口句柄获得
HINSTANCE hIns=(HINSTANCE)GetWindowLong(HWND,GWL_HINSTANCE);//该函数获得指定窗口的有关信息
3.通过窗口过程消息参数lParam获得
((LPCREATESTRUCT)lParam)->hInstance;上面三种方法对GUI程序比较方便!对于CUI程序,我们可以使用函数GetModuleHandle以及连接器的临时变量!
4.对于函数GetModuleHandle我们如果传入NULL函数返回的就是相应的hIns
HINSTANCE hIns=GetModuleHandle(NULL);
5.连接器的临时变量__ImageBase表示当前运行模块的基地址
extern "C" const IMAGE_DOS_HEADER __ImageBase; _tprintf("%d",(HINSTANCE),&__ImageBase);
WinMain函数中还有一个参数hPreInstance,我们一般不用,在定义函数时,我们可以只声明他,但是不定义它的变量名!!
进程的命令行
我们可以用GetCommandLine函数我们完整的命令行!C/C++运行库正是如此,之后对于GUI程序,可执行文件的名称被忽略,剩下的信息被传递个参数pszCmdLine,对于CUI程序,argv中获得的命令行是完整的,argv[0]指向可执行文件的名称!
我们可以调用函数CommandLineToArgvW分解从GetCommandLine得到的命令行串,这个函数在函数内部动态分配内存,因此使用完之后要调用HeapFree释放内存空间。
进程的环境变量
正如书中所说,每一个进程都有一个以=::=::\开头的环境块,我们可以预计这个标记之后紧接的就是格式为VarName1=VarValue1\0的环境变量!!(书中有一个点没描述清楚,=::=::\之后紧跟一个\0)!
下面的函数输出环境变量!
void PrintEnvirBlock() { PTSTR ENVBlock=GetEnvironmentStrings(); PTSTR pCur=ENVBlock; if(ENVBlock == NULL) { _tprintf(TEXT("获取环境块失败(%d)\n"), GetLastError()); return; } //环境变量字符串是以\0分隔的,内存块以\0结尾 pCur+=_tcslen(TEXT("=::=::\\")); pCur+=1; while(*pCur) { _tprintf(TEXT("%s\n"),pCur); pCur+= _tcslen(pCur) + 1; //移动指针,跳过一个\0 } FreeEnvironmentStrings(ENVBlock); }
注意,函数GetEnvironmentStrings返回的内存是内部分配的,需要用专门的函数FreeEnvironmentStrings释放!!
同时,我们还应该留意,对于环境变量来说,空格也算一个字符!!
下面几个函数能让我操纵程序的环境变量
DWORD GetEnvironmentVariable(PCTSTR pszName,PTSTR pszValue,DWORD cchValue); //查找对应的环境变量(名称为pszName),后面两个参数指向保存环境变量值的缓冲区和其大小,允许传送以个NULL获得环境变量的长度 DWORD ExpandEnvironmentStrings(PCTSTR pszSrc,PTSTR pszDst,DWORD chSize); //用来进两个百分号之间的替换,比如替换字符串%HOME% BOOL SetEnvironmentVariable(PCTSTR pszName,PCTSTR pszValue); //添加、修改、删除一个环境变量!如果环境变量名存在就修改,不存在就添加,如果pszValue=NULL,就删除对应环境变量
当前所在驱动器目录
这个很好理解,GetCurrentDirectory函数和SetCurrentDirectory函数很方便能获得或者设定进程的当前所在驱动器目录(这里提一下一个常量MAX_PATH,这个值是当前驱动器目录的最大字符数!)!一般情况下当前所在驱动器目录的默认值就是EXE文件所在的那个文件夹!
除了这个当前所在驱动器,硬盘里还有其他的驱动器,驱动器这些驱动器也有一个当前目录!我们把这些目录信息保存在环境变量里,叫做进程的当前目录!
举个例子:我们在C: \ LQ \ test.exe,当我运行test时,默认条件下当前所在的驱动器和目录就是C: \ LQ \。但是,我还可以为我的每个驱动器指定一个当前目录,我可以把它保存在环境块里!
比如这样:
=D:=D: \ LBN
=E:=E: \ CLP
那么上面这个的东西有什么用呢?我们知道:程序要打开一个文件,会按照下面的目录搜索
1。EXE文件所在的目录;2。进程的当前目录;3。Windows系统目录;4.Windows目录.;5。Path中列出的目录;
如果我们可以直接访问进程的当前目录!比如我要打开D: \ LBN中的Read.txt。我只要这样OpenFile(D:Read.txt),程序会直接在D: \ LBN路径下寻找目录!
我们应该使用_chdir来更改当前目录(<direct.h>),这个函数会修改或者保存当前目录到环境变量!调要GetFullPathName可以获得任意一个驱动器的当前目录!
相关文章推荐
- Windows命令行获取当前bat文件所在目录,添加永久系统环境变量的方法
- PHP输出当前进程所有变量 / 常量 / 模块 / 函数 / 类
- PHP输出当前进程所有变量 / 常量 / 模块 / 函数 / 类
- 【C语言】【unix c】如何将当前目录添加到环境变量
- PHP输出当前进程所有变量 / 常量 / 模块 / 函数 / 类
- Windows命令行获取当前bat文件所在目录,添加永久系统环境变量的方法
- 与进程的环境变量相关的函数
- 修改环境变量为当前工作目录的.bat文件
- PHP输出当前进程所有变量 / 常量 / 模块 / 函数 / 类
- ubuntu 添加当前目录到环境变量
- PHP输出当前进程所有变量 / 常量 / 模块 / 函数 / 类
- PHP输出当前进程所有变量 / 常量 / 模块 / 函数 / 类
- linux 打印当前进程环境变量
- windows下批处理更新当前目录到系统环境变量中
- 64位环境下32位进程获取64位进程的命令行参数和当前目录
- PHP输出当前进程所有变量 / 常量 / 模块 / 函数 / 类
- 选择目录,选择文件夹的COM组件问题。在可以调用 OLE 之前,必须将当前线程设置为单线程单元(STA)模式。请确保您的 Main 函数带有 STAThreadAttribute 标记。 只有将调试器附加到该进程才会引发此异常。
- PHP输出当前进程所有变量 / 常量 / 模块 / 函数 / 类
- Linux系统environ环境变量 打印当前进程环境变量信息
- PHP输出当前进程所有变量 / 常量 / 模块 / 函数 / 类