如果建立的是Win32控制台工程(入口函数是main函数)的话,WinMain函数不能作为入口函数,如果想要解决这个问题的话,可以打开项目属性->链接器->系统->子系统,把子系统对应的“控制台”改为“窗口”。
名词释义:
API:应用程序编程接口(Aplication Programma Interface),是一组定义、程序及协议的集合,是一些预先定义的接口(如函数、HTTP接口),或指软件系统不同组成部分衔接的约定。
SDK:软件开发包(Software Development Kit),SDK是一些被软件工程师用于为特定的软件包、软件框架、硬件平台、操作系统等创建应用软件的开发工具的集合。它可以简单的为某个程序设计语言提供应用程序接口API的一些文件,但也可能包括能与某种嵌入式系统通讯的复杂的硬件。SDK还经常包括示例代码、支持性的技术注解或者其他的为基本参考资料澄清疑点的支持文档。
窗口:包含标题栏、菜单栏、系统菜单、最下化框,最大化框,滚动等,窗口主要分为客户区和非客户区;
句柄:是一个标识符,是用来标识对象或者项目的。从数据类型上来看它只是一个16位的无符号整数。应用程序几乎总是通过调用一个Windows函数来获得一个句柄,之后其他的Windows函数就可以使用该句柄,以引用相应的对象。在 Windows编程中会用到大量的句柄。在 Windows程序中并不是用物理地址来标识一个内存块、文件、任务或动态装入模块的。相反地,Windows API给这些项目分配确定的句柄,并将句柄返回给应用程序,然后通过句柄来进行操作。
窗口程序的入口函数:WinMain 控制台窗口应用程序入口函数:main
WINAPI:本质是__stdcall,(stdcall调用方式又被称作Pascal调用方式)是C/C++调用约定的一种,意思是函数的参数从右到左推送到栈上,被调用函数在返回之前从堆栈中弹出这些参数;
WINAPI可以看做是函数调用约定,约定了:
函数的入栈方式——从右往左
函数调用后,由谁来将堆栈恢复原状
WINAPI、CALLBACK、APIENTRY:从右往左入栈,本质都是__stdcall(标准调用方式);
WINAPIV:从右往左入栈,本质是__cdecel;
//转到定义,可以看到:
#define CALLBACK __stdcall
#define WINAPI __stdcall
#define WINAPIV __cdecl
#define APIENTRY WINAPI
C++函数调用的几种方式:
stdcall
stdcall调用方式的函数声明为:int __stdcall function(int a,intb);
stdcall的调用方式意味着:
参数从右往左依次压入堆栈;
由被调用函数自己来恢复堆栈;
函数名自动加前导下划线,后面紧跟着一个@,其后紧跟着参数的尺寸。
上面函数翻译成汇编语言将变成:
push b先压入第二个参数
push a再压入第一个参数
call function调用函数
在编译时,此函数的名字翻译成_function@8
cdecl
cdecl调用函数又称为C调用方式,是C语言缺省的调用方式,它的语法为:
int function(int a,int b)//不加修饰符就是C调用方式
int __cdecl function(int a,int b)//明确指定C调用方式
cdecel调用方式决定了:
参数从右向左依次压入堆栈;
由调用者恢复堆栈;
函数名自动加前导下划线。
由于是由调用者来恢复堆栈,因此C调用方式允许函数的参数个数是不固定的,这是C语言的一大特色。
此函数被翻译为:
push b //先压入第二个参数
push a //再压入第一个参数
call function // 调用函数
add esp, 8 //清理堆栈(此内容在汇编中esp寄存器的功能部分有讲)
在编译时,此方式的函数被翻译成:__function
主函数参数:
HINSTANCE:应用程序实例句柄类型;
//转到定义,看到本质为结构体指针:
DECLARE_HANDLE(HINSTANCE);
#define DECLARE_HANDLE(HINSTANCE)
struct HINSTANCE__
{
int unused;
};
typedef struct HINSTANCE__* HINSTANCE
hInstance :当前应用程序实例句柄;
hPreInstance:当前程序的前一个实例句柄,在32和64位机器上已废弃,现在不再使用,值为NULL,在16位机器上使用(任何一个基于GDI的Windows程序以WinMain函数作为入口被系统调用,在Win16中,hPreInstance指向程序的前一个实例句柄,但在Win32中,每一个进程都有一个独立的4G地址空间,从0到2G属于进程私有,对其他进程来说是不可见的。所以在Win32中,hPreInstance总是为NULL);
LPSTR:long point string;
typedef CHAR *NPSTR, *LPSTR, PSTR;
//上式表示 CHAR <=> NPSTR <=> LPSTR <=> PSTR
typedef char CHAR;
//LPSTR <=> char*
//LPCSTR <=> const char*
lpCmdLine:[in] Pointer to a null-terminated string specifying the command line for the application, excluding the program name. To retrieve the entire command line, use the GetCommandLine function.(指向一个应用程序命令中,除了程序名的null字符结束的字符串。GetCommandLine函数可以返回整个命令行)
lpCmdLine是一个以空终止的字符串,指定传递给应用程序的命令行参数。
nCmdShow:窗口显示方式(最大化,最小化,普通,正常显示);
MessageBox:Windows编程中经常用到的一个API,作用是弹出一个消息提示框;
int WINAPI MessageBox (
In_opt HWND hWnd,
In_opt LPCTSTR lpText, //要在消息框中显示的文本
In_opt LPCTSTR lpCaption, //对话框标题。如果为 NULL(默认值),则使用标题“错误”
In UINT uType //指定对话框的内容和行为。有关可用不同消息框的列表,请参阅 Windows SDK 文档中的消息框条目。默认值提供一个简单的“确定”按钮。
);
第一个参数:
HWND : 窗口句柄类型(可以理解为窗口的ID)
hWnd : 父窗口句柄,如果没有父窗口,就写NULL
HWND 本质为结构体指针
/* HWND : 窗口句柄类型(可以理解为窗口的ID)
hWnd : 父窗口句柄,如果没有父窗口,就写NULL
HWND 本质为结构体指针 */
DECLARE_HANDLE (HWND);
#define DECLARE_HANDLE(name)
struct HWND__
{
int unused;
};
typedef struct HWND__ *HWND
/*LPCTSTR:显示内容*/
//LPCTSTR在Unicode编码下
typedef LPCWSTR PCTSTR, LPCTSTR; //LPCWSTR <=> PCTSTR <=> LPCTSTR
typedef CONST WCHAR *LPCWSTR, *PCWSTR; //CONST WCHAR* <=> LPCWSTR <=> PCWSTR
#define CONST const //CONST <=> const
typedef wchar_t WCHAR; //wchar_t <=> WCHAR
//const wchar_t* <=> LPCTSTR
//上述内容:LPCTSTR <=> const wchar_t*
/*LPSTR在多字节字符集情况下*/
typedef LPCTSTR PCTSTR, LPCTSTR; //LPCTSTR <=> PCTSTR <=> LPCTSTR
typedef CONST CHAR *LPCWSTR, *PCWSTR; //CONST CHAR* <=> LPCTSTR <=> PCWSTR
#define CONST const //CONST <=> const
typedef char CHAR; //char <=> CHAR
//上述内容:LPSTR <=> const char*
实操
1.Hello World!
#include <Windows.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPSTR lpCmdLine , int nCmdShow)
{
int n = MessageBox(NULL, L"Hello World!", L"温馨提示", MB_OK);
if (n == IDOK)
{
MessageBox(NULL, L"点了OK", L"提示", MB_OK);
}
else if (n == IDNO)
{
MessageBox(NULL, L"点了No", L"提示", MB_OK);
}
else if (n == IDYES)
{
MessageBox(NULL, L"点了Yes", L"提示", MB_OK);
}
return 0;
}
2.FirstWindow
流程:①设计窗口;②注册窗口;③创建窗口;④显示窗口;⑤更新窗口;⑥消息循环。
图标制作软件:icoFX3-v3.8.1链接:https://u9baoku.lanzoui.com/b01olvqxc 密码:8udj
窗口处理函数:
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
//typedef LONG_PTR LRESULT;
/*virtual LRESULT WindowProc(
UINT message, //message:指定要处理的Windows消息
WPARAM wParam, //wParam:提供用于处理的其他信息。参数值取决于消息
LPARAM lParam //lParam:提供用于处理消息的其他信息。参数值取决于消息
);
*/
源代码
#include <Windows.h>
#include "resource.h"
//LRESULT:
//typedef LONG_PTR LRESULT; long
//第一个参数:窗口句柄
//当前窗口的窗口句柄
//第二个参数:消息编号 unsigned int
//第三个参数:WPARAM unsigned int
//窗口附加信息
//第四个参数:LPARAM long
//窗口附加信息
//窗口处理函数
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPSTR lpCmdLine, int nCmdShow)
{
/*
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;
UINT
*/
//UINT //typedef unsigned int UINT;
//一、设计窗口类
wchar_t szAppClassName[] = L"My_GUI";
wchar_t szWindowName[] = L"这是我的第一个Windows程序";
//LoadIcon
//加载一个图标文件
//第一个参数:应用程序实例句柄
//如果是系统资源,传递NULL
//如果是自定义资源,传递hInstance
//第二个参数:资源ID
//LoadCursor
//加载一个光标文件
//第一个参数:应用程序实例句柄
//如果是系统资源,传递NULL
//如果是自定义资源,传递hInstance
//第二个参数:资源ID
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW; //窗口类的风格
wc.lpfnWndProc = WindowProc; //窗口处理函数
wc.cbClsExtra = 0; //窗口类的额外扩展空间大小(字节)
wc.cbWndExtra = 0; //窗口的额外扩展空间大小(字节)
wc.hInstance = hInstance; //当前应用程序实例句柄
wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_LOGO)); //窗口图标句柄,NULL:没有图标
wc.hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_CURSOR)); //光标图标句柄
wc.hbrBackground = CreateSolidBrush(RGB(255,255,255)); //背景画刷句柄, 0-》255,0:最暗;255:表示最亮 白色:RGB(255,255,255) 红色:RGB(255,0,0)
wc.lpszMenuName = NULL; //菜单名
wc.lpszClassName = szAppClassName; //窗口类型名
//二、注册窗口类
//ATOM <= > WORD <=> unsigned short
//DWORD double WORD
//DWORD <=> unsigned long
if (0 == RegisterClass(&wc))
{
MessageBox(NULL, L"此程序不能运行在Windows NT上", L"温馨提示", MB_OK);
return 0;
}
//三、创建窗口
HWND hWnd = CreateWindow(
szAppClassName, //窗口类型名
szWindowName, //窗口的标题
WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX,//窗口的风格
200, //窗口左上角横坐标
200, //窗口左上角纵坐标
800, //窗口的宽度
600, //窗口的高度
NULL, //父窗口句柄
NULL, //菜单句柄
hInstance, //应用程序实力句柄
NULL //创建窗口需要传递的信息
);
if (hWnd == NULL)
{
MessageBox(NULL, L"创建窗口失败", L"温馨提示", MB_OK);
return 0;
}
//四、显示窗口
ShowWindow(hWnd, SW_SHOW);
//五、更新窗口
UpdateWindow(hWnd);
//六、消息循环
/*
typedef struct tagMSG {
HWND hwnd; //消息发给哪一个窗口的窗口句柄
UINT message; //消息编号,
WPARAM wParam; //附加信息
LPARAM lParam; //附加信息
DWORD time; //消息投放到消息队列里面的时间
POINT pt; //消息放入消息队列的时候鼠标的坐标
} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
*/
//只要收到了 WM_QUIT消息,函数返回:0
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
//将虚拟键消息转为字符消息
TranslateMessage(&msg);
//将虚拟键消息分发给窗口处理函数
DispatchMessage(&msg);
}
return 0;//程序退出了,结束了
}
//窗口处理函数
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CLOSE://窗口关闭消息
//MessageBox(NULL, L"点了关闭按钮", L"提示", MB_OK);
DestroyWindow(hWnd);//销毁窗口
break;
case WM_DESTROY://窗口销毁消息
PostQuitMessage(0); //发出一个退出消息 :WM_QUIT
break;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);//操作系统默认处理消息的函数
}
//Windows消息机制原理