Win32学习笔记

0.Win32程序

可以在32位或者以上的Windows系统中运行的程序
SDK:以Windows API编写的程序通常称为SDK程序

1.让窗口显示出来

这里先介绍一个最简单地win32程序,了解一下来显示一个窗口需要些哪些东西

步骤:

(1)设计窗口
(2)注册窗口
(3)创建窗口
(4)显示窗口

代码示例

#include <windows.h>

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

int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int iCmdShow)
{
	HWND hWnd;
	MSG msg;

	//设计窗口
	WNDCLASSEX windowEx;
	windowEx.style = CS_HREDRAW | CS_VREDRAW;
	windowEx.cbSize = sizeof(windowEx);
	windowEx.cbClsExtra = 0;
	windowEx.cbWndExtra = 0;
	windowEx.hInstance = hInstance;
	windowEx.hIcon = NULL;
	windowEx.hIconSm = NULL;
	windowEx.hCursor = NULL;
	windowEx.hbrBackground = CreateSolidBrush(RGB(0,255,0));
	windowEx.lpfnWndProc =  &WndProc;
	windowEx.lpszMenuName = NULL; 
	windowEx.lpszClassName = L"myWindows"; 

	//注册窗口
	RegisterClassEx(&windowEx);

	//创建窗口
	hWnd = CreateWindow(windowEx.lpszClassName,L"haha",WS_OVERLAPPEDWINDOW,100,100,500,500,NULL,NULL,hInstance,NULL);
	
	//显示窗口
	ShowWindow(hWnd,SW_SHOW);

	//消息循环
	while(GetMessage(&msg,NULL,0,0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);

	}

	return 0;
}

//消息处理函数
LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{
	switch(message)
	{
		case WM_CLOSE:
			PostQuitMessage(0);
			break;
	}

	return DefWindowProc(hWnd,message,wParam,lParam);
}

代码说明

1.主函数:

  • 功能:用户为基于windows的图形化应用程序提供的入口点。WinMain是应用程序入口点的常规名称。
  • 函数原型:
int CALLBACK WinMain(
  _In_  HINSTANCE hInstance,
  _In_  HINSTANCE hPrevInstance,
  _In_  LPSTR lpCmdLine,
  _In_  int nCmdShow
);
  • 参数:

hInstance 当前应用程序实例句柄
hPrevInstance 前一个应用程序实例句柄
lpCmdLine 命令行参数
nCmdShow 窗体显示方式

  • 返回值:

如果函数成功,当它接收到WM_QUIT消息时终止,它应该返回包含在该消息的wParam参数中的退出值。如果函数在进入消息循环之前终止,它应该返回0。

2.用于设计窗口的结构体:WNDCLASSEX

  • 功能:包含窗口类信息的用于注册窗口使用的一个结构体
  • 原型:
typedef struct tagWNDCLASSEX {
  UINT      cbSize;
  UINT      style;
  WNDPROC   lpfnWndProc;
  int       cbClsExtra;
  int       cbWndExtra;
  HINSTANCE hInstance;
  HICON     hIcon;
  HCURSOR   hCursor;
  HBRUSH    hbrBackground;
  LPCTSTR   lpszMenuName;
  LPCTSTR   lpszClassName;
  HICON     hIconSm;
} WNDCLASSEX, *PWNDCLASSEX;


  • 成员:

style (CS_HREDRAW:如果移动或大小调整改变了客户区的宽度,则重绘整个窗口。CS_VREDRAW:如果移动或大小调整改变了客户区域的高度,则重绘整个窗口。)
cbSize 当前结构体大小(以字节为单位)
cbClsExtra 在窗口类结构之后分配的额外字节数。系统将字节初始化为零。
cbWndExtra 在窗口实例之后分配的额外字节数。系统将字节初始化为零。
hInstance 应用程序句柄
hIcon 指定窗口类的图标句柄(工具栏显示的程序图标),为NULL时采用系统默认
hIconSm 窗体图标(左上角的小图标),为NULL时采用窗体类的图标
hCursor 鼠标图标,为NULL时采用系统默认(默认是加载中的图标)
hbrBackground 指定背景画刷句柄,为NULL时采用系统默认(默认和标题栏同一颜色)
lpfnWndProc 消息处理函数指针
lpszMenuName 菜单名,为NULL表示没有菜单
lpszClassName 窗口的名字,左上角显示的名字

3.注册窗口函数:RegisterClassEx()

  • 功能:注册窗口
  • 注意:RegisterClass()函数和RegisterClassEx()函数的区别:前者已经被后者取代,当然我们仍然可以使用前一个函数,只是它所对应的结构体WNDCLASS中少了cbSize和hIconSm这两个成员。
  • 函数原型:
ATOM WINAPI RegisterClassEx(
  _In_  const WNDCLASSEX *lpwcx
);
  • 参数:

lpwcx 传入指向窗口设计时初始化的结构体的指针

  • 返回值:

注册成功:返回一个唯一标识要注册的一个原子类
注册失败:返回0,可以通过函数GetLastError()查看错误

4.创建窗口函数:CreateWindow()

  • 功能:创建窗口
  • 函数原型:
HWND WINAPI CreateWindow(
  _In_opt_  LPCTSTR lpClassName,
  _In_opt_  LPCTSTR lpWindowName,
  _In_      DWORD dwStyle,
  _In_      int x,
  _In_      int y,
  _In_      int nWidth,
  _In_      int nHeight,
  _In_opt_  HWND hWndParent,
  _In_opt_  HMENU hMenu,
  _In_opt_  HINSTANCE hInstance,
  _In_opt_  LPVOID lpParam
);
  • 参数:

lpClassName 指定窗口类名,可以是windowEx.lpszClassName
lpWindowName 指定窗口的名字,该名称将显示在标题栏中
dwStyle 正在创建的窗口的样式(WS_OVERLAPPEDWINDOW 重叠的窗口)
x 窗口的初始水平位置
y 窗口的初始垂直位置
nWidth 窗口的宽度
nHeight 窗口的高度
hWndParent 父窗口句柄
hMenu 菜单句柄
hInstance 指定窗口所属应用程序句柄
lpParam 默认为NULL,创建多文档时再赋值

  • 返回值:

窗口创建成功:返回新窗口的句柄
创建失败:返回值为空,使用GetLastError获取扩展的错误信息

5.显示窗口函数:ShowWindow()

  • 功能:显示窗口
  • 函数原型:
BOOL WINAPI ShowWindow(
  _In_  HWND hWnd,
  _In_  int nCmdShow
);
  • 参数:

hWnd 该窗口的句柄
nCmdShow 控制窗口的显示方式(常用:SW_HIDE隐藏窗口并激活其他窗口;SW_SHOW在窗口原来位置原来的大小激活窗口)

  • 返回值:

窗口以前是可见的:返回值为非零值
窗口以前是隐藏的:返回值为零

6.消息处理函数:

  • 功能:处理发送到窗口的消息的函数。WNDPROC类型定义了一个指向这个回调函数的指针。(WindowProc是应用程序定义的函数名的占位符)
  • 说明:该函数被系统调用
  • 函数原型:
LRESULT CALLBACK WindowProc(
  _In_  HWND hwnd,
  _In_  UINT uMsg,
  _In_  WPARAM wParam,
  _In_  LPARAM lParam
);
  • 参数:

hwnd 窗口句柄,制定了该函数处理哪个窗口的消息
uMsg 有关系统提供的消息列表
wParam 消息附加信息
lParam 消息附加信息

  • 返回值

LRESULT 返回值是消息处理的结果,取决于发送的消息。

7.未处理消息默认处理函数

  • 功能:调用默认窗口过程,为应用程序未处理的任何窗口消息提供默认处理。此函数确保处理每条消息。DefWindowProc用窗口过程接收到的相同参数被调用。
  • 说明:该函数被消息处理函数返回
  • 函数原型:
LRESULT WINAPI DefWindowProc(
  _In_  HWND hWnd,
  _In_  UINT Msg,
  _In_  WPARAM wParam,
  _In_  LPARAM lParam
);
  • 参数:

和消息处理函数参数相同

  • 返回值

LRESULT 返回值是消息处理的结果,取决于发送的消息。

8.消息循环:

窗口上的各种操作都是基于消息的,我们有消息处理函数对各种消息做出响应,但是这些消息从哪里来?我们需要在主函数里面不断的从消息队列中去取消息

系统中有一个消息队列,每次产生I/O操作的时候,都会产生一条消息进入这个队列中

计算机上运行的每一个进程都有自己的一个消息队列

系统负责将系统消息队列中的消息分发到每个进程各自的消息队列中,但是,需要进程自己去自己消息队列中取到消息,然后进行相应的操作

如果这个消息有用,就执行对应的响应,如果这个消息没有用,就退回到系统消息队列

(1)MSG(消息结构体)

  • 功能:包含来自线程的消息队列的消息信息。
  • 原型:
typedef struct tagMSG {
  HWND   hwnd;
  UINT   message;
  WPARAM wParam;
  LPARAM lParam;
  DWORD  time;
  POINT  pt;
} MSG, *PMSG, *LPMSG;
  • 成员:

hwnd 窗口句柄(哪个窗口的消息)
message 消息标识符(宏)。应用程序只能使用低级词;高位字由系统保留。
wParam 关于消息的其他信息。一般是来源的ID号
lParam 关于消息的其他信息。一般为通过消息传输的数据
time 消息产生的时间。
pt 消息发生的位置

(2)获取消息函数:GetMessage()

  • 功能:从调用线程的消息队列检索消息。
  • 函数原型:
BOOL WINAPI GetMessage(
  _Out_     LPMSG lpMsg,
  _In_opt_  HWND hWnd,
  _In_      UINT wMsgFilterMin,
  _In_      UINT wMsgFilterMax
);
  • 参数:

lpMsg 指向一个MSG(消息结构体)的指针
hWnd 指向要接收哪个窗口的消息,如果为NULL用于接收所有窗口
wMsgFilterMin 指定要获取消息的最小值,通常为0
wMsgFilterMax 指定要获取消息的最大值,如果最大值和最小值都为0就接收所有的消息

  • 返回值:

如果函数检索到WM_QUIT(退出的消息)以外的消息,返回值是非零的。

如果函数检索到WM_QUIT消息,返回值为0。

如果有错误,返回值是-1。例如,如果hWnd是无效的窗口句柄或lpMsg是无效的指针,则函数失败。要获得扩展的错误信息,调用GetLastError。

(3)翻译消息函数:TranslateMessage()

  • 功能:将虚拟键消息转换为字符消息,存放于wParam和lParam中,将这些消息组合转换为一条WM_CHAR消息,最后把消息投递到消息队列中,字符消息会在下一次调用GetMessage()函数的时候被取出。
  • 函数原型:
BOOL WINAPI TranslateMessage(
  _In_  const MSG *lpMsg
);
  • 参数:

lpMsg 一个指向MSG结构体的指针

  • 返回值:

如果消息被转换(即字符消息被传递到线程的消息队列),返回值是非零的。

如果消息是WM_KEYDOWN、WM_KEYUP、WM_SYSKEYDOWN或WM_SYSKEYUP,则无论转换如何,返回值都是非零的。

如果消息没有转换(即字符消息没有投递到线程的消息队列),则返回值为0。

(4)分发消息函数:DispatchMessage()
  • 功能:将消息分派给窗口过程。它通常用于分派由GetMessage函数检索到的消息。
  • 函数原型:
LRESULT WINAPI DispatchMessage(
  _In_  const MSG *lpmsg
);
  • 参数:

lpmsg 指向包含消息的结构体的指针

  • 返回值:

返回值指定窗口过程返回的值。尽管它的含义取决于被分派的消息,但返回值通常会被忽略。

9.关闭窗口

我们要退出一个桌面基于窗口的应用程序时,一般做法是点击右上角的“x”号来退出,我们知道在Win32中所有的操作都是基于消息,那么实际上在这个点击“x”号过程中,经历了关闭销毁退出三个过程。
PostQuitMessage()

  • 功能:指示系统线程已发出终止(退出)请求。
  • 原型:
VOID WINAPI PostQuitMessage(
  _In_  int nExitCode
);
  • 参数:

nExitCode 应用程序退出代码。这个值用作WM_QUIT消息的wParam参数。

  • 返回值:无返回值

10.弹出对话框:MessageBox()

  • 功能:显示一个模态对话框,其中包含一个系统图标、一组按钮和一个简短的应用程序特定消息,如状态或错误信息。消息框返回一个整数值,指示用户单击了哪个按钮。
  • 原型:
int WINAPI MessageBox(
  _In_opt_  HWND hWnd,
  _In_opt_  LPCTSTR lpText,
  _In_opt_  LPCTSTR lpCaption,
  _In_      UINT uType
);
  • 参数:

hWnd 要创建的消息框的所有者窗口的句柄。如果此参数为空,则消息框没有所有者窗口。
lpText 要显示的消息。如果字符串由多行组成,可以在每行之间使用回车符和/或换行符分隔行。
lpCaption 对话框标题。如果此参数为空,则默认标题为Error。
uType 对话框风格(决定了对话框的内容和行为)。

这里列举一部分例子,其他更多参数请查阅帮助文档

Value Meaning
MB_ABORTRETRYIGNORE(0x00000002L) The message box contains three push buttons: Abort, Retry, and Ignore.
MB_CANCELTRYCONTINUE(0x00000006L) The message box contains three push buttons: Cancel, Try Again, Continue. Use this message box type instead of MB_ABORTRETRYIGNORE.
MB_HELP(0x00004000L) Adds a Help button to the message box. When the user clicks the Help button or presses F1, the system sends a WM_HELP message to the owner.
MB_OK(0x00000000L)默认的 The message box contains one push button: OK. This is the default
MB_OKCANCEL(0x00000001L) The message box contains two push buttons: OK and Cancel.
MB_RETRYCANCEL(0x00000005L) The message box contains two push buttons: Retry and Cancel.
MB_YESNO(0x00000004L) The message box contains two push buttons: Yes and No.
MB_YESNOCANCEL(0x00000003L) The message box contains three push buttons: Yes, No, and Cancel.
  • 返回值

如果消息框有取消按钮,如果ESC键被按下或取消按钮被选中,则函数返回IDCANCEL值。如果消息框没有取消按钮,按ESC无效。
如果函数失败,返回值为零。要获得扩展的错误信息,调用GetLastError。
如果函数成功,返回值是下列菜单项值之一。

Return code/valus Description
IDABORT (3) The Abort button was selected.
IDCANCEL (2) The Cancel button was selected.
IDCONTINUE (11) The Continue button was selected.
IDIGNORE (5) The Ignore button was selected.
IDNO (7) The No button was selected.
IDOK (1) The OK button was selected.
IDRETRY (4) The Retry button was selected.
IDTRYAGAIN (10) The Try Again button was selected.
IDYES (6) The Yes button was selected.

2.键盘消息

按键消息

当您按下一个键时,Windows把WM_KEYDOWN或者WM_SYSKEYDOWN消息放入有输入焦点的窗口的消息队列;当您释放一个键时,Windows把WM_KEYUP或者WM_SYSKEYUP消息放入消息队列中。

系统按键和非系统按键

WM_SYSKEYDOWN和WM_SYSKEYUP中的「SYS」代表「系统」,它表示该按键对Windows比对Windows应用程序更加重要。WM_SYSKEYDOWN和WM_SYSKEYUP消息经常由与Alt相组合的按键产生

按键 键按下 键释放
非系统键 WM_KEYDOWN WM_KEYUP
系统键 WM_SYSKEYDOWN WM_SYSKEYUP

虚拟键码

虚拟键码保存在WM_KEYDOWN、WM_KEYUP、WM_SYSKEYDOWN和WM_SYSKEYUP消息的wParam参数中。此代码标识按下或释放的键。

您使用的大多数虚拟键码的名称在WINUSER.H表头文件中都定义为以VK_开头。

鼠标按键

十进制 十六进制 WINUSER.H标识 IBM兼容键盘
1 01 VK_LBUTTON 鼠标左键
2 02 VK_RBUTTON 鼠标左键
3 03 VK_CANCEL Ctrl+Break
4 04 VK_MBUTTON 鼠标中键

下面这些键通常用于Windows程序。不过,Windows一般用字符消息(而不是键盘消息)来处理这些键。Windows程序通常不需要监视Shift、Ctrl或Alt键的状态。

十进制 十六进制 WINUSER.H标识 IBM兼容键盘
8 08 VK_BACK Backspace
9 09 VK_TAB Tab
12 0C VK_CLEAR Num Lock关闭时的数字键盘5
13 0D VK_RETURN Enter(或者另一个)
16 10 VK_SHIFT Shift(或者另一个)
17 11 VK_CONTROL Ctrl(或者另一个)
18 12 VK_MENU Alt(或者另一个)
19 13 VK_PAUSE Pause
20 14 VK_CAPITAL Caps Lock
27 1B VK_ESCAPE Esc
32 20 VK_SPACE Spacebar

以下列出的前八个码可能是与VK_INSERT和VK_DELETE一起最常用的虚拟键码。Print Screen键在平时都被Windows应用程序所忽略。

十进制 十六进制 WINUSER.H标识 IBM兼容键盘
33 21 VK_PRIOR Page Up
34 22 VK_NEXT Page Down
35 23 VK_END End
36 24 VK_HOME Home
37 25 VK_LEFT
38 26 VK_UP
39 27 VK_RIGHT
40 28 VK_DOWN
41 29 VK_SELECT
42 2A VK_PRINT
43 2B VK_EXECUTE
44 2C VK_SNAPSHOT Print Screen
45 2D VK_INSERT Insert
46 2E VK_DELETE Delete
37 2F VK_HELP

Windows也包括在主键盘上的字母和数字键的虚拟键码(数字键盘将单独处理)。注意,数字和字母的虚拟键码是ASCII码。Windows程序几乎从不使用这些虚拟键码;实际上,程序使用的是ASCII码字符的字符消息。

十进制 十六进制 WINUSER.H标识 IBM兼容键盘
48~57 30~39 主键盘上的0~9
65~90 41~5A 主键盘上的A~Z

以下的代码是由Microsoft Natural Keyboard及其兼容键盘产生的:

十进制 十六进制 WINUSER.H标识 IBM兼容键盘
91 5B VK_LWIN 左Windows键
92 5C VK_RWIN 右Windows键
93 5D VK_APPS Applications键

以下的代码用于数字键盘上的键(如果有的话):

十进制 十六进制 WINUSER.H标识 IBM兼容键盘
96~105 60~69 VK_NUMPAD0~VK_NUMPAD9 NumLock打开时数字键盘上0~9
106 6A VK_MULTIPLY 数字键盘上的*
107 6B VK_ADD 数字键盘上的+
108 6C VK_SEPARATOR
109 6D VK_SUBTRACT 数字键盘上的-
110 6E VK_DECIMAL 数字键盘上的.
111 6F VK_DIVIDE 数字键盘上的/

功能键,虽然多数的键盘都有12个功能键,但Windows只需要10个,而位旗标却有24个。另外,程序通常用功能键作为键盘快捷键,这样,它们通常不处理下表所示的按键:

十进制 十六进制 WINUSER.H标识 IBM兼容键盘
112~121 70~79 VK_F1~VK_F10 功能键F1~F10
122~135 7A~87 VK_F11~VK_F24 功能键F11~F24
144 90 VK_NUMLOCK Num Lock
145 91 VK_SCROLL Scroll Lock

所以如果你要处理一个键盘消息你可以在消息处理函数中这样写:

case WM_KEYDOWN:
	switch (wParam) 
    {
    case 65:
            //A键被按下的处理
            break ;
    case VK_UP:
            //上键被按下的处理
            break ;
    }
    break;

3.鼠标消息

鼠标按键

Windows只把键盘消息发送给拥有输入焦点的窗口。鼠标消息与此不同:只要鼠标跨越窗口或者在某窗口中按下鼠标按键,那么窗口消息处理程序就会收到鼠标消息,而不管该窗口是否活动或者是否拥有输入焦点。

Windows为鼠标定义了21种消息,不过,其中有11个消息和显示区域无关(下面称之为「非显示区域」消息),Windows程序经常忽略这些消息。

当鼠标移过窗口的显示区域时,窗口消息处理程序收到WM_MOUSEMOVE消息。当在窗口的显示区域中按下或者释放一个鼠标按键时,窗口消息处理程序会接收到下面这些消息:

按下 释放 按下(双键)
WM_LBUTTONDOWN WM_LBUTTONUP WM_LBUTTONDBLCLK
WM_MBUTTONDOWN WM_MBUTTONUP WM_MBUTTONDBLCLK
WM_RBUTTONDOWN WM_RBUTTONUP WM_RBUTTONDBLCLK
只有对三键鼠标,窗口消息处理程序才会收到MBUTTON消息 ,只有当定义的窗口类别能接收DBLCLK(双击)消息,窗口消息处理程序才能接收到这些消息。

对于所有这些消息来说,其lParam值均含有鼠标的位置:低字组为x坐标,高字组为y坐标,这两个坐标是相对于窗口显示区域左上角的位置。您可以用LOWORD和HIWORD宏来提取这些值:

x = LOWORD(lParam);
y = HIWORD(lParam);

wParam的值指示鼠标按键以及Shift和Ctrl键的状态。您可以使用表头文件WINUSER.H中定义的位屏蔽来测试wParam。MK前缀代表「鼠标按键」。

MK_LBUTTON 按下左键
MK_MBUTTON 按下中键
MK_RBUTTON 按下右键
MK_SHIFT 按下shift键
MK_CONTROL 按下ctrl键

例如,如果收到了WM_LBUTTONDOWN消息,而且值

wparam & MK_SHIFT

是TRUE(非0),您就知道当左键按下时也按下了Shift键。

当您把鼠标移过窗口的显示区域时,Windows并不为鼠标的每个可能的图素位置都产生一个WM_MOUSEMOVE消息。您的程序接收到WM_MOUSEMOVE消息的次数,依赖于鼠标硬件,以及您的窗口消息处理程序在处理鼠标移动消息时的速度。换句话说,Windows不能用未处理的WM_MOUSEMOVE消息来填入消息队列。

格式化函数:sprintf()

  • 头文件:stdio.h
  • 功能:将格式化数据写入字符串。其中一些功能有更安全的版本;参见sprintf_s, _sprintf_s_l, swprintf_s, _swprintf_s_l。swprintf和_swprintf_l的安全版本不接受计数参数。
  • 原型:
int sprintf(
   char *buffer,
   const char *format [,
   argument] ... 
);
  • 参数:

buffer 输出存储位置
format 格式控制字符串
argment 可选参数

  • 返回值:

写入的字符数,如果发生错误则为-1。如果缓冲区或格式是空指针,将调用无效的参数处理程序,如参数验证中所述。如果允许继续执行,这些函数返回-1并将errno设置为EINVAL。

sprintf返回存储在缓冲区中的字节数,不计算结束时的null字符。

在鼠标消息的处理中你可以这样写:

case WM_LBUTTONDOWN:
	//鼠标左键按下的处理
	break;
case WM_LBUTTONUP:
	//鼠标左键抬起的处理
	break;

如果你需要知道鼠标按下的位置,可以这样做:

char str[20] = {0};

x = LOWORD (lParam);   //获取鼠标按下位置的x坐标
y = HIWORD (lParam);   //获取鼠标按下位置的y坐标
sprintf(str,"%d,%d",x,y);  //两个坐标值格式化成一个字符串,方便其他地方使用
MessageBox(hWnd,str,"title",MB_YESNOCANCEL);  //弹出的messageBox中显示鼠标坐标

4.绘图

当窗口发生变化的时候就会处理重绘消息(WM_PAINT)

在窗口上绘制一个矩形

参见《win32API》中的第四章图形设备接口函数/填充形态函数一节
我们得知,这里我们需要使用函数Rectangle()

1.绘制矩形函数Rectangle()

  • 函数功能:该函数画一个矩形,用当前的画笔画矩形轮廓,用当前画刷进行填充。
  • 函数原型:
BOOL Rectangle(
	HDC hdc, 
	int nLeftRect,
	int nTopRect,
	int nRightRect, 
	int nBottomRect
);
  • 参数:

hdc:设备环境句柄。
nLeftRect:指定矩形左上角的逻辑X坐标。
nTopRect:指定矩形左上角的逻辑Y坐标。
nRightRect:指定矩形右下角的逻辑X坐标。
nBottomRect:指定矩形右下角的逻辑Y坐标。

  • 返回值:

如果函数调用成功,返回值非零,否则返回值为0。

我们有了函数就相当于有了画笔,那么我们要画一个图的话,要往哪里画,在什么上面画,我们需要明确,在上面函数参数中可以看到hdc这一参数,这里可以这样理解,我们要绘图的窗口就相当于画板,而这个hdc就相当于纸,我们要画图光有画板还不行,我们还需要有纸,纸(hdc)从哪里来?

2.BeginPaint()函数

  • 功能:BeginPaint函数为绘画准备指定的窗口,并用关于绘画的信息填充一个PAINTSTRUCT结构。
  • 函数原型:
HDC BeginPaint(
  __in   HWND hwnd,
  __out  LPPAINTSTRUCT lpPaint
);
  • 参数:

hwnd 要重绘的窗口的窗口句柄
lpPaint 指向将接收绘画信息的 PAINTSTRUCT 结构的指针。

  • 返回值:

如果函数成功,返回值是指定窗口的 hdc。
如果函数失败,返回值为NULL。

3.EndPaint()函数

  • 功能:EndPaint函数在指定的窗口中标记绘制的结束。这个函数在每次调用BeginPaint函数时都是必需的,但只有在绘制完成之后才需要。
  • 函数原型:
BOOL EndPaint(
  __in  HWND hWnd,
  __in  const PAINTSTRUCT *lpPaint
);
  • 参数:

hWnd 已被重新绘制的窗口的句柄。
lpPaint 指向包含由BeginPaint检索到的绘画信息的PAINTSTRUCT结构的指针。

  • 返回值:

返回值总是非零值

注意:以上两个函数只能在重绘消息中使用(case WM_PAINT:),在其他消息中获取hdc可以使用以下两个函数:

4.GetDC()函数

  • 功能:GetDC函数为指定窗口的客户区或整个屏幕检索hdc。
  • 函数原型:
HDC GetDC(
  __in  HWND hWnd
);
  • 参数:

hWnd 要检索其DC的窗口的句柄。如果该值为NULL, GetDC将检索整个屏幕的DC。

  • 返回值:

如果函数成功,返回值是指定窗口的客户区DC的句柄。
如果函数失败,返回值为空。

5.ReleaseDC()函数

  • 功能:ReleaseDC函数释放一个DC,释放它以供其他应用程序使用。
  • 函数原型:
int ReleaseDC(
  __in  HWND hWnd,
  __in  HDC hDC
);
  • 参数:

hWnd 要释放的窗口的句柄
hDC 要释放的hdc

  • 返回值:

返回值指示DC是否已被释放。如果DC被释放,返回值为1。如果DC没有被释放,返回值为零。

所以我们,要在重绘消息中去画一个矩形,可以这样写:

PAINTSTRUCT ps;
HDC hdc;

case WM_PAINT:
		hdc = BeginPaint(hWnd,&ps);
		Rectangle(hdc,100,100,150,150);
		EndPaint(hWnd,&ps);
	break;

在重绘以外的消息中去画一个圆,可以这样写:

	hdc = GetDC(hWnd);
	Ellipse(hdc,100,100,200,200);
	ReleaseDC(hWnd,hdc);

现在我们可以画一个矩形了,但是我们现在想要得到一个红色的矩形怎么做到呢?

首先我们需要有一个红色的画刷,选择这个红色的画刷,用它去画图

6.创建画刷函数CreateSolidBrush()

  • 功能:创建一个具有指定的纯色的逻辑画笔。
  • 原型:
HBRUSH CreateSolidBrush(
  __in  COLORREF crColor
);
  • 参数:

crColor 画笔的颜色。创建一个COLORREF颜色值,使用RGB宏。

  • 返回值:

如果函数成功,HBRUSH类型的画刷。
如果函数失败,返回值为空。

7.选择画刷:使用选择设备函数SelectObject()

  • 功能:选择一个对象到指定的DC中。新对象替换相同类型的前一个对象。
  • 原型:
HGDIOBJ SelectObject(
  __in  HDC hdc,
  __in  HGDIOBJ hgdiobj
);
  • 参数:

hdc
hgdiobj 要选择的对象的句柄。指定的对象必须是通过使用下列函数之一创建的。

Ojbect Functions
Bitmap CreateBitmap, CreateBitmapIndirect, CreateCompatibleBitmap, CreateDIBitmap, CreateDIBSection. Bitmaps can only be selected into memory DC's. A single bitmap cannot be selected into more than one DC at the same time.
Brush CreateBrushIndirect, CreateDIBPatternBrush, CreateDIBPatternBrushPt, CreateHatchBrush, CreatePatternBrush, CreateSolidBrush
Font CreateFont, CreateFontIndirect
Pen CreatePen, CreatePenIndirect
Region CombineRgn, CreateEllipticRgn, CreateEllipticRgnIndirect, CreatePolygonRgn, CreateRectRgn, CreateRectRgnIndirect
  • 返回值:

如果所选对象不是region并且而函数执行成功,则返回值是被替换对象的句柄。
如果所选对象是一个region,且函数执行成功,则返回值为以下值之一。

Value Meaning
SIMPLEREGION Region consists of a single rectangle.
COMPLEXREGION Region consists of more than one rectangle.
NULLREGION Region is empty.

如果发生错误,且所选对象不是region,则返回值为NULL。否则,它是HGDI_ERROR。

按照我们的要求,如果要去画一个红色的矩形,你可以将以下代码写到Rectangle()函数之前

	HBRUSH hBrush;

	hBrush = CreateSolidBrush(RGB(255,0,0));
	SelectObject(hdc,hBrush);

5.定时器

定时器的作用

每隔一段时间就向窗口发送一个定时器消息

定时器如何使用(三种方法)

第一种方法:

这是最方便的一种方法,他让Windows把WM_TIME消息发送到应用程序的正常窗口消息处理程序中,SetTime()函数介绍如下:

设定定时器:SetTime()

  • 功能:用指定的超时值创建计时器。
  • 原型:
UINT_PTR WINAPI SetTimer(
  _In_opt_  HWND hWnd,
  _In_      UINT_PTR nIDEvent,
  _In_      UINT uElapse,
  _In_opt_  TIMERPROC lpTimerFunc
);
  • 参数:

hWnd 窗口消息处理程序将接收WM_TIMER消息的窗口句柄
hIDEvent 定时器ID,它是一个非0数值。
uElapse 一个32位无正负号整数,以毫秒为单位指定一个时间间隔,一个60,000的值将使Windows每分钟发送一次WM_TIMER消息。
lpTimerFunc 定时器消息处理函数,这个参数用于第二种和第三种定时器使用的消息,采用第一种方式时,通常为NULL

  • 返回值

如果函数成功,且hWnd参数为空,则返回值是标识新计时器的整数。应用程序可以将这个值传递给KillTimer函数来销毁计时器。

如果函数成功,且hWnd参数不为空,则返回值为一个非零整数。应用程序可以将nIDEvent参数的值传递给KillTimer函数来销毁计时器。

如果函数未能创建计时器,则返回值为零。要获得扩展的错误信息,调用GetLastError。

如果你想停止WM_TIME消息,可以使用KillTime()函数在任何时刻停止WM_TIME消息(即使正在处理WM_TIME)。下面是函数KillTime()的介绍:

销毁定时器:KillTime()

  • 功能:销毁指定的计时器。
  • 原型:
BOOL WINAPI KillTimer(
  _In_opt_  HWND hWnd,
  _In_      UINT_PTR uIDEvent
);
  • 参数:

hWnd 窗口消息处理程序将接收WM_TIMER消息的窗口句柄
uIDEvent 该参数是SetTimer调用中所用的同一个定时器ID

  • 返回值

如果函数成功,返回值是非零的。
如果函数失败,返回值为零。要获得扩展的错误信息,调用GetLastError

在终止程序之前,您应该响应WM_DESTROY消息停止任何活动的定时器。

当您的窗口消息处理程序收到一个WM_TIMER消息时,wParam参数等于定时器的ID值(上述情形为1),lParam参数为0。如果需要设定多个定时器,那么对每个定时器都使用不同的定时器ID。wParam的值将随传递到窗口消息处理程序的WM_TIMER消息的不同而不同。为了使程序更具有可读性,您可以使用#define叙述定义不同的定时器ID:

#define TIMER_SEC 1     
#define TIMER_MIN 2

然后您可以使用两个SetTimer呼叫来设定两个定时器:

//这应该写在主函数中的创建之后,消息循环之前
SetTimer (hwnd, TIMER_SEC, 1000, NULL) ;
SetTimer (hwnd, TIMER_MIN, 60000, NULL) ;

WM_TIMER的处理如下所示:

caseWM_TIMER:    
    switch (wParam) 
    {
    case TIMER_SEC:
            //每秒一次的处理
            break ;
    case TIMER_MIN:
            //每分钟一次的处理
            break ;
    }
	break;

第二种方法

设定定时器的第一种方法是把WM_TIMER消息发送到通常的窗口消息处理程序,而第二种方法是让Windows直接将定时器消息发送给您程序的另一个函数(这是一个回调函数)。

我们可以这样写这个定时器函数,它看起来和消息处理函数很像:

定时器函数

VOID CALLBACK TimerProc (  HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime)
{
	//处理WM_TIMER消息
}

TimerProc的参数hwnd是在呼叫SetTimer时指定的窗口句柄。Windows只把WM_TIMER消息送给TimerProc,因此消息参数总是等于WM_TIMER。iTimerID值是定时器ID,dwTimer值是与从GetTickCount函数的传回值相容的值。这是自Windows启动后所经过的毫秒数。

您使用callback函数处理WM_TIMER消息时,SetTimer的第四个参数由callback函数的地址取代,如下所示:

SetTimer (hwnd, iTimerID, iMsecInterval, TimerProc) ;

第三种方法

设定定时器的第三种方法类似于第二种方法,只是传递给SetTimer的hwnd参数被设定为NULL,并且第二个参数(通常为定时器ID)被忽略了,最后,此函数传回定时器ID:

iTimerID = SetTimer (NULL, 0, wMsecInterval, TimerProc) ;

如果没有可用的定时器,那么从SetTimer传回的iTimerID值将为NULL。

KillTimer的第一个参数(通常是窗口句柄)也必须为NULL,定时器ID必须是SetTimer的传回值:

KillTimer (NULL, iTimerID) ;

传递给TimerProc定时器函数的hwnd参数也必须是NULL。这种设定定时器的方法很少被使用。如果在您的程序在不同时刻有一系列的SetTimer呼叫,而又不希望追踪您已经用过了那些定时器ID,那么使用此方法是很方便的。


差不多就这些,其他的遇到了之后再补充好了。

本文作者:Free152

本文链接:https://www.cnblogs.com/free152/p/17218355.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @ 2023-03-15 14:16  Free152  阅读(87)  评论(0编辑  收藏  举报