Windows窗口程序

简介: Windows窗口程序

Windows窗口程序

应用程序分类
  • 控制台程序Console
    DOS程序,没有窗口,通过DOS窗口执行
    入口函数: main
  • 窗口程序
    拥有自己的窗口,可以与用户交互
    入口函数: WinMain
  • 库程序存放代码、数据的程序,执行文件可以从中取出代码执行或获取数据
  • 静态库程序:扩展名LIB, 在编译链接程序时,将代码放入到执行文件中
    静态库没有入口函数 --> 没法执行 --> 没法进入内存
  • 动态库程序:扩展名DLL,在文件执行时从中获取代码
    动态库有入口函数–>可以执行,但是不能独立执行( 必须依附其他程序 )
    入口函数: DLLMain
开发工具和类
开发工具
  • 编译器
    CL.EXE 将源代码编译成目标文件.obj
  • 链接器
    LINK.EXE 将目标代码,库链接生成最终文件
  • 资源编译器
    RC.EXE (.rc) 将资源编译,最终通过链接器存入最终文件

Visual Studio 路径: C:\Projram Files(x86)\Microsoft Visual Studio xx\vc\bin

Windows库

  • kernel32.dll
    提供核心的API, 例如进程,线程,内存管理等
  • user32.dll
    提供了窗口,消息等API
  • gdi32.dll
    绘图相关的API

路径: C:\Windows\System32

头文件
  • windows.h
    所有windows头文件的集合 --> 包含了其他头文件
  • windef.h
    windows数据类型
  • winbase.h
    kernel32的API
  • wingdi.h
    gdi32的API
  • winuser.h
    user32的API
  • winnt.h
    UNICODE字符集的支持
相关函数
int WINAPI WinMain(
  HINSTANCE hInstance,// 当前程序的实例句柄
    HINSTANCE hPrevInstance, // 当前程序前一个实例句柄  --> 已经废弃
    LPSTR lpCmdLine, // 命令行参数字符串  char* 类型  --> 只能传递一个命令行参数
    int nCmdShow // 窗口的显示方式 最大化显示,最小化显示,原样显示
);

句柄可以找到进程对应的内存 --> 句柄是表的索引

int MessageBox(
  HWND hWnd, // 父窗口句柄   
    LPCTSTR lpText, // 显示在消息框中的文字
    LPCTSTR lpCaption, // 显示在标题栏中的文字
    UINT uType // 消息框中的按钮,图标显示类型
); // 返回点击的按钮ID

Hxxx --> 大概率是句柄

rc资源文件

后缀: .rc

100 ICON small.ico

100 数字标识 ICON 图标资源 small.ico 文件名称

编译后称为 .res文件

.obj & .res 统称为目标文件

程序编译过程

Demo
#include <windows.h>
int WinMain(HINSTANCE hIns,HINSTANCE hPreIns,LPSTR lpCmdLine, int nCmdShow){
  MessageBox(NULL,"hello world","Information",MB_YESNO);
  return 0;
}

第一个windows窗口
步骤
  1. 定义WinMain函数
  2. 定义窗口处理函数(自定义,处理消息)
  3. 注册窗口类(向操作系统写入一些数据)
  4. 创建窗口(内存中创建窗口)
  5. 显示窗口(绘制窗口的图像)
  6. 消息循环(获取/翻译/派发消息)
  7. 消息处理
#include<windows.h>
// 窗口处理函数(自定义,处理消息)
LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,WPARAM wParam,LPARAM lParam){
    return DefWindowProc(hWnd,msgID,wParam,lParam);
}
// 入口函数
int CALLBACK WinMain(HINSTANCE hIns,HINSTANCE hPerIns,LPSTR lpCmdLine,int nCmdShow){
    // 注册窗口类 --> 向系统的内核写入一些数据
    WNDCLASS wc={0}; // 结构体变量
    wc.cbClsExtra = 0; // 开缓冲区  --> n 字节的缓冲区
    wc.cbWndExtra = 0; // 开缓冲区  另一种缓冲区
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); // 背景色
    wc.hCursor = NULL; // 默认光标位置
    wc.hIcon = NULL;  // 默认图标
    wc.hInstance = hIns; // 当前程序实例句柄
    wc.lpfnWndProc = WndProc; // 窗口处理函数
    wc.lpszClassName = "窗口类名字"; // 窗口类的名字
    wc.lpszMenuName = NULL; // 没有菜单
    wc.style = CS_HREDRAW | CS_VREDRAW ; // 水平或者垂直有变化,重画窗口
    // 将上面复制写入操作系统
    RegisterClass(&wc);
    // 内存中创建窗口
    HWND hWnd = CreateWindow("窗口类名字","标题",WS_OVERLAPPEDWINDOW,100,100,100,100,NULL,NULL,hIns,NULL);
    //HWND hWnd = CreateWindow("窗口类名字","标题",WS_OVERLAPPEDWINDOW,x,y,w,h,父窗口,菜单,hIns,NULL);
    // WS_OVERLAPPEDWINDOW最后一个参数窗口风格
    // 父窗口,菜单没有就置位NULL
    // 最后一个参数没用,置为NULL
    // 显示窗口
    ShowWindow(hWnd,SW_SHOW);
    // 风格 SW_SHOW 原样显示
    UpdateWindow(hWnd);
    // 刷新 --> 再画一遍
    // 消息循环
    MSG nMsg = {0};
    while(GetMessage(&nMsg,NULL,0,0)){
    TranslateMessage(&nMsg); // 翻译消息
        DispatchMessage(&nMsg) ; // 派发消息,交给窗口处理函数来处理
    }
    return 0;
}
字符编码
历史背景
  • ASC
    7位代表一个字符
  • ASCII
    8位一个字符
  • DBCS
    double byte
    单双字节混合编码
  • UNICODE
  • linux 一般utf8
  • windows 一般 utf16
宽字节字符
  • wchar_t 每个字符占2个字节
    char每个字符占1个字节
    wchar_t 实际上是 unsigned short 类型,定义时,需要在前面增加"L",通知编译器按照双字节编译字符串,采用UNICODE编译
  • 需要使用支持wchar_t函数操作宽字节字符串
wchar_t * pwszText = L"Hello wchar";
wprintf(L"%s\n",pwszText);
  • demo
#include<windows.h>
#include<stdio.h>
int main(){
    wchar_t * pszText = L"Hello wchar";
    int len = wcslen(pszText); // 返回字符个数
    wprintf(L"%d,%s",len,pszText);
    return 0;
}
  • TCHAR 数据类型
    定义在WINNT.h中:
#ifdef UNICODE
  typedef wchar_t TCHAR
    #define _TEXT(quote) L##quote
#else
    typedef char TCHAR
    #define _TEXT(quote) quote
#endif

## 是拼接的作用

TCHAR * pszText = _TEXT("Hello,wkk");
  • 定义UNICODE 宏要在window.h头文件前面
  • UNICODE字符打印
    wprintf 对UNICODE 字符打印支持不完善
    在Windows使用WriteConsole API打印UNICODE 字符 GetStdHandle
//WriteConsole(标准输出句柄,输出的缓冲区,输出长度,实际输出的长度,备用参数);
wchar_t *pszText = L"哇咔咔";
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE),pszText,wcslen(pszText),NULL,NULL);
// GetStdHandle() 获取标准句柄
// STD_INPUT_HANDLE
// STD_OUTPUT_HANDLE
// STD_ERROR_HANDLE
  • 项目属性设置为UNICODE 字符集
    系统会自动增加UNICODE宏的定义

如果是TCHAR * 类型增加了UNICODE宏,字符串字面量前面要加L

  • 系统调用函数的参数类型
LPSTR === char * 
LPCSTR === const char *
LPWSTR === wchar_t * 
LPCWSTR === const wchar_t * 
LPTSTR === TCHAR *
LPCTSTR === const TCHAR*
注册窗口类
窗口类

概念

  • 窗口类包含了窗口的各种参数信息的数据结构
  • 每个窗口都具有窗口类,基于窗口类创建窗口
  • 每个窗口类都具有一个名称,使用前必须注册到系统

分类

  • 系统窗口类
    系统已经定义好的窗口类,所有应用程序都可以直接使用
  • 应用程序全局窗口类
    由用户自己定义,当前应用程序所有模块都可以使用
  • 应用程序局部窗口类
    由用户自己定义,当前应用程序中本模块可以使用
系统窗口类

不需要用户注册,直接使用即可,系统已经注册好了

  • 按钮 - BUTTON
  • 编辑框 - EDIT
#include<windows.h>
int CALLBACK WinMain(HINSTANCE hIns,HINSTANCE hPerIns,LPSTR lpCmdLine,int nCmdShow){
    HWND hWnd = CreateWindow("Button","按钮",WS_OVERLAPPEDWINDOW,100,100,100,100,NULL,NULL,hIns,NULL);
    ShowWindow(hWnd,SW_SHOW);
    UpdateWindow(hWnd);
    MSG nMsg = {0};
    while(GetMessage(&nMsg,NULL,0,0)){
    TranslateMessage(&nMsg); // 翻译消息
        DispatchMessage(&nMsg) ; // 派发消息,交给窗口处理函数来处理
    }
    return 0;
}
全局窗口类
ATOM RegisterClass(
  CONST WNDCLASS * lpWndClass // 窗口类的数据
);
// 注册成功,返回一个数字标识 非0
// 失败,返回0
// 注册窗口类的结构体
typedef struct _WNDCLASS {
    UINT style; // 窗口类的风格
    WNDPROC lpfnWndProc; // 窗口处理函数
    int cbClsExtra ; // 窗口类的附加数据buff大小
    int cbWndExtra;  // 窗口的附加数据buff大小
    HINSTANCE hInstance; // 当前模块的实例了句柄
    HICON hIcon; // 窗口图标句柄
    HCURSOR hCursor; // 鼠标句柄
    HBRUSH hbrBackground; // 绘制窗口背景的画刷句柄
    LPCTSTR lpszMenuName; // 窗口菜单的资源ID字符串
    LpCTSTR lpszClassName; // 窗口类的名称
}WNDCLASS,*PWNDCLASS;
  • style 窗口类风格
    应用程序全局窗口类的注册,需要在窗口类的风格中增加CS_GLOBALCLASS.
WNDCLASS wce = {0};
wce.style = ... | CS_GLOBALCLASS;

应用程序局部窗口类,在注册窗口类时,不添加CS_GLOBALCLASS风格

  • CS_HREDRAW 当窗口水平变化时,窗口重新绘制
    CS_VERDRAW 当窗口垂直变化时,窗口重新绘制
    CS_DBLCLKS 允许窗口接收鼠标双击
    CS_NOCLOSE 窗口没有关闭按钮

一般不建议使用全局窗口类

局部窗口类

在注册窗口类时,不添加CS_GLOBALCLASS风格

窗口创建

CreateWindow / CreateWindowEx --> 加强版

加强版增加了扩展风格dwExStyle参数

HWND CrateWindowEx(
  DWORD dwExStyle , // 窗口的扩展风格
    LPCTSTR lpClassName, // 已经注册的窗口类名称
    LPCTSTR lpWindowName, // 窗口标题栏的名字
    DWORD dwStyle, // 窗口的基本风格
    int x, // 左上角水平坐标
    int y, // 左上角垂直坐标
    int nWidth,
    int nHeight,
    HWND hWndParent, // 窗口的父窗口句柄  --> 如果是子窗口要写这个参数
    HMENU hMenu, // 窗口菜单句柄
    HINSTANCE hInstance, // 应用程序实例句柄 --> WinMain 第一个参数
    LPVOID lpParam // 窗口创建时附加参数 --> 一般给NULL
); // 创建成功返回窗口句柄

窗口基本风格

WS_BORDER   有黑色的边界线
WS_CAPTION  有标题栏
WS_CHILD    子窗口
WS_CHILDWINDOW  同上
WS_CLIPCHILDREN  裁剪窗口  --> 不规则窗口
WS_CLIPSIBLINGS  
WS_DISABLED  禁用 --> 常用按钮控件
WS_DLGFRAME  对话框
WS_GROUP     分组
WS_HSCROLL   水平滚动条
WS_ICONIC    最初状态最小化状态
WS_MAXIMIZE  最大化状态
WS_MAXIMIZEBOX  最大化按钮
WS_MINIMIZE
WS_MINIMIZEBOX
WS_OVERLAPPED 交叠窗口  --> 标题栏+边框
WS_OVERLAPPEDWINDOW   基本都有 *********
WS_POPUP  弹出式对话框
WS_SIZEBOX 可以改变大小    
WS_TABSTOP   支持tap键顺序
WS_SYSMENU  系统菜单
WS_TILED
WS_VISIBLE  可见的 --> 显式子窗口   主窗口 -> showWindow 函数
WS_VSCROLL   垂直滚动条

CreateWindowEx 内部实现

  • 函数内部根据传入的窗口类名称,在应用程序局部窗口类中查找,如果找到执行2, 没有找到执行3
  • 比较局部窗口类与创建窗口时传入的HINSTANCE变量,如果发现相等,创建和注册的窗口类在同一模块,创建窗口返回。如果不相等执行3
  • 在应用程序全局窗口类,如果找到执行4,如果没有找到执行5
  • 使用找到的窗口类的信息,创建窗口返回
  • 在系统窗口类中查找,如果找到创建窗口返回,否则创建窗口失败
匹配查找窗口类
if(找到窗口类){
    申请一大块内存,将窗口的数据信息存入这块内存
    return 内存的句柄
}else{
    return NULL;
}
基本demo
#include<windows.h>
// 窗口处理函数(自定义,处理消息)
LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,WPARAM wParam,LPARAM lParam){
  switch(msgID){
    case WM_DESTROY:
      PostQuitMessage(0); // 可以使GetMessage 函数返回0 
      break;
  }
    return DefWindowProc(hWnd,msgID,wParam,lParam);
}
// 入口函数
int CALLBACK WinMain(HINSTANCE hIns,HINSTANCE hPerIns,LPSTR lpCmdLine,int nCmdShow){
    // 注册窗口类 --> 向系统的内核写入一些数据
    WNDCLASS wc={0}; // 结构体变量
    wc.cbClsExtra = 0; // 开缓冲区  --> n 字节的缓冲区
    wc.cbWndExtra = 0; // 开缓冲区  另一种缓冲区
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); // 背景色
    wc.hCursor = NULL; // 默认光标位置
    wc.hIcon = NULL;  // 默认图标
    wc.hInstance = hIns; // 当前程序实例句柄
    wc.lpfnWndProc = WndProc; // 窗口处理函数
    wc.lpszClassName = "窗口类名字"; // 窗口类的名字
    wc.lpszMenuName = NULL; // 没有菜单
    wc.style = CS_HREDRAW | CS_VREDRAW ; // 水平或者垂直有变化,重画窗口
    // 将上面复制写入操作系统
    RegisterClass(&wc);
    // 内存中创建窗口
    HWND hWnd = CreateWindow("窗口类名字","标题",WS_OVERLAPPEDWINDOW,100,100,100,100,NULL,NULL,hIns,NULL);
    //HWND hWnd = CreateWindow("窗口类名字","标题",WS_OVERLAPPEDWINDOW,x,y,w,h,父窗口,菜单,hIns,NULL);
    // WS_OVERLAPPEDWINDOW最后一个参数窗口风格
    // 父窗口,菜单没有就置位NULL
    // 最后一个参数没用,置为NULL
    // 显示窗口
    ShowWindow(hWnd,SW_SHOW);
    // 风格 SW_SHOW 原样显示
    UpdateWindow(hWnd);
    // 刷新 --> 再画一遍
    // 消息循环
    MSG nMsg = {0};
    while(GetMessage(&nMsg,NULL,0,0)){
    TranslateMessage(&nMsg); // 翻译消息
        DispatchMessage(&nMsg) ; // 派发消息,交给窗口处理函数来处理
    }
    return 0;
}
创建子窗口
  • 创建时要设置父窗口句柄
  • 创建风格要增加WS_CHLID | WS_VISIBLE
WNDCLASS wc={0}; // 结构体变量
wc.cbClsExtra = 0; // 开缓冲区  --> n 字节的缓冲区
wc.cbWndExtra = 0; // 开缓冲区  另一种缓冲区
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); // 背景色
wc.hCursor = NULL; // 默认光标位置
wc.hIcon = NULL;  // 默认图标
wc.hInstance = hIns; // 当前程序实例句柄
wc.lpfnWndProc = DefWindowProc; // 系统默认处理函数 **************
wc.lpszClassName = "Child"; // 窗口类的名字
wc.lpszMenuName = NULL; // 没有菜单
wc.style = CS_HREDRAW | CS_VREDRAW ; // 水平或者垂直有变化,重画窗口
// 将上面复制写入操作系统
RegisterClass(&wc);
HWND hChlid1 = CreateWindowEx(0,"Child","child1",WS_CHILD|WS_VISIBLE|WS_OVERLAPPEDWINDOW,0,0,200,200,
      hWnd,NULL,hIns,NULL
);
HWND hChlid2 = CreateWindowEx(0,"Child","child2",WS_CHILD|WS_VISIBLE|WS_OVERLAPPEDWINDOW,200,0,200,200,
      hWnd,NULL,hIns,NULL
);
显式窗口
ShowWindow(hWnd,SW_SHOW);
// 根据窗口句柄,找到内存,绘制窗口
UpdateWindow(hWnd);
消息基础
消息
  • 消息组成(windows下)
  • 窗口句柄
  • 消息ID
  • 消息的两个参数(两个附带信息)
  • 消息的产生的时间
  • 消息产生时的鼠标位置
typedef struct tagMSG{
    HWND hwnd;
    UINT message;
    WPARAM wparam;
    LPARAM lParam;
    DWORD time;
    POINT pt;
}MSG;
  • 消息的作用
    当系统通知窗口工作时,采用消息的方法派发给窗口的处理函数

每个窗口都有窗口处理函数

MSG nMsg = {0};
while(GetMessage(&nMsg,NULL,0,0)){
    TranslateMessage(&nMsg); // 翻译消息
    DispatchMessage(&nMsg) ; // 派发消息,交给窗口处理函数来处理
}

DispatchMessage函数

DispatchMessage(&nMsg){
    nMsg.hwnd --> 保存窗口数据的内存 --> WndProc
    WndProc(...){
        // 处理消息
    }
}
LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,WPARAM wParam,LPARAM lParam);
/**
* hWnd
* msgID
* wParam
* lParam
*/
消息的最后两个参数 消息的时间,消息时的鼠标位置没有传递
窗口处理函数
  • 每个窗口都必须有窗口处理函数
LRESULT CALLBACK WindowProc(
  HWND hwnd,// 窗口句柄
    UINT uMsg, // 消息ID
    WPARAM wParam, // 消息函数
    LPARAM lParam  // 消息参数
);
  • 当系统通知窗口时,会调用窗口处理函数,同时将消息ID和消息参数传递给窗口处理函数。 在窗口处理函数中,不处理的消息,使用缺省窗口处理函数,DefWindowProc
  • DefWindowProc 给各种消息做默认处理
消息相关函数
  • GetMessage - 获取本进程的消息
BOOL GetMessage(
  LPMSG lpMsg,// 存放获取到的消息BUFF
    HWND hWnd,  // 窗口句柄  --> 抓指明的句柄的消息  NULL--> 抓所有的
    // 消息的范围 (0,0)本进程的消息都抓
    UINT wMsgFilterMin, // 获取消息的最小ID
    UINT wMsgFilterMax  // 获取消息的最大ID
);
  • lpMsg 当获取到消息后,将消息的参数存放到MSG结构中hWnd 获取到hWnd所指定窗口的消息wMsgFilterMin 和 wMsgFilterMax 只能获取到由它们指定的消息范围内的消息,如果都为0,表示没有范围返回值:
  • 如果message 为 WM_QUIT返回0, 其余消息返回非0
  • TranslateMessage 翻译消息
    将按键消息,翻译成字符消息 --> 可见字符按键
BOOL TranslateMessage(
  CONST MSG*lpMsg // 要翻译的消息地址
);
  • 检查消息是否是按键的消息,如果不是按键消息,不做任何处理,继续执行
  • DispatchMessage 派发消息
LRESULT DispatchMessage(
  CONST MSG * lpmsg // 要派发的消息
);
  • 将消息派发到该消息所属窗口的窗口处理函数上
常见消息

产生时间,附带的两个参数,一般用来做什么

  • WM_DESTROY
  • 产生时间 窗口被销毁时
  • 附带消息:
    wParam: 0
    lParam: 0
  • 常用于在窗口被销毁之前,做相应的善后处理,例如:资源,内存等
  • WM_SYSCOMMAND
  • 产生时间: 点击窗口的最大化,最小化,关闭等
  • 附带消息
    wParam: 具体点击的位置 例如关闭: SC_CLOSE
    lParam: 鼠标光标的位置 LOWORD(lParam) 水平位置 HIWORD(lParam) 垂直位置
  • 常用在窗口关闭时,提示用户处理
  • WM_CREATE
  • 产生时间: 窗口创建成功,但还没显式时,CreateWindow 和 ShowWindow之间
  • 附带信息
    wParam 为0
    lParam 为CREATESTRUCT 类型的指针,通过这个指针可以获取CreateWindowEx中的全部12个参数的信息
  • 一般用于初始化窗口的参数、资源等等,包含创建子窗口
  • WM_SIZE
  • 产生时间 在窗口的大小发生变化后( 第一次显示,从无到有也会触发 )
  • 附带消息wParam: 窗口大小变化的原因lParam: 窗口变化后的大小
  • LOWORD(lParam) 变化后的宽度
  • HIWORD(lParam) 变化后的高度
  • 常用于窗口大小变化后,调整窗口内各个部分的布局
  • WM_QUIT
  • 产生时间: 用户发送
  • 附带消息
    wParam : PostQuitMessage 函数传递的参数
    lParam : 0
  • 用于结束消息循环,当GetMessage收到这个消息后,会返回FALSE结束while处理,退出消息循环

不需要手动处理

消息循环
消息循环的阻塞
  • GetMessage 从系统获取消息,将消息从系统移除,阻塞函数。当系统无消息时,会等候下一条消息
  • PeekMessage 以查看的方式从系统获取消息,可以不将消息从系统中移除,非阻塞函数。当系统没有消息时,返回FALSE。
BOOL PeekMessage(
  LPMSG lpMsg,  // 消息信息
    HWND hWnd,   // 窗口句柄
    UINT wMsgFilterMin, // first message
    UINT wMsgFilterMax, // last message
    UINT wRemoveMsg     // 是否移除标识  PM_REMOVE / PM_NOREMOVE
);
while(1){
  if(PeekMessage(&nMsg,NULL,0,0,PM_NOREMOVE)){
        // 有消息
        if(GetMessage(&nMsg,NULL,0,0)){
            TranslateMessage(&nMsg);
            DispatchMessage(&nMsg);
        }else{
            break;
        }
    }else{
        // 无消息
        // 空闲处理
        
    }
}
发送消息
  • SendMessage 发送消息,会等待消息处理的结果 ( 等到消息处理完 , 会阻塞)
  • PostMessage 投递消息,消息发出后立刻返回,不等待消息执行结果
    将消息放到系统消息队列中
BOOL SendMessage / PostMessage (
  HWND hWnd , // 消息发送的目的窗口
    UINT Msg, // 消息ID
    WPARAM wParam , // 消息参数
    LPARAM lParam   // 消息参数
);
消息分类
  • 系统消息 ID范围 0-0x03ff (0-1024)
    系统定义好的消息,可以在程序中直接使用
  • 用户自定义消息 ID范围 0x0400 - 0x7fff ( 一共31743)
    用户自定义,满足用户自己的需求,由用户自己发出消息,并相应处理
    自定义消息宏: WM_USER
消息队列
概念
  1. 消息队列用于存放消息的队列
  2. 先进先出
  3. 所有窗口程序都具有消息队列
  4. 程序可以从队列中获取消息
分类
  • 系统消息队列,由系统维护的消息队列。存放系统产生的消息,例如:鼠标,键盘等
  • 程序消息队列 属于每一个应用程序(线程)的消息队列,由应用程序维护

消息和消息队列的关系
  • 消息和消息队列的关系
  1. 产生消息时,会将消息存放到系统消息队列中
  2. 系统根据存放的消息,找到对应程序的消息队列
  3. 将消息投递到程序的消息队列中
  • 根据消息和消息队列之间使用关系,消息分为两类
  • 队列消息
    消息的发送和获取,都是通过消息队列完成
  • 非队列消息
    消息的发送和获取,是直接调用消息的窗口处理完成
  • 队列消息
    消息发送后,首先放入队列,然后通过消息循环,从队列中获取
    GetMessage 从消息队列获取消息
    PostMessage 将消息投递到消息队列
    常见队列消息: WM_PAINT, 键盘,鼠标,定时器

WM_QUIT 必须进队列,不然GetMessage的循环不会结束

  • 非队列消息
    消息发送时,首先查找消息接收窗口的窗口处理函数,直接调用函数,完成消息
    SendMessage 直接将消息发送给窗口的处理函数,并等候处理结果
    常见消息: WM_CREATE, WM_SIZE等

WM_CREATE 必须不能进队列 --> 此时窗口还没有显示

GetMessage详细解释
  • 在程序消息队列中查找信息,如果队列有消息,检查消息是否满足指定条件( HWND, ID范围 ), 不满足条件就不会取出消息,否则取出返回
  • 如果程序队列没有消息,向系统消息队列获取数据本程序的消息。如果系统队列的当前消息属于本程序,系统会将消息转发到程序消息队列中
  • 如果系统消息队列也没有消息,检查当前进程的所有窗口的需要重新绘制的区域,如果发现有需要绘制的区域,产生WM_PAINt 消息,取得消息返回处理
  • 如果没有重新绘制区域,检查定时器如果有到的定时器,产生WM_TIMER返回处理执行
  • 如果没有到时的定时器,整理程序的资源,内存等等
  • GetMessage 会继续等候下一条消息。PeekMessage 返回 FALSE, 交出程序的控制权
  • GetMessage 如果获取到是WM_QUIT函数返回FALSE
WM_PAINT消息
  • 产生时间: 当窗口需要绘制的时候( 并且系统没有其他消息时 )
  • 附带信息: wParam : 0 lParam : 0
  • 专职用法: 用于绘图
// paint demo
// 窗口无效区域: 需要重新绘制的区域
BOOL InvalidateRect(
  HWND hWnd, // 窗口句柄
    CONST RECT * lpRect, // 区域的矩形坐标, NULL -> 整个窗口
    BOOL bErase // 重绘前是否先擦除 TRUE / FALSE 
);

WM_LBUTTONDOWN 鼠标左键按下消息

绘图

步骤

  1. 开始绘图
HDC BeginPain(
  HWND hwnd , // 绘图窗口
    LPPAINTSTRUCT lpPaint // 绘图参数的buff
); // 返回绘图设备句柄HDC
  1. 正式绘图
  2. 结束绘图
BOOL EndPaint(
  HWND hWnd, // 绘图窗口
    CONST PAINTSTRUCT * lpPaint // 绘图参数的指针
);
PAINTSTRUCT ps = {0};
HDC hdc = BeginPaint(hWnd,&ps);
TextOut(hdc,100,100,"Hello,Wkk",strlen(Hello,Wkk));
EndPaint(hWnd,&ps);
// 以上绘图的代码,必须放在处理WM_PAINT消息中
键盘消息
键盘消息分类
  • WM_KEYDOWN 按键按下产生
  • WM_KEYUP 按键被放开时产生
// 附带信息
WPARAM  按键的Virtual Key --> 虚拟键码值不能区分大小写
LPARAM  按键的参数,例如按下次数 --> 不重要
  • WM_SYSKEYDWON 系统键按下时产生 比如: ALT, F10
  • WM_SYSKEYUP 系统键放开时产生
字符消息( WM_CHAR )
  • TranslateMessage 在转换WM_KEYDOWN消息时,对于可见字符会产生WM_CHAR,不可见字符无此消息。
TranslateMessage(&nMsg){
    // 只翻译可见字符的消息
    if(nMsg.message != WM_KEYDWON) return ;
    根据nMsg.wParam键码值可以获知哪个按键被按下
    if(不可见字符的按键) return ;
    查看CapsLock是否处于打开状态
    if(打开) PostMessage(nMsg.hwnd,WM_CHAR,大写字符,...);
    else PostMessage(nMsg.hwnd,WM_CHAR,小写字符,...);
}
  • 附带信息
WPARAM - 输入的字符的ASCII字符编码值
LPARAM - 按键的相关参数  --> 不重要
鼠标消息
鼠标消息分类
  • 基本鼠标消息WM_LBUTTONDOWN 鼠标左键按下WM_LBUTTONUP 左键抬起WM_RBUTTONDWONWM_RBUTTONUPWM_MOUSEMOVE 鼠标移动消息
  1. 附带信息wPARAM: 其他按键的状态,例如: Ctrl / Shift等

鼠标左键:1, Ctrl: 8 ,Shift: 4

  1. IPARAM: 鼠标的位置,窗口客户区坐标系
  • LOWORD x坐标
  • HIWORD y坐标
  1. 一般情况鼠标按下/ 抬起是成对出现的。
    在鼠标移动过程中,会根据移动速度产生一系列的WM_MOUSEMOVE消息
    移动慢产生消息多,移动快产生消息少
  • 双击消息WM_LBUTTONDBLCLK 鼠标右键双击WM_RBUTTONDBLCLK 左键双击
  1. 附带消息
    同基本鼠标消息
  2. 消息产生顺序
    以左键双击为例:
    WM_LBUTTONDOWN
    WM_LBUTTONUP
    WM_LBUTTONDBLCLK
    WM_LBUTTONUP
    使用时需要在注册窗口类的时候添加CS_DBLCLKS 风格, 加上才会出现双击事件
  • 滚轮消息WM_MOUSEWHEEL 鼠标滚轮消息
  1. 附带信息wPARAM
  • LOWORD 其他按键的状态
  • HIWORD 滚轮的偏移量,通过正负值表示滚动方向
    正:向前滚动, 负:向后滚动
  1. IPARAM: 鼠标当前的位置,屏幕坐标系
  • LOWORD x坐标
  • HIWORD y坐标
  1. 使用
    通过偏移量,获取滚动的方向和距离

一般的滚动偏移都是120的倍数

定时器消息
定时器消息介绍
  • 产生时间
    在程序中创建定时器,当到达时间间隔时,定时器( 实际是GetMessage )会向程序发送一个WM_TIMER消息
    定时器的精度是毫秒,但是准确度低
    可以设置时间间隔为1000ms, 但是会在非1000毫秒到达消息
  • 附带信息
    wPARAM: 定时器ID
    IPARAM: 定时器处理函数的指针 --> 一般没用
  • 用途
    周期性做,时间要求不严
创建销毁定时器
  • 创建定时器
UINT_PTR SetTimeer(
  HWND hWnd,// 定时器窗口句柄
    UINT_PTR nIDEvent, // 定时器ID
    UINT uElapse, // 时间间隔   ms为单位
    TIMERPROC lpTimerFunc // 定时器处理函数指针(一般不使用,为NULL)  
); // 创建成功,返回非0
  • 关闭定时器
BOOL KillTimer(
  HWND hWnd, // 定时器窗口句柄
  UINT_PTR uIDEvent  // 定时器ID
);
菜单资源
菜单分类
  • 窗口的顶层菜单
  • 弹出式菜单
    例如:鼠标右键菜单
  • 系统菜单
HMENU 类型表示菜单
ID 表示菜单项  --> 每一个菜单项都有一个ID
资源相关
  • 资源脚本文件: *.rc文件
    描绘图片、菜单资源
  • 编译器: RC.EXE

菜单资源的使用
  1. 添加菜单资源
  • 增加一个.rc文件 --> 会自动增加一个resource.h文件
  • 选中rc文件 --> Add Resource
  • 添加菜单资源
  • 编辑菜单,下拉项 右键可以查看&修改属性
  • 分割线 Separator -> true
  • 菜单ID
  1. 加载菜单资源
  • 注册窗口类时设置菜单
wc.lpszMenuName = (LPCTSTR)IDR_MENU1;  // 强转一下路径
  • 创建窗口时传递参数

倒数第三个参数 传递菜单句柄 ( 不是上面的菜单ID )

HWND hWnd = CreateWindowEx(0,"Main","window",WS_OVERLAPPEDWINDOW,100,100,500,500,NULL,LoadMenu(hIns,(LPCTSTR)IDR_MENU1),hIns,NULL)
  • 加载菜单资源:
HMENU LoadMenu (
  HINSTANCE hInstance, // handle to module  --> 找本进程的内存
    LPCTSTR lpMenuName   // menu name or resource identifier
);
  • 在主窗口WM_CREATE消息中利用SetMenu函数设置菜单
HMENU hMenu = LoadMenu(hIns,(LPCTSTR)IDR_MENU1));
SetMenu(hWnd,hMenu);
命令消息处理

单击菜单项发出WM_COMMAND消息

  • 附带信息
wPARAM : HIWORD 对于菜单为0 
         LOWORD 菜单项的ID
lPARAM : 对于菜单为0
图标资源

以.ico结尾的图片

  • 添加资源
    注意图标的大小,一个图标文件中,可以有多个不同大小的图标
  • 加载
HICON LoadIcon(
  HINSTANCE hInstance, // 当前程序实例句柄
    LPCTSTR lpIconName  // 图标资源id
);// 成功返回HICON句柄
  • 设置
    注册窗口类
wc.hIcon = LoadIcon(hIns,(LPCTSTR)IDI_ICON1);
光标资源
  • 添加光标的资源
    关标的大小默认是32x32像素,每个光标有HotSpot( 热点 ), 是当前鼠标的热点
  • 加载资源
HCURSOR LoadCursor(
  HINSTANCE hInstance, // 程序实例
    LPCTSTR lpCursorName // 资源ID
); // hInstance 可以为NULL, 获取系统默认的Cursor
  • 设置资源
  • 在注册窗口时,设置光标
wc.hCursor = LoadCursor(hIns,(char*)IDC_CURSOR1);
  • 使用SetCursor设置光标
HCURSOR SetCursor(
  HCURSOR hCursor // 光标
); // 返回原来的光标
  • 必须放在消息的处理中设置
WM_SETCURSOR 消息参数
    wPARAM 当前使用的光标句柄
    IPARAM - LOWORD 当前区域的代码 在哪个区域活动  HICLIENT  / HTCAPTION  客户区/标题区
           - HIWORD 当前鼠标消息ID,有没有点右键左键等等
DefWindowPro() 默认处理,会将光标重新改成注册窗口类的关标
所以SetCursor后要直接返回,不能经过DefWindowPro函数
字符串资源
  • 添加字符串资源
    添加字符串表,在表中添加字符串
  • 字符串资源的使用
int LoadString(
  HINSTANCE hInstance, //程序实例
    UINT uID, // 字符串ID
    LPTSTR lpBuffer, // 存放字符串的BUFF
    int nBufferMax // 字符串BUFF长度
); // 成功返回字符串长度,失败0
快捷键资源
  • 添加 资源添加快捷键表,增加命令ID对应的快捷键 Accelerator
    让快捷键的ID 和 命令的ID一样 就是绑定
  • 使用
// 加载加速键表 --> 表
HACCEL LoadAccelerators(
  HINSTANce hInstance, // 程序实例句柄
    LPCTSTR lpTableName   // 快捷键表名
); // 返回快捷键表句柄
// 翻译快捷键
int TranslateAccelerator(
  HWND hWnd, // 处理消息的窗口句柄
    HACCEL hAccTable, // 快捷键表句柄
    LPMSG lpMsg// 消息
); // 如果快捷键,返回非零
TranslateAccelerator(hWnd,hAccel,&nMsg){
    if(nMsg.message != WM_KEYDOWN ) return 0; // 没有按键按下,一定不是快捷键
    根据nMsg.wParam(键码值),获知哪些按键按下
    到快捷键表中去匹配查找
    if(找不到) return 0; 
    else{
        SendMessage(hWnd,WM_COMMAND,(HI)1(LO)快捷键对应的ID,...);
        return 1;
    }
}
// 调用
while( GetMessage(&nMsg,NULL,0,0)){
  if( !TranslateAccelerator(hWnd,hAccel,&nMsg)){
    TranslateMessage(&nMsg);
    DispatchMessage(&nMsg);
  }
}
  • 在WM_COMMAND中相应消息,消息参数
    wPARAM : HWORD 为1表示加速键,为0表示菜单
    LOWORD 为命令ID
    lParam: 为0
绘图编程
绘图基础
  • 绘图设备 DC (Device Context), 绘图上下文/绘图描述表
  • HDC-DC 句柄,表示绘图设备
  • GDI - Windows graphics device interface ( Win32 提供的绘图API )
  • 颜色 RGB

32位: 8,8,8,8 --> 透明度

  • 颜色的使用
    COLORREF–> DWORD
COLORREF nColor = 0;
  • 赋值使用RGB宏
nColor = RGB(0,0,255);
  • 获取RGB值
GetRValue / GetGValue / GetBValue
BYTE nRed = GetRValue( nColor );
HDC hdc = BeginPaint(hWnd,...);  //--> 抓绘图设备
TextOut(hdc,x,y,"hello",...);
...
EndPaint(hwnd,...);
基本图形绘制
  • SetPixel 设置指定点的颜色
COLORREF SetPixel(
  HDC hdc, // DC句柄
    int X,
    int Y,
    COLORREF crColor 
);// 返回原来的颜色
PAINTSTRUCT ps = {0};
HDC hdc = BeginPaint(hWnd,&ps);
SetPixel(hdc,100,100,RGB(255,0,0));
EndPaint(hWnd,&ps);
  • 线得使用(直线,弧线)
    MoveToEx – 指明窗口当前点
    LineTo 从窗口当前点到指定点绘制一条直线
    当前点: 上一次绘制时的最后一点,初始为(0,0)点
MoveToEx(hdc,100,100,NULL); // 最后一个参数返回上一次的当前点
LineTo(hdc,300,300);
  • 封闭图形:能够用画刷填充的图形
    Rectangle / Ellipse
Rectangle(hdc,x,y,x+w,y+h);
Ellipse(hdc,100,100,x+w,y+h); // 外接矩形
GDI绘图对象
画笔
  • 画笔的作用
    线的颜色,线型(虚线画笔、点线画笔…),线粗
    HPEN : 画笔句柄
  • 画笔的使用
  1. 创建画笔
HPEN CreatePen(
  int fnPenStyle, // 画笔的样式
    int nWidth, // 画笔的粗细
    COLORREF crColor // 画笔的颜色
); // 创建成功返回句柄
  • PS_SOILD - 实心线,可以支持多个像素宽,其他线型只能是一个像素宽
  • PS_DASH - 虚线画笔
  1. 将画笔应用到DC中
HGDIOBJ SelectObject(
    HDC hdc, // 绘图设备句柄
      HGDIOBJ hgdiobj  // GDI绘图对象句柄
  ); // 返回原来的GDI绘图对象句柄   --> 注意保存原来DC当中画笔
  1. 绘图
  2. 取出DC中的画笔,将原来的画笔,使用SelectObject函数,放入到设备DC中
  3. 释放画笔
BOOL DeleteObject(
  HGDIOBJ hObject   // GUI绘图对象句柄
);
  1. 只能删除不被DC使用的画笔,所以在释放前,必须将画笔从DC中取出
画刷

封闭图形的填充的颜色、图案

HBRUSH 画刷句柄

  1. 创建画刷
HBRUSH hBursh = CreateSolidBrush(RGB(0,255,0));
//CreateSolidBrush 创建实心画刷
HBRUSH hBurshhat = CreateHatchBrush(HS_CROSS,RGB(0,255,0)); // HS_CROSS 经纬线
//CreateHatchBrush 创建纹理画刷
  1. 将画刷应用到DC中
HBRUSH oldBrush = SelectObject(hdc,hBursh); // 原本的刷子是白色的
  1. 绘图
  2. 将画刷从DC中取出
SelectObject(hdc,oldBrush);
  1. 删除画刷
DeleteObject(hPen);

用一条条的单独直线围起来的图形,不是封闭图形,不能填充

系统GDI对象

使用GetStockObject 函数获取系统维护的画刷,画笔等

如果不使用画刷填充,需要使用NULL_BRUSH 参数,获取透明画刷。

GetStockObject 返回的画刷不需要DeleteObject

// 透明画刷
HGDIOBJ hBursh = GetStockObject(NULL_BRUSH);
// 应用刷子
// 恢复刷子
位图
位图绘制
  • 位图相关
    光栅图形: 记录图像中每一点的颜色等信息
    矢量图形: 记录图像算法,绘图指令等等
    HBITMAP 位图句柄
  • 位图的使用
  1. 在资源中添加位图资源 --> 资源ID
  2. 从资源中加载位图LoadBitmap
HBITMAP hBmp = LoadBitmap(g_hInstance,(char*)IDB_BITMAP1);
  1. 创建一个与当前DC相匹配的DC(内存DC) --> 在内存中绘图
HDC CreateCompatibleDC(
  HDC hdc  // 当前DC句柄,可以为NULL, 使用屏幕DC
); // 返回创建好的DC句柄
  1. 将位图放入匹配的内存DC中 --> 在虚拟区域中绘制图形
SelectObject
  1. 成像 (1:1)
BOOL BitBlt(
  HDC hdcDest; // 目的DC
  int nXDest, 
  int nYDest,
  int nWidth,
  int nHeight,
  HDC hdcSrc, // 源DC --> 内存DC
  int nXSrc,  // 源左上X
  int nYSrc,  // 源左上Y
  DWORD dwRop  // 成像方法,SRCCOPY
);
  1. 缩放成像
BOOL StretchBlt(
  HDC hdcDest,
    int nXOriginDest,
    int nYOriginDest,
    int nWidthDest,
    int nHeightDest,
    HDC hdcSrc,
    int nXOriginSrc,
    int nYOriginSrc,
    int nWidthSrc,   // 源DC宽
    int nHeightSrc,  // 源DC高
    DWORD dwRop
);
// 目的区域比原始图像小  --> 缩小
// 目的区域比原始图像大  --> 放大
  1. 取出位图
SelectObject   从内存DC取数据
  1. 释放位图
DeleteObject(hBmp);
  1. 释放匹配的内存DC
DeleteDC(hMemdc);
文本绘制
  • 文字绘制
    TextOut -> 将文字绘制在指定坐标位置,只能单行绘制
TextOut(hdc,x,y,strdata,nCount);
RECT rc;
rc.left = 100;  // (left,top) (right,bottom)
rc.top= 150;
rc.right = 200;
rc.bottom= 200;
int DrawText(
  HDC hDC,    // DC句柄
    LPCTSTR lpString,  // 字符串
    int nCount,        // 字符数量
    LPRECT lpRect,     // 绘制文字的矩形框
    UINT uFormat       // 绘制的方式  // DT_LEFT|DT_TOP  水平靠左|垂直靠上  DT_WORDBREAK 多行绘制
);                                  // DT_NOCLIP 打破矩形区域的限制,不裁剪
// DT_CENTER DT_VCENTER --> DT_VCENTER、DT_BOTTOM只适用于单行DT_SINGLELINE,和DT_WORDBREAK冲突
  • 文本颜色和背景文字颜色: SetTextColor(hdc,RGB(…));文字背景色: SetBkColor(hdc,RGB(…)); --> 仅适用于不透明模式文字背景模式: SetBkMode(QPAQUE / TRANSPARENT );
  • QPAQUE 不透明,默认
  • TRANSPARENT 透明
  • 字体Windows常用的字体为 TrueType格式的字体文件 --> 保存点阵字型字体名 - 标识字体类型 --> 第一行有字体名称HFONT - 字体句柄
  1. 创建字体
HFONT CreateFont(
  int nHeight, // 字体高度
    int nWidth, // 字体宽度     一般给一个高度,宽度为0,系统字节匹配一个合适的宽度
    int nEscapement, // 字符串倾斜角度(以0.1度为单位)  --> 和斜体不同 
    int nOrientation, // 字符旋转角度   二维看不出效果
    int fnWeight, // 字体的粗细 ,不是以像素为单位  ,  900是细体
    DWORD fdwItalic, // 斜体
    DWORD fdwUnderline, // 下划线
    DWORD fdwStrikeOut, // 删除线    1/0
    DWORD fdwCharSet, // 字符集 --> GB2312_CHARSET  涵盖基本所有的汉字
    DWORD fdwOutputPrecision, // 输出精度  , 废弃
    DWORD fdwClipPrecision, // 剪切精度  , 废弃
    DWORD fdwQuality,  // 输出质量  , 废弃
    DWORD fdwPitchAndFamily,// 匹配字体  , 废弃
    LPCTSRT lpszFace  // 字体名称  ---> 字体文件内容第一行
);
  1. 应用字体到DC
    HGDIOBJ oldfont = SelectObject(hdc,hFont);
  2. 绘制文字
    DrawText / TextOut
  3. 取出字体
    SelectObject(hdc,oldfont);
  4. 删除字体
    DeleteObject(hFont);
对话框
  • 普通窗口: 自定义函数调用缺省函数
  • 对话框窗口: 缺省函数调用自定义函数
缺省函数(...){
    ...
    自定义函数(...)
}
对话框原理
  • 对话框分类
    模态对话框:当对话框显式时,会禁止其他窗口和用户的交互操作
    非模态对话框:在对话框显示后,其他窗口仍然可以和用户交互操作
  • 对话框基本使用
  1. 对话框窗口处理函数
  2. 注册窗口类( 不使用 ), 操作系统帮忙注册, 名字–>Dialog
  3. 创建对话框
  4. 对话框的关闭
  • 对话框窗口处理函数 --> 并非真正的对话框窗口处理函数
INT_PTR CALLBACK DialogProc(
  HWND hwndDlg , // 窗口句柄
    UINT uMsg, // 消息ID
    WPARAM wParam , // 消息参数
    LPARAM lParam // 消息参数
);
// 返回TRUE   缺省处理函数不需要处理
// 返回FALSE  交给缺省处理函数处理
// 一般不需要调用缺省对话框窗口处理函数
模态对话框
  • 创建对话框
INT DialogBox(
  HINSTANCE hInstance, // 应用程序实例句柄
    LPCTSTR lpTemplate, // 对话框资源ID
    HWND hWndParent, // 对话框父窗口
    DLGPROC lpDialogFunc  // 自定义函数
);
  • DialogBox事一个阻塞函数,只有当对话框关闭后,才会返回,继续执行后续代码
    返回值通过EndDialog设置 --> 通过EndDialog结束对话框

点击关闭按钮消息: WS_SYSCOMMAND

  • 对话框处理函数
INT_PTR CALLBACK DlgProc(HWND hwndlg,UINT msgID,WPARAM wParam,LPARAM lParam){
    switch(msgID){
        case  WM_SYSCOMMAND:
            if(wParam == SC_CLOSE){ // 点击关闭按钮
        EndDialog(hwndlg,100);
            }
            break;
    }
    return FALSE; // 将消息交给真正的对话框处理函数的后续处理部分处理
}
  • 对话框的关闭
BOOL EndDialog(
  HWND hDlg, // 关闭的对话框窗口句柄
    INT_PTR nResult // 关闭的返回值
);// 消除模式对话框 & 解除模式对话框的阻塞状态
  • 关闭模式对话框,只能使用EndDialog,不能使用DestroyWindow(销毁窗口)等函数。

DestroyWindow 可以销毁对话框,但是不能消除阻塞

  • nResult 是DialogBox函数退出时的返回值
  • 对话框的消息
    WM_INITDIALOG 对话框创建之后显示之前,通知对话框窗口处理函数,可以完成自己的初始化相关的操作。

其他消息和窗口的消息一致

非模态对话框
  • 创建对话框
HWND CreateDialog(
  HINSTANCE hInstance , // 应用程序实例句柄
    LPCTSTR lpTemplate, // 模板资源ID
    HWND hWndParent, // 父窗口
    DLGPROC lpDialogFunc // 自定义函数
);
  • 非阻塞函数,创建成功后返回窗口句柄**,需要使用ShowWindow函数显示对话框**
  • 对话框的关闭
    关闭时使用DestroyWindow销毁窗口,不能使用EndDialog关闭对话框
其他
增加DOS窗口

用于调试

HANDLE g_hOutput = 0 ;// 接收标准输出句柄
WinMain(...){
    AllocConsole(); //增加入口函数
    g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
    // FreeConsole(); // 释放控制台
    
    WriteConsole(g_hOutput,text,strlen(text),NULL,NULL);
}
参考
  1. https://www.bilibili.com/video/BV1Qb4y1o7u9
相关文章
|
1月前
|
Windows
实现Windows程序的数据更新
实现Windows程序的数据更新
12 0
|
1月前
|
Windows
构建布局良好的Windows程序
构建布局良好的Windows程序
11 0
|
1月前
|
C# Windows
初识Windows程序
初识Windows程序
10 0
|
2月前
|
Java Linux Windows
windows实现自动部署jar包运行程序
windows实现自动部署jar包运行程序
43 0
|
4月前
|
移动开发
【实用】一组原创原生样式的基础控件、UI组件样式(偏向移动端H5页面的样式)
【实用】一组原创原生样式的基础控件、UI组件样式(偏向移动端H5页面的样式)
【实用】一组原创原生样式的基础控件、UI组件样式(偏向移动端H5页面的样式)
|
2月前
|
Windows
火山中文编程 -- 第一个windows程序
火山中文编程 -- 第一个windows程序
12 0
|
4月前
|
Windows
(查看,和保存)windows下通过cmd命令符窗口查看、保存文件目录结构
(查看,和保存)windows下通过cmd命令符窗口查看、保存文件目录结构
64 0
|
4月前
|
Shell 数据安全/隐私保护 Windows
Windows Server【开机启动和任务计划程序】实现服务器重启后项目自启动(Windows Server 任务计划程序无法执行问题处理)
Windows Server【开机启动和任务计划程序】实现服务器重启后项目自启动(Windows Server 任务计划程序无法执行问题处理)
180 0
|
API Windows
win32 api Windows窗口的创建
windows窗口的创建有以下几个步骤: 1、创建注册窗口类 2、创建窗口句柄 3、显示更新窗口 4、消息循环 1、创建注册窗口类 所谓创建窗口类就是定义一个WNDCLASS类对象,并将该对象进行初始化。
1404 0

热门文章

最新文章