您的位置:首页 > 其它

进程的入口函数、环境变量和当前目录

2014-12-01 16:32 197 查看
《windows核心编程》第四章--进程

好长的一章呀~~~!内容基本有五点: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可以获得任意一个驱动器的当前目录!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐