如何定义进程

进程一般定义为正在运行的程序的一个实例,由以下两部分组成:

  • 一个内核对象,用于保存进程统计信息并管理进程
  • 一的地址空间,其中包括所有可执行文件或动态链接库(DLL)模板的代码和数据,与此同时,还包含动态内存分配,比如线程堆栈和堆的分配

进程是惰性的,进程要做任何事情,都要让一个线程在它的上下文中运行,该线程负责指向进程地址空间包含的代码,当系统创建一个进程的时候,会自动为进程创建一个线程,称为主线程,然后这个主线程再创建更多的线程。
对于所有要运行的进程,操作系统会分配CPU时间片来运行这些进程,在宏观上并行,在微观上串行。
但是如果系统装备了多个CPU,就可以实现真正意义上的并发执行。

编写第一个Windows应用程序

GUI与CUI

GUI:图形用户界面
CUI:控制台用户界面

入口函数

_tmain与_tWinmain函数

应用程序类型 入口点函数 嵌入可执行文件的启动函数
处理ANSI字符和字符串的GUI应用程序 _tWinMain(Winmain) WinmainCRTStartup
处理Unicode字符和字符串的GUI应用程序 _tWinMain(wWinmain) wWinmainCRTStartup
处理ANSI字符和字符串的CUI应用程序 _tmain(Main) mainCRTStartup
处理Unicode字符和字符串的CUI应用程序 _tmain(Wmain) wmainCRTStartup

启动函数的用途:

  1. 获取指向新进程完整命令行的一个指针
  2. 获取指向新进程环境变量的一个指针
  3. 初始化C/C++运行库的全局变量,例如_osver(操作系统Build版本号)
  4. 初始化C运行库内存分配函数(malloc和calloc)和其他I/O利用使用的堆
  5. 调用所有全局和静态C++类对象的构造函数

入口函数返回后

入口函数返回后就会把返回值(nMainRetVal)传递给exit函数,然后会执行以下内容:

  1. 调用_onexit函数调用所注册的任何一个函数
  2. 调用所有全局和静态C++类对象的析构函数
  3. 如果是debug模式,设置了_CRTDBG_LEAK_CHECK_DF标志,会调用_CrtDumpMemoryLeaks函数生成内存泄露报告
  4. 调用API函数ExitProcess,传入nMainRetVal作为参数,操作系统会帮助结束进程,并且设置退出代码

进程实例句柄

加载到进程地址空间的每个可执行文件或动态链接库都被赋予了一个独一无二的实例句柄.。


在需要加载资源的函数调用中,一般都需要提供该句柄的值(LoadIcon函数的第一个参数)

在GetModuleFileName函数中,第一个参数的HMODULE类型实际上就是与前者就是一回事,之所以有两种数据类型,是因为在16位Windows中这两个类型表示的数据不同。

winmain函数中的HINSTANCE参数实际上就是一个内存基地址,系统将可执行文件的映像加载到了进程地址空间的这个位置,如果系统打开这个可执行文件,并将其加载到地址0x00400000,则winmain的HINSTANCE参数值位0x00400000。

可以通过GetModuleHandle函数返回可执行文件/动态链接库文件映像加载到的基地址,如果没找到就会返回NULL

还可以使用GetModuleHandleEx来获取:

第一个参数为GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,第二个参数为当前函数的地址,第三个参数是指向HMODULE的指针,该函数会使用第二个参数(传入函数所在的基地址)来填写该指针。
GetModuleHandle函数有两个重要特征:

  • 该函数只检查主调进程的地址空间,如果主调函数没有使用任何通用对话框函数,一旦调用GetModuleHandle,并向其传递ComDlg32,就会导致返回NULL
  • 调用该函数并向其传递NULL值,会返回进程的地址空间中的可执行文件的基地址

进程前一个实例的句柄

C/C++运行库启动代码总是向WinMain的hPrevInstance参数传递NULL,该参数用于16位Windows系统,因此仍然将其保留为Winmain的一个参数,在实际使用过程中千万不能引用该参数。

进程的命令行

系统在创建一个新进程时,会传给一个命令行给它,这个命令行基本上不会为空:命令行上的第一个标记(token)就是用于创建新进程的可执行文件的名称。

通过调用GetCommandLine函数来获得一个指向进程完整命令行的指针:

进程的环境变量

每个进程都有与它相关联的环境块,这是在进程地址空间内分配的一块内存,其中包含字符串类似:

=::=\...
VarName1=VarValue1\0
VarName1=VarValue2\0
VarName1=VarValue3\0
VarName1=VarValueN\0
\0

每个字符串的第一部分是一个环境变量的名称,等号之后是希望赋给此变量的值
获取完整的环境块:

释放环境块:

系统环境变量和用户环境变量

  • 系统环境变量:
    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
  • 用户环境变量:
    HKEY_CURRENT_USER\Environment

添加环境变量

可替换字符串:
原始:%USERPROFILE%
替换:C:/Users/…
用于添加环境变量:

进程的错误格式

进程可以调用一个名为SetErrorMode的API来告诉系统如何处理错误:

通过四个宏来对其定义处理错误方式的标志

SEM_FAILCRITICALERRORS   //系统不显示严重错误处理程序消息框,并将错误返回主调进程SEM_NOGPFAULTERRORBOX      //系统不显示常规保护错误消息框,此标志只应该由调试程序设置;//该调试程序用一个异常处理程序来自行处理常规保护错误SEM_NOOPENFILEERRORBOX     //系统查找文件失败时,不显示消息框SEM_NOALIGNMENTFAULTEXCEPT //系统自动修复内存对齐错误,并使应用程序看不到这些错误.此标准对x86/x64处理器无效

进程当前所在的驱动器和目录

如果不提供完整的路径名,各种Windows函数会在当前驱动器的当前目录查找文件和目录
一个线程可以调用两种函数来获取和设置当前所在位置的驱动器和目录:


如果提供的缓冲区不够大,GetCurrentDirectory函数就会返回保存文件夹所需要的字符数,而且不会往缓冲区复制任何内容,此时将缓冲区设置为NULL,就会返回字符串的长度(字符数),不包括结尾的’\0’

进程的当前目录

系统跟踪记录着进程的当前驱动的驱动器和目录,但是未记录每个驱动器的当前目录,而这些目录是通过环境变量来提供的,一个进程可以有如下所示的两个环境变量:

=C:=C:\Utilty\Bin
=D:=D:\Program Files

如果调用一个函数,且传入的路径名限定的是当前驱动器意外的驱动器,系统会在进程的环境块中查找与指定的驱动器相关联的变量如果找到,就当成当前目录使用,如果没有就是假定指定驱动器的当前目录是它的根目录.

可以通过GetFullPathName函数来得到当前目录:

系统版本

通过GetVersionExW函数获得所运行的版本信息:


LPOSVERSIONINFOW参数结构如下:

typedef struct _OSVERSIONINFOW {DWORD dwOSVersionInfoSize;      //初始化结构大小DWORD dwMajorVersion;           //主版本号DWORD dwMinorVersion;           //次版本号DWORD dwBuildNumber;            //构建版本号DWORD dwPlatformId;             //标识当前系统支持的套件WCHAR  szCSDVersion[ 128 ];     // Maintenance string for PSS usage,额外信息
} OSVERSIONINFOW, *POSVERSIONINFOW, *LPOSVERSIONINFOW, RTL_OSVERSIONINFOW, *PRTL_OSVERSIONINFOW;

Windows核心编程(四)进程-1相关推荐

  1. windows核心编程之进程间共享数据

    有时候我们会遇到window进程间共享数据的需求,例如说我想知道系统当前有多少某个进程的实例. 我们能够在程序中定义一个全局变量.初始化为0.每当程序启动后就加1.当然我们我们能够借助第三方介质来储存 ...

  2. 转 windows核心编程 学习笔记 目录

    windows核心编程--SEH(结构异常处理) SEH 的工作原理.         Windows 程序设计中最重要的理念就是消息传递,事件驱动.当GUI应用程序触发一个消息时,系统将把该消息放入 ...

  3. 虚拟内存——Windows核心编程学习手札之十四

    虚拟内存 --Windows核心编程学习手札之十四 系统信息 有些操作系统的值是根据主机而定的,如页面大小.分配粒度大小等,这些值不用硬编码形式,进程初始化时应检索这些值以使用.函数GetSystem ...

  4. 进程——Windows核心编程学习手札系列之四

    进程 --Windows核心编程学习手札系列之四 进程是一个正在运行的程序的实例,有两个部分组成:一个是操作系统用来管理进程的内核对象,内核对象是系统用来存放关于进程的统计信息的地方:另一个是地址空间 ...

  5. 异常处理程序和软件异常——Windows核心编程学习手札之二十四

    异常处理程序和软件异常 --Windows核心编程学习手札之二十四 CPU负责捕捉无效内存访问和用0除一个数值这种错误,并相应引发一个异常作为对错误的反应,CPU引发的异常称为硬件异常(hardwar ...

  6. 《windows核心编程系列》十八谈谈windows钩子

    windows应用程序是基于消息驱动的.各种应用程序对各种消息作出响应从而实现各种功能. windows钩子是windows消息处理机制的一个监视点,通过安装钩子能够达到监视指定窗体某种类型的消息的功 ...

  7. 窗口消息——Windows核心编程学习手札之二十六

    窗口消息 --Windows核心编程学习手札之二十六 Windows允许一个进程至多建立10000个不同类型的用户对象(user object):图符.光标.窗口类.菜单.加速键表等,当一个线程调用一 ...

  8. 内存映射文件——Windows核心编程学习手札之十七

    内存映射文件 --Windows核心编程学习手札之十七 与虚拟内存一样,内存映射文件保留地址空间,并将物理存储器提交给该区域,差别在于所提交的物理存储器是磁盘上有文件存在的空间,而非系统的页文件,一旦 ...

  9. 线程基础知识——Windows核心编程学习手札系列之六

    线程基础知识 --Windows核心编程学习手札系列之六 线程与进程一样由两部分构成:一是线程的内核对象,操作系统用它来对线程实施管理,也是系统用来存放线程统计信息的地方:二是线程堆栈,用于维护线程在 ...

  10. 作业——Windows核心编程学习手札系列之五

    作业 --Windows核心编程学习手札系列之五 Windows提供作业内核对象,可以将进程组合在千毫 ,并创建一个"沙框"以便限制进程能够进行的操作.作业可视为进程的容器,win ...

最新文章

  1. int 和integer的区别
  2. 对accuracy、precision、recall、F1-score、ROC-AUC、PRC-AUC的一些理解
  3. ElasticSearch 6.x 学习笔记:12.字段类型
  4. 3.菜鸟教你一步一步开发 web service 之 axis 服务端创建
  5. 大熊猫卸妆后_您不应错过的6大熊猫行动
  6. objectid.go源码阅读
  7. IntelliJ IDEA 2018.2.2远程调试Tomcat的配置方法
  8. 在Keil中利用AStyle插件格式化代码
  9. Creating and Interning Symbols
  10. javaSE基础之字符串
  11. fiddler使用详细教程
  12. HTML 标签参考手册 - 功能排序
  13. 服务器虚拟网卡驱动卸载,Win10安装和卸载万能网卡版驱动的方法
  14. 图片加密信息(16进制)
  15. python 协程加多线程下载asyncio、ThreadPoolExecutor
  16. 开机内存占用过高解决方案
  17. 搜狗输入法转语音体验报告
  18. 哈希表的介绍_以Python为例
  19. 最新Mysql 8.0.27安装指南
  20. 学习会计实操真的有用吗?

热门文章

  1. 使用js-export-excel插件实现前端导出excel表格
  2. 文字转载-《 我忍你,一辈子》
  3. 如何使用Selenium自动化测试工具获取动态图片验证码?
  4. cache-cloud 编译
  5. 用上赶机场带你行走天下,让你想去哪就去哪
  6. windows下通过关键字批量删除远程k8s下deployment及pods
  7. [附源码]Nodejs计算机毕业设计金牛社区疫情防控系统Express(程序+LW)
  8. NvInfer.h: No such file or directory
  9. 香港商报:高铁出行受港人热捧 逐渐融入市民生活
  10. 【Linux C语言查看SD卡大小】