Win32 获取EXE/DLL文件版本信息
CFileVersion.h
#pragma once#include <windows.h>
#include <string>
#include <tchar.h>#ifdef _UNICODE
using _tstring = std::wstring;
#else
using _tstring = std::string;
#endif// 版本号辅助类
class CVersionNumber
{
public:// 无参构造CVersionNumber();// 从版本字符串构造CVersionNumber(LPCTSTR lpStrVer);// 从指定版本号构造CVersionNumber(WORD v1, WORD v2, WORD v3, WORD v4);// 从版本字符串构造CVersionNumber& operator = (LPCTSTR lpStrVer);// 比较版本号大小int CompareVersion(const CVersionNumber& ref) const;//获取版本号字符串_tstring GetVersionStr() const;// 比较运算重载bool operator == (const CVersionNumber& ref);bool operator != (const CVersionNumber& ref);bool operator < (const CVersionNumber& ref);bool operator <= (const CVersionNumber& ref);bool operator > (const CVersionNumber& ref);bool operator >= (const CVersionNumber& ref);private:WORD m_nVer[4]; //版本号
};typedef struct _FILE_VERSION_INFO
{_tstring strComments; //文件注释_tstring strInternalName; //内部名称_tstring strProductName; //产品名称_tstring strCompanyName; //公司名称_tstring strLegalCopyright; //法律版权_tstring strProductVersion; //产品版本_tstring strFileDescription; //文件描述_tstring strLegalTrademarks; //合法商标_tstring strPrivateBuild; //私有构建_tstring strFileVersion; //文件版本_tstring strOriginalFilename; //原始文件名_tstring strSpecialBuild; //特殊构建_tstring strFileVersionEx; //文件版本(从 VS_FIXEDFILEINFO中 获取)_tstring strProductVersionEx; //产品版本(从 VS_FIXEDFILEINFO中 获取)CVersionNumber FileVerNumber; //文件版本号CVersionNumber ProductVerNumber; //产品版本号
}FILE_VERSION_INFO;class CFileVersion
{struct LANGANDCODEPAGE {WORD wLanguage;WORD wCodePage;LANGANDCODEPAGE():wLanguage(0),wCodePage(0){}};public:CFileVersion();~CFileVersion();CFileVersion operator = (const CFileVersion& r) = delete;static FILE_VERSION_INFO GetVersionInfo(const _tstring& strFile);static FILE_VERSION_INFO GetCurrentModuleVersionInfo();public:bool LoadFile(const _tstring& strFile); //加载文件信息void Close();FILE_VERSION_INFO GetVersionInfo() const; //整个版本信息_tstring GetComments() const; //文件注释_tstring GetInternalName() const; //内部名称_tstring GetProductName() const; //产品名称_tstring GetCompanyName() const; //公司名称_tstring GetLegalCopyright() const; //法律版权_tstring GetProductVersion() const; //产品版本_tstring GetFileDescription() const; //文件描述_tstring GetLegalTrademarks() const; //合法商标_tstring GetPrivateBuild() const; //私有版本_tstring GetFileVersion() const; //文件版本_tstring GetOriginalFilename() const; //原始文件名_tstring GetSpecialBuild() const; //特别版本_tstring GetFileVersionEx() const; //文件版本(从 VS_FIXEDFILEINFO中 获取)_tstring GetProductVersionEx() const; //产品版本(从 VS_FIXEDFILEINFO中 获取)VS_FIXEDFILEINFO GetFixedFileInfo() const; //文件的版本信息private:_tstring _QueryInfo(const _tstring& strName) const;void _LoadAllInfo();LPVOID m_lpVerData; //文件信息数据指针LANGANDCODEPAGE m_Translate; //语言代码页VS_FIXEDFILEINFO m_VsFixedFileInfo; //文件的版本信息FILE_VERSION_INFO m_VersionInfo; //版本信息
};
CFileVersion.cpp
#include "CFileVersion.h"
#include <tchar.h>
#include <strsafe.h>#pragma comment(lib, "Version.lib")#pragma pack(push)
#pragma pack(1)
// 包含文件的版本信息。 此信息与语言和代码页无关
// https://learn.microsoft.com/zh-cn/windows/win32/menurc/vs-versioninfo
typedef struct {WORD wLength; // VS_VERSIONINFO 结构的长度(以字节为单位),此长度不包括在 32 位边界上对齐任何后续版本资源数据的填充WORD wValueLength; // Value 成员的长度(以字节为单位)WORD wType; // 版本资源中的数据类型, 1: 资源包含文本数据 0: 版本资源包含二进制数据WCHAR szKey[15]; // Unicode 字符串 L“VS_VERSION_INFO”WORD Padding1; // 在 32 位边界上对齐 Children 成员所需的任意或零个 WORD//VS_FIXEDFILEINFO Value//WORD Padding2//WORD Children
} VS_VERSIONINFO, * PVS_VERSIONINFO;#pragma pack(pop)CVersionNumber::CVersionNumber():m_nVer{ 0 }
{
};CVersionNumber::CVersionNumber(LPCTSTR lpStrVer):m_nVer{ 0 }
{_stscanf_s(lpStrVer, _T("%hd.%hd.%hd.%hd"), &m_nVer[0], &m_nVer[1], &m_nVer[2], &m_nVer[3]);
}CVersionNumber::CVersionNumber(WORD v1, WORD v2, WORD v3, WORD v4):m_nVer{ v1, v2, v3, v4 }
{
}CVersionNumber& CVersionNumber::operator = (LPCTSTR lpStrVer)
{_stscanf_s(lpStrVer, _T("%hd.%hd.%hd.%hd"), &m_nVer[0], &m_nVer[1], &m_nVer[2], &m_nVer[3]);return *this;
}int CVersionNumber::CompareVersion(const CVersionNumber& ref) const
{for (int i = 0; i < _countof(m_nVer); i++){if (m_nVer[i] != ref.m_nVer[i]){return (m_nVer[i] > ref.m_nVer[i] ? 1 : -1);}}return 0;
}_tstring CVersionNumber::GetVersionStr() const
{TCHAR szBuf[MAX_PATH] = { 0 };(void)::StringCchPrintf(szBuf, _countof(szBuf), _T("%hd.%hd.%hd.%hd"),m_nVer[0],m_nVer[1],m_nVer[2],m_nVer[3]);return szBuf;
}bool CVersionNumber::operator == (const CVersionNumber& ref)
{return CompareVersion(ref) == 0;
}bool CVersionNumber::operator != (const CVersionNumber& ref)
{return CompareVersion(ref) != 0;
}bool CVersionNumber::operator < (const CVersionNumber& ref)
{return CompareVersion(ref) < 0;
}bool CVersionNumber::operator <= (const CVersionNumber& ref)
{return CompareVersion(ref) <= 0;
}bool CVersionNumber::operator > (const CVersionNumber& ref)
{return CompareVersion(ref) > 0;
}bool CVersionNumber::operator >= (const CVersionNumber& ref)
{return CompareVersion(ref) >= 0;
}CFileVersion::CFileVersion():m_lpVerData(NULL)
{memset(&m_VsFixedFileInfo, 0, sizeof(m_VsFixedFileInfo));
}CFileVersion::~CFileVersion()
{this->Close();
}FILE_VERSION_INFO CFileVersion::GetVersionInfo(const _tstring& strFile)
{CFileVersion verInfo;verInfo.LoadFile(strFile);return verInfo.GetVersionInfo();
}FILE_VERSION_INFO CFileVersion::GetCurrentModuleVersionInfo()
{TCHAR szBuf[MAX_PATH] = { 0 };::GetModuleFileName(NULL, szBuf, _countof(szBuf));return GetVersionInfo(szBuf);
}bool CFileVersion::LoadFile(const _tstring& strFile)
{PVOID pFsRedirectionOldValue = NULL;bool isDisableWow64Fs = false;bool isSuccess = false;UINT cbTranslate = 0;DWORD dwVerSize;if (strFile.empty()){return false;}this->Close();m_VersionInfo = FILE_VERSION_INFO();// 禁用文件重定向isDisableWow64Fs = ::Wow64DisableWow64FsRedirection(&pFsRedirectionOldValue);do{// 获取版本信息数据大小dwVerSize = ::GetFileVersionInfoSize(strFile.c_str(), 0);if (0 == dwVerSize){break;}// 分配版本信息缓冲m_lpVerData = ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, dwVerSize);if (!m_lpVerData){break;}// 获取版本信息if (!::GetFileVersionInfo(strFile.c_str(), 0, dwVerSize, m_lpVerData)){break;}// 获取语言代码LANGANDCODEPAGE* lpTranslate = NULL;if (!::VerQueryValue(m_lpVerData, _T("\\VarFileInfo\\Translation"), (LPVOID*)&lpTranslate, &cbTranslate)){break;}m_Translate = *lpTranslate;// 获取 VS_FIXEDFILEINFO 信息PVS_VERSIONINFO lpVersion = (PVS_VERSIONINFO)m_lpVerData;if (0 != lpVersion->wValueLength){VS_FIXEDFILEINFO* pFixedFileInfo = (VS_FIXEDFILEINFO*)((LPBYTE)lpVersion + sizeof(VS_VERSIONINFO));DWORD dwAlign = 4;DWORD_PTR dwPadding = ((DWORD_PTR)pFixedFileInfo % dwAlign);if (0 != dwPadding){pFixedFileInfo = (VS_FIXEDFILEINFO*)((LPBYTE)pFixedFileInfo + (dwAlign - dwPadding));}m_VsFixedFileInfo = *pFixedFileInfo;}// 加载所有信息_LoadAllInfo();isSuccess = true;} while (false);// 不成功则关闭if (!isSuccess){this->Close();}// 恢复文件重定向if (isDisableWow64Fs){::Wow64RevertWow64FsRedirection(pFsRedirectionOldValue);}return isSuccess;
}void CFileVersion::Close()
{if (m_lpVerData){HeapFree(::GetProcessHeap(), 0, m_lpVerData);m_lpVerData = NULL;}
}void CFileVersion::_LoadAllInfo()
{m_VersionInfo.strComments = _QueryInfo(_T("Comments"));m_VersionInfo.strInternalName = _QueryInfo(_T("InternalName"));m_VersionInfo.strProductName = _QueryInfo(_T("ProductName"));m_VersionInfo.strCompanyName = _QueryInfo(_T("CompanyName"));m_VersionInfo.strLegalCopyright = _QueryInfo(_T("LegalCopyright"));m_VersionInfo.strProductVersion = _QueryInfo(_T("ProductVersion"));m_VersionInfo.strFileDescription = _QueryInfo(_T("FileDescription"));m_VersionInfo.strLegalTrademarks = _QueryInfo(_T("LegalTrademarks"));m_VersionInfo.strPrivateBuild = _QueryInfo(_T("PrivateBuild"));m_VersionInfo.strFileVersion = _QueryInfo(_T("FileVersion"));m_VersionInfo.strOriginalFilename = _QueryInfo(_T("OriginalFilename"));m_VersionInfo.strSpecialBuild = _QueryInfo(_T("SpecialBuild"));m_VersionInfo.strFileVersionEx = GetFileVersionEx();m_VersionInfo.strProductVersionEx = GetProductVersionEx();m_VersionInfo.FileVerNumber = m_VersionInfo.strFileVersionEx.c_str();m_VersionInfo.ProductVerNumber = m_VersionInfo.strProductVersionEx.c_str();}_tstring CFileVersion::_QueryInfo(const _tstring& strName) const
{_tstring strRes;TCHAR strQuery[MAX_PATH] = { 0 };LPCTSTR lpQueryRes = NULL;UINT uQueryCchSize;do{if (!m_lpVerData){break;}(void)::StringCchPrintf(strQuery, _countof(strQuery),_T("\\StringFileInfo\\%04x%04x\\%s"),m_Translate.wLanguage,m_Translate.wCodePage,strName.c_str());if (!::VerQueryValue(m_lpVerData, strQuery, (LPVOID*)&lpQueryRes, &uQueryCchSize)){break;}strRes = lpQueryRes;} while (false);return strRes;
}_tstring CFileVersion::GetComments() const
{return m_VersionInfo.strComments;
}_tstring CFileVersion::GetInternalName() const
{return m_VersionInfo.strInternalName;
}_tstring CFileVersion::GetProductName() const
{return m_VersionInfo.strProductName;
}_tstring CFileVersion::GetCompanyName() const
{return m_VersionInfo.strCompanyName;
}_tstring CFileVersion::GetLegalCopyright() const
{return m_VersionInfo.strLegalCopyright;
}_tstring CFileVersion::GetProductVersion() const
{return m_VersionInfo.strProductVersion;
}_tstring CFileVersion::GetFileDescription() const
{return m_VersionInfo.strFileDescription;
}_tstring CFileVersion::GetLegalTrademarks() const
{return m_VersionInfo.strLegalTrademarks;
}_tstring CFileVersion::GetPrivateBuild() const
{return m_VersionInfo.strPrivateBuild;
}_tstring CFileVersion::GetFileVersion() const
{return m_VersionInfo.strFileVersion;
}_tstring CFileVersion::GetOriginalFilename() const
{return m_VersionInfo.strOriginalFilename;
}_tstring CFileVersion::GetSpecialBuild() const
{return m_VersionInfo.strSpecialBuild;
}_tstring CFileVersion::GetFileVersionEx() const
{TCHAR szBuf[MAX_PATH] = { 0 };(void)::StringCchPrintf(szBuf, _countof(szBuf), _T("%hd.%hd.%hd.%hd"),HIWORD(m_VsFixedFileInfo.dwFileVersionMS),LOWORD(m_VsFixedFileInfo.dwFileVersionMS),HIWORD(m_VsFixedFileInfo.dwFileVersionLS),LOWORD(m_VsFixedFileInfo.dwFileVersionLS));return szBuf;
}_tstring CFileVersion::GetProductVersionEx() const
{TCHAR szBuf[MAX_PATH] = { 0 };(void)::StringCchPrintf(szBuf, _countof(szBuf), _T("%hd.%hd.%hd.%hd"),HIWORD(m_VsFixedFileInfo.dwProductVersionMS),LOWORD(m_VsFixedFileInfo.dwProductVersionMS),HIWORD(m_VsFixedFileInfo.dwProductVersionLS),LOWORD(m_VsFixedFileInfo.dwProductVersionLS));return szBuf;
}VS_FIXEDFILEINFO CFileVersion::GetFixedFileInfo() const
{return m_VsFixedFileInfo;
}FILE_VERSION_INFO CFileVersion::GetVersionInfo() const
{return m_VersionInfo;
}
main.cpp
#include <locale.h>
#include "Win32Utils/CFileVersion.h"int _tmain(int argc, LPCTSTR argv[])
{setlocale(LC_ALL, "");FILE_VERSION_INFO info = CFileVersion::GetCurrentModuleVersionInfo();return 0;
}
相关文章:

Win32 获取EXE/DLL文件版本信息
CFileVersion.h #pragma once#include <windows.h> #include <string> #include <tchar.h>#ifdef _UNICODE using _tstring std::wstring; #else using _tstring std::string; #endif// 版本号辅助类 class CVersionNumber { public:// 无参构造CVersionN…...

回显服务器的制作方法
文章目录 客户端和服务器TCP和UDP的特点UDP socket api的使用DatagramSocketDatagramPacketInetSocketAddress API 做一个简单的回显服务器UDP版本的回显服务器TCP版本的回显服务器 客户端和服务器 在网络中,主动发起通信的一方是客户端,被动接受的这一方…...

w28DVWA-csrf实例
DVWA-csrf实例 low级别 修改密码:修改的密码通过get请求,暴露在url上。 写一个简单的html文件,里面伪装修改密码的文字,代码如下: <html><body><a href"http://dvwa:7001/vulnerabilities/csr…...
子网络划分与互通,上网行为审计
网络环境需求:在办公网络环境中,由于公司部门的划分,以及服务器、电脑、手机等设备类型,一般都需要划分多个网段,便于进行网络管理,并提升网络通信效率。各个子网段管理员控制设备的接入,子网段之间需要进行局域网通信,发送消息和文件,通常使用飞秋。服务器网段,禁止…...
如何快速删除node_module依赖包
利用npm:输入 npm install rimraf -g rimraf node_modules...
async/await 的用法
一、async和await定义 async 是异步的意思,而 await 是等待的意思,await 用于等待一个异步任务执行完成的结果。 1.async/await 是一种编写异步代码的新方法(以前是采用回调和 promise)。 2. async/await 是建立在 promise 的基础…...

JAVA面试汇总总结更新中ing
本人面试积累面试题 基础RocketMQSpring登录技能操作线程事务微服务JVMKAFKAMYSQLRedislinux 基础 1.面向对象的三个特征 封装,继承,多态,有时候也会加上抽象。 2.多态的好处 允许不同类对象对同一消息做出响应,即同一消息可以根…...

vue-利用属性(v-if)控制表单(el-form-item)显示/隐藏
表单控制属性 v-if 示例: 通过switch组件作为开关,控制表单的显示与隐藏 <el-form-item label"创建数据集"><el-switch v-model"selectFormVisible"></el-switch></el-form-item><el-form-item label&…...

数据结构-邻接矩阵
介绍 邻接矩阵,是表示图的一种常见方式,具体表现为一个记录了各顶点连接情况的呈正方形的矩阵。 假设一共有以下顶点,其连接关系如图所示 那么,怎么表示它们之间的连接关系呢? 我们发现,各条边所连接的都…...

基于CNN-GRU-Attention的时间序列回归预测matlab仿真
目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 CNN(卷积神经网络)部分 4.2 GRU(门控循环单元)部分 4.3 Attention机制部分 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版…...

Docker部署Halo容器并结合内网穿透实现公网访问本地个人博客
文章目录 1. Docker部署Halo1.1 检查Docker版本如果未安装Docker可参考已安装Docker步骤:1.2 在Docker中部署Halo 2. Linux安装Cpolar2.1 打开服务器防火墙2.2 安装cpolar内网穿透 3. 配置Halo个人博客公网地址4. 固定Halo公网地址 本文主要介绍如何在CentOS 7系统使…...
纯css实现文字左右循环滚动播放效果
思路:由两个span模块组成,第一个为空的span内容,为的是实现第二个span内容缓慢出现的效果。 代码如下: <div class"scrollingStyle"><span class"first-marquee"></span><span class&q…...

【Java EE初阶二十二】https的简单理解
1. 初识https 当前网络上,主要都是 HTTPS 了,很少能见到 HTTP.实际上 HTTPS 也是基于 HTTP.只不过 HTTPS 在 HTTP 的基础之上, 引入了"加密"机制;引入 HTTPS 防止你的数据被黑客篡改 ; HTTPS 就是一个重要的保护措施.之所以能够安全, 最关键的…...
系统学习Python——装饰器:类装饰器-[跟踪对象接口:基础知识]
分类目录:《系统学习Python》总目录 文章《系统学习Python——装饰器:类装饰器-[单例类:基础知识]》的单例示例阐明了如何使用类装饰器来管理一个类的所有实例。类装饰器的另一个常用场景是为每个生成的实例扩展接口。类装饰器基本上可以在实…...
go-redis 使用 redis 6.0.14 版本错误: consider implementing encoding.BinaryMarshaler
使用方法 err : bp.data.redis.Get(ctx, policyKey).Scan(&result)起初在 redis 5.x.x 版本并没有遇到错误,但是在切换 redis 实例之后就出现了错误(他们之间只是版本不同)。 修复方法 看错误日志的描述,大概含义就是需要我们…...

计网 - 域名解析的工作流程
文章目录 Pre引言1. DNS是什么2. 域名结构3. 域名解析的工作流程4. 常见的DNS记录类型5. DNS安全6. 未来的发展趋势 Pre 计网 - DNS 域名解析系统 引言 在我们日常使用互联网时,经常会输入各种域名来访问网站、发送电子邮件或连接其他网络服务。然而,我…...

普中51单片机学习(EEPROM)
EEPROM IIC串行总线的组成及工作原理 I2C总线的数据传送 数据位的有效性规定 I2C总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许…...
智能风控体系之供应链业务模式
供应链金融是一种针对中小企业的新型融资模式,将资金流有效整合到供应链管理的过程中,既为供应链各环节企业提供贸易资金服务,又为供应链弱势企业提供新型贷款融资服务,以核心客户为依托,以真实贸易背景为前提…...
最少停车数(C 语言)
题目描述 特定大小的停车场,数组cars[]表示,其中1表示有车,0表示没车。车辆大小不一,小车占一个车位(长度1),货车占两个车位(长度2),卡车占三个车位…...

MAC M1安装vmware和centos7虚拟机并配置静态ip
一、下载vmware和centos7镜像 1、VMWare Fusion 官网的下载地址是:下载地址 下载好之后注册需要秘钥,在官网注册后使用免费的个人秘钥 2、centos7 下载地址: https://biosyxh.cn:5001/sharing/pAlcCGNJf 二、虚拟机安装 直接将下…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

龙虎榜——20250610
上证指数放量收阴线,个股多数下跌,盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型,指数短线有调整的需求,大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的:御银股份、雄帝科技 驱动…...

国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...
【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15
缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下: struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...

在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】
大家好,我是java1234_小锋老师,看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】,分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...