飘云阁

 找回密码
 加入我们

QQ登录

只需一步,快速开始

查看: 5221|回复: 8

[Delphi] [Delphi]Windows SDK编程(Delphi版) 之 Windows应用程序框架

  [复制链接]
  • TA的每日心情
    奋斗
    2017-4-20 22:12
  • 签到天数: 1 天

    [LV.1]初来乍到

    发表于 2010-6-7 12:27:30 | 显示全部楼层 |阅读模式
    既然前面讲了Windows SDK编程的一些基本知识,现在就需要用这些知识来实现一点简单的功能。不像以前咱们用C编写DOs模式的程序时一样,可能最小的程序只用三行,WIndows程序至少有几十行的代码。因此编写Windows程序时,通常会使用一些框架或者封装(比如MFC,VCL)等,但是这里我们只讲SDK的框架。使用C来实现的话,必须有的东西就是WinMain入口函数。WinMain中的常见写法顺序:
        1、定义一个窗口类
        2、注册类
        3、创建窗口
        4、显示窗口
        5、开始消息循环
    对于Delphi来讲,Delphi已经将WinMain编译在Delphi的内部,我们可以省去WinMain的入口函数这一步,后面的步骤和上面一样。所以,一般我们还是写一个函数比如就叫做WinMain,实现了上面的方法之后,直接在工程文件的
    begin...end之间加上WinMain()的调用就可。下面,就来给一个这样的框架
    1. program Project1;

    2. uses
    3.   WIndows,messages;



    4. //窗口过程
    5. function WndProc(hwnd: THandle;MsgId: Longint;wParam: WParam;lParam: LParam): LRESULT;stdcall;
    6. begin
    7.   Case msgId of
    8.   WM_DESTROY:
    9.   begin
    10.     PostQuitMessage(0);
    11.     result := 1;
    12.   end;
    13.   else
    14.     result := DefWindowProc(Hwnd,MsgId,WParam,LParam);
    15.   end;

    16. end;

    17. procedure WinMain(HthisInstance: LongInt);
    18. var
    19.   msg: Tmsg;
    20.   MainHwnd: THandle;
    21.   WndClass: TWNdClassex;
    22. begin
    23.   WndClass.cbSize := Sizeof(WNdClass);//指定结构大小
    24.   WndClass.hInstance := HThisInstance;//指定宿主为当前应用程序的实例
    25.   WndClass.lpszClassName := 'DxWindow';//指定类名
    26.   WndClass.lpfnWndProc := @WndProc;//指定窗口过程
    27.   WndClass.style := 0;//指定样式为普通样式
    28.   WndClass.hIcon := LoadIcon(0,IDI_Application);//普通图标32*32大小的
    29.   WndClass.hIconSm := LoadIcon(0,IDI_WINLogo);//指定小图标16*16的
    30.   WndClass.hCursor := LoadCursor(0,IDC_Arrow);//指定光标
    31.   WndClass.lpszMenuName := nil;//指定菜单
    32.   WndClass.cbClsExtra := 0;
    33.   WndClass.cbWndExtra := 0;
    34.   WndClass.hbrBackground := GetStockObject(White_Brush);
    35.   if RegisterClassex(WndClass) <> 0 then
    36.   begin
    37.     MainHwnd := CreateWindowEx(0,WndClass.lpszClassName,'测试窗口标题',WS_OVERLAPPEDWINDOW,
    38.                                CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,0,0,hTHisInstance,nil);
    39.     if MainHwnd <> 0 then
    40.     begin
    41.       ShowWindow(MainHwnd,sw_ShowNormal);
    42.       UpdateWindow(MainHwnd);
    43.       while GetMessage(msg,0,0,0) do
    44.       begin        //这里开始消息循环
    45.         TranslateMessage(msg);
    46.         DIspatchMessage(msg);
    47.       end;
    48.       ExitCOde := Msg.wParam;//退出
    49.     end;
    50.   end;
    51. end;

    52. begin
    53.   WinMain(Hinstance);
    54. end.
    复制代码
    用C来实现这个,也是一样的啦,只是多一个WInMain的入口声明。当然这个框架程序无任何功能,只是显示窗体。
    然后我们来逐渐研究这个框架程序,首先,我们必须引用Windows,Message这两个单元,Windows单元中包括了大部分windows API的函数声明,Messages单元包含了Windows大部分消息的声明。所以这两个单元咱是需要的。程序中有一个窗口函数WndProc,这个窗口函数就是Windows用来与应用程序交互通信的函数。因此,它被说明为一个回调函数。在上一篇中,已经说了,Windows程序由WInMain入口,只是我们在Delphi中不用来声明这个WinMain入口函数,而实际上,这个WinMain有四个参数,真实声明应该为:
    C格式
    int WINAPI WinMain(Hinstance HThisInst,Hinstance hPrevInst,LPSTR lpszArgs,Int nWinMode);
    Delphi格式
    function WinMain(HThisInst: LongInt;hPrevInst: LogInt;lpszArgs: Pchar;nWinMode: integer);
    有四个参数,HThisInst表示为当前程序实例,因为Windows是多任务系统,所以一个程序可能运行有多个实例,所以由HThisInst来指定是属于哪个程序实例,便于Windows管理
    hPrevInst是指定为前一个实例,而这个是为了与以前的Win3.1相兼容,现在的Windows系统中,本参数永远为nil。
    lpszArgs是一个字符串参数,表示的是传递给程序的命令行参数,比如Delphi的Paramstr()这个就是可以获得参数。
    nWInMode指定为窗口的显示模式。

    然后我们声明了几个变量
    var
      msg: Tmsg;
      MainHwnd: THandle;
      WndClass: TWNdClassex;
    msg用来进行消息循环的结构体
    MainHwnd指定为窗口句柄,至于这个句柄是啥,那是众说纷纭啊,不过个人认为是唯一标记窗口的一个指针。
    WndClass用来指定要注册的窗口样式的结构。

    然后程序开始对WndClass这个样式类进行了设置,设置中,我们指定了窗口的光标,窗口过程,窗口类的类名还有菜单,背景等。可以看到我在上面指定光标用的是LoadCursor,指定图标用的是LoadIcon这两个函数。
    两个函数声明为:
    function LoadIcon(hInstance: HINST; lpIconName: PChar): HICON; stdcall;
    本函数返回一个图标句柄,Hinstance指定我们前面说的实例句柄,这里可不能指定为我们的Hinstance了哈,因为这里调用的是系统的的默认图标,所以我们指定为0,如果是指定我们程序内部的资源图标,那么就是指定为本程序实例。第二个参数指定为图标资源名称。系统有一些默认图标资源
    IDI_APPLICATION 缺省图标
    IDI_ERROR  错误符号
    IDI_INFORMATION  信息
    IDI_QUESTION  问号
    IDI_WARNING  感叹号
    IDI_WINLOGO  窗口标志

    function LoadCursor(hInstance: HINST; lpCursorName: PChar): HCURSOR; stdcall;
    使用方法和上面的差不多,系统默认的光标样式为:
    IDC_ARROW  缺省箭头光标
    IDC_CROSS  十字线
    IDC_IBEAM  垂直工字型
    IDC_WAIT  沙漏

    然后是背景的设置,我上面指定的背景是白色的。通过GetStockObject来获得一个指定的GDIObject的句柄,我那个指定的是画刷句柄,系统默认的集中画刷样式为:
    BLACK_BRUSH  黑色
    DKGRAY_BRUSH  黑灰色
    HOLLOW_BRUSH  从窗口中看到的
    LIGRAY_BRUSH  浅色
    WHITE_BURSHE  白色。
    你可以使用GetStockObject来获得系统默认的画刷,也可以使用CreateSolidBrush来创建一个画刷,比如CreateSolidBrush(RGB(236,233,216));就可以创建一个和我们默认用Delphi创建的窗体一样的背景了。
    这些都指定好了之后,就可以注册这个窗口类到Windows中了,注册了之后,就可以使用CreateWindow这样的函数来根据我们的样式创建窗口。
    CreateWindowEx的原型为
    function CreateWindowEx(dwExStyle: DWORD; lpClassName: PChar;
      lpWindowName: PChar; dwStyle: DWORD; X, Y, nWidth, nHeight: Integer;
      hWndParent: HWND; hMenu: HMENU; hInstance: HINST; lpParam: Pointer): HWND;
    dwExStyle表示为扩展样式
    lpClassName指定为类名,直接使用WndClass.lpSzClassName则可,
    LpWindowName指定为窗口标题
    DwStyle指定为窗口样式,我这里指定的为WS_OVERLAPPEDWINDOW,表示为边框重叠的窗口,共有
    WS_OVERLAPPEDWINDOW  边框重叠的窗口
    WS_MAXIMIZEBOX 最大化
    WS_MINIMIZEBOX 最小化
    WS_SYSMENU  系统菜单
    WS_HSCROLL  水平滚动条
    WS_VSCROLL  垂直滚动条

    之后是指定窗口的位置,以及宽和高(在上面我用的是系统默认CW_USEDEFAULT),然后指定他的父窗口句柄,这里我们是没有父窗口的,所以指定为桌面为0,无菜单,指定为0,然后是指定窗口所属的程序实例。最后指定一些其他附加信息,这里无附加信息,所以指定为nil。

    到这里为止 ,窗口就创建完成了,运行一下,可以发现窗口并不会显示,要显示窗口,我们必须调用ShowWindow这个API来显示
    Bool ShowWindow(HWnd hwnd,int nhow)
    nhow指定为显示模式,常用的几种显示模式为
    SW_HIDE  隐藏窗口
    SW_MINIMIZE 窗口最小化
    SW_RESTORE  恢复窗口
    ShowWindow返回窗口的前一个显示状态,如果显示窗口则返回非0值,如果没显示则返回0
    另外,还有一个UpdateWindow函数,目的是告诉Windows向用户的应用程序中发送一条消息,用该消息来更新主窗口。

    最后就是消息循环了,消息循环是Windows程序的重要组成部分,它的作用是接收系统发送的消息,运行程序的时候,他不断的接收消息,直到这些消息被读取以及处理,否则他们一直保存在应用程序的消息队列里。每次应用程序准备好去读取另一个消息时,就必须调用API函数GetMessage()
    Bool GetMessage(LPMSG msg,HWND hwnd,UINT min,UINT Max);
    参数msg指定消息结构体,其中包括了消息ID,消息对应的响应消息的窗口句柄,以及消息的参数等。
    tagMSG = packed record
        hwnd: HWND;
        message: UINT;
        wParam: WPARAM;
        lParam: LPARAM;
        time: DWORD;
        pt: TPoint;
      end;
    WParam和lparam中存放着每条消息相关的附加信息。
    time中放着消息的发送时间,以毫秒为单位指定
    pt包含着鼠标的坐标。
      在应用程序的消息队列中如果没有一条消息,则GetMessage()调用会向Windows回传一个控制指令(这个,咱么在以后在说)
      GetMessage的hwnd参数将指定所获得的消息传给哪个窗口,一个应用程序可能有很多窗口,用户也许指向接收某个具体窗口的消息。如果用户想接收指向应用程序的所有消息,则指定为0。其余两个参数指定了要接收的消息的范围。通常用户希望自己的应用程序能够接收所有的消息,此时将min和max都指定为0。
      当用户结束程序时,GetMessage()返回0,于是消息循环结束。如果发生从无,返回-1。此外,在消息循环中还有两个函数TranslateMessage和DIspatchMessage,
    TranslateMessage的目的是将系统产生的虚拟键码转换成字符消息。以使用户的应用程序能响应所有的键盘。当转换了消息之后,使用DIspatchMessage函数来派发消息返回给Windows,于是Windows保存该消息,直到能将他传递给程序的窗口为止。
      最后当用户退出程序,消息循环结束,并且将msg.WParam的值返回给Windows就是我上面指定的ExitCode.
    PYG19周年生日快乐!

    该用户从未签到

    发表于 2012-6-5 19:18:32 | 显示全部楼层
    看帖子的要发表下看法
    PYG19周年生日快乐!
  • TA的每日心情
    开心
    2024-4-8 21:54
  • 签到天数: 28 天

    [LV.4]偶尔看看III

    发表于 2014-12-8 10:47:55 | 显示全部楼层
    虽然帖子比较老,但比较经典,讲的也详细,谢谢楼主
    PYG19周年生日快乐!
  • TA的每日心情
    奋斗
    3 天前
  • 签到天数: 699 天

    [LV.9]以坛为家II

    发表于 2018-7-1 16:26:31 | 显示全部楼层
    看上去不错,感谢分享
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2020-3-22 09:49
  • 签到天数: 284 天

    [LV.8]以坛为家I

    发表于 2018-12-9 16:15:34 | 显示全部楼层
    太好了,哈哈哈
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    无聊
    2019-1-25 12:46
  • 签到天数: 4 天

    [LV.2]偶尔看看I

    发表于 2018-12-15 10:48:10 | 显示全部楼层
    Delphi  现在还有人用这东西啊?
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    发表于 2019-2-15 18:01:31 | 显示全部楼层
    学习一下顶!!!!!!!!!!!
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2024-3-22 23:57
  • 签到天数: 174 天

    [LV.7]常住居民III

    发表于 2019-2-28 13:24:38 | 显示全部楼层
    多谢楼主分享~~,收藏了~~
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    昨天 11:05
  • 签到天数: 184 天

    [LV.7]常住居民III

    发表于 2022-6-11 14:55:02 | 显示全部楼层
    感谢分享这些知识   学习
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 登录 | 加入我们

    本版积分规则

    快速回复 返回顶部 返回列表