一、异常

  • 来源
    • CPU执行时检测到指令规范中定义的非法情况
    • 特殊指令(INT 3)
    • 硬件错误

二、中断

  • 中断请求的目的是希望CPU暂时停止执行 当前正在执行的程序,转去执行中断请求所对应的中断处理例程

三、函数调用约定

cdecl

cdecl调用约定又称为C调用约定,是c/c++语言缺省的调用约定。参数按照从右至左的方式入栈,函数本身不清理栈,此工作有调用者负责,返回值在eax中。由于由调用者清理栈,所以允许可变参数函数存在(⽀持不确定的参数个数)。

stdcall

stdcall常用于win API,参数按照从右至左的方式入栈函数自身清理堆栈,返回值在eax中。参数个数必须确定。

fastcall

fastcall的调用方式运行相对快,因为它通过寄存器来传递参数剩下的参数按照从右至左的方式入栈函数自身清理堆栈。
Win32平台(x86): 前两个参数采⽤ECX、 EDX传递,其余参数采⽤栈传递
Win64平台(x64): 前4个参数采⽤RCX 、 RDX 、 R8、 R9传递,其余参数采⽤栈传递 注:如果有this指针,则this指针通过RCX传递(作为第⼀个参数)
64位Linux平台(x64): 前6个参数采⽤RDI、 RSI、 RDX、 RCX、 R8、 R9传递,其余参数采⽤栈传递

四、windows内核加载器

五、调试器原理

1、调试事件与调试循环

  • 启动被调试程序:CreateProcess
    • 第六个参数使用了DEBUG_ONLY_THIS_PROCESS,这意味着调用CreateProcess的进程成为了调试器,而它启动的子进程成了被调试的进程。除了DEBUG_ONLY_THIS_PROCESS之外,还可以使用DEBUG_PROCESS,两者的不同在于:DEBUG_PROCESS会调试被调试进程以及它的所有子进程,而DEBUG_ONLY_THIS_PROCESS只调试被调试进程,不调试它的子进程。一般情况下我们只想调试一个进程,所以应使用后者。
    • 建议在第六个参数中加上CREATE_NEW_CONSOLE标记,被调试程序就会在一个新的控制台窗口中输出信息
  • 调试循环

    • 一个进程成为被调试进程之后,在完成了某些操作或者发生异常时,它会发送通知给调试器,然后将自身挂起,直到调试器命令它继续执行。这有点像Windows窗口的消息机制。
    • 被调试进程发送的通知称为调试事件DEBUG_EVENT结构体描述了调试事件的内容
    • 调试器通过WaitForDebugEvent函数获取调试事件,通过ContinueDebugEvent继续被调试进程的执行。ContinueDebugEvent有三个参数,第一和第二个参数分别是进程ID和线程ID,表示让指定进程内的指定线程继续执行。通常这是在一个循环中完成的。

      2、异常

  • 分类:错误异常,陷阱异常以及中止异常

    • 错误异常恢复执行时,是从引发异常的那条指令开始执行;而陷阱异常是从引发异常那条指令的下一条指令开始执行。中止异常属于严重的错误,程序不可以再继续执行。

image.png

  • 读取寄存器
    • 获取线程上下文环境:GetThreadContext,第二个参数指向CONTEXT结构,调用之前设置结构中的ContextFlags字段的值,指明想获得那部分寄存器的值,调用GetThreadContext后CONTEXT
    • 相应字段就会被赋值。
  • 读取内存
    • ReadProcessMemory
      • 要想成功读取到进程的内存,需要两个条件:一是hProcess句柄具有PROCESS_VM_READ的权限;二是由lpBaseAddress和nSize指定的内存范围必须位于用户模式地址空间(0x00010000~0x7FFEFFFF)内,而且是已分配的。
        • 在32位保护模式下,进程的4GB地址空间是虚拟的,在物理内存中不存在。如果要使用某一部分地址空间的话,必须先向操作系统提交申请,让操作系统为这部分地址空间分配物理内存。只有经过分配之后的地址空间才是可访问的,试图访问未分配的地址空间仍然会引发ACCESS_VIOLATION异常。

3、调试符号

  • 源代码中每个符号的名称
    • 编译器在编译每个源文件的时候都会收集该源文件中的符号的信息,在生成目标文件的时候将这些信息保存到符号表中。链接器使用符号表中的信息将各个目标文件链接成可执行文件,同时将多个符号表整合成一个文件,这个文件就是用于调试的符号文件,它既可以嵌入可执行文件中,也可以独立存在。
  • 创建一个符号处理器使用SymInitialize函数
  • 清理:SymCleanup函数就可以完成这个操作,该函数接受一个符号处理器的标识符。

4、断点

  • 软件断点(CC断点)int3
    • 软件断点总述
    • 原理
    • 对于eip-1的理解:
  • 内存断点
  • 硬件断点
  • 单步断点(TF)
  • 条件断点
  • 消息断点

image.png

5、单步执行

  • StepIn
  • StepOver
    • 检查当前指令是否CALL指令。如果是,则在下一条指令设置一个断点,然后让被调试进程继续运行;如果不是,则设置TF位,开始CPU的单步执行
  • StepOut
    • 获取当前函数的RET指令地址,并在该指令设置断点,让被调试进程继续执行。
    • 处理断点异常时,恢复断点所在指令的第一个字节。从线程栈的顶部中获取返回地址,在该地址设置断点,然后让被调试进程继续执行。
    • 再次处理断点异常,恢复断点所在指令的第一个字节,结束StepOut。

6、显示函数调用栈

  • 这个栈是由高地址向低地址增长的,即栈底的地址比栈顶的地址大。ESP寄存器的值是栈顶的地址,通过增加或减小ESP的值可以缩减或扩大栈的大小

image.png

六、从源文件到可执行文件

image.png

1、预处理

预处理过程主要处理那些源文件中的以“#”开始的预编译指令。包括#include,#define, #if,等等。

主要的处理规则如下:

(1)将所有的#define删除,并且展开所有的宏。如#define a b 就是将所有的a替换成b。但作为字符串常量a则不替换。
(2)处理所有的条件预编译指令,如#if,#ifdef,#else,#endif(以此来决定对哪些代码进行处理,将那些不必要的代码过滤掉)
(3)处理#include预编译指令,将被包含的文件插入到该预编译指令的位置。()这个过程是递归进行的。其中系统提供的头文件一般放在/usr/include下面,用<>表示。开发人员自定义的头文件放在与源程序同一个目录下,用“”表示。
(4)过滤所有的注释“//“和”/ /“之间的内容。
(5)添加行号和文件名标识。比如 #2 “test.c” 2
(6)保留所有的#pragma编译器指令,因为编译器需要使用他们。

2、编译、汇编

预处理的文件进行一系列的词法分析语法分析语义分析以及优化后产生相应的汇编代码文件。
image.png

3、链接

把各个模块间的引用处理好。

  • 地址和空间分配
  • 符号决议
  • 重定位

1)静态链接

  • 对函数库的链接是放在编译时期完成的是静态链接(xxx.a)

2)动态链接

  • 把库函数的链接载入推迟到程序运行时期(runtime xxx.so)

七、NAT

网络地址转换(Network Address Translation)简称为NAT,是将IP数据包包头中的IP地址转换为另一个IP地址的协议。 当IP数据包通过路由器或者安全网关时,路由器或者安全网关会把IP数据包的源IP地址和/或者目的IP地址进行转换。

八、VPN

让外地员工访问到内网资源,利用VPN的解决方法就是在内网中架设一台VPN服务器。外地员工在当地连上互联网后,通过互联网连接VPN服务器,然后通过VPN服务器进入企业内网。为了保证数据安全,VPN服务器和客户机之间的通讯数据都进行了加密处理。有了数据加密,就可以认为数据是在一条专用的数据链路上进行安全传输,就如同专门架设了一个专用网络一样,但实际上VPN使用的是互联网上的公用链路,因此VPN称为虚拟专用网络,其实质上就是利用加密技术在公网上封装出一个数据通讯隧道。有了VPN技术,用户无论是在外地出差还是在家中办公,只要能上互联网就能利用VPN访问内网资源,这就是VPN在企业中应用得如此广泛的原因。

九、各个类型在64和32位系统中所占字节数

1、64位系统和32位有什么区别?

  • 64bit CPU拥有更大的寻址能力,最大支持到16GB内存(2^64),而32bit只支持4G内存(2^32)
  • 64位CPU一次可提取64位数据,比32位提高了一倍,理论上性能会提升1倍。但这是建立在64bit操作系统,64bit软件的基础上的。

2、xx位处理器

  • 处理器(CPU)一次处理数据的能力也是2的倍数。8位处理器、16位处理器、32位处理器和64位处理器,其计数都是2的倍数。一次处理的数据越大,该电脑处理信息的能力越大;因此64位处理在先天就比32位处理器具有快速的能力。那为什么不用更高级的128位处理器呢?因为位数越高,处理器芯片的设计也就越复杂,目前的技术水平暂时无法制造这么复杂的芯片。

3、数据类型对应字节数(编译器决定)

  • long/size_t 等于机器位数(32位机器为4,64位机器为8)
  • 指针 等于机器位数
  • 引用 等于所引用数据类型
    1. sizeof() //C
    2. sys.getsizeof() //python
数据类型 32bit 64bit 取值范围
bool 1 1 true,false
char 1 1 -128~127
unsigned char 1 1 0~255
short 2 2 -32768~32767
unsigned short 2 2 0~65535
int 4 4 -2147483648~2147483647
unsigned int 4 4 0~4294967295
long 4 8
unsigned long 4 8
long long 8 8 -2^64~2^64-1
float 4 4 范围-2^128~2^128精度为6~7位有效数字
double 8 8 范围-2^1024~2^1024精度为15~16位
long double 8 8 范围-2^1024~2^1024精度为15~16位
* 4 8

十、C++中类所占地址空间

  • 类所占内存的大小是由成员变量(静态变量除外)决定的,成员函数(这是笼统的说,后面会细说)是不计算在内的。同类对象的成员函数的代码是共享的
  • 创建一个对象时,为该对象分配的存储空间为它的数据成员所占用的存储空间的总和
  • 内存字节对齐!!:内存按每8个字节分一块(64位机)【32位机以4字节分一块】,内存会对齐到类内定义的最大的那个数据类型的size上,类内所有的变量根据定义顺序占的内存都会按照那个size对齐。

    1、情况一

  • c++要求每个实例在内存中都有独一无二的地址, 空类也会被实例化,所以编译器会给空类隐含的添加一个字节,这样空类实例化之后就有了独一无二的地址了。所以空类的sizeof为1。 ```c class CBase { }; sizeof(CBase)=1;

class C{ static int a; }; sizeof(C)=1;

  1. <a name="zMd6m"></a>
  2. ## 2、情况二
  3. - 记得对齐的问题。int 占4字节//注意这点和struct的对齐原则很像!!!!!char占一字节,补齐3字节
  4. ```c
  5. class CBase
  6. {
  7. int a;
  8. char p;
  9. };
  10. sizeof(CBase)=8;

3、情况三

  • C++ 类中有虚函数的时候有一个指向虚函数的指针(vptr),在32位系统分配指针大小为4字节。无论多少个虚函数,只有这一个指针,4字节(32位机,8字节64位机)。注意一般的函数是没有这个指针的,而且也不占类的内存。
    1. class CBase
    2. {
    3. public:
    4. CBase(void);
    5. virtual ~CBase(void); //4
    6. private:
    7. int a; //4
    8. char *p; //4
    9. };
    10. sizeof(CBase)=12

4、情况四

  • 可见子类的大小是本身成员变量的大小加上父类的大小。其中有一部分是虚拟函数表的原因,一定要知道父类子类共享一个虚函数指针(!!)
    1. class CChild : public CBase
    2. {
    3. public:
    4. CChild(void);
    5. ~CChild(void);
    6. virtual void test();
    7. private:
    8. int b;
    9. };
    10. sizeof(CChild)=16

5、情况五

  • 类d是由类b,c派生来的,它的大小应该为二者之和5,为什么却是8 呢?这是因为为了提高实例在内存中的存取效率.类的大小往往被调整到系统的整数倍.并采取就近的法则,里哪个最近的倍数,就是该类的大小,所以类d的大小为8个字节. ```c

    include

class a {}; //1

class b{}; //1

class c:public a{//8

  1. virtual void fun()=0;//纯虚函数,这种函数在派生类中必须重写,否则该派生来也是个虚基类,而虚基类是不能定义对象的

};

class d:public b,public c{};//8

int main() {

  1. cout<<"sizeof(a)"<<sizeof(a)<<endl;
  2. cout<<"sizeof(b)"<<sizeof(b)<<endl;
  3. cout<<"sizeof(c)"<<sizeof(c)<<endl;
  4. cout<<"sizeof(d)"<<sizeof(d)<<endl;
  5. return 0;

}

  1. <a name="xv7Zj"></a>
  2. ## 总结
  3. 空的类是会占用内存空间的,而且大小是1,原因是C++要求每个实例在内存中都有独一无二的地址。<br />(一)**类内部的成员变量:**
  4. - **普通的变量**:是要占用内存的,但是要注意对齐原则(这点和struct类型很相似)。
  5. - **static修饰的静态变量**:不占用内存,原因是编译器将其放在全局变量区。
  6. (二)**类内部的成员函数:**
  7. - **普通函数**:不占用内存。
  8. - **虚函数**:要占用4个字节,用来指定虚函数的虚拟函数表的入口地址。所以一个类的虚函数所占用的地址是不变的,和虚函数的个数是没有关系的
  9. <a name="PHQrW"></a>
  10. # 十一、C/C++里的引用和指针,在汇编层面没有区别
  11. - 可以把引用看做是通过一个**指针常量**来实现的,它只能绑定到初始化它的对象上。计算机必须**在声明**引用**的同时就要对它初始化**,并且,引用**一经声明,就不可以再和其它对象绑定在一起了。**
  12. - **汇编层面,**引用会被c++编译器当做const指针来进行操作。
  13. <a name="zaZpk"></a>
  14. # 十二、申请内存与释放内存(堆区)
  15. <a name="LtvJG"></a>
  16. ## 1、new申请单个空间
  17. ```c
  18. int *p = new int;//申请,等价于int *p = new int(6)
  19. *p = 6;//赋值
  20. cout << *p << endl;//输出
  21. delete p;//释放
  22. p=NULL;//上完厕所洗手的好习惯!!

2、new申请数组空间

  • 关于new和delete: new运算符返回的是一个指向所分配类型变量的指针。 delete p实际意思是删除p所指向的目标地址,释放它所占的内存,而非删除p指针。 ```c int p1 = new int[5];//申请数组 memset(p, 0, 5 4);//内存设置 p1[0] = 12; p1[1] = 23;//赋值 cout << p1[0] << “ “<<p1[1]<<” “<<p1[4];//输出 delete[]p1;//释放 p=NULL;//上完厕所洗手的好习惯

//二维 int *p = new int[m]; //开辟行
for (int i = 0; i < m; i++) p[i] = new int[n]; //开辟列
for (i = 0; i < m; i++) delete[] p[i]; delete[] p; p=NULL;

  1. <a name="Hz7ie"></a>
  2. ## 3、malloc申请单个空间
  3. ```c
  4. #include<stdlib.h>
  5. #include<malloc.h>
  6. int*p=(int*)malloc(4);
  7. int*p1;
  8. p1=(int*)malloc(4);
  9. free(p);
  10. p=NULL;

4、malloc申请数组空间

  1. int *p = (int*)malloc(sizeof(int)*n);
  2. free(p);
  3. p=NULL;
  4. //二维
  5. p = (int**)malloc(sizeof(int*)*m);
  6. for(i = 0; i < m; i++)
  7. {
  8. *(p + i) = (int*)malloc(sizeof(int)*n);//开辟列
  9. }
  10. for (i = 0; i < m; i++)
  11. free(*(p + i));
  12. p=NULL;

5、memset函数

  • 一般用在对定义的字符串进行初始化为‘ ’或‘/0’

    1. char a[100];
    2. memset(a, '/0', sizeof(a));
  • 方便的清空一个结构类型的变量或数组 ```c 如: struct sample_struct { char csName[16]; int iSeq; int iType; };

对于变量 struct sample_strcut stTest;

一般情况下,清空stTest的方法: stTest.csName[0]=’/0’; stTest.iSeq=0; stTest.iType=0;

用memset就非常方便: memset(&stTest,0,sizeof(struct sample_struct)); ```

十三、windows进程间通信(IPC)

  • Inter-Process Communication(IPC)机制:进程通信就是指不同进程间进行数据共享和数据交换
  • 方式的选择
    • 应用程序是否都在同一机器还是在网络上不同的机器上
    • 应用程序是否运行在不同的平台上。
    • 应用程序如何找到通信的双方,是否有限定通信的双方。
    • 应用程序间通信的方式在交互上是否有要求,比如windows上复制粘贴为不同程序间通信提供一致性的交互方式。
    • 应用程序间通信的开销是否要考虑。
    • 应用程序是有界面的(GUI)还是控制台的。

      1、文件映射(File Mapping)

  文件映射(Memory-Mapped Files)能使进程把文件内容当作进程地址区间一块内存那样来对待。因此,进程不必使用文件I/O操作,只需简单的指针操作就可读取和修改文件的内容。Win32 API允许多个进程访问同一文件映射对象,各个进程在它自己的地址空间里接收内存的指针。通过使用这些指针,不同进程就可以读或修改文件的内容,实现了对文件中数据的共享。
  应用程序有三种方法来使多个进程共享一个文件映射对象。
  (1)继承:第一个进程建立文件映射对象,它的子进程继承该对象的句柄
  (2)命名文件映射:第一个进程在建立文件映射对象时可以给该对象指定一个名字(可与文件名不同)。第二个进程可通过这个名字打开此文件映射对象。另外,第一个进程也可以通过一些其它IPC机制(有名管道、邮件槽等)把名字传给第二个进程。
  (3)句柄复制:第一个进程建立文件映射对象,然后通过其它IPC机制(有名管道、邮件槽等)把对象句柄传递给第二个进程。第二个进程复制该句柄就取得对该文件映射对象的访问权限。
  文件映射是在多个进程间共享数据的非常有效方法,有较好的安全性。但文件映射只能用于本地机器的进程之间,不能用于网络中,而开发者还必须控制进程间的同步

2、共享内存 (Shared Memory)

  Win32 API中共享内存(Shared Memory)实际就是文件映射的一种特殊情况。进程在创建文件映射对象时用0xFFFFFFFF来代替文件句柄(HANDLE),就表示了对应的文件映射对象是从操作系统页面文件访问内存,其它进程打开该文件映射对象就可以访问该内存块。由于共享内存是用文件映射实现的,所以它也有较好的安全性,也只能运行于同一计算机上的进程之间。

3、匿名管道(Anonymous Pipe)

  管道(Pipe)是一种具有两个端点的通信通道:有一端句柄的进程可以和有另一端句柄的进程通信。管道可以是单向-一端是只读的,另一端点是只写的;也可以是双向的一管道的两端点既可读也可写。
  匿名管道(Anonymous Pipe)是 在父进程和子进程之间,或同一父进程的两个子进程之间传输数据的无名字的单向管道。通常由父进程创建管道,然后由要通信的子进程继承通道的读端点句柄或写 端点句柄,然后实现通信。父进程还可以建立两个或更多个继承匿名管道读和写句柄的子进程。这些子进程可以使用管道直接通信,不需要通过父进程。
  匿名管道是单机上实现子进程标准I/O重定向的有效方法,它不能在网上使用,也不能用于两个不相关的进程之间。

4、命名管道 (Named Pipe,可靠连接)

  命名管道(Named Pipe)是服务器进程和一个或多个客户进程之间通信的单向或双向管道。不同于匿名管道的是命名管道可以在不相关的进程之间和不同计算机之间使用,服务器建立命名管道时给它指定一个名字,任何进程都可以通过该名字打开管道的另一端,根据给定的权限和服务器进程通信。
  命名管道提供了相对简单的编程接口,使通过网络传输数据并不比同一计算机上两进程之间通信更困难,不过如果要同时和多个进程通信它就力不从心了。

5、邮件槽 (Mailslots,不可靠连接)

  邮件槽(Mailslots)提 供进程间单向通信能力,任何进程都能建立邮件槽成为邮件槽服务器。其它进程,称为邮件槽客户,可以通过邮件槽的名字给邮件槽服务器进程发送消息。进来的消息一直放在邮件槽中,直到服务器进程读取它为止。一个进程既可以是邮件槽服务器也可以是邮件槽客户,因此可建立多个邮件槽实现进程间的双向通信。
  通过邮件槽可以给本地计算机上的邮件槽、其它计算机上的邮件槽或指定网络区域中所有计算机上有同样名字的邮件槽发送消息。广播通信的消息长度不能超过400字节,非广播消息的长度则受邮件槽服务器指定的最大消息长度的限制。
  邮件槽与命名管道相似,不过它传输数据是通过不可靠的数据报(如TCP/IP协议中的UDP包)完成的,一旦网络发生错误则无法保证消息正确地接收,而命名管道传输数据则是建立在可靠连接基础上的。不过邮件槽有简化的编程接口和给指定网络区域内的所有计算机广播消息的能力,所以邮件槽不失为应用程序发送和接收消息的另一种选择。

6、剪贴板 (Clipped Board)

  剪贴板(Clipped Board)实质是Win32 API中一组用来传输数据的函数和消息,为Windows应用程序之间进行数据共享提供了一个中介,Windows已建立的剪切(复制)-粘贴的机制为不同应用程序之间共享不同格式数据提供了一条捷径。当用户在应用程序中执行剪切或复制操作时,应用程序把选取的数据用一种或多种格式放在剪贴板上。然后任何其它应用程序都可以从剪贴板上拾取数据,从给定格式中选择适合自己的格式。
  剪贴板是一个非常松散的交换媒介,可以支持任何数据格式,每一格式由一无符号整数标识,对标准(预定义)剪贴板格式,该值是Win32 API定义的常量;对非标准格式可以使用Register Clipboard Format函数注册为新的剪贴板格式。利用剪贴板进行交换的数据只需在数据格式上一致或都可以转化为某种格式就行。但剪贴板只能在基于Windows的程序中使用,不能在网络上使用。

7、动态数据交换 (DDE)

  动态数据交换(DDE)是使用共享内存在应用程序之间进行数据交换的一种进程间通信形式。应用程序可以使用DDE进行一次性数据传输,也可以当出现新数据时,通过发送更新值在应用程序间动态交换数据。
  DDE和剪贴板一样既支持标准数据格式(如文本、位图等),又可以支持自己定义的数据格式。但它们的数据传输机制却不同,一个明显区别是剪贴板操作几乎总是用作对用户指定操作的一次性应答-如从菜单中选择Paste命令。尽管DDE也可以由用户启动,但它继续发挥作用一般不必用户进一步干预。DDE有三种数据交换方式:
  (1) 冷链:数据交换是一次性数据传输,与剪贴板相同。
  (2) 温链:当数据交换时服务器通知客户,然后客户必须请求新的数据。
  (3) 热链:当数据交换时服务器自动给客户发送数据。
  DDE交换可以发生在单机或网络中不同计算机的应用程序之间。开发者还可以定义定制的DDE数据格式进行应用程序之间特别目的IPC,它们有更紧密耦合的通信要求。大多数基于Windows的应用程序都支持DDE。

8、对象连接与嵌入(OLE)

  应用程序利用对象连接与嵌入(OLE)技术管理复合文档(由多种数据格式组成的文档),OLE提供使某应用程序更容易调用其它应用程序进行数据编辑的服务。例如,OLE支持的字处理器可以嵌套电子表格,当用户要编辑电子表格时OLE库可自动启动电子表格编辑器。当用户退出电子表格编辑器时,该表格已在原始字处理器文档中得到更新。在这里电子表格编辑器变成了字处理器的扩展,而如果使用DDE,用户要显式地启动电子表格编辑器。
  同DDE技术相同,大多数基于Windows的应用程序都支持OLE技术。

9、动态连接库 (DLL)

  Win32动态连接库(DLL)中的全局数据可以被调用DLL的所有进程共享,这就又给进程间通信开辟了一条新的途径,当然访问时要注意同步问题。
  虽然可以通过DLL进行进程间数据共享,但从数据安全的角度考虑,我们并不提倡这种方法,使用带有访问权限控制的共享内存的方法更好一些。

10、远程过程调用 (RPC)

  Win32 API提供的远程过程调用(RPC)使应用程序可以使用远程调用函数,这使在网络上用RPC进行进程通信就像函数调用那样简单。RPC既可以在单机不同进程间使用也可以在网络中使用。
  由于Win32 API提供的RPC服从OSF-DCE(Open Software Foundation Distributed Computing Environment)标准。所以通过Win32 API编写的RPC应用程序能与其它操作系统上支持DEC的RPC应用程序通信。使用RPC开发者可以建立高性能、紧密耦合的分布式应用程序。

11、NetBios函数

  Win32 API提供NetBios函数用于处理低级网络控制,这主要是为IBM NetBios系统编写与Windows的接口。除非那些有特殊低级网络功能要求的应用程序,其它应用程序最好不要使用NetBios函数来进行进程间通信。

12、 Sockets

  Windows Sockets规范是以U.C.Berkeley大学BSD UNIX中流行的Socket接口为范例定义的一套Windows下的网络编程接口。除了Berkeley Socket原有的库函数以外,还扩展了一组针对Windows的函数,使程序员可以充分利用Windows的消息机制进行编程。
  现在通过Sockets实现进程通信的网络应用越来越多,这主要的原因是Sockets的跨平台性要比其它IPC机制好得多,另外WinSock 2.0不仅支持TCP/IP协议,而且还支持其它协议(如IPX)。Sockets的唯一缺点是它支持的是底层通信操作,这使得在单机的进程间进行简单数据传递不太方便,这时使用下面将介绍的WM_COPYDATA消息将更合适些。

13、WM_COPYDATA消息

  WM_COPYDATA是一种非常强大却鲜为人知的消息。当一个应用向另一个应用传送数据时,发送方只需使用调用SendMessage函数,参数是目的窗口的句柄、传递数据的起始地址、WM_COPYDATA消息。接收方只需像处理其它消息那样处理WM_COPY DATA消息,这样收发双方就实现了数据共享。
  WM_COPYDATA是一种非常简单的方法,它在底层实际上是通过文件映射来实现的。它的缺点是灵活性不高,并且它只能用于Windows平台的单机环境下。