来源:远方网络 | 2005-4-27 15:00:32 | (有6890人读过)
windows编程基础
现在大多介绍windows程序运作原理的电子书都是以C++作为配合语言,我自己写了一些与语言无关个资料,更多初学者就可以参考了。
一、动态连接库与api
动态连接库是一个dll文件,想必大家都见过。这种文件里有一块一块的程序(函数或子程序),有时还有一些资源(指不是程序,但和程序放在一个文件中,可以临时调用的数据)。程序运行时可以通过api函数(也是一种dll函数)把这些dll加载到进程(就是这个程序本身。进程,就是被调到内存里的程序)里,api函数会返回一个值给程序,使这个Dll在系统内的编号叫做句柄(handle,把手)。如果这个dll已经加载,这时系统就会把当前的这个dll的句柄返回给程序。这时程序就可以使用该句柄,利用api函数访问该dll。
与普通dll不同的事,api加载时不能用api,要复杂得多。一般系统会自己负责加载。而使用时也不必用句柄,因为VB、VC++、Delphi所有这些编程工具已经为我们写好了这些代码,而且为所有api函数都起了一个容易使用的名字,比如SetWindowPos(…………)等等。我们在使用时,只要在程序里加入使用这些名字的文件,一般就可以直接使用了,像这样:
SetWindowPos(hWnd,1,1,1,1,1,0)
二、线程
一个进程至少有一个线程。CPU为了处理程序,有一些临时存储数据的存储单元,叫做寄存器。多任务操作系统下,CPU必须定时去更换任务,这时许多数据就必须放入内存备份。因为寄存器里的内容是不断更换的,很容易(可以说肯定会)丢失数据。此外,一个程序里的跳转如果想返回(比如调用函数,最后还要回到主程序里),参数、原地址都要保存在内存里。一般这些内容都保存于堆栈(一片内存区,有一个栈顶指针,放入数据指针向上移动,读出数据指针下移,指针用CPU专门的一个寄存器保存(说是指针,其实就是一个内存地址))里。这样,只要准备N个虚拟寄存器和N个堆栈就可以有同时N个线程同时运行同一个程序(因为寄存器里还保存有程序当前执行的位置,(虚拟)寄存器不同,位置就可以不同,这N个线程就基本互不干扰)。
三、窗口
在Windows下,普通应用程序必须通过创建窗口来处理所有的用户控制信息,所有的按钮、单选框、下拉列表都是由窗口构成的。窗口在创建前,首先要创建窗口类。这是通过API函数RegisterClassEx来完成的。这样,程序把图表、颜色、鼠标光标的信息提交给系统,并以一个窗口类名的字符串标识。(系统管理信息一般有两种方式,一种是不断的添加信息,为每个信息编上一个号,把这个号传给程序。比如系统载入程序,就是先把程序调入内存,然后为它创建一个PID(进程编号),告诉程序该进程的编号。因为这些ID是随机的,进程之间就不能依靠它来传递信息,因为一个进程无法知道另一个进程所拥有的信息。此时另一种方式就是由程序自己指定,此时用字符串。我创建一个窗口类,用一个字符串标识,比如"MyWndClass",由于这是编程时决定的,其他程序也就可以在编程时决定使用"MyWndClass"来与我的这个程序共享我创建的这个信息。)然后,程序就可以使用该窗口类,通过API函数CreateWindow()来创建一个窗口。在注册串口类时的诸多信息中,有一个参数是最重要的,就是一个指向窗口过程的指针(一个保存过程起始地址的指针)。为什么呢?下一节讲。另外,窗口分为两类,一类是普通窗口,另一类是子窗口。子窗口保存有其父窗口的句柄,可以向其父窗口发送一些消息。一般的“控件”,比如按钮、下拉列表等,都是由子窗口构成,在其被单击、选种时,就会向父窗口发送消息。消息是什么呢?下一节讲。
四、消息
在第三节中,我们已经创建了一个主窗口,负责程序消息的处理。那么,程序接下来要干什么呢?就是进入一个消息循环。
线程在运行时,系统给它预留了一个“消息队列”。所有在程序窗口内发生的鼠标键盘动作都会产生一个“消息”,被系统暂存在此。消息循环就用API函数GetMessage()取得消息,然后用DispatchMessage()把消息传给对应的窗口(消息在程序中用一个结构表示,里面存有:消息号(像鼠标单击某一坐标位置,系统把这个消息发来,有系统标准的消息格式和编号。而按钮时程序员确定的,对于某一按钮被单击这种消息,则由程序员或编程工具为它制定一个消息。可以为它设定于标准消息不冲突的消息编号;也可以用一个统一的消息编号表示这是用户消息,然后在附加信息中给出不同的信息。)、接收窗口、附加信息(包括详细信息、发送时间、位置坐标等))。
在消息循环调用DispatchMessage时,系统就会根据消息中的窗口句柄查找到窗口的信息。在根据信息中的窗口类名,查找到窗口类的信息。窗口类中存有窗口过程地址。然后系统就会调用该窗口过程,把消息作为参数传给它。窗口过程就会用一个大分支来处理该消息:不同消息作出不同反应。——以上几乎所有的经历在VB、Delphi的快速开发中都被掩盖掉了。
下面举一个例子:一个子窗口,句柄保存在变量hWnd中,他要作为一个按钮出现。用户点击(按下鼠标)他,系统记录了这次袭击事件,写了一个消息:
hWnd,鼠标按下,……
消息循环,我们的邮电局,得到了这个消息,马上要求DispacthMessage。于是经过有关部门(系统)的查找,得知他的秘书的地址(窗口过程地址)。于是消息被送到他秘书的手上。他的秘书发现这是个“按下按钮事件”,于是他通知下属,给窗口换一件防弹衣,于是按钮的图片就换成了下凹的了。
过了一会儿,鼠标在按钮上松开了,这个消息又同前一个一样,被送到了秘书手上。窗口恢复了原样。同时,秘书还要把这次单击事件反映给上级有关部门(父窗口)。秘书与上级早已制定了规则:如果子窗口被单击,我会先写号收信人地址(父窗口句柄),告诉你这是一个私人事件(自定消息代号,一般是十六进制数0111),同时我会告诉你我的老板是谁(子窗口的编号,比如说是250)。秘书把消息写好,然后寄出去(消息到达消息队列)。消息循环得到消息,再DispatchMessage。因为消息上写的是父窗口的句柄,于是父窗口的秘书得到了消息。秘书通过分支处理,发现是私人事件(0111),再仔细看,发现是子窗口250的消息,于是秘书就做出一些反应。
当然,如果秘书对有些事情不感兴趣,就可以交给其他部门,也可以让垃圾桶处理(调用DefWindowProc()进行默认处理)。
如果主窗口过程发现是一个关闭窗口的消息,他就关闭窗口,同时给消息队列用PostQuitMessage()发送一个退出消息。消息循环中,GetMessage()如果得到这个消息,它的值就是零。消息循环发现这是0,就会立刻退出。
退出了就完了。全文完。
|