【第39节】windows编程:打造MFC版本任务管理器

目录

一、项目概述

二、项目开发的各种功能关键

2.1 进程信息的获取

2.2 线程信息的获取

2.3 进程模块信息的获取

2.3.1 模块快照

2.3.2 枚举模块

2.4 进程堆信息的获取

2.5 窗口信息的获取

2.6 文件信息的获取

2.7 内存信息和CPU占用率的获取

2.7.1 内存信息相关结构体

2.7.2 相关函数

2.7.3 使用例子

2.7.4 CPU占用率

三、其它功能要点

3.1 关机、重启,注销,休眠

3.2 老板键

 四、完整示例demo

4.1 完成的基础功能

4.2 界面说明

4.3 操作说明


一、项目概述

        在软件开发领域,构建一个带界面且功能丰富的项目往往充满挑战。本次项目不仅要应对多样化的数据展示需求,涵盖文件、进程、线程等多源信息,还需处理各类用户交互操作。接下来,让我们一同深入探索这个项目的关键要点,包括复杂数据的获取方法、界面展示的考量,以及如关机操作、老板键设置等特色功能的实现途径 ,为项目开发提供全面且清晰的指引,并提供实现demo。 

        本次开发基础除了MFC相关知识要求,还要熟悉windows sdk开发知识点,不会的请看前面的章节。下面简单介绍该项目要点,界面不在此讲述细节实现。

二、项目开发的各种功能关键

2.1 进程信息的获取

        在Windows系统里,要获取当前正在运行的进程,可以依靠快照系列API 。这套API挺厉害的,它不光能把正在运行的进程找出来,还能获取到进程的模块列表、堆这些信息。使用这套API有这么几个步骤:
(1)拍摄快照
(2)对快照进行遍历
(3)把快照关闭

下面就是创建进程快照,然后获取进程列表的具体做法:

//1.先创建一个进程快照
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 stcPe32;
//2.通过一组API遍历进程快照
Process32First(hProcessSnap, &stcPe32);
do {//3.使用从进程快照中提取出来的信息,信息已经提取到stcPe32变量中//这些信息包含进程的PID, 进程名等数据,这些数据可以显示到界面上//4.获取快照中下一个进程的信息
} while (Process32Next(hProcessSnap, &stcPe32));
//5关闭快照
CloseHandle(hSanp);

2.2 线程信息的获取

        获取线程信息的办法和获取进程信息差不多,也能用快照API来操作。但是,通过这个方法遍历得到的线程,是系统里所有的线程,并不是属于某一个特定进程的线程。这就可能出问题了,当我们要把这些线程数据显示到界面上的时候,就容易产生冲突。比如说,我们已经遍历出了进程列表,接下来想查看某个特定进程的线程列表,要是直接用刚才那种获取所有线程的方式,得到的线程就不是我们想要的。不过还好,遍历得到的线程信息里面,有一项是父进程的PID,利用这个PID进行筛选,就能找到指定进程的全部线程了。具体的操作方法如下:

//1.创建一个线程相关的快照句柄
hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
//2.循环遍历线程信息
//通过线程快照句柄获取第一个线程信息
Thread32First(hThreadSnap, &stcTe32))
do {//判断这个线程的父进程IDif (stcTe32.th320wnerProcessID == 指定进程的PID) {//将线程的信息显示到界面.}//获取快照中下一个线程信息
} while (Thread32Next(hThreadSnap, &stcTe32))

2.3 进程模块信息的获取

        模块信息也可以通过快照API来获取。每个进程都有自己特有的模块信息,当我们创建快照的时候,得指定一个进程的PID,这样创建快照的函数就能根据这个PID,把指定进程的模块列表给取出来。

        在获取模块信息的过程中,经常会碰到一个问题,那就是一般只能获取到32位进程的模块信息,对于64位进程的模块,就获取不了了。不过有个解决办法,就是用EnumProcessModulesEx函数,这个函数比较强大,32位和64位进程的模块它都能获取。但是要注意,使用这个函数有个前提,就是调用这个API的进程必须得是64位的。

        下面就分别讲讲这两种获取模块信息方法的具体使用:

2.3.1 模块快照

//1.创建模块快照
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPId);
//2.循环遍历快照中的模块信息
MODULEENTRY32 Module32;
Module32First(hModuleSnap, Module32);
do {//将获取到的信息显示到界面//4.获取快照中下一个模块信息
} while (Module32Next(hModuleSnap, &m_Module32));
CloseHandle(hSnap);

2.3.2 枚举模块

(想要同时遍历出32位和64位进程的模块,项目必须被编译成64位的)

HANDLE hProcess = NULL;
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwPID/*进程ID*/);
HMODULE hModules[0x2000] = {}; // 只用于获取模块句柄个数
DWORD dwNeed = 0; // 获取模块句柄个数
//枚举进程模块,函数会输出模块句柄数组
EnumProcessModulesEx(hProcess, hModules, sizeof(hModules), &dwNeed, LIST_MODULES_ALL);
DWORD dwModuleCount = dwNeed / sizeof(HMODULE);
MODULEINFO moif = {0}; //保存模块信息的结构体
//循环获取模块信息
for (SIZE_T i = 0; i < dwModuleCount; ++i) {//根据进程句柄和模块句柄获取模块路径GetModuleFileNameEx(hProcess, hModules[i], 缓冲区, 长度);//根据进程句柄和模块句柄获取模块其他信息GetModuleInformation(hProcess, hModules[i], &moif, sizeof(MODULEINFO))moif.EntryPoint; //dl1入口点moif.lpBaseOfD11; //dll基址moif.SizeOfImage; //dll大小//将获取到的模块信息显示到界面上.
}

2.4 进程堆信息的获取

一个进程堆的数量众多,但遍历方式同样可使用快照系列API。

//1.创建一个线程相关的快照句柄
hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SMAPTHREAD, O);
//2.循环遍历线程信息
do {//1)通过线程快照句柄获取第一个线程信息Thread32First(hThreadSnap, &stcTe32))//2)将信息显示到界面//3)获取快照中下一个线程信息
} while (Thread32Next(hThreadSnap, &stcTe32))

2.5 窗口信息的获取

        窗口信息说的就是当前系统里,正在显示的或者隐藏着的窗口的相关内容。在Windows系统里,EnumWindow函数专门用来把当前系统上所有窗口都列出来。这里的枚举,意思就是Windows会逐个查看所有窗口,在这个查看的过程中,它会调用一个回调函数,然后把查看到的窗口信息传送给这个回调函数。所以,要完成枚举窗口这件事,实际上包含两个部分:第一,调用EnumWinodw函数;第二,在回调函数里把枚举出来的窗口信息保存好。 

        下面就是枚举窗口函数用到的API原型: 

BOOL CALLBACK EnumWindowsProc(_In_    HWND hwnd, //遍历到的窗口句柄_In_    LPARAM 1Param  //EnumWindows函数传来的参数
);

枚举窗口的用法为:

BOOL CALLBACK EnumWinProc(HWND hWnd, LPARAM 1Param) { //回调函数//根据窗口句柄获取窗口名TCHAR buff[200];GetWindowText(hWnd, buff, 200); //得到窗口名,可显示到界面中.//判断窗口是否被隐藏if (IsWindowVisible(hWnd) == TRUE && wcslen(buff)!= 0)//窗口没有被隐藏且窗口标题长度不为0,则将窗口信息显示到界面中.
}
int main() {EnumWindow(&EnumWinProc/*枚举窗口的回调函数*/, NULL/*回调函数的附加参数*/);
}

2.6 文件信息的获取

        Windows系统提供了一组能用来遍历文件的API,分别是FindFirstFile和FindNextFile。FindFirstFile这个API的作用是找到一个文件夹里的第一个文件或者子文件夹。而FindNextFile呢,它负责一个接一个地去查找剩下的文件或文件夹,直到全部找完为止。所以,在使用这组API的时候,通常得先调用FindFirstFile,它会返回一个用来查找文件的句柄,拿到这个句柄后,再用FindNextFile去把剩下的文件都找出来。不过这组API有个局限性,它们只能找到当前目录下的文件和文件夹。要是想把所有层级文件夹里的文件都找出来,那就得自己写个递归函数才能做到了。

        下面给你看看这两个函数具体怎么简单使用:

HANDLE hFind ;
WIN32_FIND_DATA fData;
hFind = FindFirstFile(L"D:\\*", &fData);
if (hFind == (HANDLE)-1) return ;
do {//将遍历到的信息(fData结构体变量的内容)显示到界面.
} while (FindNextFile(hFind, &fData));

2.7 内存信息和CPU占用率的获取

2.7.1 内存信息相关结构体

typedef struct _MEMORYSTATUS {DWORD dwLength; //该结构体大小DWORD dwMemoryLoad; //当前系统内存的占用率(百分比)SIZE_T dwTotalPhys; //总的物理内存大小SIZE_T dwAvailPhys; //可能的物理内存大小以字节为单位SIZE_T dwTotalPageFile; //交换文件总大小SIZE_T dwAvailPageFile; //交换文件中空闲部分大小SIZE_T dwTotalVirtual; //总的虚拟内存大小SIZE_T dwAvailVirtual; //可用虚拟内存大小
}MEMORYSTATUS, *LPMEMORYSTATUS;

2.7.2 相关函数

获取系统内存信息

void WINAPI GlobalMemoryStatus(_Out_      LPMEMORYSTATUS lpBuffer //MEMORYSTATUS结构体指针
);

2.7.3 使用例子

//1.创建结构体对象并调用获取内存信息的函数
MEMORYSTATUS memStatus;
GlobalMemoryStatus(&memStatus);
//当前内存占用率:memStatus.dwMemoryLoad
//已用物理内存大小:memStatus.dwTotalPhys - memStatus.dwAvailPhys

2.7.4 CPU占用率

        CPU占用率,说的就是在单位时间里,CPU处于被占用状态的总时长。要算出这个数值,我们得先知道在一段时间内CPU实际被使用的时间,然后通过下面这个公式来计算它的占用率:

        使用率 = 100.0 - (空闲时间÷使用时间)× 100.0;

        至于获取当前CPU被占用的时间,就得借助GetSystemTimes函数了。这个函数可以获取到三个时间数据:CPU空闲下来的时间、CPU在内核态下被使用的时间,还有CPU在用户态下被使用的时间。这里面,把CPU在内核态和用户态被使用的时间加起来,得到的就是CPU总的使用时间。下面的代码展示了具体计算CPU占用率的方法:

double FILETIME2Double(const _FILETIME& fileTime) {return double(fileTime.dwHighDateTime * 4.294967296e9) + double(fileTime.dwLowDateTime);
}
Int GetCpuUsage(){// 空闲时间   内核时间    用户时间_FILETIME idleTime, kernelTime, userTime;//获取时间GetSystemTimes(&idleTime, &kernelTime, &userTime);//等待1000毫秒HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);//等待1000毫秒,使用内核对象等待会更精确WaitForSingleObject(hEvent, 1000);//获取新的时间_FILETIME newIdleTime, newKernelTime, newUserTime;GetSystemTimes(&newIdleTime, &newKernelTime, &newUserTime);//将各个时间转换double d01dIdleTime = FILETIME2Double(idleTime);double dNewIdleTime = FILETIME2Double(newIdleTime);double d0IdKernelTime = FILETIME2Double(kernelTime);double dNewKernelTime = FILETIME2Double(newKernelTime);double d01dUserTime = FILETIME2Double(userTime);double dNewUserTime = FILETIME2Double(newUserTime);//计算出使用率return int(100.0 - (dNewIdleTime - d0ldIdleTime) / (dNewKernelTime - d01dKernelTime + dNewUserTime - d0ldUserTime) * 100.0);
}

三、其它功能要点

3.1 关机、重启,注销,休眠

        关机、重启、注销以及休眠,这些操作都需要较高的权限才能执行。要是一个进程的权限不够高,去执行这些操作就会失败。所以,要是一个程序打算进行这些操作,第一步得用管理员权限来运行,第二步还得通过专门的权限提升函数,让程序获得关机的特权。具体的操作步骤是这样的: 

        (1)把关机特权获取到 

        (2)进行关机相关的操作 

        下面这段代码就是用来开启关机特权的 

HANDLE hToken = NULL;
HANDLE hProcess = GetCurrentProcess(); //该函数能得到该进程的伪句柄
OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
TOKEN_PRIVILEGES tp = {0 };
LookupPrivilegeValue(0, SE_SHUTDOWN_NAME, &tp.Privileges[0].Luid));
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
//调用函数提升权限
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);

下面的代码可用于关机等操作:
1. 关机:ExitWindowsEx(EWX_POWEROFF|EWX_FORCE);
2. 重启:ExitWindowsEx(EWX_REBOOT|EWX_FORCE);
3. 注销:ExitWindowsEx(EWX_LOGOFF|EWX_FORCE);
4. 休眠:SetSuspendState(TRUE,FALSE,FALSE);
5. 睡眠:SetSuspendState(FALSE,FALSE,FALSE);
6. 锁屏:LockWorkStation();

3.2 老板键

        老板键其实就是能快速响应全局按键,这样不用激活窗口,按一组特定按键,窗口就能迅速“消失”。 

        用这种加强版的快捷键分两步。第一步,用RegisterHotKey函数向系统注册一个全局热键,注册的时候能对热键进行设置,像热键的ID、快捷键之类的。函数原型如下: 

BOOL WINAPI RegisterHotKey(_In_opt_   HWND hWnd,      //响应该热键的窗口句柄_In_       int  id,         //该热键的ID,ID值可以是自定义的一个数值_In_       UINT fsModifiers,//该热键的辅助按键例如Ctrl键盘)_In_       UINT vk          //该热键的键值,一个按键码
);

注册热键的例子如下:

//注册一个快捷键:Ctrl +Shift+H
::RegisterHotKey(hWnd, //当前窗口的句柄0x1234, //自定义热键ID值MOD_CONTROL|MOD_SHIFT, //同时按下ctrl,shift'h'); //ctrl+shift+h

        第二步呢,要在消息响应函数里对WM_HOTKEY消息做出反应。这个消息的WPARAM参数,其实就是我们注册热键时自己设定的那个热键ID 。接着在这个消息处理过程中,就能让窗口显示或者隐藏。 

        要是用SDK开发,直接在回调函数里写case WM_HOTKEY消息的处理代码就行。但如果是用MFC开发,因为没办法直接编写消息回调函数,就得在类向导里添加PreTranslateMessage虚函数来处理这个消息。下面就是相关代码: 

BOOL CXXXDlg::PreTranslateMessage(MSG* pMsg) {//判断消息是否是全局热键消息,并判断该热键的ID是否是我们注册过的if ((pMsg->message == WM_HOTKEY) && (pMsg->wParam == 0x1234)) {if (IsWindowVisible(m_hWnd) == TRUE)ShowWindow(SW_HIDE); //隐藏窗口elseShowWindow(SW_SHOW); //显示窗口}return CDialogEx::PreTranslateMessage(pMsg);
}

 四、完整示例demo

MFC任务管理器说明书

4.1 完成的基础功能

1 遍历进程
2 遍历线程
3 遍历模块
4 遍历堆
5 结束进程,挂起线程,恢复运行线程,结束线程
6 获取CPU,内存信息
7 遍历文件信息:文件名,创建时间,修改时间,文件大小
8 遍历窗口信息
9 扫描并清理VS工程垃圾

附加功能如下:
1 老板键及其他快捷键:如ctrl+7隐藏程序,再次恢复显示程序
2 进程列表按列排序,根据整数或者字符串排序显示
3 利用定时器自主设置进程表刷新速度
4 主菜单项:关机,重启,注销,休眠,睡眠,锁屏
5 刷新窗口信息和关闭窗口
6 增加线程更新cpu和内存信息
7 查看有些堆信息有点卡顿,增加线程查看堆信息

开发环境:Windows10+VS2015+CPP

4.2 界面说明

1 菜单选项有:
刷新速度
关机 ctrl+1
重启 ctrl+2
注销 ctrl+3
休眠 ctrl+4
睡眠 ctrl+5
锁屏 ctrl+6
隐藏 ctrl+7

2 CPU利用率 内存占用率

3 TAB选项
进程 文件 窗口 清理

进程:显示进程列表,根据进程ID可以查看对应的线程,模块和堆
文件:查看某文件夹或磁盘的文件
窗口:查看当前所有显示窗口
清理:清理VS工程的工具

4.3 操作说明

        可以鼠标左键单击菜单选项操作,在进程列表,线程模块堆和窗口列表可以使用鼠标右键菜单操作。其他子窗口看界面说明。

        注意事项:程序操作中,如结束进程,挂起线程,恢复运行线程,结束线程,查看模块,堆信息有些会失败,可能因为权限不够或者被限制。

进程

文件

窗口

清理

demo代码:https://download.csdn.net/download/linshantang/90530350

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算轴承|滚动轴承故障频率

一、轴承故障频率概述 在旋转机械故障诊断中&#xff0c;轴承故障频率&#xff08;BPFO、BPFI、BSF、FTF&#xff09;是重要的分析依据。通过计算这些特征频率&#xff0c;可以帮助工程师&#xff1a; 识别轴承故障类型&#xff08;内圈/外圈/滚动体故障&#xff09;制定振动…

【数据结构与算法】ArrayList 和 顺序表

文章目录 &#x1f332;List&#x1f332;1. 线性表&#x1f332;2. 顺序表&#x1f33f;2.1 MyArrayList2.1.1 类中重写所有接口方法1.新增元素2.在pos位置新增元素(指定位置)3.判定是否包含了某个特定元素 4.查找特定元素对应的位置 5.获取pos下标的元素 6.给pos位置的元素替…

OceanBase 推出单机版 ,为中小规模业务提供高性价比方案

近日&#xff0c;OceanBase正式推出了全新的单机版数据库。这款产品基于OceanBase自主研发的单机分布式一体化架构&#xff0c;具有精简的架构设计和出色的兼容性&#xff0c;能够为中小规模业务场景提供高性价比的数据库解决方案&#xff0c;充分满足客户在不同业务规模下的多…

如何在 Vue 3 中实现百度地图位置选择器组件

如何在 Vue 3 中实现百度地图位置选择器组件 前言 在开发前端应用时&#xff0c;地图选择器是一个非常常见的需求。尤其是在一些需要用户选择地址的场景&#xff0c;如电商平台、旅游网站、酒店预定等&#xff0c;百度地图组件能提供准确的地理位置服务。在本文中&#xff0c…

Python中如何用正则表达式精准匹配IP地址?

在网络编程和数据处理时&#xff0c;我们经常需要从文本中提取或验证IP地址。Python的正则表达式(re模块)是完成这个任务的利器。但你知道怎么写才能准确匹配各种合法的IP地址吗&#xff1f;今天我们就来详细探讨这个问题。 为什么需要IP正则表达式&#xff1f; 假设你正在分…

spring--声明式事务

声明式事务 1、回顾事务 要么都成功&#xff0c;要么都失败&#xff01; 事务在项目开发中&#xff0c;十分重要&#xff0c;涉及数据的一致性问题 确保完整性和一致性 事务ACID&#xff1a; 原子性&#xff1a;事务是原子性操作&#xff0c;由一系列动作组成&#xff0c;…

Kotlin 学习-集合

/*** kotlin 集合* List:是一个有序列表&#xff0c;可通过索引&#xff08;下标&#xff09;访问元素。元素可以在list中出现多次、元素可重复* Set:是元素唯一的集合。一般来说 set中的元素顺序并不重要、无序集合* Map:&#xff08;字典&#xff09;是一组键值对。键是唯一的…

WPF 五子棋项目文档

WPF 五子棋项目文档 1. 项目概述 本项目是一个使用 Windows Presentation Foundation (WPF) 技术栈和 C# 语言实现的桌面版五子棋&#xff08;Gomoku&#xff09;游戏。它遵循 MVVM&#xff08;Model-View-ViewModel&#xff09;设计模式&#xff0c;旨在提供一个结构清晰、可…

计算机操作系统——死锁(详细解释和处理死锁)

系列文章目录 计算机操作系统-计算机系统中的死锁 文章目录 系列文章目录前言一、资源问题&#xff1a; 计算机系统当中的死锁&#xff1a; 二、死锁的定义、必要条件和处理方法&#xff1a; 1.死锁的定义&#xff1a;2.产生死锁的必要条件&#xff1a;3.处理死锁的方法&#…

Springboot项目正常启动,访问资源却出现404错误如何解决?

我在自己的springboot项目中的启动类上同时使用了SprinBootApplication和ComponentScan注解, 虽然项目能够正常启动,但是访问资源后,返回404错误,随后在启动类中输出bean,发现controller创建失败: 而后我将ComponentScan去掉后资源就能访问到了. 原因 SprinBootApplication本身…

第十五届蓝桥杯C/C++B组省赛真题讲解(分享去年比赛的一些真实感受)

试题A——握手问题 一、解题思路 直接用高中学的排列组合思路 二、代码示例 #include<bits/stdc.h> using namespace std; int fun(int n) {int sum0;for(int i0;i<n;i){for(int ji1;j<n;j)sum; } return sum; } int main() {cout<<fun(50)-fun(7); }三、…

动态规划(6)——01背包问题

欢迎来到博主的专栏&#xff1a;算法解析 博主ID&#xff1a;代码小号 文章目录 牛客网——【模板】01背包题目解析题目1算法原理题目1题解代码。问题2算法原理问题2题解代码01背包问题的滚动数组优化 牛客网——【模板】01背包 题目解析 关于I/O相关的东西博主就不多赘述了&a…

TQTT_KU5P开发板教程---实现流水灯

文档实现功能介绍 本文档是学习本开发板的基础&#xff0c;通过设置计数器使led0到led7依次闪烁&#xff0c;让用户初步认识vivado基本的开发流程以及熟悉项目的创建。本开发板的所有教程所使用的软件都是vivado2024.1版本的。可以根据网上的教程下载与安装。 硬件资源 此次教程…

Spring 中的 @Cacheable 缓存注解

1 什么是缓存 第一个问题&#xff0c;首先要搞明白什么是缓存&#xff0c;缓存的意义是什么。 对于普通业务&#xff0c;如果要查询一个数据&#xff0c;一般直接select数据库进行查找。但是在高流量的情况下&#xff0c;直接查找数据库就会成为性能的瓶颈。因为数据库查找的…

SEER: Self-Aligned Evidence Extraction for Retrieval-AugmentedGeneration

一、动机 如何从检索到的段落中提取证据&#xff0c;以降低计算成本并提升最终的RAG性能&#xff0c;然而这一问题仍然具有挑战性。 现有方法 严重依赖于基于启发式的增强&#xff0c;面临以下几个问题&#xff1a; &#xff08;1&#xff09;由于手工制作的上下文过滤&…

毫米波测试套装速递!高效赋能5G/6G、新材料及智能超表面(RIS)研发

德思特&#xff08;Tesight&#xff09;作为全球领先的测试测量解决方案提供商&#xff0c;始终致力于为前沿技术研发提供高精度、高效率的测试工具。 针对毫米波技术在高频通信、智能超表面&#xff08;RIS&#xff09;、新材料等领域的快速应用需求&#xff0c;我们推出毫米…

三维激光测量助力企业检测效率提升3倍

智能制造与数字化浪潮席卷下&#xff0c;三维扫描技术已成为工业检测领域不可或缺的工具。面对传统检测手段的精度瓶颈与效率局限&#xff0c;三维扫描仪&#xff0c;以毫米级精度、非接触式测量与超高速扫描三大核心优势&#xff0c;为汽车制造、航空航天、消费电子等行业的品…

SQL:Normalization(范式化)

目录 Normalization&#xff08;范式化&#xff09; 为什么需要 Normalization&#xff1f; &#x1f9e9; 表格分析&#xff1a; 第一范式&#xff08;1NF&#xff09; 什么是第一范式&#xff08;First Normal Form&#xff09;&#xff1f; 第二范式&#xff08;2NF&am…

#MES系统运维问题分析思路

一套适用于90% MES运维现场问题的排查分析思维模型&#xff0c;叫做&#xff1a; &#x1f50d; MES系统问题分析七步法&#xff08;现场实战适用&#xff09; ✅ 第一步&#xff1a;明确问题现象&#xff08;What&#xff09; 问题要说清楚&#xff0c;“不能操作”这种模糊描…

达梦数据库-学习-18-ODBC数据源配置(Linux)

一、环境信息 名称值CPU12th Gen Intel(R) Core(TM) i7-12700H操作系统CentOS Linux release 7.9.2009 (Core)内存4G逻辑核数2DM版本1 DM Database Server 64 V8 2 DB Version: 0x7000c 3 03134284194-20240703-234060-20108 4 Msg Versi…