微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Windows编程之SDK-01.基础篇

【前言】

1.第一个窗口程序

  • 对于空项目创建的窗口程序,第一步是先修改项目属性链接器->系统->子系统->窗口】

1.1WinMain函数【A版】【W版】【T版】--Windows API 都有的版本

  • ASC对应A版;UniCode对应W版;公共对应T版;
  • 一般【属性->高级->字符集->UNICODE字符集(认的)】

  • 认的都是UniCode工程,意思认使用W版的API
  • 但是不管是ASC还是UniCode都有弊端,不能综合,因此推荐使用T版
#include<Windows.h>
#include<tchar.h>

//**********************W版本**********************
//关于APIENTRY宏代表的含义->__stdcall
//#define APIENTRY    WINAPI
//#define WINAPI      __stdcall
int APIENTRY WinMain(

	HINSTANCE hInstance,       //实例句柄[重点]
	HINSTANCE hPreInstance,    //前实例句柄
	LPSTR     lpCmdLine,       //命令行参数
	int       nCmdshow         //显示方式

)
//①因为认的是UNICODE;所以现在的WinMain其实是W版的->wWinMain
//②因为认的是UNICODE;所以现在LPSTR其实也是W版的->LPWSTR


//**********************A版本**********************
//关于APIENTRY宏代表的含义->__stdcall
//#define APIENTRY    WINAPI
//#define WINAPI      __stdcall
int APIENTRY WinMain(

	HINSTANCE hInstance,       //实例句柄[重点]
	HINSTANCE hPreInstance,    //前实例句柄
	LPSTR     lpCmdLine,       //命令行参数
	int       nCmdshow         //显示方式

)
//①因为认的是ASC;所以现在的WinMain其实是W版的->AWinMain
//②因为认的是ASC;所以现在LPSTR其实也是W版的->PWSTR


//**********************T版本**********************
//关于APIENTRY宏代表的含义->__stdcall
//#define APIENTRY    WINAPI
//#define WINAPI      __stdcall
int APIENTRY _tWinMain(

	HINSTANCE hInstance,       //实例句柄[重点]
	HINSTANCE hPreInstance,    //前实例句柄
	LPTSTR    lpCmdLine,      //命令行参数
	int       nCmdshow         //显示方式

)
{
	//MessageBoxW(0, L"Hello", L"First Window", 0);
	//MessageBoxA(0, "Hello", "First Window", 0);

	//不管认的是A版还是W版,我们直接用MessageBox使用的是认的版本
	//如果想用其他版本,则需要根据版本再MessageBox后+W/A/T
	//但是因为A版和W版的输入内容有差别,无法综合使用,因此Windows推荐使用T版

	MessageBox(0,_T("Hello"), _T("First Window"), 0);
	//_T需包含头文件#include<tchar.h>
    return 0;
}

 

  • 【注意】T版的输入内容_T需包含头文件#include
  • 认的版本,直接用MessageBox就能输出;如果想输出其他版本,则需要再后面+A/W;切记T版不用加

2.数字和字符串的转换

#include<Windows.h>
#include<tchar.h>
#include<stdio.h>

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPTSTR lpCmdLine, int nCmdshow)
{
	int a = 100;
	int b = 200;
	int c = a + b;
	//将int转换成字符串
	CHAR cBuff[20] = {};
	//_itoa_s(c, cBuff, 20, 10);//参数:需转换的int变量;接收转换的char变量;接收变量大小;转换进制
	sprintf_s(cBuff, 20, "%d", c);//需包含头文件stdio.h
	MessageBoxA(0, cBuff, 0, 0);

	//wchar_t to int 宽字节转换成整数
	WCHAR wBuff[20] = { L"3000" };
	DWORD number1 = 0;
	//swscanf_s(wBuff, L"%d", &number1);
	//to给的是返回值
	number1 = _wtoi(wBuff);

	//T版函数
	//int 转 wchar_t ->T版参数:宽字节类型地址;大小;L+%d;转换的int
	//_stprintf_s(wBuff, 20,L"%d",c);
	
	//wchar_t 转 int ->T版参数:宽字节类型地址;L+%d;转换的int的地址
	//_stscanf_s(wBuff, L"%d", &c);
	//to给的是返回值
	c=_tstoi(wBuff);

	return 0;
}

3.A版字符和W版字符转换

【注意】:W2A和A2W;返回一个值需要用对应的类型指针接收;

#include<Windows.h>
#include<tchar.h>
#include<atlbase.h>

//宏定义:
//宽字符转换多字符Unicode-->ASCII
#define WCHAR_TO_CHAR(lpW_Char,lpChar)\
WideCharToMultiByte(CP_ACP,NULL,lpW_Char,-1,lpChar,_countof(lpChar),NULL,FALSE)
//lpW_Char->宽字符缓存区;lpChar->多字符缓存区;

//多字符转换为宽字符ASCII-->Unicode
#define CHAR_TO_WCHAR(lpChar,lpW_Char)\
MultiBytetoWideChar(CP_ACP,NULL,lpChar,-1,lpW_Char,_countof(lpW_Char))


int APIENTRY _tWinMain(
	HINSTANCE hInstace,
	HINSTANCE hPreInstace,
	LPTSTR    lpCmdLine,
	int       nCmdshow
)
{
	CHAR cBuff[20] = { "hello char" };
	WCHAR wBuff[20] = { L"hello wchar_t" };
	CHAR cBuffTemp[20] = {};

	CHAR* pcBuff = NULL;
	WCHAR* pwBuff = NULL;

	WCHAR_TO_CHAR(wBuff, cBuffTemp);

	//包含头文件atlbase.h
	//使用之前需要声明 USES_CONVETSION
	USES_CONVERSION;
	pcBuff = W2A(wBuff);
	pwBuff = A2W(cBuff);
	return 0;
}

4.调试相关问题

#include<Windows.h>
#include<tchar.h>

//输出信息显示->自己封装的_trace函数
bool _trace(const TCHAR* format, ...)//变参函数-->参数是格式化字符串
{
	TCHAR Buffer[1000];
	va_list argptr;   //va_list是宏->char*
	va_start(argptr, format);//char* argptr=(char*) format
	//将格式化信息写入指定的缓冲区 Buffer
	wvsprintf(Buffer, format, argptr);//参数:1.输出缓冲区,最大为1024字节 
	                                  //      2.格式字符串
	                                  //      3.需输出的参数
	va_end(argptr);//argptr=nullptr
	//将缓冲区信息输出
	OutputDebugString(Buffer);
	return true;
}

//弹出信息显示->自己封装的MyMessageBox函数
bool MyMessageBox(const TCHAR* format, ...)//变参函数-->参数是格式化字符串
{
	TCHAR Buffer[1000];
	va_list argptr;   //va_list是宏->char*
	va_start(argptr, format);//char* argptr=(char*) format
	//将格式化信息写入指定的缓冲区 Buffer
	wvsprintf(Buffer, format, argptr);//参数:1.输出缓冲区,最大为1024字节 
									  //      2.格式字符串
									  //      3.需输出的参数
	va_end(argptr);//argptr=nullptr
	//将缓冲区信息输出
	MessageBox(0, Buffer, _T("提示信息"), 0);
	return true;
}

//弹出错误码
void MyGetErrorInfo(LPCTSTR lpErrInfo, UINT unErrCode, UINT unLine)//unLine=__LINE__
{
	LPTSTR lpMsgBuf = nullptr;
	WCHAR  szMessage[128] = { 0 };
	WCHAR  szCaption[32] = { 0 };

	//第三个参数是错误码,第五个参数是输出缓冲区
    FormatMessage(0x1300, NULL, unErrCode, 0x400, (LPTSTR)&lpMsgBuf, 64, NULL);

	swprintf_s(szMessage, 128, L"Error_0x%08X:%s", unErrCode, lpMsgBuf);
	swprintf_s(szCaption, 32, L"%s(Error Line:%05d)", lpErrInfo, unLine);
	MessageBox(NULL, szMessage, szCaption, MB_OK);
}




int APIENTRY _tWinMain(
	HINSTANCE hInstance,
	HINSTANCE hPreInstance,
	LPTSTR    lpCmdLine,
	int       nCmdshow
)
{
	//Windows 编程种,天使信息查看的几种方式
	//1.通过OutputDebugString将信息输出输出窗口上
	//调试模式下:调试->窗口->输出
	//双击运行的时候,使用Debug View 查看输出信息
	OutputDebugString(L"hello");

	//2.使用自己封装的_trace函数
	_trace(_T("%d %d %s"), 100, 200, _T("hello"));
	MyMessageBox(_T("%d %d %s"), 100, 200, _T("hello"));

	//错误查询的三种方法【主用第三种】
	CreateWindow(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);

	//1.通过GetLastError函数获取上次执行后的错误码,然后查询
	//工具->错误查找
	int nErrCode = GetLastError();//必须打断点,只看现在之前的错误,后面会覆盖
	
	//2.自己封装函数弹出错误信息
	//__LINE__   获取当前行号
	//__FILE__   获取当前文件名
	//__FUNCTION__  获取当前函数名
	MyGetErrorInfo(_T("错误提示"), nErrCode, __LINE__);
	
	//3.最长用的方式,监视窗口->【err,hr】查看错误信息
	return 0;
}

5.创建窗口的相关内容

/*********************************************************************
窗口创建:
1.设计窗口:窗的结构体
2.注册窗口
3.创建窗口
4.显示窗口
5.消息泵
**********************************************************************/

#include<Windows.h>
#include<tchar.h>
HINSTANCE g_hInstance = 0;
// 自定义消息
#define WM_GETID WM_USER + 1


// Windows 消息分为三大类
// 1. 通用消息,WM_XXX
//      窗口消息:任何一个窗口都有的消息
//      命令消息:WM_COMMAND:简单的控件产生的。按钮,文本框,...
//      通知消息:WM_NOTIFY: 复杂控件产生的消息。比如,列表,树,
//      
// 2. 控件消息
//      BM_XXX,EM_XXX,特定控件产生的消息
// 
// 3. 自定义消息,用户自己定义,在 WM_USER 之后
//     #define WM_GETID WM_USER + 1

//回调函数
//*********************************************************************************
LRESULT CALLBACK WndProc(HWND hWnd, UINT Message, WParaM wParam, LParaM lParam)
{
	switch (Message)
	{
	case WM_CREATE://窗口创建
	{
		//在主窗口创建的时候,创建一个按钮
		CreateWindow(_T("button"), _T("按钮1"), WS_CHILD | WS_VISIBLE, 10, 10, 80, 30, hWnd,
			(HMENU)0x1000,  //当创建的是一个标准控件的时候,此时是一个控件ID
			g_hInstance, NULL);
		//在主窗口创建的时候,创建第二个按钮
		CreateWindow(_T("button"), _T("按钮2"), WS_CHILD | WS_VISIBLE, 10, 50, 80, 30, hWnd,
			(HMENU)0x1001,  //当创建的是一个标准控件的时候,此时是一个控件ID
			g_hInstance, NULL);
		break;
	}
	// WM_COMMAND 消息是由控件产生的
	case WM_COMMAND:
	{
		//WParaM的低两字节是控件ID
		//高两字节是消息码
		//LParaM保存的是窗口的句柄
		BN_CLICKED;//当用户单击按钮时发送。
		WORD Id = LOWORD(wParam);
		switch (Id)
		{
		case 0x1000:
		{
			//给指定窗口发送指定消息
			SendMessage(hWnd, WM_GETID, wParam, lParam);
			break;
		}
		case 0x1001:
		{
			//获取计算机的句柄,给计算器0x83这个按钮发送消息
			//FindWindow函数用来查询主窗口(子窗口不能查询),并且返回窗口句柄。
			//	函数原型:
			//	praram[in] lpClassName:以NULL结尾的字符串,如果为NULL,则查找所有与lpWindowName的窗口;
			//  param[in] lpWindowName:以NULL结尾的字符串,表示需要查找的窗口的名称(窗口标题),如果为NULL,查找所有窗口。
			//	return 返回窗口的句柄,如果没有查询到返回NULL,可以用GetLastError()获取失败原因。
			HWND hCalc = FindWindow(NULL, _T("计算器"));
			BN_CLICKED;//当用户单击按钮时发送。
			//构造 按钮被点击的事件
			//按钮被点击的时候,响应的是WM_COMMAND消息
			//WParaM高两字节保存的是消息码,BN_CLICKED是0
			//      低两字节保存的是ID,通过spy++获取
			SendMessage(hCalc, WM_COMMAND, WParaM(0x83), NULL);
			break;
		}
		default:
			break;
		}
		break;
	}
	case WM_GETID: //自定义的
	{
		MessageBox(0, _T("获取id"), 0, 0);
		break;
	}
	case WM_KEYDOWN:
	{
		OutputDebugString(_T("key_down\n"));
		break;
	}
	case WM_KEYUP:
	{
		OutputDebugString(_T("key_up\n"));
		break;
	}
	case WM_CHAR:
	{
		OutputDebugString(_T("key_char\n"));
		break;
	}
	case WM_CLOSE:
	{
		//队列消息
		//PostMessage:将消息放进消息队列中,继续往下执行
		PostMessage(hWnd,WM_GETID,wParam,lParam);
		//非队列消息
		//SendMessage:直接将消息发送到回调函数,等回调函数执行完成,
		//             才返回,继续执行代码
		//SendMessage(hWnd, WM_GETID, wParam, lParam);
		PostQuitMessage(0);
		break;
	}
	default:
		break;
	}
	// 不需要自己处理的消息,就返回到认的消息处理函数
	return DefWindowProc(hWnd, Message, wParam, lParam);
}

int APIENTRY _tWinMain(
	HINSTANCE hInstance,
	HINSTANCE hPreInstance,
	LPTSTR    lpCmdLine,
	int       nCmdshow
)
{
	g_hInstance = hInstance;
	//1.设计窗口
	//*********************************************************************************
	//WNDCLASS是一个结构体,包含了窗口的一些成员属性
//    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;

	WNDCLASS ws = {};
	//类的风格
	//1.CS_VREDRAW 高度改变重绘 CS_HREDRAW 宽度改变重绘
	//2.CS_DBLCLKS 鼠标双击时系统所发的消息
	//3.CS_NOCLOSE 禁用系统菜单种的关闭命令
	//4.CS_OWNDC   为该窗口类的各窗口分配各自独立的设备环境
	//5.CS_CLASSDC 为该窗口类的各窗口分配一个共享的设备环境
	//6.CS_PARENtdC指定子窗口继承其父窗口的设备环境
	ws.style = CS_VREDRAW | CS_HREDRAW;
	//窗口回调函数【非常重要】
	ws.lpfnWndProc = WndProc;//要自己写一个回调函数
	//实例句柄,窗口类注册到哪个实例程序中
	//理解为生成的同一个程序,实例句柄都是一样的
	ws.hInstance = hInstance;
	//窗口类名【非常重要】
	ws.lpszClassName = _T("窗口类名");
	//窗口背景色
	//宏代表数字
	//ws.hbrBackground = (HBrush)COLOR_HIGHLIGHT;
	ws.hbrBackground = CreateSolidBrush(RGB(255, 0, 0));

	//-----------------------------------------------------
	//资源部分
    // HICON       hIcon;          // 图标
	// HCURSOR     hCursor;        // 光标
	// LPCWSTR     lpszMenuName;   // 菜单

	//2.注册窗口
	//*********************************************************************************
	//窗的结构体定义了一个变量ws,并且我们设计好了这个窗口的一些基本内容
	RegisterClass(&ws);

	//3.创建窗口
    //*********************************************************************************
	//CreateWindowExW(
	//	_In_ DWORD dwExStyle,
	//	_In_opt_ LPCWSTR lpClassName,      窗口类名
	//	_In_opt_ LPCWSTR lpWindowName,     窗口名
	//	_In_ DWORD dwStyle,                窗口风格
	//	_In_ int X,                        窗口左上角坐标
	//	_In_ int Y,
	//	_In_ int nWidth,                   窗口宽高
	//	_In_ int nHeight,
	//	_In_opt_ HWND hWndParent,          父窗口/桌面/NULL
	//	_In_opt_ HMENU hMenu,              菜单
	//	_In_opt_ HINSTANCE hInstance,      实例句柄
	//	_In_opt_ LPVOID lpParam);   
	//返回值是一个句柄
	HWND hWnd = CreateWindow(
		_T("窗口类名"),      //和设计窗口时候的窗口类名要一致
		_T("窗口名"),
		WS_OVERLAPPEDWINDOW,
		10, 10, 500, 200,
		NULL,
		NULL,
		hInstance,
		NULL,
		);
	//同一个exe打开,这个句柄是不一样的
	//int c = (int)hWnd;
	//CHAR cBuff[20] = {};
	//_itoa_s(c, cBuff, 20, 10);
	//MessageBoxA(0, cBuff, 0, 0);


	//4.显示更新窗口
    //*********************************************************************************
	ShowWindow(hWnd, SW_SHOW);
	//UpdateWindow函数通过向窗口发送WM_PAINT消息来更新指定窗口的工作区
	//(如果该窗口的更新区域不为空)。
	//该函数绕过应用程序队列,将WM_PAINT消息直接发送到指定窗口的窗口过程。
	//如果更新区域为空,则不发送任何消息。
	UpdateWindow(hWnd);


	//5.消息循环
	//*********************************************************************************
//	typedef struct tagMSG {
//		HWND        hwnd;      //这个消息所在的窗口句柄
//		UINT        message;   //消息标识符,如WM_SIZE、WM_COMMAND、WM_QUIT等等
//		WParaM      wParam;    //32位消息的特定附加信息
//		LParaM      lParam;    // 32位消息的特定附加信息
//		DWORD       time;      // /消息创建时的时间
//		POINT       pt;        //消息创建时的鼠标位置
//#ifdef _MAC
//		DWORD       lPrivate;
//#endif
//	} MSG, * PMSG, NEAR* NPMSG, FAR* LPMSG;

	MSG msg = {};//定义这个结构体为了接收程序队列拿出来的消息
	// GetMessage 第二个参数 指定了接受哪一个窗口的消息,
    // 填 0 就是全都接受,通常也是写 0

//	GetMessageW(
//		_Out_ LPMSG lpMsg,         指向MSG结构的指针
//		_In_opt_ HWND hWnd,        //传入参数。你要获取你程序中哪个窗口的消息,那就把相应的窗口句柄代入其中
//		_In_ UINT wMsgFilterMin,   
//		_In_ UINT wMsgFilterMax);
//#ifdef UNICODE
//#define GetMessage  GetMessageW
	while (GetMessage(&msg, 0, 0, 0))
	{
		// 对于可现实字符,会将 key_down 和 key_up 多出一个 key_char 
		TranslateMessage(&msg);
		// 分发消息
		dispatchMessage(&msg);
	}


	return 0;
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐