首页 > 其他分享 >win32基础界面开发

win32基础界面开发

时间:2023-05-28 11:11:05浏览次数:64  
标签:mainWndClass 界面 hwnd win32 开发 lpDisplayBuf msg 窗口 NULL

项目创建

在VS2022中选择空项目,点击下一步,输入相关信息后点击创建

用鼠标右键点击右边解决方案下的项目名字,打开属性页,将配置改为所有配置平台改为所有平台

接着找到配置属性中的链接器中的系统,将子系统控制台 (/SUBSYSTEM:CONSOLE)改成窗口 (/SUBSYSTEM:WINDOWS),点击确定。

新建一个main.c源文件:

#ifndef UNICODE
#define UNICODE
#endif 

启用UNICODE调用unicode版本的函数

引入头文件:

#include <windows.h>

win32 gui 应用的入口函数:

int WINAPI  WinMain(
	_In_ HINSTANCE hInstance,
	_In_opt_ HINSTANCE _,
	_In_ LPSTR     lpCmdLine,
	_In_ int       nShowCmd
) {
	return 0;
}

其中的_In_或者_In_opt_或者_Out_都是微软自己拓展的东西,给预处理器和人看的,经过预处理器处理后不会留下了,_In_表示是输入参数,_Out_是输出参数,_In_opt_ 表示是可选的输入参数。

参考链接: winMain函数(winbase.h)

注册 Window 类

想要显示一个window,首先需要注册Window类。

注册窗口类的第一步是使用窗口类信息填充WNDCLASSEXW结构。并该将结构传递给 RegisterClassExW函数。

const wchar_t MainClassName[] = L"Goodbye";
WNDCLASSEXW mainWndClass;
// 不初始化的话,可能会导致RegisterClassExW调用失败
memset(&mainWndClass, 0, sizeof(WNDCLASSEXW));
mainWndClass.hInstance = instance;
// 窗口消息处理函数
mainWndClass.lpfnWndProc = MainWndProc;
mainWndClass.cbSize = sizeof(WNDCLASSEXW);
mainWndClass.lpszClassName = MainClassName;
mainWndClass.style = CS_HREDRAW|CS_VREDRAW;
// 没有这一行代码的话,改变窗口大小会变成黑色
mainWndClass.hbrBackground = (HBRUSH)COLOR_WINDOWFRAME;

if (0 == RegisterClassExW(&mainWndClass)) {
	ErrorExit(L"RegisterClassExW");
}

每一个window都有自己的lpszClassName(类名),每个window的类名都不一样。

cbSize设置为 sizeof(WNDCLASSEXW)。 在调用 GetClassInfoExW 函数之前,请务必设置此成员。

style设置为CS_HREDRAW|CS_VREDRAW表示窗口水平和垂直方向大小发生变化时重绘窗口。

hbrBackground设置为(HBRUSH)COLOR_WINDOWFRAME是为了给窗口重绘时指定一个背景色,不然窗口大小改变时,会变成黑色。

消息处理

当消息到达的时候,系统会调用WNDCLASSEXWlpfnWndProc指针所指向的函数来处理消息,这个函数的定义如下:

LRESULT CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
	switch (msg)
	{
	case WM_DESTROY: 
	{
		PostQuitMessage(0);
		return 0;
	}
	}
	return DefWindowProc(hwnd, msg, wParam, lParam);
}

这是最基础的消息处理,可以选择感兴趣的消息增加对应的case。

错误处理

ErrorExit汇报出错的函数以及系统错误代码,其实现是从检索Last-Error代码复制过来的。

#include <strsafe.h>

void ErrorExit(LPTSTR lpszFunction) 
{ 
    // Retrieve the system error message for the last-error code

    LPVOID lpMsgBuf;
    LPVOID lpDisplayBuf;
    DWORD dw = GetLastError(); 

    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dw,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf,
        0, NULL );

    // Display the error message and exit the process

    lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, 
        (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR)); 
    StringCchPrintf((LPTSTR)lpDisplayBuf, 
        LocalSize(lpDisplayBuf) / sizeof(TCHAR),
        TEXT("%s failed with error %d: %s"), 
        lpszFunction, dw, lpMsgBuf); 
    MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK); 

    LocalFree(lpMsgBuf);
    LocalFree(lpDisplayBuf);
    ExitProcess(dw); 
}

注册窗口

RegisterClassExW函数向系统注册我们的window成功时,我们才能去创建出这个窗口。RegisterClassExW注册成功时返回一个HWND句柄,失败时返回0。

创建窗口及消息分发

hwnd = CreateWindowExW(
		WS_EX_LTRREADING,
		MainClassName,
		L"再见咯",
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		800,
		600,
		NULL,
		NULL,
		instance,
		NULL
	);
	if (NULL == hwnd) {
		ErrorExit(L"CreateWindowExW");
	}
	ShowWindow(hwnd, showCmd);
	
	while (GetMessageW(&msg, NULL, 0, 0) > 0)
	{
		TranslateMessage(&msg);
		DispatchMessageW(&msg);
	}

通过CreateWindowExW创建我们的窗口,参数的具体意思看官方的文档,这里就不介绍了。在创建成功后通过调用ShowWindow来显示我们的窗口,之后要建立一个while循环来读取系统消息队列里的消息,并通过TranslateMessage来将虚拟密钥消息转换为字符消息,字符消息将发布到调用线程的消息队列,下次线程调用 GetMessageWPeekMessageW 函数时要读取。然后是用DispatchMessageW将消息调度到窗口过程。

最后

至此,一个简单的基本窗口程序就算是成功建立了。全部代码如下:

// main.c

#ifndef UNICODE
#define UNICODE
#endif 
#include<windows.h>
#include<strsafe.h>

LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

void ErrorExit(LPTSTR lpszFunction)
{
	// Retrieve the system error message for the last-error code

	LPVOID lpMsgBuf;
	LPVOID lpDisplayBuf;
	DWORD dw = GetLastError();

	FormatMessage(
		FORMAT_MESSAGE_ALLOCATE_BUFFER |
		FORMAT_MESSAGE_FROM_SYSTEM |
		FORMAT_MESSAGE_IGNORE_INSERTS,
		NULL,
		dw,
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
		(LPTSTR)&lpMsgBuf,
		0, NULL);

	// Display the error message and exit the process

	lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
		(lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
	StringCchPrintf((LPTSTR)lpDisplayBuf,
		LocalSize(lpDisplayBuf) / sizeof(TCHAR),
		TEXT("%s failed with error %d: %s"),
		lpszFunction, dw, lpMsgBuf);
	MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);

	LocalFree(lpMsgBuf);
	LocalFree(lpDisplayBuf);
	ExitProcess(dw);
}

int WINAPI  WinMain(
	_In_ HINSTANCE instance,
	_In_opt_ HINSTANCE _,
	_In_ LPSTR     cmdLine,
	_In_ int       showCmd
) {
	const wchar_t MainClassName[] = L"Goodbye";
	WNDCLASSEXW mainWndClass;
	MSG msg;
	HWND hwnd;
	memset(&mainWndClass, 0, sizeof(WNDCLASSEXW));
	mainWndClass.hInstance = instance;
	mainWndClass.lpfnWndProc = MainWndProc;
	mainWndClass.cbSize = sizeof(WNDCLASSEXW);
	mainWndClass.lpszClassName = MainClassName;
	mainWndClass.style = CS_HREDRAW|CS_VREDRAW;
	// 没有这一行代码的话,改变窗口大小会变成黑色
	mainWndClass.hbrBackground = (HBRUSH)COLOR_WINDOWFRAME;

	if (0 == RegisterClassExW(&mainWndClass)) {
		ErrorExit(L"RegisterClassExW");
	}
	
	hwnd = CreateWindowExW(
		WS_EX_LTRREADING,
		MainClassName,
		L"再见咯",
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		800,
		600,
		NULL,
		NULL,
		instance,
		NULL
	);
	if (NULL == hwnd) {
		ErrorExit(L"CreateWindowExW");
	}
	ShowWindow(hwnd, showCmd);
	
	while (GetMessageW(&msg, NULL, 0, 0) > 0)
	{
		TranslateMessage(&msg);
		DispatchMessageW(&msg);
	}
	return (int)msg.wParam;
}

LRESULT CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
	switch (msg)
	{
	case WM_DESTROY: 
	{
		PostQuitMessage(0);
		return 0;
	}
	}
	return DefWindowProc(hwnd, msg, wParam, lParam);
}

标签:mainWndClass,界面,hwnd,win32,开发,lpDisplayBuf,msg,窗口,NULL
From: https://www.cnblogs.com/ReginaQ/p/17437933.html

相关文章

  • 用Java语言springboot框架开发工艺管理系统
    技术架构技术框架:SpringBoot2.0.0+Mybatis1.3.2+Shiro+jpa+lombok+Vue2+Mysql5.7+redis+nodejs16运行环境:jdk8+IntelliJIDEA+maven+宝塔面板宝塔部署教程回到IDEA,点击编辑器右侧maven图标,切换至prod,执行package,完成后就会在根目录里生成一个target目录,......
  • 项目开发计划
    1.项目背景和目标本项目旨在开发一款医学文献检索及管理工具,以满足医学工作者方便快捷地获取、管理和搜索医学文献的需求。本项目的主要目标是实现以下功能:可批量导入医学文献,医学文献格式为PDF文件;输入需要检索的关键信息,精准查询到所有相关的医学文献,包括能够识别到医学文献......
  • XXX项目前端开发样式统一规范
    前端开发样式规范参考资料:https://element.eleme.cn/#/zh-CN/component/form规范1:报错提示统一用浅红色,成功提示为绿色,警告性提示为橙色规范2: 页面有多个必填字段未填写,一次性提示,同时填写文本框爆红,文本框下方有红字提醒红字提醒可按需展示,本文框间距有限,红字展示可能......
  • 团队开发一些思考
    团队项目总结设想和目标我们的项目是智能排班系统。1.我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述?智能排班系统旨在更加高效地管理和优化人员排班流程,以满足各种行业(如医疗、零售、制造业等)的排班需求,其中主要解决以下问题:优化排班流程:......
  • 拓端荣获腾讯云开发者社区“2022年度优秀作者”称号
    全文链接:http://tecdat.cn/?p=32574原文出处:拓端数据部落公众号近日,拓端获得了腾讯云开发者社区的“2022年度优秀作者”称号。 自入驻腾讯云开发者社区以来,我们共发布了980篇文章,内容涵盖数据资讯、行业动态、技术发展趋势等。同时,我们也一直在扎实生产内容,不断更新内容形式,致......
  • java后端开发流程总结
    流程简介:1、数据库见表(工具建表和cmd命令行(sql语言)两种方式)2、前端页面准备(html+css+js)3、controler层编写(针对具体功能编写,比如登录功能,在这一层获取前台输入的账号密码。这是就可以等待来自数据库里的数据了)4、接着编写serverdao层依据controler层的功能编写相应的get......
  • C#窗体应用开发
    简述C#窗体应用开发之窗体布局内容窗体的处理3.2.1控件随窗体等比缩放 首先需要设定两个字段,来表示窗体的长和宽:privatefloatX;//当前窗体的宽度privatefloatY;//当前窗体的高度此后在窗体的Design界面双击窗体添加Load事件、在事件栏内找到Resize事件,双击添加......
  • Python丨tkinter开发常用的29种功能用法(建议码住)
    在Python软件开发中,tkinter中command功能的作用是为按钮、菜单等组件绑定回调函数,用户操作该组件时会触发相应的函数执行。本文涵盖了各种组件和功能:1、为Button组件(按钮)绑定回调函数importtkinterastkdefsay_hello():print("HelloWorld!")root=tk.Tk()......
  • 【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存C
    缓存的理解缓存的工作机制是先从缓存中读取数据,如果没有再从慢速设备上读取实际数据,并将数据存入缓存中。通常情况下,我们会将那些经常读取且不经常修改的数据或昂贵(CPU/IO)的且对于相同请求有相同计算结果的数据存储到缓存中。它能够让数据更加接近于使用者,下图所示。+-------------......
  • 源代码管理工具:提升团队协作与开发效率的利器
    在软件开发领域,源代码管理是一项至关重要的任务。随着团队规模的扩大和项目复杂性的增加,有效地管理和协调代码的变更变得尤为重要。为了应对这一挑战,源代码管理工具应运而生。本文将介绍源代码管理工具的概念、作用以及一些流行的工具,以帮助读者理解并选择适合自己团队的工具。......