win32程序之窗口程序,以及消息机制

      

一丶简介

  通过上一讲.我们了解了窗口其实是绘制出来的.而且是不断绘制的过程. 所以窗口的本质是绘制. 但是我们现在看到的窗口程序.都可以点击关闭按钮. 使用鼠标点击会有反应.

而我们要怎么实现那. 

  其实鼠标点击是产生了一个消息.  window把这个消息封装成了消息结构体. 发送给了我们的窗口程序.  那么windows怎么知道你点击的那个窗口那?

是这样的. 当我们点击的时候. 会记录点击坐标.消息.等等. windows系统会接受到. 然后遍历内核中的WINOBJ结构. 而这个结构中存储着窗口对象. 窗口对象对应着消息线程.

所以windows一层一层的遍历.则找到了对应的窗口以及窗口对应的线程.然后发送给我们的应用程序. 

上面说的我们需要了解. 要知道消息怎么产生的. 怎么传递的.那么下面编程就明白了.

例如下图:

每个应用程序都有一个线程对象. 而这个线程对象如果创建窗口.那么内核中就有这个窗口对象.

如果我们有鼠标点击的消息.键盘消息等等.操作系统都会遍历窗口对象. 而窗口对象也会保存着创建这个窗口对象对应的线程对象. 而这个线程对象中则有消息队列.

这样的话操作系统则会封装消息发送给我们窗口对象.

二丶Wind窗口类结构.创建窗口程序.

1.进行窗口编程需要注意的问题

  在Windows中进行窗口编程.入口点已经改成WinMain了. 有四个参数.

如以下代码所示

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,               //窗口的实例句柄 hinstance代表模块意思 HWND代表窗口意思. HANDLE代表内核对象. HDC 设备上下文.
                     _In_opt_ HINSTANCE hPrevInstance,        //父窗口句柄.不需要.
                     _In_ LPWSTR    lpCmdLine,                //命令行参数
                     _In_ int       nCmdShow)                 //命令. 最大化命令.还是最小化命令.
{
  
    return 0;
}

2.进行Windows编程的调试手法

  在Windows中我们调试程序不能简单的使用printf进行调试.或者打印输出了. 我们可以使用两个API进行操作.

1.Sprintf() 格式化字符串. 

2.OutPutDebugString() 输出调试字符串.

具体两个API. 不再累赘.百度搜索即可.

因为OutPutDebugString() 只能打印固定字符串.所以使用sprintf进行格式化字符串.如下面代码.

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
  
    TCHAR str[30] = { NULL };
    wsprintf(str,TEXT( "%s"),TEXT("HelloWin32"));      //因为是Unicode所以使用W版本.
    OutputDebugStringW(str);
    return 0;
}

我们编译出程序之后.可以使用DebugView这款工具查看.

3.窗口编程的步骤

1.创建窗口类. windows提供的窗口样式.我们需要给定.

2.注册窗口类.创建了窗口我们需要注册到windows系统中.

3.创建窗口.如果注册窗口成功.那么我们需要创建出来这个窗口.并且显示跟更新.

4.消息处理

4.窗口编程需要的主要结构

  窗口的创建Windows已经为我们提供了. 这个结构就是WNDCLASSEXW 结构

看下这个结构中的内容吧

typedef struct _WNDCLASSEX { 
    UINT       cbSize;                   扩展的大小. 自己WndClass本身大小.
    UINT       style;                    风格
    WNDPROC    lpfnWndProc;              窗口回调.消息都要进入这个回调
    int        cbClsExtra;              
    int        cbWndExtra;  
    HINSTANCE  hInstance;                实例句柄
    HICON      hIcon;                    图标
    HCURSOR    hCursor;                  光标
    HBRUSH     hbrBackground;            背景
    LPCTSTR    lpszMenuName;             菜单名称
    LPCTSTR    lpszClassName;            类名称
    HICON      hIconSm;                  最小化图标
} WNDCLASSEX, *PWNDCLASSEX; 

这个结构就是说.你的窗口是什么样式. 大小.是否有图标. 消息处理函数在哪里等等.需要我们给指定.

 5.完整代码.

// WindoS.cpp : 定义应用程序的入口点。
//

#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include "WindoS.h"

#define MAX_LOADSTRING 100

// 全局变量: 
HINSTANCE hInst;                                // 当前实例
WCHAR szTitle[MAX_LOADSTRING]  = TEXT("第一个我的窗口");                  // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING] = TEXT("MyWindow");            // 主窗口类名

// 此代码模块中包含的函数的前向声明: 

LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);


int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    //1.自定义窗口样式
    WNDCLASS wcex;


    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;                          //设置回调
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;                         //设置模块实例句柄
    wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINDOS));
    wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);    //设置背景颜色
    wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WINDOS);
    wcex.lpszClassName = szWindowClass;                //设置类名
    
  //上面主要就是4个参数有用. 回调函数 背景颜色 模块句柄. 类名
    //2.注册窗口类
    BOOL bRet = RegisterClass(&wcex); //A RegisterClass U RegisterClassW  扩展 RegisterClassExA /ExW
    if (bRet == FALSE)
    {
        return 0;
    }
    //3.创建窗口 并且显示跟更新窗口
    HWND hWnd = CreateWindowW( 
        szWindowClass,                 //我们的类名
        szTitle,                       //我们自定义的窗口名称
        WS_OVERLAPPEDWINDOW,           //窗口的创建样式
        CW_USEDEFAULT,
        0,
        CW_USEDEFAULT,
        0, 
        nullptr,
        nullptr,
        hInstance,                    //实例句柄
        nullptr);
  //上面重要的也就是4个参数.其余参数查询下MSDN.
    if (!hWnd)
    {
        return FALSE;
    }

    ShowWindow(hWnd, SW_SHOW);
    UpdateWindow(hWnd);

    //4.消息循环.
    MSG msg;
    /*    1参数是消息结构体.操作系统会往里面填写消息. 
        2 参数窗口句柄 因为每个线程可以有多个窗口.表示我要取那个窗口的消息
        3.4 参数表示我要取这个窗口的那个消息. 后面三个参数属于过滤条件


    */
    while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)        
    {
        if (bRet == -1)
        {
            // handle the error and possibly exit
        }
        else
        {
            TranslateMessage(&msg);      //键盘消息转换为小写.
            DispatchMessage(&msg);      //分发消息.将我们的消息传递给我们的回调函数处理. 重要函数.此消息会将Windows的消息.发送给我们 定义窗口类的时候给的回调函数.这样我们就可以根据消息执行我们代码了.
        }
    }

    return 0;
}






//
//  函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目的:    处理主窗口的消息。
//
//  WM_COMMAND  - 处理应用程序菜单
//  WM_PAINT    - 绘制主窗口
//  WM_DESTROY  - 发送退出消息并返回
//
//   我们的窗口回调.
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_COMMAND:                             //菜单消息类型
        {
            int wmId = LOWORD(wParam);           //取低两位为菜单ID.根据菜单ID可以进行操作我们的窗口
            // 分析菜单选择: 
            switch (wmId)
            {
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);  //如果不处理.则必须调用这个函数教给默认的窗口回调处理
            }
        }
        break;
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: 在此处添加使用 hdc 的任何绘图代码...
            LineTo(hdc, 0, 100);
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

创建完毕的结果

三丶消息类型

我们回调中有我们的消息类型.我们可以判断消息类型进行我们不同的操作.

比如菜单消息.

WM_COMMAND.  如果是这个消息.那么回调函数的 wparam等附加信息就是WM_COMMAND的附加消息了. 我们可以取低位得出操作的菜单ID.进而进行消息处理.

WM_PAINT  这个消息是绘制的消息.我们知道.窗口是不断绘制的.所以绘制消息会一直来.

WM_DESTROY  窗口关闭消息. 如果接受到这个消息.则调用API往消息队列中(MSG)中传递退出消息. 此时外层主线程就会结束.

具体API:

  postQuitMessage(0);

当前具体的消息还要查询MSDN. 因为消息种类很多.

windows消息都是WM开头的.

比如查询WM_COMMAND消息

可以清楚的看到.她会告诉你如果是WM_COMMAND消息来了.那么回调函数的参数.分别代表的是什么意思.

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏逸鹏说道

VSCode中文乱码问题+Git环境配置

异常处理汇总-开发工具 http://www.cnblogs.com/dunitian/p/4522988.html 1.VSCode中文乱码问题 (file...

4238
来自专栏小程序容器

OpenApplus小程序容器

OpenApp+ (https://www.openapplus.com)一个小程序容器,配置简单、功能完善、界面流畅、开箱即用!使用OpenApp+可以快速扩...

5219
来自专栏Google Dart

AngularDart4.0 英雄之旅-教程-02启动应用

码云项目页:https://gitee.com/scooplolwiki/toh-0

842
来自专栏西安-晁州

vuejs、eggjs、mqtt全栈式开发设备管理系统

vuejs、eggjs、mqtt全栈式开发简单设备管理系统 业余时间用eggjs、vuejs开发了一个设备管理系统,通过mqtt协议上传设备数据至web端实时展...

2.6K7
来自专栏Jerry的SAP技术分享

S/4HANA for Customer Management里的搜索分页处理

这篇文章的英文版我发在了SAP Community上:Paging Implementation in S/4HANA for Customer Managem...

3934
来自专栏LIN_ZONE

laravel5.5+vue+Element-ui+vux环境搭建(webpack+laravelMix)(转)

本教程例子可到GitHub 上下载 Laravel5.5-Vue-Element-ui-Vux

1682
来自专栏游戏杂谈

Xcode打包踩过的那些坑

一、file was built for archive which is not the architecture being linked (armv7s)

1183
来自专栏c#开发者

Silverlight 4 RIA Service dataform Template, 代码选择控件,Validate验证使用技巧

Silverlight 4 RIA Service dataform Template, 代码选择控件,Validate验证使用技巧 功能 定义只读,新增,...

3435
来自专栏技术博客

MVC项目开发中那些用到的知识点(MvcContrib分离ASP.NET MVC项目)

在http://www.cnblogs.com/aehyok/archive/2013/04/07/3006438.html这篇随笔中,我简单的介绍了,asp...

1073
来自专栏前端杂货铺

Callbacks vs Events

  前言:本文翻译自Dean Edwards的一篇文章,原文地址:http://dean.edwards.name/weblog/2009/03/callbac...

2264

扫码关注云+社区

领取腾讯云代金券