Windows下QT使用WinRing0控制主板的蜂鸣器
- 一、前言
- 二、使用WinRing0的几种方式
- 1.静态编译方式
- 2.动态编译方式
- 3.使用QLibrary类动态调用方式
- 三、WinRing0驱动主板的蜂鸣器代码
- 1.定义一个beep类:
- 2.定义需要用到的函数指针:
- 3.编写函数构造函数:
- 4.编写析构函数
- 5.编写beep函数(有一部分是ChatGPT写的,我具体也不是很清楚):
- 6.主函数调用,会听见主板上的哔的一声:
- 四、常见问题
- 1.使用过程中初始化失败
- 五、文件下载
一、前言
最近在windows10下用QT做项目,需要在用户进行某项操作时进行声音提示,扬声器的话一来增加成本,二来也受系统提示音困扰。搜索网上有两种解决办法,一个是WinIO的方式,这种处理起来比较麻烦,量产时费人费力还容易造成系统不稳定。另外一种就是WinRing0了,也就是这篇博客讲的。
二、使用WinRing0的几种方式
1.静态编译方式
使用这种方式对使用者的素养要求比较高,由于本人属于嵌入式开发范畴,不会将源代码编译成lib文件。这种方式需要使用者用与QT版本相同的编译器去编译,否则QT项目会在编译阶段报错。
2.动态编译方式
使用动态编译也需要自己编译WinRing0的源码,生成DLL文件及相关库文件。
3.使用QLibrary类动态调用方式
我目前使用的就是这种方法,手头上有个DLL文件以及sys驱动,因为水平有限,不会自己编译库。使用此种方式的原理就是QLibrary类的resolve方法会根据你传的函数名返回DLL中对应的函数指针。你再去执行此函数即可。
三、WinRing0驱动主板的蜂鸣器代码
1.定义一个beep类:
#include <QObject>
#include <QLibrary>
class WioBeep : public QObject{
Q_OBJECT;
public:
WioBeep();
~WioBeep();
void beep(uint16_t ms);
private:
QLibrary mylib; //声明所用到的dll文件
};
2.定义需要用到的函数指针:
#include <windows.h>
//初始化函数
typedef bool(__stdcall *InitializeWinIoType)();
typedef void(__stdcall *DeinitializeWinIoType)();
typedef DWORD(__stdcall *GetDllStatusType)();
//读取端口的数值
typedef BYTE(__stdcall *GetPortValType)(unsigned short PortAddr);
//写入端口的数值
typedef void(__stdcall *SetPortValType)(unsigned short PortAddr, unsigned long PortVal);
3.编写函数构造函数:
WioBeep::WioBeep(){
mylib.setFileName("WinRing0x64.dll");
if (mylib.load())
qDebug( "WinRing0x64.dll load succuse!\n");
else
qDebug( "WinRing0x64.dll load failed!\n");
InitializeWinIoType pFunc = (InitializeWinIoType)mylib.resolve("InitializeOls");
GetDllStatusType GetDllStatus = (GetDllStatusType)mylib.resolve("GetDllStatus");
if (pFunc != NULL)
{
bool Result = pFunc();
if (!Result)
{
DWORD str = GetDllStatus();//获取失败原因代码
qDebug( "Error In InitializeWinIo %d!\n",str);
}
}
}
4.编写析构函数
WioBeep::~WioBeep(){
DeinitializeWinIoType pFunc = (DeinitializeWinIoType)mylib.resolve("DeinitializeOls");
if (pFunc != NULL)
{
pFunc();
}
}
5.编写beep函数(有一部分是ChatGPT写的,我具体也不是很清楚):
void WioBeep::beep(uint16_t ms){
if (!mylib.isLoaded()){
qDebug( "WinRing0x64.dll not load!\n");
return;
}
GetPortValType ReadIoPortByte = (GetPortValType)mylib.resolve("ReadIoPortByte");
SetPortValType WriteIoPortByte = (SetPortValType)mylib.resolve("WriteIoPortByte");
if (ReadIoPortByte == NULL || WriteIoPortByte == NULL)
{
qDebug( "WinRing0x64.dll function load faild!\n");
return;
}
// 设置蜂鸣器频率
unsigned int frequency = 2000; // 设置频率为400Hz
unsigned short count = static_cast<unsigned short>(1193180 / frequency); // 计算计数器的值
unsigned char lowByte = static_cast<unsigned char>(count & 0xFF); // 取计数器的低字节
unsigned char highByte = static_cast<unsigned char>((count >> 8) & 0xFF); // 取计数器的高字节
// 控制蜂鸣器
WriteIoPortByte(0x43, 0xB6); // 向IO端口写入字节,设置蜂鸣器工作方式
WriteIoPortByte(0x42, lowByte); // 向IO端口写入字节,设置蜂鸣器计数器的低字节
WriteIoPortByte(0x42, highByte); // 向IO端口写入字节,设置蜂鸣器计数器的高字节
//开启蜂鸣器
DWORD data = ReadIoPortByte(0x61);
data |= 0x03;
WriteIoPortByte(0x61, data);
Sleep(ms);
//关闭蜂鸣器
data = ReadIoPortByte(0x61);
data &= 0xFC;
WriteIoPortByte(0x61, data);
}
6.主函数调用,会听见主板上的哔的一声:
WioBeep *beep = new WioBeep();
beep->beep(200);
四、常见问题
1.使用过程中初始化失败
确保WinRing0x64.dll和WinRing0x64.sys和你的.exe文件在同一目录下。
程序需要以管理员权限运行,QT的pro文件需要添加如下内容:
RC_FILE=main.rc
main.rc文件放在与.pro文件同目录下,文件内容如下:
// 图标
IDI_ICON1 ICON "/icon/main.ico"
1 24 uac.manifest
uac.manifest文件放在与main.rc文件同目录下,文件内容如下:
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level='requireAdministrator' uiAccess='false' />
</requestedPrivileges>
</security>
</trustInfo>
</assembly>
如果你使用的是32位的编译器,请将相关代码及文件替换成WinRing0.dll和WinRing0.sys。
五、文件下载
工程源代码因为是公司项目,无法发出来,之后会发相关的DLL及sys文件。