基于Windows系统用C++做一个点名工具

目录

一、前言        

二、主要技术点

三、准备工作

四、主界面

1.绘制背景图

2、实现读取花名册功能

3.实现遍历花名册功能

4.实现储存功能

4.1创建数据库

4.2存储数据到数据库表

4.3读取数据库表数据


一、前言        

        人总是喜欢回忆过去,突然回忆起大学时光来,我的计算机老师是用自己做的点名工具,来抽人回答问题的。那么本次的主题就是做一个点名工具,咱们在点名的基础上加一个存储功能,在多次抽取的情况下进行存储。

二、主要技术点

Sqlite数据的增删改查、GDI/GDI+绘制、COM接口、文件数据读取、定时器。

三、准备工作

1.SQLite数据库操作工具

适用于 SQLite 的 DB 浏览器

2.SqLiteCpp第三方库

GitHub - SRombauts/SQLiteCpp:SQLiteC++ (SQLiteCpp) 是一个智能且易于使用的 C++ SQLite3 包装器。

3.一个.txt格式的花名册

四、主界面

主界面包含三个控件:静态文本控件、按钮控件、编辑框控件。分别用于响应选择本地花名册文件、遍历花名册名单、显示花名册名单。

1.绘制背景图

映射WM_PAINT消息进行绘制,可以使用GDI或者GDI+方法。

MESSAGE_HANDLER(WM_PAINT, OnPaint)

GDI的方法:

    LRESULT CRandomDlg::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(&ps);
        HDC hdcMem = CreateCompatibleDC(hdc);
        if (lstrlen(m_picPath) != 0)
        {
            HBITMAP hBitmap = (HBITMAP)LoadImage(NULL, m_picPath, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE);
            HBITMAP oldBitmap = (HBITMAP)SelectObject(hdcMem, hBitmap);
            BITMAP bitmap;
            GetObject(hBitmap, sizeof(bitmap), &bitmap);
            SetStretchBltMode(hdc, STRETCH_HALFTONE);   //设置位图拉伸模式,解决模糊问题
            StretchBlt(hdc, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left,
                ps.rcPaint.bottom - ps.rcPaint.top, hdcMem, 0, 0, bitmap.bmWidth, bitmap.bmHeight, SRCCOPY);
            SelectObject(hdcMem, oldBitmap);
            DeleteObject(hBitmap);
        }
        EndPaint(&ps);
        DeleteDC(hdcMem);
        return 0;
    }

GDI+方法:

#include <gdiplus.h>
using namespace Gdiplus;
    
LRESULT CRandomDlg::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(&ps);
        if (lstrlen(m_picPath) != 0)
        {
            Graphics gh(hdc);
            RectF rect(ps.rcPaint.left, ps.rcPaint.top,
                ps.rcPaint.right - ps.rcPaint.left,
                ps.rcPaint.bottom - ps.rcPaint.top);
           Image* image = Image::FromFile(m_picPath);
            gh.DrawImage(image, rect);
        }

        EndPaint(&ps);
        return 0;
    }

如果你写代码所使用的框架没有初始化GDI+,那么需要我们手动初始化和释放:

//初始化GDI+
ULONG_PTR gdiplusToken;   
GdiplusStartupInput gdiplusStartupInput;

GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
//释放GDI+
GdiplusShutdown(gdiplusToken);

2、实现读取花名册功能

映射WM_COMMAND消息。

MESSAGE_HANDLER(WM_COMMAND, OnCommand)

处理静态文本控件被点击时的通知消息STN_CLICKED,以此来弹出选择窗口选择花名册文件。我们使用IFileDialog接口来打开一个shell窗口进行文件选择,获取选择文件的路径之后通过CreateFile()函数打开文件,再通过ReadFile()函数读取其内容:

 LRESULT CRandomDlg::OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
        int wmId = LOWORD(wParam);
        int messageId = HIWORD(wParam);
        switch (messageId)
        {
        case STN_CLICKED:
        {
            if (wmId == IDC_CHOOSESTATIC)
            {
                
            IFileDialog* pFileDialog = NULL;
                //创建IFileDialog接口实例
                HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pFileDialog));
                if (SUCCEEDED(hr))
                {
                    IShellItem* pItem = nullptr;
                    DWORD dwOptions;
                    hr = pFileDialog->GetOptions(&dwOptions);
                    //设置窗口选项
                    pFileDialog->SetOptions(dwOptions | FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST);
                    //设置窗口标题
                    pFileDialog->SetTitle(L"请选择文件:");
                    //设置筛选器
                    COMDLG_FILTERSPEC fileSpec[] =
                    {
                        { L"文本文件", L"*.txt"},
                    };
                    pFileDialog->SetFileTypes(1, fileSpec);
                    hr = pFileDialog->Show(GetWindow(IDD_RANDOMDIALOG));
                    //获取用户选择
                    if (SUCCEEDED(hr))
                    {
                        hr = pFileDialog->GetResult(&pItem);
                        if (SUCCEEDED(hr))
                        {
                            //获取选择文件的路径
                            hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &m_pszFilePath);
                            HANDLE hFile = CreateFile(m_pszFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
                            if (hFile != INVALID_HANDLE_VALUE)
                            {
                                //打开文件读取内容
                                DWORD size = GetFileSize(hFile, NULL);
                                if (size != INVALID_FILE_SIZE)
                                {
                                    nameVec.clear();
                                    char* pBuffer = new char[size + 1];
                                    memset(pBuffer, 0, size + 1);
                                    DWORD dwBytesRead = 0;
                                    WCHAR tmp[100] = { 0 };
                                    if (ReadFile(hFile, pBuffer, size, &dwBytesRead, NULL))
                                    {
                                        splitString(pBuffer);
                                        if (nameVec.empty())
                                            MessageBox(L"所选文件没有内容!", L"Warning", MB_OK | MB_ICONWARNING);
                                        else
                                        {
                                            stringToLPCWSTR(*nameVec.begin(), tmp);
                                            SetDlgItemText(IDC_NAMESTATIC, tmp);
                                        }
                                    }
                                }
                                CloseHandle(hFile);
                            }
                            else
                                MessageBox(L"CreateFile Error!", L"Error", MB_OK | MB_ICONERROR);
                        }
                        else
                        {
                            MessageBox(L"GetResult Error!", L"Error", MB_OK | MB_ICONERROR);
                        }
                    }

                    // 释放资源
                    pFileDialog->Release();
                    CoTaskMemFree(m_pszFilePath);
                    m_pszFilePath = nullptr;
                }
                else
                {
                    MessageBox(L"CoCreateInstance Error!", L"Error", MB_OK | MB_ICONERROR);
                }
}

效果图:

3.实现遍历花名册功能

我们通过响应按钮被按下的通知消息BN_CLICKED,来开始遍历花名册。将选中的名字显示在主窗口上,再用定时器来延迟3秒之后清除显示,通过AnimateWindow()函数将显示和清除动作加上动画效果。

LRESULT CRandomDlg::OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
        int wmId = LOWORD(wParam);
        int messageId = HIWORD(wParam);
        switch (messageId)
        {
        case BN_CLICKED:
        {
            if (wmId == IDC_STOPBUTTON)
            {
                //响应开始/停止按钮
                isStop = !isStop;
                BOOL errorFlag = FALSE;
                HANDLE handlePro = GetCurrentProcess();
                SetUserObjectInformation(handlePro, UOI_TIMERPROC_EXCEPTION_SUPPRESSION, &errorFlag, sizeof(BOOL));
                if (isStop)
                {
                    SetDlgItemText(IDC_STOPBUTTON, L"开始");
                    KillTimer(ID_SHOWNAME);
                    WCHAR tmpStr[100] = { 0 };
                    stringToLPCWSTR(nameVec.at(index), tmpStr);
                    choosedVec.push_back(tmpStr);
                    HWND hwnd = GetDlgItem(IDC_POPSTATIC);
                    ::SetWindowText(hwnd, tmpStr);
                    AnimateWindow(hwnd, 300, AW_ACTIVATE | AW_HOR_POSITIVE);  //动态显示窗口
                    SetTimer(ID_POPNAME, 3000, PopNameProc);
                }
                else
                {
                    if (nameVec.empty())
                    {
                        MessageBox(L"未选择抽奖名单或者名单为空", L"Warning", MB_OK | MB_ICONWARNING);
                        isStop = TRUE;
                        return FALSE;
                    }
                    ::ShowWindow(GetDlgItem(IDC_POPSTATIC), SW_HIDE);
                    SetDlgItemText(IDC_STOPBUTTON, L"停止");
                    SetTimer(ID_SHOWNAME, 50, ShowNameProc);
                    InvalidateRect(0, 1);
                    UpdateWindow();
                }

            }
}

效果图:

4.实现储存功能

存储方式有很多种,我们本次是使用储存到数据库的方法。使用第三方库SqliteCPP操作sqlite数据库。

4.1创建数据库

我们需要在程序初始化时创建一个数据库文件,并且创建储存数据的表。

创建表指令:

CREATE TABLE 表名 (列名1 列1对应值的类型,……, 列名n 列n对应值的类型)

try
    {
        //创建数据库文件
        if (!PathFileExists(L"C\\ToolBox"))
            CreateDirectory(L"C:\\ToolBox", NULL);

        SQLite::Database db("C:\\ToolBox\\TB.db", SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE);
        //创建表
        string sql = R"(
            CREATE TABLE IF NOT EXISTS RANDOM (
                id INTEGER PRIMARY KEY NOT NULL,
                name TEXT NOT NULL,
                week INTEGER NOT NULL,
                todayDate TEXT NOT NULL
            )
        )";
        db.exec(sql);
    }
    catch (const exception& e)
    {
        MessageBoxA(NULL, e.what(), "Create table failed!", MB_OK | MB_ICONERROR);
    }

4.2存储数据到数据库表

我们在第3点中响应按钮消息,将抽中的名单暂时存储到一个vector容器中,再统一保存到本地数据库。

插入数据指令 :

 INSERT INTO 表名 (列名1,……,列名n)VALUES (列名1 对应的值,……,列名n对应的值)

try 
        {
            SQLite::Database db("C:\\ToolBox\\TB.db", SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE);
            string sql;
            for (int i = 0; i < choosedVec.size(); i++)
            {
                sql = "INSERT INTO RANDOM (name, week, todayDate) VALUES (?,?,?)";
                SQLite::Statement query(db, sql);
                query.bind(1, UnicodeToUtf8(choosedVec.at(i)));
                query.bind(2, m_curWeek);
                query.bind(3, UnicodeToUtf8(m_curDate));
                query.exec();
            }

            nameVec.clear();
            choosedVec.clear();
            if (m_staticFont != NULL)
                DeleteObject(m_staticFont);
            if (m_titleFont != NULL)
                DeleteObject(m_titleFont);
            if (m_bkBrush != NULL)
                DeleteObject(m_bkBrush);
        }
        catch (const exception& e)
        {
            MessageBoxA(NULL, e.what(), "Store to DB failed!", MB_OK | MB_ICONERROR);
            return 0;
        }

注意:sqlite数据库默认的编码模式是UTF-8,所以当我们上传的数据是字符串格式时且编译器设置的字符集非UTF8格式,那么需要先转换为UTF-8格式再上传,否则数据库存储的数据可能就是乱码了。

再储存完成之后我们可以使用事先准备的数据库工具来打开创建的数据库,以此查看数据:

4.3读取数据库表数据

我们可以在程序中读取数据库中储存的数据,读取数据指令:

SELECT 列名 FROM 表名        读取表中某一列的数据

SELECT * FROM 表名                读取整个表的数据

                    WCHAR date[50] = { 0 };
                    GetNowDateString(date);
                    wstring total;
                    try
                    {
                        SQLite::Database db(L"C:\\ToolBox\\TB.db", SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE);
                        SQLite::Statement query2(db, "SELECT name FROM RANDOM WHERE todayDate = ?");
                        query2.bind(1, UnicodeToUtf8(date));

                        while (query2.executeStep())
                        {
                            wstring data;
                            UTF8ToUniocde(query2.getColumn(0).getString().c_str(), data);
                            total += data + L";";
                        }

                        if (!total.empty())
                        {
                            MessageBox(hWnd, total.c_str(), L"上期中奖名单", MB_OK);
                        }
                        else
                        {
                            MessageBox(hWnd, L"暂无数据", L"Tip", MB_OK | MB_ICONINFORMATION);
                        }
                    }
                    catch (const exception& e)
                    {
                        MessageBoxA(NULL, e.what(), "GetData failed!", MB_OK | MB_ICONERROR);
                        return FALSE;
                    }

效果图:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/920644.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

11.9K Star!强大的 Web 爬虫工具 FireCrawl:为 AI 训练与数据提取提供全面支持

在这个信息爆炸的时代&#xff0c;数据就是力量。尤其是对于开发者来说&#xff0c;获取并利用好数据&#xff0c;就意味着拥有更多的主动权和竞争力。 无论是用来训练大语言模型&#xff0c;还是用于增强检索生成&#xff08;RAG&#xff09;&#xff0c;数据都扮演着至关重要…

云原生之k8s服务管理

文章目录 服务管理Service服务原理ClusterIP服务 对外发布应用服务类型NodePort服务Ingress安装配置Ingress规则 Dashboard概述 认证和授权ServiceAccount用户概述创建ServiceAccount 权限管理角色与授权 服务管理 Service 服务原理 容器化带来的问题 自动调度&#xff1a;…

前端面试题整理-前端异步编程

1. 进程、线程、协程的区别 在并发编程领域&#xff0c;进程、线程和协程是三个核心概念&#xff0c;它们在资源管理、调度和执行上有着本质的不同。 首先&#xff0c;进程是操作系统进行资源分配和调度的独立单位&#xff08;资源分配基本单位&#xff09;&#xff0c;每个进…

动静态库:选择与应用的全方位指南

目录 1 软链接 1.1 软链接的建立方式和观察现象 1.2 软链接的原理 2 硬链接 2.1 硬链接的建立方式和观察现象 2.2 硬链接的本质 2.3 我们用户不能给目录建立硬链接 3. 动静态库复习 4 动静态库的制作 4.1 静态库的制作与使用 4.1.2 打包 4.1.3 静态库的使用 4.2 动…

【ROS2】多传感器融合、实现精准定位:robot_localization

1、简述 robot_localization在SLAM建图、导航中常用于将多个传感器融合(IMU、里程计、深度相机、GPS等),以提高定位精度,为机器人提供了在三维空间中的非线性状态估计 robot_localization包含两个状态估计节点: ekf_localization_node:扩展卡尔曼滤波(EKF),缺点是非…

极客大挑战2024wp

极客大挑战2024wp web 和misc 都没咋做出来&#xff0c;全靠pwn✌带飞 排名 密码学和re没做出几个&#xff0c;就不发了 web ez_pop 源代码 <?php Class SYC{public $starven;public function __call($name, $arguments){if(preg_match(/%|iconv|UCS|UTF|rot|quoted…

40分钟学 Go 语言高并发:并发下载器开发实战教程

并发下载器开发实战教程 一、系统设计概述 1.1 功能需求表 功能模块描述技术要点分片下载将大文件分成多个小块并发下载goroutine池、分片算法断点续传支持下载中断后继续下载文件指针定位、临时文件管理进度显示实时显示下载进度和速度进度计算、速度统计错误处理处理下载过…

李宏毅机器学习课程知识点摘要(1-5集)

前5集 过拟合&#xff1a; 参数太多&#xff0c;导致把数据集刻画的太完整。而一旦测试集和数据集的关联不大&#xff0c;那么预测效果还不如模糊一点的模型 所以找的数据集的量以及准确性也会影响 由于线性函数的拟合一般般&#xff0c;所以用一组函数去分段来拟合 sigmoi…

Spring Boot教程之五:在 IntelliJ IDEA 中运行第一个 Spring Boot 应用程序

在 IntelliJ IDEA 中运行第一个 Spring Boot 应用程序 IntelliJ IDEA 是一个用 Java 编写的集成开发环境 (IDE)。它用于开发计算机软件。此 IDE 由 Jetbrains 开发&#xff0c;提供 Apache 2 许可社区版和商业版。它是一种智能的上下文感知 IDE&#xff0c;可用于在各种应用程序…

本地Docker部署开源WAF雷池并实现异地远程登录管理界面

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

如何快速将Excel数据导入到SQL Server数据库

工作中&#xff0c;我们经常需要将Excel数据导入到数据库&#xff0c;但是对于数据库小白来说&#xff0c;这可能并非易事&#xff1b;对于数据库专家来说&#xff0c;这又可能非常繁琐。 这篇文章将介绍如何帮助您快速的将Excel数据导入到sql server数据库。 准备工作 这里&…

[产品管理-91]:产品经理的企业运营的全局思维-1

目录 前言&#xff1a;企业架构图 产品经理的企业运营全局思维 1、用户 - 用户价值与体验&#xff1a;真正的需求&#xff0c;真正的问题&#xff0c;一切的原点 2、大势 - 顺应宏观大势&#xff1a;政策趋势、行业趋势、技术趋势 3、市场 - 知己知彼&#xff1a;市场调研…

简单实现vue2响应式原理

vue2 在实现响应式时&#xff0c;是根据 object.defineProperty() 这个实现的&#xff0c;vue3 是通过 Proxy 对象实现&#xff0c;但是实现思路是差不多的&#xff0c;响应式其实就是让 函数和数据产生关联&#xff0c;在我们对数据进行修改的时候&#xff0c;可以执行相关的副…

论文解析:EdgeToll:基于区块链的异构公共共享收费系统(2019,IEEE INFOCOM 会议);layer2 应对:频繁小额交易,无交易费

目录 论文解析:EdgeToll:基于区块链的异构公共共享收费系统(2019,IEEE INFOCOM 会议) 核心内容概述 核心创新点原理与理论 layer2 应对:频繁小额交易,无交易费 论文解析:EdgeToll:基于区块链的异构公共共享收费系统(2019,IEEE INFOCOM 会议) 核心内容是介绍了一个…

基于python Django的boss直聘数据采集与分析预测系统,爬虫可以在线采集,实时动态显示爬取数据,预测基于技能匹配的预测模型

本系统是基于Python Django框架构建的“Boss直聘”数据采集与分析预测系统&#xff0c;旨在通过技能匹配的方式对招聘信息进行分析与预测&#xff0c;帮助求职者根据自身技能找到最合适的职位&#xff0c;同时为招聘方提供更精准的候选人推荐。系统的核心预测模型基于职位需求技…

SemiDrive E3 硬件设计系列---唤醒电路设计

一、前言 E3 系列芯片是芯驰半导体高功能安全的车规级 MCU&#xff0c;对于 MCU 的硬件设计部分&#xff0c;本系列将会分模块进行讲解&#xff0c;旨在介绍 E3 系列芯片在硬件设计方面的注意事项与经验&#xff0c;本文主要讲解 E3 硬件设计中唤醒电路部分的设计。 二、RTC 模…

Leetcode198. 打家劫舍(HOT100)

代码&#xff1a; class Solution { public:int rob(vector<int>& nums) {int n nums.size();vector<int> f(n 1), g(n 1);for (int i 1; i < n; i) {f[i] g[i - 1] nums[i - 1];g[i] max(f[i - 1], g[i - 1]);}return max(f[n], g[n]);} }; 这种求…

一文探究48V新型电气架构下的汽车连接器

【哔哥哔特导读】汽车电源架构不断升级趋势下&#xff0c;48V系统是否还有升级的必要&#xff1f;48V新型电气架构将给连接器带来什么改变&#xff1f; 在插混和纯电车型逐渐普及、800V高压平台持续升级的当下&#xff0c;48V技术还有市场吗? 这个问题很多企业的回答是不一定…

React学习05 - redux

文章目录 redux工作流程redux理解redux理解及三个核心概念redux核心apiredux异步编程react-redux组件间数据共享 纯函数redux调试工具项目打包 redux工作流程 redux理解 redux是一个专门用于状态管理的JS库&#xff0c;可以用在react, angular, vue 等项目中。在与react配合使…

2024年11月最新 Alfred 5 Powerpack (MACOS)下载

在现代数字化办公中&#xff0c;我们常常被繁杂的任务所包围&#xff0c;而时间的高效利用成为一项核心需求。Alfred 5 Powerpack 是一款专为 macOS 用户打造的高效工作流工具&#xff0c;以其强大的定制化功能和流畅的用户体验&#xff0c;成为众多效率爱好者的首选。 点击链…