Windows via C/C++ Chapter 4 : 进程 – 第一个程序

基础知识

1.一般将进程定义为一个正在运行的程序的一个实例,有两部分构成:
a.一个内核对象,操作系统用它来管理进程。内核对象也是系统保存进程体积信息的地方。
b.一个地址空间,其中包含所有可执行文件(executable)或DLL模块的代码和数据。此外,它还包含动态内存分配,比如线程堆栈和堆的分配。
2.进程是有“惰性”的。进程要做任何事情,都必须让一个线程在它的上下文中运行。如果没有线程要执行进程地址空间包含的代码,进程就失去了继续存在的理由。这是系统会自动销毁进程及其地址空间。
3.对于所有要运行的线程,操作系统会轮流为每个线程调度一些CPU时间。它会采取循环(round-robin,轮询或轮流)方式,为每个线程都分配时间片(称为“量”或者“量程”,即quantum),从而营造出所有线程都在“并发”运行的假象。
4.Windows支持两种类型的应用程序:GUI程序和CUI程序。前者是“图形用户界面”(Graphical User Interface)的简称,后者是“控制台用户界面”(Console User Interface)的简称。
5.Windows应用程序可以使用以下两种入口函数:_tWinMain()和_tmain()。
6.加载到进程地址空间的每一个可执行文件或者DLL文件都被赋予了一个独一无二的实例句柄。可执行文件的实例被当作(w)WinMain函数的第一个参数hInstanceExe传入。在需要加载资源的函数调用中,一般都要提供此句柄的值。
7.为了知道一个可执行文件或DLL文件被加载到进程地址空间的什么位置,可以使用如下的GetModuleHandle函数来返回一个句柄/基地址:
HMODULE GetModuleHandle(PCTSTR pszModule);
8.每个进程都有一个与它关联的环境块(environment block),这是在进程地址空间内存分配的一块内存。
9.进程中的线程可以在主机的任何CPU上执行。然而,也可以强迫线程在可用CPU的一个子集上运行,这称为“处理器关联性”(processor affinity)。
10.如果不提供完整的路径名,各种Windows函数会在当前驱动器的当前目录查找文件和目录。
11.可以用GetVersion函数获取用户运行的Windows系统版本。
Continue Reading…

Windows via C/C++ Chapter 3 : 内核对象

基础知识

1.理解Windows应用程序编程接口(application programming interface,API),首先需要探讨一下内核对象(kernel object)及其句柄(handle)。在系统和我们写的应用程序中,内核对象用于管理进程、线程和文件等诸多种类的大量资源。
2.每个内核对象都只是一个内存块,它由操作系统内核分配,并只能由操作系统内核访问。这个内存块是一个数据结构,其成员维护着与对象相关的信息。少数成员(安全描述符和使用计数等)是所有对象都有的,但其他大多数成员都是不同类型的对象持有的。
3.内核对象的数据结构只能有操作系统访问,所以应用程序不能在内存中定位这些数据结构并直接修改内容。Microsoft有意强化了这个限制,所以Microsoft能自由地添加、删除和修改这些结构中的成员,同时不会干扰任何应用程序的正常运行。
4.利用Windows提供的一组函数,这组函数会以恰当的方式来操纵这些结构。我们始终可以使用这些函数来访问这些内核对象。调用一个会创建内核对象的函数以后,函数会返回一个句柄(handle),它标识了所创建的对象。
5.内核对象的所有者是操作系统内核而不是进程。如果进程调用一个函数来创建内核对象,进程终止运行时内核对象并不一定会销毁。每个对象都包含一个使用计数(usage count),是每个内核对象类型都有的一个数据成员。初次创建一个对象时,使用计数为1,另一个进程获得现有内核对象访问后,使用计数递增。进程终止后操作系统会自动递减该对象的使用计数。一旦对象的使用计数变为0,操作系统内核就会销毁该对象。
6.内核对象可以用一个安全描述符(security descriptor,SD)来保护。安全描述符描述了谁(通常是对象的创建者)拥有对象;哪些组和用户被允许访问或使用此对象;哪些组和用户被拒绝访问此对象。
7.一个进程初始化时,系统将会为它分配一个句柄表(handle table)。首次初始化的时候,其句柄表为空。当进程内的一个线程调用一个会创建内核对象的函数时,内核将为这个对象分配并初始化一个内存块。然后,内核扫描进程的句柄表,查找一个空白的记录项(empty entry),对其进行初始化。用于创建内核对象的任何函数都会返回一个与线程相关的句柄,这个句柄可由同一个进程中运行的所有线程使用。
8.句柄被设计成“与进程相关的”(process-relative),其中最重要的是原因是健壮性/可靠性,还有安全性。
9.只有在进程之间有一个父-子关系的时候,才可以使用对象句柄继承。只有句柄才是可以继承的,对象本身不能继承。
10.为了使子进程得到它想要的一个内核对象的句柄值,最常见的方式是将句柄值作为命令行参数传给子进程。子进程初始化代码将解析命令行(通常调用_stscanf_s),并提取句柄值。子进程获得句柄值后,就会拥有和父进程一样的内核对象访问权限。句柄继承之所以能够实现,唯一的原因就是“共享的内核对象”的句柄值在父进程和子进程中是完全一样的。这正是父进程能将句柄值作为命令行参数来传递的原因。
11.跨进程边界共享内核对象的方法有:使用对象句柄继承、为对象命名、复制对象句柄。
12.终端服务(Terminal Service)情况不同,正在运行终端服务的计算机中,有多个用于内核对象的命名空间。其中一个是全局命名空间,所有客户端都能访问的内核对象都放在这个命名空间中。这命名空间主要由服务使用。
13.想确保我们的应用程序创建的内核对象名称永远不会和其他应用程序名称冲突,或者想确保它们免遭劫持,可以定义一个自定义的前缀,并把它作为自己的专有命名空间使用,这和使用Global和Local前缀是相似的。
Continue Reading…

理解并使用 Windows 函数

我们在Windows上进行一些开发的时候,能善于利用Windows开放出来的应用程序编程接口(Windows API)往往能使我们事半功倍。而且往往有些操作不可避免的需要使用Windows的内部函数。
Microsoft在MSDN上将每个Windows函数的原型和使用方法(基于C++)都公布出来了。

(小技巧:在已知函数名的情况下可以百度或Google“函数名 function (Windows)”获得其在MSDN中的页面。)

怎样阅读并使用Windows API?

以ShellExecute函数为例:

http://msdn.microsoft.com/en-us/library/windows/desktop/bb762153%28v=vs.85%29.aspx

函数原型句法(Syntax):

Continue Reading…