SDK学习笔记1

来源:互联网 发布:手机淘宝抢购如何成功 编辑:程序博客网 时间:2024/05/22 17:14

开始新的学习了.

   从今天下午开始,正式开始接触了一下Windows SDK.

发现原来用这个东西弄起程序来,异常的麻烦啊!建个窗体都要N多行代码.

(大部分来源于VC++深入详解中的WinMain一章)

大致的代码流程是

首先包含Windows.h这个头文件

然后,就是程序的入口函数,和C语言的不同,其入口函数不是main而是WinMain.原形如下:

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)

先看前面的修饰符号

WINAPI利用goto definition功能查看WINAPI的定义,可以看到WINAPI其实就是__stdcall。也就是一种调用

的时候函数参数的传递方式了

hInstance就是指当前应用程序的实例句柄,在Delphi中也是有一个hinstance,想想也应该就是这样的一个句柄吧.

这是一个数值。当程序在Windows下运行时,它唯一标识运行中的实例(注意,只有运行中的程序实例,才有实例句柄)。一个应用程序可以运行多个实例,每运行一个实例,系统都会给该实例分配一个句柄值,并通过hInstance参数传递给WinMain函数。

hPrevInstance表示当前实例的前一个实例的句柄。通过查看MSDN我们可以知道,在Win32环境下,这个参数总是NULL,即在Win32环境下,这个参数不再起作用。其存在的目的是兼容以前的程序.

szCmdLine是一个以空终止的字符串,指定传递给应用程序的命令行参数。例如:在D盘下有一个sunxin.txt文件,当我们用鼠标双击这个文件时将启动记事本程序(notepad.exe),此时系统会将D:/sunxin.txt作为命令行参数传递给记事本程序的WinMain函数,记事本程序在得到这个文件的全路径名后,就在窗口中显示该文件的内容。要在VC++开发环境中向应用程序传递参数,可以单击菜单Project】→【Settings】,选择“Debug选项卡,在“Program arguments”编辑框中输入你想传递给应用程序的参数。

iCmdShow指定程序的窗口应该如何显示,例如最大化、最小化、隐藏等。这个参数的值由该程序的调用者所指定,应用程序通常不需要去理会这个参数的值。

WinMain入口函数的参数的意思明白了,则进入到一个简单的程序.呵呵,我没有使用大师级常用的

Hello World作为第一个程序.而是创建一个窗体.呵呵,初学来着.

要创建窗体,首先就是要 注册一个窗口类,然后才可根据注册的窗体类的类名使用CreateWindow来创建一个窗体

注册窗体类.

使用RegisterClass函数来注册.该函数需要一个WNDCLASS结构的参数.

关于该结构的说明,请看我转的一篇文章

第一个成员变量style指定这一类型窗口的样式,常用的样式如下:
CS_HREDRAW
当窗口水平方向上的宽度发生变化时,将重新绘制整个窗口。当窗口发生重绘时,窗口中的文字和图形将被擦除。如果没有指定这一样式,那么在水平方向上调整窗口宽度时,将不会重绘窗口。
CS_VREDRAW
当窗口垂直方向上的高度发生变化时,将重新绘制整个窗口。如果没有指定这一样式,那么在垂直方向上调整窗口高度时,将不会重绘窗口。
CS_NOCLOSE
禁用系统菜单的Close命令,这将导致窗口没有关闭按钮。
CS_DBLCLKS
当用户在窗口中双击鼠标时,向窗口过程发送鼠标双击消息。
注册成功之后,就可以使用CreateWindow函数来创建刚刚注册的那个类名的窗体了
CreateWindow函数声明如下:

HWND CreateWindow(

     LPCTSTR lpClassName,    // pointer to registered class name

     LPCTSTR lpWindowName,   // pointer to window name

     DWORD dwStyle,          // window style

     int x,                   // horizontal position of window

     int y,                   // vertical position of window

     int nWidth,              // window width

     int nHeight,             // window height

     HWND hWndParent,        // handle to parent or owner window

     HMENU hMenu,             // handle to menu or child-window identifier

     HANDLE hInstance,       // handle to application instance

     LPVOID lpParam          // pointer to window-creation data

);

lpClassName指定窗口类的名称,即我们在步骤1设计一个窗口类中为WNDCLASS的lpszClassName成员指定的名称.产生窗口的过程是由操作系统完成的,如果在调用CreateWindow函数之前,没有用RegisterClass函数注册过窗体类名称的窗口类型,操作系统将无法得知这一类型窗口的相关信息,从而导致创建窗口失败。
参数lpWindowName指定窗口的名字。如果窗口样式指定了标题栏,那么这里指定的窗口名字将显示在标题栏上。
参数dwStyle指定创建的窗口的样式。就好像同一型号的汽车可以有不同的颜色一样,同一型号的窗口也可以有不同的外观样式。要注意区分WNDCLASS中的style成员与CreateWindow函数的dwStyle参数,前者是指定窗口类的样式,基于该窗口类创建的窗口都具有这些样式,后者是指定某个具体的窗口的样式。
在这里,我们可以给创建的窗口指定WS_OVERLAPPEDWINDOW这一类型,该类型的定义为:
#define WS_OVERLAPPEDWINDOW (WS_OVERLAPPED       | /
                                  WS_CAPTION          | /
                                  WS_SYSMENU          | /
                                  WS_THICKFRAME      | /
                                  WS_MINIMIZEBOX     | /
                                     WS_MAXIMIZEBOX)
可以看到,WS_OVERLAPPEDWINDOW是多种窗口类型的组合,其原理和前面知识点所讲的内容是一致的。下面是这几种常用窗口类型的说明。
 WS_OVERLAPPED:产生一个层叠的窗口,一个层叠的窗口有一个标题栏和一个边框。
 WS_CAPTION:创建一个有标题栏的窗口。
 WS_SYSMENU:创建一个在标题栏上带有系统菜单的窗口,要和WS_CAPTION类型一起使用。
 WS_THICKFRAME:创建一个具有可调边框的窗口。
 WS_MINIMIZEBOX:创建一个具有最小化按钮的窗口,必须同时设定WS_ SYSMENU类型。
 WS_MAXIMIZEBOX:创建一个具有最大化按钮的窗口,必须同时设定WS_ SYSMENU类型。
使用WS_OVERLAPPEDWINDOW类型的窗口如图1.1所示。
CreateWindow函数的参数xy,nWidth,nHeight分别指定窗口左上角的xy坐标,窗口的宽度,高度。如果参数x被设为CW_USEDEFAULT,那么系统为窗口选择默认的左上角坐标并忽略y参数。如果参数nWidth被设为CW_USEDEFAULT,那么系统为窗口选择默认的宽度和高度,参数nHeight被忽略。
参数hWndParent指定被创建窗口的父窗口句柄。在1.2节中已经介绍了,窗口之间可以有父子关系,子窗口必须具有WS_CHILD样式。对父窗口的操作同时也会影响到子窗口,表1.1列出了对父窗口的操作如何影响子窗口。
表1.1  对父窗口的操作对子窗口的影响
  
  
销毁
在父窗口被销毁之前销毁
隐藏
在父窗口被隐藏之前隐藏,子窗口只有在父窗口可见时可见
移动
跟随父窗口客户区一起移动
显示
在父窗口显示之后显示
参数hMenu指定窗口菜单的句柄。
参数hInstance指定窗口所属的应用程序实例的句柄。
参数lpParam:作为WM_CREATE消息的附加参数lParam传入的数据指针。在创建多文档界面的客户窗口时,lpParam必须指向CLIENTCREATESTRUCT结构体。多数窗口将这个参数设置为NULL。
如果窗口创建成功,CreateWindow函数将返回系统为该窗口分配的句柄,否则,返回NULL。注意,在创建窗口之前应先定义一个窗口句柄变量来接收创建窗口之后返回的句柄值。
显示及更新窗口
(1)显示窗口
窗口创建之后,我们要让它显示出来,这就跟汽车生产出来后要推向市场一样。调用函数ShowWindow来显示窗口,该函数的原型声明如下所示:
BOOL ShowWindow(
  HWND hWnd,     // handle to window
  int nCmdShow   // show state
);
ShowWindow函数有两个参数,第一个参数hWnd就是在上一步骤中成功创建窗口后返回的那个窗口句柄;第二个参数nCmdShow指定了窗口显示的状态,常用的有以下几种。
 SW_HIDE:隐藏窗口并激活其他窗口。
 SW_SHOW:在窗口原来的位置以原来的尺寸激活和显示窗口。
 SW_SHOWMAXIMIZED:激活窗口并将其最大化显示。
 SW_SHOWMINIMIZED:激活窗口并将其最小化显示。
 SW_SHOWNORMAL:激活并显示窗口。如果窗口是最小化或最大化的状态,系统将其恢复到原来的尺寸和大小。应用程序在第一次显示窗口的时候应该指定此标志。
关于iCmdShow参数的详细内容请参见MSDN。
(2)更新窗口
在调用ShowWindow函数之后,我们紧接着调用UpdateWindow来刷新窗口,就好像我们买了新房子,需要装修一下。UpdateWindow函数的原型声明如下:
BOOL UpdateWindow(
  HWND hWnd   // handle to window
);
其参数hWnd指的是创建成功后的窗口的句柄。UpdateWindow函数通过发送一个WM_PAINT消息来刷新窗口,UpdateWindow将WM_PAINT消息直接发送给了窗口过程函数进行处理,而没有放到我们前面所说的消息队列里,请读者注意这一点。关于WM_PAINT消息的作用和窗口过程函数,后面我们将会详细讲解。
到此,一个窗口就算创建完成了。
然后下面就要响应窗体的消息循环了.
调用GetMessage()函数从消息队列中取出消息
BOOL GetMessage(
        LPMSG lpMsg,            // address of structure with message
        HWND hWnd,               // handle of window
        UINT wMsgFilterMin,     // first message
        UINT wMsgFilterMax      // last message
);
参数lpMsg指向一个消息(MSG)结构体,GetMessage从线程的消息队列中取出的消息信息将保存在该结构体对象中。
参数hWnd指定接收属于哪一个窗口的消息。通常我们将其设置为NULL,用于接收属于调用线程的所有窗口的窗口消息。
参数wMsgFilterMin指定要获取的消息的最小值,通常设置为0。
参数wMsgFilterMax指定要获取的消息的最大值。如果wMsgFilterMin和wMsgFilter Max都设置为0,则接收所有消息。
GetMessage函数接收到除WM_QUIT外的消息均返回非零值。对于WM_QUIT消息,该函数返回零。如果出现了错误,该函数返回-1,例如,当参数hWnd是无效的窗口句柄或lpMsg是无效的指针时。
GetMessage函数只有在接收到WM_QUIT消息时,才返回0。此时while语句判断的条件为假,循环退出,程序才有可能结束运行。在没有接收到WM_QUIT消息时,Windows应用程序就通过这个while循环来保证程序始终处于运行状态。
TranslateMessage函数用于将虚拟键消息转换为字符消息。字符消息被投递到调用线程的消息队列中,当下一次调用GetMessage函数时被取出。当我们敲击键盘上的某个字符键时,系统将产生WM_KEYDOWN和WM_KEYUP消息。这两个消息的附加参数(wParam和lParam)包含的是虚拟键代码和扫描码等信息,而我们在程序中往往需要得到某个字符的ASCII码,TranslateMessage这个函数就可以将WM_KEYDOWN和WM_ KEYUP消息的组合转换为一条WM_CHAR消息(该消息的wParam附加参数包含了字符的ASCII码),并将转换后的新消息投递到调用线程的消息队列中。注意,TranslateMessage函数并不会修改原有的消息,它只是产生新的消息并投递到消息队列中。
DispatchMessage函数分派一个消息到窗口过程,由窗口过程函数对消息进行处理。DispachMessage实际上是将消息回传给操作系统,由操作系统调用窗口过程函数对消息进行处理(响应)。
Windows应用程序的消息处理机制如图1.2所示。
(1)操作系统接收到应用程序的窗口消息,将消息投递到该应用程序的消息队列中。
(2)应用程序在消息循环中调用GetMessage函数从消息队列中取出一条一条的消息。取出消息后,应用程序可以对消息进行一些预处理,例如,放弃对某些消息的响应,或者调用TranslateMessage产生新的消息。
(3)应用程序调用DispatchMessage,将消息回传给操作系统。消息是由MSG结构体对象来表示的,其中就包含了接收消息的窗口的句柄。因此,DispatchMessage函数总能进行正确的传递。
(4)系统利用WNDCLASS结构体的lpfnWndProc成员保存的窗口过程函数的指针调用窗口过程,对消息进行处理(即“系统给应用程序发送了消息”)。
以上就是Windows应用程序的消息处理过程。

1)从消息队列中获取消息还可以调用PeekMessage函数,该函数的原型声明如下所示:

BOOL PeekMessage(

  LPMSG lpMsg,           // message information

  HWND hWnd,              // handle to window

  UINT wMsgFilterMin, // first message

  UINT wMsgFilterMax// last message

  UINT wRemoveMsg    // removal options

);

4个参数和GetMessage函数的4个参数的作用相同。最后1个参数指定消息获取的方式,如果设为PM_NOREMOVE,那么消息将不会从消息队列中被移除;如果设为PM_REMOVE,那么消息将从消息队列中被移除(与GetMessage函数的行为一致)。关于PeekMessage函数的更多信息,请参见MSDN

2)发送消息可以使用SendMessagePostMessage函数。SendMessage将消息直接发送给窗口,并调用该窗口的窗口过程进行处理。在窗口过程对消息处理完毕后,该函数才返回(SendMessage发送的消息为不进队消息)。PostMessage函数将消息放入与创建窗口的线程相关联的消息队列后立即返回。除了这两个函数外,还有一个PostThreadMessage函数,用于向线程发送消息,对于线程消息,MSG结构体中的hwnd成员为NULL

呵呵,抄了一大把,下面,附上一段代码.


#include <windows.h>

HINSTANCE AppInstance;
int CmdShow;
LRESULT CALLBACK WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
 //处理感兴趣的消息
 switch (message)
 {
 case WM_DESTROY:
  //当用户关闭窗口,窗口销毁,程序需结束,发退出消息,以退出消息循环
  PostQuitMessage (0) ;
  return 0 ;
 case WM_CLOSE:
       if(IDYES==MessageBox(hwnd,"想要结束吗?","message",MB_YESNO))
  {
   DestroyWindow(hwnd);
  }
  break;
 default://默认窗口消息处理
  return DefWindowProc (hwnd, message, wParam, lParam) ; 
 }
 //其他消息交给由系统提供的缺省处理函数
 return 0;
}

HWND CreateForm(TCHAR* ClassFormName,TCHAR *Caption)
{
  WNDCLASS     wndclass ;
  HWND hwnd;
  wndclass.style = CS_VREDRAW | CS_HREDRAW | CS_OWNDC | CS_DBLCLKS;//窗体样式
  wndclass.lpfnWndProc   = WindowProc;//关联消息处理函数 
 wndclass.cbClsExtra    = 0 ;//表示窗口类结构之后分配的额外的字节数。系统将该值初始化为0 
 wndclass.cbWndExtra    = 0 ;//表示窗口实例之后分配的额外的字节数。系统将该值初始化为0.如果使用资源文件里的CLASS指令
 //创建对话框,并用WNDCLASS注册该对话框时,cbWndExtra必须设置成DLGWNDOWEXTRA。
 
 wndclass.hInstance     = AppInstance;//应用程序实例句柄
 wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ; //图标
 wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;  //光标
 wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH); //画刷
 wndclass.lpszMenuName  = NULL ;//菜单资源名称
 wndclass.lpszClassName = ClassFormName;//类名称
 
      if (!RegisterClass (&wndclass))
 {
  MessageBox (NULL, TEXT (strcat("注册窗体类",strcat(ClassFormName,"失败!"))),
   ClassFormName, MB_ICONERROR) ;
  return 0 ;
 }
      hwnd = CreateWindow (ClassFormName, //窗口类名称
   TEXT (Caption),  //窗口标题
   WS_OVERLAPPEDWINDOW, //窗口风格
   CW_USEDEFAULT,
   CW_USEDEFAULT,
   CW_USEDEFAULT,
   CW_USEDEFAULT,
   NULL,
   NULL,
   AppInstance, //实例句柄
   NULL);
     
 ShowWindow (hwnd, CmdShow) ;
 UpdateWindow (hwnd) ; 
 return hwnd; 
}

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
  AppInstance = hInstance;
  CmdShow = iCmdShow;
  CreateForm("TForm1","test");
  //消息循环
 MSG  msg ;
 while (GetMessage (&msg, NULL, 0, 0)) //从消息队列中取消息
 {
  TranslateMessage (&msg) ;  //转换消息
  DispatchMessage (&msg) ;  //派发消息
 }
 return msg.wParam ;
}

按照Delphi的设想来的,如此一想,写一个Form类,应该也不难的吧!按照OOP的思想把这些底层的创建按照自己的思想封装一下.

原创粉丝点击