#include<Windows.h>
//窗口程序的入口函数: WinMain
//控制台窗口应用程序入口函数: main
//WinMain 有四个参数
//WINAPI: 函数调用约定
int /*WINAPI*/ WinMain(HINSTANCE hInstance,HINSTANCE hPreInstance,LPSTR lpCmdLine,int nCmdShow)
{
MessageBox(NULL, L"Hello world!", L"温馨提示", MB_OK);
return 0;
}
1>C:\Users\Desktop\WindowsProject1\WindowsProject1\WindowsProject1.cpp(7,5):
warning C4007: “WinMain”: 必须是“__stdcall”
#define CALLBACK __stdcall
#define WINAPI __stdcall
#define APIENTRY WINAPI
#define APIPRIVATE __stdcall
#define PASCAL __stdcall
WINAPI、CALLBACK、APIENTRY. . . 函数调用约定,本质是 __stdcall:标准调用方式
约定了:① 函数的入栈方式 ② 由谁来平衡堆栈
约定了 WinMain 的四个参数是从左往右入栈还是从右往左入栈 nCmdShow→ lpCmdLine→ hPreInstance→ hInstance
规定了清理栈的操作:是由调用者来清理还是被调用者来清理
WinMain 调用 MessageBox,调用者 WinMain 进行释放,MessageBox 是被调者
#define WINAPIV __cdecl
__cdecl 是 C语言 中常用的函数 printf,scanf 的调用约定,也是从右往左入栈
HINSTANCE:应用程序实例句柄类型
//通过一个宏来声明一个句柄
DECLARE_HANDLE(HINSTANCE);
//宏定义
#define DECLARE_HANDLE(name) struct name##__{int unused;}; typedef struct name##__ *name
#define DECLARE_HANDLE(name)
struct name##__
{
int unused;
};
typedef struct name##__ *name
#define DECLARE_HANDLE(HINSTANCE)
struct HINSTANCE##__
{
int unused;
};
typedef struct HINSTANCE##__ *HINSTANCE
##:宏定义连接符
//定义了HINSTANCE,指向结构体的指针
#define DECLARE_HANDLE(HINSTANCE)
struct HINSTANCE__
{
int unused;
};
typedef struct HINSTANCE__* HINSTANCE
hInstance:当前应用程序实例句柄
开一次程序是一个实例句柄,开两个是两个实例句柄 . . . 实例句柄就代表当前程序
hPreInstance:当前程序的前一个实例句柄
第三个程序的前一个实例句柄是第二个程序
在 32 位机器和 64 位机器上已经废弃了,现在不再使用,值为 NULL,是一个空指针
在 16 位机器上使用
LPSTR:char*
typedef _Null_terminated_ CHAR *NPSTR, *LPSTR, *PSTR;
//CHAR* 等价于 NPSTR, LPSTR, PSTR
typedef CHAR *NPSTR, *LPSTR, *PSTR;
//CHAR 等价于 char
typedef char CHAR;
lpCmdLine:命令行参数
nCmdShow:窗口的显示方式
最大化,最小化,普通,正常显示
MessageBox:弹出一个消息提示框
int MessageBox(
[in, optional] HWND hWnd,
[in, optional] LPCTSTR lpText,
[in, optional] LPCTSTR lpCaption,
[in] UINT uType
);
HWND:窗口句柄类型(可以理解为窗口的ID)
传入 NULL 表示没有父窗口句柄,窗口是独立的
DECLARE_HANDLE (HWND);
#define DECLARE_HANDLE(name) struct name##__{int unused;}; typedef struct name##__ *name
#define DECLARE_HANDLE(name)
struct name##__
{
int unused;
};
typedef struct name##__ *name
//结构体指针
#define DECLARE_HANDLE(HWND)
struct HWND__
{
int unused;
};
typedef struct HWND__ *HWND
LPCTSTR lpText:显示内容
//Unicode编码
//LPCWSTR 等价于 PCTSTR, LPCTSTR
typedef LPCWSTR PCTSTR,LPCTSTR;
//CONST WCHAR* 等价于 LPCWSTR, PCWSTR
typedef _Null_terminated_ CONST WCHAR *LPCWSTR, *PCWSTR;
//CONST 等价于 const
#define CONST const
//wchar_t 等价于 WCHAR
#ifndef _MAC
typedef wchar_t WCHAR; // wc, 16-bit UNICODE character
#else
// some Macintosh compilers don't define wchar_t in a convenient location, or define it as a char
typedef unsigned short WCHAR; // wc, 16-bit UNICODE character
#endif
//const char_t* 等价于 LPCTSTR
//多字节字符集
//const char* 等价于 LPCTSTR
lpCaption:对话框的标题,是一个字符串
如果此参数为 NULL ,则默认标题为 Error
统一采用 Unicode 字符集,在 Windows API 中的函数,字符串前面都需要加 L,表示为宽字符集
UINT uType:按钮和图标的组合(用位运算 | )
注意:不能按钮和按钮组合、不能图标和图标组合
MB_OK:确定按钮
MB_YESNO:是、否按钮
MB_YESNOCANCEL:是、否、取消按钮
MessageBox 的返回值:点击了哪个按钮就返回哪个按钮,按钮的值是一个整数
#define MB_OK 0x00000000L
#define MB_OKCANCEL 0x00000001L
#define MB_ABORTRETRYIGNORE 0x00000002L
#define MB_YESNOCANCEL 0x00000003L
#define MB_YESNO 0x00000004L
#define MB_RETRYCANCEL 0x00000005L
#if(WINVER >= 0x0500)
#define MB_CANCELTRYCONTINUE 0x00000006L
#endif /* WINVER >= 0x0500 */
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPSTR lpCmdLine, int nCmdShow)
{
int n = MessageBox(NULL, L"Hello world!", L"温馨提示", MB_YESNO);
if (n == IDYES)
{
MessageBox(NULL, L"点击YES", L"提示", MB_OK);
}
else if (n == IDNO)
{
MessageBox(NULL, L"点击NO", L"提示", MB_OK);
}
return 0;
}
创建一个 Windows 程序
什么是 API ?
类似 printf,scanf,strlen 等函数
由 Windows 操作系统提供的接口:创建窗口、弹出消息提示框,关闭窗口,显示窗口,更新窗口 . . .
把这些接口称为应用程序编程接口 (Application Programma Interface)
Windows API 函数
什么是 SDK(资源的集合) ?
软件开发包(Software Development Kit)
包含了:API 函数库、帮助手册、辅助工具、使用文档
什么是窗口 ?
包含:标题栏、菜单栏、系统菜单、最小化框、最大化框、滚动条
窗口主要分为客户区和非客户区
客户区盖在非客户区上
什么是句柄 ?
在 Windows 中有很多句柄,用于标志一个资源,或者标志一个窗口
窗口句柄、图标句柄、光标句柄、菜单句柄、进程句柄、线程句柄
如果不小心把项目工程建为控制台项目,会导致报错:建立 Win32 控制台工程,入口函数应该是 main 函数
可以重新建立工程,或者按如下操作:
1.设计窗口类
//第一个参数:窗口句柄 结构体
//当前窗口的窗口句柄
//第二个参数:用无符号整型存储消息编号 unsigned int
//鼠标在窗口中点一下会有鼠标点击和鼠标抬起消息、鼠标在屏幕上移动有鼠标移动消息...
//第三个参数:WPARAM unsigned int
//窗口附加信息-> 鼠标点击的坐标
//第四个参数:LPARAM long
//窗口附加信息
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
//窗口处理函数
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
//窗口关闭消息
case WM_CLOSE:
MessageBox(NULL,L"点了关闭按钮",L"提示",MB_OK);
//销毁当前处理的窗口句柄-> 发出 WM_DESTROY 信息
DestroyWindow(hWnd);
break;
//窗口销毁消息
case WM_DESTROY:
//发出一个退出消息 WM_QUIT -> 传入0正常退出
PostQuitMessage(0);
break;
}
//调用操作系统默认窗口处理函数
//如果不想自己处理、处理不了...调用这个函数就交给操作系统处理
return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
WINUSERAPI
VOID
WINAPI
PostQuitMessage(
_In_ int nExitCode);
//窗口类型名 宽字节用两个字节来存储一个字符
//字符数组
wchar_t szAPPClassName[] = L"EDU_GUI";
//窗口标题
wchar_t szWindowName[] = L"这是我的第一个Windows程序";
//结构体变量
WNDCLASS wc;
//窗口类的风格
//当窗口在水平方向和竖直方向发生变化就让它重绘
wc.style = CS_HREDRAW | CS_VREDRAW;
//窗口处理函数
wc.lpfnWndProc = WindowProc;
//窗口类的额外拓展空间大小(字节)-> 窗口类想额外存储一些信息可以存储多少个字节自己决定 默认值为0
wc.cbClsExtra = 0;
//窗口的额外拓展空间大小(字节)-> 窗口创建好后窗口里面想附带一些数据可以给它准备一块空间 默认值为0
wc.cbWndExtra = 0;
//当前应用程序实例句柄
wc.hInstance = hInstance;
//窗口左上角的图标句柄-> NULL:没有图标
wc.hIcon = NULL;
//光标图标句柄-> 鼠标放在窗口上显示的图标 NULL:没有图标
wc.hCursor = NULL;
//背景画刷句柄-> 可以放图片 0:最暗-> 255:最亮
//白色:RGB(255,255,255) 红色:RGB(255,0,0)
wc.hbrBackground = CreateSolidBrush(RGB(255,255,255));
//菜单名
wc.lpszMenuName = NULL;
//窗口类型名
wc.lpszClassName = szAPPClassName;
//W 表示使用 Unicode 字符集
typedef struct tagWNDCLASSW {
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCWSTR lpszMenuName;
LPCWSTR lpszClassName;
} WNDCLASSW, *PWNDCLASSW, NEAR *NPWNDCLASSW, FAR *LPWNDCLASSW;
#ifdef UNICODE
typedef WNDCLASSW WNDCLASS;
typedef LONG_PTR LRESULT;
typedef _W64 long LONG_PTR, *PLONG_PTR;
//无符号整型
typedef unsigned int UINT;
typedef UINT_PTR WPARAM;
typedef _W64 unsigned int UINT_PTR, *PUINT_PTR;
typedef LONG_PTR LPARAM;
typedef _W64 long LONG_PTR, *PLONG_PTR;
//窗口类的风格
#define CS_VREDRAW 0x0001
#define CS_HREDRAW 0x0002
#define CS_DBLCLKS 0x0008
#define CS_OWNDC 0x0020
#define CS_CLASSDC 0x0040
#define CS_PARENTDC 0x0080
#define CS_NOCLOSE 0x0200
#define CS_SAVEBITS 0x0800
#define CS_BYTEALIGNCLIENT 0x1000
#define CS_BYTEALIGNWINDOW 0x2000
#define CS_GLOBALCLASS 0x4000
#define CS_IME 0x00010000
#if(_WIN32_WINNT >= 0x0501)
#define CS_DROPSHADOW 0x00020000
#endif /* _WIN32_WINNT >= 0x0501 */
//操作系统默认窗口处理函数 参数类型都一样
WINUSERAPI
#ifndef _MAC
LRESULT
WINAPI
#else
LRESULT
CALLBACK
#endif
DefWindowProcW(
_In_ HWND hWnd,
_In_ UINT Msg,
_In_ WPARAM wParam,
_In_ LPARAM lParam);
#ifdef UNICODE
#define DefWindowProc DefWindowProcW
#else
#define DefWindowProc DefWindowProcA
#endif // !UNICODE
LRESULT:表示一个处理结果
typedef LONG_PTR LRESULT;
typedef _W64 long LONG_PTR, *PLONG_PTR;
RGB 宏函数
#define RGB(r,g,b) ((COLORREF)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(b))<<16)))
HICON 句柄类型
//声明一个句柄
DECLARE_HANDLE(HICON);
H 开头的一般是句柄类型
2.注册窗口类
if(0 == RegisterClass(&wc))
{
MessageBox(NULL,L"此程序不能运行在Windows NT上",L"温馨提示",MB_OK);
return 0;
}
ATOM
WINAPI
RegisterClassW(
_In_ CONST WNDCLASSW *lpWndClass);
#ifdef UNICODE
#define RegisterClass RegisterClassW
typedef WORD ATOM; //BUGBUG - might want to remove this from minwin
//一个字占两个字节
typedef unsigned short WORD;
//双字占四个字节
typedef unsigned long DWORD;
3.创建窗口
窗口样式详见:Window Styles (Winuser.h) - Win32 apps | Microsoft Docs
HWND hWnd = CreateWindow(
szAppClassName, //注册窗口的类型名
szWindowName, //窗口的标题
WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINMIZEBOX, //窗口的风格 标题栏|系统菜单
200, //窗口距离屏幕左上角横坐标
200, //窗口距离屏幕左上角纵坐标
800, //窗口的宽度
600, //窗口的高度
NULL, //父窗口句柄
NULL, //菜单句柄
hInstance, //应用程序实例句柄
NULL, //创建窗口时需要传递的信息 NULL:没有要传递的
);
if(NULL == hWnd)
{
MessageBox(NULL,L"创建窗口失败",L"温馨提示",MB_OK);
return 0;
}
#define CreateWindow CreateWindowW
#define CreateWindowW(lpClassName, lpWindowName, dwStyle, x, y,\
nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam)\
CreateWindowExW(0L, lpClassName, lpWindowName, dwStyle, x, y,\
nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam)
4.显示窗口
//要显示的窗口 怎么显示:普通显示
ShowWindow(hWnd,SW_SHOW);
WINUSERAPI
BOOL
WINAPI
ShowWindow(
_In_ HWND hWnd,
_In_ int nCmdShow);
5.更新窗口
UpdateWindow(hWnd);
WINUSERAPI
BOOL
WINAPI
UpdateWindow(
_In_ HWND hWnd);
6.消息循环
处理关闭:点击了关闭按钮,程序的进程并没有结束
点击关闭按钮,有很多消息
MSG msg;
//只要收到 WM_QUIT 消息 函数返回值为0 while循环结束
//不断从消息队列中获取消息,把获取到的消息保存到 GetMessage
//消息编号是一个编号 0,1,2,3...一般所有消息都要获取
while(GetMessage(&msg,NULL,0,0))
{
//将虚拟键消息转为字符消息
//当在键盘上按下某个字符,比如按下A,产生按下消息和抬起消息,把这两个消息转为字母 A 需要通过以下函数
TranslateMessage(&msg);
//将虚拟键消息分发给窗口处理函数
DispatchMessage(&msg);
}
typedef struct tagMSG {
HWND hwnd; //消息发给哪一个窗口的窗口句柄
UINT message; //消息编号
WPARAM wParam; //附加消息
LPARAM lParam; //附加消息
DWORD time; //消息投放到消息队列里面的时间
POINT pt; //消息放入消息队列的时候鼠标的坐标
#ifdef _MAC
DWORD lPrivate;
#endif
} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
typedef struct tagPOINT
{
LONG x;
LONG y;
} POINT, *PPOINT, NEAR *NPPOINT, FAR *LPPOINT;
WINUSERAPI
BOOL
WINAPI
GetMessageW(
_Out_ LPMSG lpMsg,
_In_opt_ HWND hWnd, //NULL:获取当前窗口以及它的子窗口的所有消息
//想过滤哪些消息编号
_In_ UINT wMsgFilterMin, //获取大于...的消息
_In_ UINT wMsgFilterMax); //获取小于...的消息
#ifdef UNICODE
#define GetMessage GetMessageW
Windows 消息机制原理
消息队列是用于存储消息的
操作系统会把信息分发给消息队列
通过 GetMessage 不断从消息队列中循环取消息,应用程序接收到消息后通过 DispatchMessage 分发给操作系统,让操作系统调用窗口处理函数,让窗口处理函数做出响应,执行需要执行的内容
加载图标 .ico、光标 .cur
注意:不能直接改后缀名
exe 的图标由编号最小的决定,把最小的放最上面
注意打开文件拓展名
把素材放在工程目录下
#include"resource.h"
//窗口左上角的图标句柄-> NULL:没有图标 通过 MAKEINTRESOURCEA 把整数转换为字符串
//都属于自定义资源
//vs手绘
wc.hIcon = LoadIcon(hInstance,MAKEINTRESOURCE(IDI_MAIN));
//导入
wc.hIcon = LoadIcon(hInstance,MAKEINTRESOURCE(IDI_LOGO));
//光标图标句柄-> 鼠标放在窗口上显示的图标
//系统资源
wc.hCursor = LoadCursor(NULL,IDC_CROSS);
//自定义资源
wc.hCursor = LoadCursor(hInstance,MAKEINTRESOURCE(IDC_CURSOR));
//加载一个图标文件
WINUSERAPI
HICON
WINAPI
LoadIconW(
_In_opt_ HINSTANCE hInstance, //应用程序实例句柄:如果是系统资源传NULL,如果是自定义资源传hInstance
_In_ LPCWSTR lpIconName); //资源ID
#ifdef UNICODE
#define LoadIcon LoadIconW
//加载一个光标文件
WINUSERAPI
HCURSOR
WINAPI
LoadCursorW(
_In_opt_ HINSTANCE hInstance,
_In_ LPCWSTR lpCursorName); //需要传入一个字符串
#ifdef UNICODE
#define LoadCursor LoadCursorW