2007年东软实训嵌入式方向-Window编程试题.doc
东软人才储备培养中心专用试题 东软实训嵌入式方向考试题-windows编程 总分:100分时间:90分钟姓名: 学校:班级: 一、 名词解释(5分*5=25分)1. 队列消息和非队列消息队列消息和非队列消息队列消息就是windows将消息发送到每个线程所专有的队列中,然后由程序自主处理,这种消息基本上是由用户输入产生(wm_keydown,wm_keyup,wm_char,wm_mouse,以及wm_paint,wm_timer,wm_quit)或者是调用postmessage,postthreadmessage产生的消息;非队列消息就是直接发送给窗口过程的消息,就是直接调用窗口过程,上述消息以外的一般都是这种类型。2. 进程与线程进程与线程进程是一个可执行的程序,由私有虚拟地址空间、代码、数据和其他操作系统资源(如进程创建的文件、管道、同步对象等)组成。一个应用程序可以有一个或多个进程,一个进程可以有一个或多个线程,其中一个是主线程。线程是操作系统分时调度分配 CPU时间的基本实体。一个线程可以执行程序的任意部分的代码,即使这部分代码被另一个线程并发地执行;一个进程的所有线程共享它的虚拟地址空间、全局变量和操作系统资源。 3. 窗口与句柄窗口: 由应用程序创建的一个用于接收用户输入和显示输出的矩形区域。由非客户区和客户区组成, 是windows界面显示的基本单位。句柄-一个32位数值,用于标识windows的各种对象,它是windows系统内部表的索引值,而非对象所在的内存地址。4. MFC消息映射机制MFC 使用ClassWizard帮助实现消息映射,它在源码中添加一些消息映射的内容,并声明和实现消息处理函数,这些被添加的内容包括: 在类的定义(头文件)里,增加了消息处理函数声明,并添加一行声明消息映射的宏 DECLARE_MESSAGE_MAP。 在类的实现(实现文件)里,使用 IMPLEMENT_MESSAGE_MAP宏实现消息映射。一般情况下,这些声明和实现是由MFC的ClassWizard自动来维护的。在类的实现(实现文件)里,添加消息处理函数的实现框架。5. 线程局部存储线程局部存储是一个实现Thread的全局数据的机制,并且这些数据仅仅在这个Thread中可见,因为这些数据保存在该Thread的Thread DataBase中:在每一个Thread DataBase中都定义了一个64元的DWORD数组用来保存这些数据。同时操作系统也提供了相应的函数来完成对这些数据的操作,如:TlsAlloc,TlsFree,TlsSetValue,TlsGetValue。 二、 填空(2分*10=20分)1Windows的所谓事件驱动的核心是 。2MFC中可以创建的两种线程是 和 。3常用的线程同步对象包括 、 、 、 。4通常情况下,一个次级线程要为主线程完成某种特定类型的任务,这就隐含着表示在主线程和次级线程之间需要建立一个通信的通道。一般情况下,有下面的几种方法实现这种通信任务: 、 、 。三、 简答题(5分*7=35分)1 消息的发送方式和寄送方式的区别是什么? 被发送的消息是否会被立即处理,函数是否立即返回。被发送的消息会被立即处理,处理完毕后函数才会返回;被寄送的消息不会被立即处理,他被放到一个先进先出的队列中,一直等到应用程序空线的时候才会被处理,不过函数放置消息后立即返回。 实际上,发送消息到一个窗口处理过程和直接调用窗口处理过程之间并没有太大的区别,他们直接的唯一区别就在于你可以要求操作系统截获所有被发送的消息,但是不能够截获对窗口处理过程的直接调用。 以寄送方式发送的消息通常是与用户输入事件相对应的,因为这些事件不是十分紧迫,可以进行缓慢的缓冲处理,例如鼠标、键盘消息会被寄送,而按钮等消息则会被发送。2 消息的结构是怎样定义的?消息的处理方式有哪几种?并作简要说明。1) 操作系统是怎样将感知到的事件传递给应用程序的呢?这是通过消息机制(Message)来实现的。操作系统将每个事件都包装成消息的结构体MSG来传递给应用程序,MSG结构定义如下: typedef struct tagMSG HWND hWnd; / 目标窗口句柄 UINT message;/ 消息标识 WPARAM wParam;/ 消息参数1(附加信息,16位) LPARAM lParam;/ 消息参数2(附加信息,32位) DWORD time;/ 消息发送时间 POINT pt; / 消息发送时鼠标的屏幕坐标 MSG;2) 消息处理方式: 应用程序通过窗口过程来处理消息: 非队列消息由 Windows直接送给目的窗口的窗口过程,队列消息由:DispatchMessage等派发给目的窗口的窗口过程。窗口过程被调用时,接受四个参数:a window handle (窗口句柄);a message identifier (消息标识);two 32-bit values called message parameters (两个消息参数); 窗口过程用 :GetMessageTime获取消息产生的时间,用:GetMessagePos获取消息产生时鼠标光标所在的位置。 在窗口过程里,用 switch/case分支处理语句来识别和处理消息。 应用程序通过消息循环来获得对消息的处理 每个GDI应用程序在主窗口创建之后,都会进入消息循环,接受用户输入、解释和处理消息。 消息循环从消息队列中得到消息,如果不是快捷键消息或者对话框消息,就进行消息转换和派发,让目的窗口的窗口过程来处理。 当得到消息 WM_QUIT,或者:GetMessage出错时,退出消息循环。3 请用图示解释Windows程序内部运行原理。应用程序操作系统输入输出设备消息队列向下的箭头1表示操作系统能够操纵输出设备,以执行特定的功能,如让声卡发出声音,让显卡画出图形。向上的箭头2表示操作系统能够感知输入设备状态的变化,如鼠标移动,键盘按下,并且能够知道鼠标移动的具体位置,键盘按下的哪个字符。向下的箭头3表示应用程序可以通知操作系统执行某个具体的动作,如操作系统能够控制声卡发出声音,但其并不知道何时发出何种声音,得由应用程序告诉操作系统该发出什么样的声音。向上的箭头4表示操作系统能够将输入设备的变化上传给应用程序。如用户在某个程序活动时按了一下键盘,操作系统马上能够感知到这一事件,并且能够知道用户按下的是哪一个键,操作系统并不决定对这一事件如何作出反应,而是将这一事件转交给应用程序,由应用程序决定如何对这一事件作出反应。4 写出SDK开发程序的步骤,并给出一个利用SDK函数创建窗口的代码示例。答: 1) SDK开发程序的步骤:a) 包含相关头文件,如window.h及stdio.h等b) 写WinMain()入口函数² 设计一个窗口类(WNDCLASS)² 注册窗口类(RegisterClass)² 创建窗口(CreateWindow)² 显示及更新窗口(ShowWindow UpdateWindow)² 消息循环(获取、转换、投递消息函数)c) 写窗口过程函数用switch来接收各类消息并作相应处理2)代码:#include <windows.h>#include <stdio.h>WNDCLASS wc;HWND h_wnd;MSG msg;long WINAPI WindowProc(HWND,UINT,WPARAM,LPARAM);int PASCAL WinMain(HINSTANCE h_CurInstance, HINSTANCE h_PrevInstance, LPSTR p_CmdLine, int m_Show)wc.lpfnWndProc =WindowProc;wc.hInstance =h_CurInstance;wc.hbrBackground =(HBRUSH)GetStockObject(WHITE_BRUSH);wc.lpszClassName ="NEUSOFT"RegisterClass(&wc);h_wnd=CreateWindow("NEUSOFT ","My Window",WS_OVERLAPPEDWINDOW,0,0,400,500, 0,0,h_CurInstance,0);ShowWindow(h_wnd,SW_SHOWMAXIMIZED);UpdateWindow(h_wnd);while(GetMessage(&msg,NULL,0,0) TranslateMessage(&msg); DispatchMessage(&msg); return (msg.wParam );LRESULT CALLBACK WindowProc(HWND hwnd,UINT uMsg, WPARAM wParam,LPARAM lParam) switch(uMsg) case WM_LBUTTONDOWN: MessageBox(hwnd,"mouse clicked","message",0); break;case WM_CLOSE:DestroyWindow(hwnd);break; case WM_DESTROY:PostQuitMessage(0);break;default:return DefWindowProc(hwnd,uMsg,wParam,lParam); return 0;5 进程状态主要包括哪几种?并用图形表示进程的基本进程转换。执行状态:一个进程在并发执行中,由于资源共享与竞争,处于执行状态。用户执行状态(用户态):进程的用户程序段在执行时所处的状态。系统执行状态(系统态或核心态):进程的系统程序段在执行时所处的状态。等待状态:进程则因等待某种事件发生而处于等待状态。就绪状态:进程得到了除CPU之外的其他资源,只要由调度得到处理机,便可立即投入执行。内存就绪状态:可以立即投入执行。外存就绪状态:只有先成为内存就绪状态后,才可能被调度执行。进程的状态反映进程执行进程的变化。这些状态随着进程的执行和外界条件发生变化和转换。下图给出了由一个基本状态,即就绪状态、执行状态与等待状态之间的转换关系。NewExitReadyRunningBlocked接纳中断/时间片用完完成进程调度I/O完成或事件发生I/O请求或等待某事件6 MFC中的TLS是如何实现的,并说明实现中所用到的各个类之间的关系。在MFC中提供了TLS功能,为此MFC设计了一系列的类和程序来完成这个任务。具体的程序在afxtls.cpp和afxtls_.h中。涉及到的主要的类有:class CTypedSimpleList : public CSimpleList struct CThreadData : public CNoTrackObject struct CSlotData class CThreadSlotData class CThreadLocal : public CThreadLocalObject 其中CThreadSlotData是封装TLS的最重要的类,CTypedSimpleList,CSlotData,CThreadDAta都是为了封装TLS而设计的只具有辅助功能的类。CThreadLocal是更高层的封装。 (1) CThreadSlotDataclass CThreadSlotData public: DWORD m_tlsIndex; int m_nAlloc; int m_nRover; int m_nMax; CSlotData* m_pSlotData; CTypedSimpleList<CThreadData*> m_list;CRITICAL_SECTION m_sect; ; 在afxtls.cpp中定义了一个CThreadSlotData类的全局变量:_afxThreadData。在CThreadLocal的成员函数中大量使用了这个全局变量来访问TLS功能。 其中:DWORD m_tlsIndex 用来保存TLS数据的索引,也就是在Thread DataBase中64元数组中的偏移量,这个数据在 CThreadSlotData类的构造函数中初始化。int m_nAlloc, int m_nRover, int m_nMax这三个变量用来分配slot和记录相关状态,比如m_nAlloc用来保存当前已经分配的slot的个数。线程为每一个TLS数据分配一个slot。CSlotData* m_pSlotData用来记录已经分配的每一个slot的状态:已经使用或是尚未使用。CTypedSimpleList<CThreadData*> m_list: CThreadSlotData为每一个Thread实现一个并且只实现一个CThreadData对象,并且用链表类对象m_list来管理它们。实际上,真正被保存到Thread DataBase中去的是这个CThread Data对象的指针,而程序员要保存的TLS数据被保存到这个CThreadData对象的pData成员指向的动态数组中。所有Thread的CThreadData对象通过CThreadData对象的pNext成员连成链表,并由CTypedSimpleList<CThreadData*> m_list管理。CRITICAL_SECTION m_sect; 由于所有Thread的TLS操作都要靠访问_afxThreadData来实现,这样就产成了多线程同步的问题,m_sect就是用来进行线程同步的变量。保证每次只有一个Thread在访问_afxThread Data中的成员变量。(2)struct CThreadData : public CNoTrackObjectCThreadData* pNext; int nCount; LPVOID* pData; ; 每一个Thread的TLS数据都要 靠一个CThreadData对象来管理和保存。 CThreadData* pNext 在CThreadSlotData中,CThreadData由一个链表来管理,pNext用来把各个Thread的CThre adData对象连成链表。 int nCount 指出用于保存TLS数据指针的动态数组的长度。 LPVOID* pData 在CThreadData保存的实际上是各个TLS数据的指针,为此定义了一个指针数组,nCount用 来指示数组长度,pData用来指出数组的基地址。(3) struct CSlotDataDWORD dwFlags; HINSTANCE hInst; ; 每一个Thread的TLS数据都要靠 一个CThreadData对象来保存,具体实现是把TLS数据的指针保存在CThreadData对象的动态 指针数组中(基地址由pData指出)。而这个数组中每一个成员的使用状况则由一个与之长度 相同的CSlotData数组来表示,具体由DWORD dwFlags来表明。 综上所述,MFC中TLS功能的封装是这样的,所有Thread的TLS数据指针都保存在一个动态的指针数组中,而该数组的基地址由一个CThreadData对象的pData指出。 同时,保存在Thread DataBase中的是这个CThreadData对象的指针,而不是TLS数据的指针 ,并且其索引值均相同,都是CThreadSlotData类中的m_tlsIndex成员。而且,在CThread SlotData中提供了一个链表来管理所有Thread的CThreadData对象。这样CThreadSlotData 类就能访问所有的Thread的TLS数据。 7 请用代码描述用事件对象实现两个线程同步的过程。答:#include “afxmt.h”CEvent threadStart;UINT ThreadFunction1(LPVOID pParam)WaitForSingleObject(threadStart.m_hObject,INFINITE);UINT ThreadFunction2(LPVOID pParam)threadStart.SetEvent();四、 应用题(20分)利用用户界面线程和自定义消息的知识,实现具有下图所示功能的MFC程序。(给出实现过程和代码。)答:1 创建一个基于对话框的应用程序。2 设计界面。在主窗口的对话框上添加所需要的控件元素,并进行排版。3 利用classwizard为界面控件IDC_RADIO1,IDC_RADIO2,IDC_RADIO3,IDC_SUM添加相应的消息映射和消息处理函数。4 添加实现用户界面线程的线程类CcalculateThread,从CwinThread派生。5 在主界面对话框所对应的类CMultiThread7Dlg中,添加所需要的变量:在MultiThread7Dlg.h中添加:#include "CalculateThread.h"int nAddend;CCalculateThread* m_pCalculateThread;6 在类CMultiThread7Dlg的初始化函数OnInitDialog()中添加初始化代码:在CalculateThread.h中添加:(CButton*)GetDlgItem(IDC_RADIO1)->SetCheck(TRUE);Addend=10;7 添加自定义消息,并实现消息映射。在CalculateThread.h中添加:#define WM_CALCULATE WM_USER+1afx_msg LONG OnCalculate(UINT wParam,LONG lParam);在CalculateThread.cpp中添加:ON_THREAD_MESSAGE(WM_CALCULATE,OnCalculate)LONG CalculateThread:OnCalculate(UINT wParam,LONG lParam)int nTmpt=0;for(int i=0;i<=(int)wParam;i+)nTmpt=nTmpt+i;Sleep(500); :PostMessage(HWND)(GetMainWnd()->GetSafeHwnd(),WM_DISPLAY,nTmpt,NULL);return 0;在MultiThread7Dlg.h中添加:#define WM_DISPLAY WM_USER+2LRESULT OnDisplay(WPARAM wParam,LPARAM lParam);在MultiThread7Dlg.cpp中添加:ON_MESSAGE(WM_DISPLAY,OnDisplay)LRESULT CMultiThread7Dlg:OnDisplay ( WPARAM wParam, LPARAM lParam)int nTemp=(int)wParam;SetDlgItemInt(IDC_STATUS,nTemp,FALSE);return 0;8 实现利用classwizard映射的消息处理函数。在MultiThread7Dlg.cpp中添加:#include "CalculateThread.h"在CMultiThread7Dlg:OnRadio1()中添加代码:nAddend=10;在CMultiThread7Dlg:OnRadio2()中添加代码:nAddend=50;在CMultiThread7Dlg:OnRadio3()中添加代码:nAddend=100;在CMultiThread7Dlg:OnSum()中添加代码:m_pCalculateThread=(CCalculateThread*)AfxBeginThread(RUNTIME_CLASS(CCalculateThread);Sleep(500);m_pCalculateThread->PostThreadMessage(WM_CALCULATE,nAddend,NULL);- 11 -