赞
踩
最近在windows10下用QT做项目,需要在用户进行某项操作时进行声音提示,扬声器的话一来增加成本,二来也受系统提示音困扰。搜索网上有两种解决办法,一个是WinIO的方式,这种处理起来比较麻烦,量产时费人费力还容易造成系统不稳定。另外一种就是WinRing0了,也就是这篇博客讲的。
使用这种方式对使用者的素养要求比较高,由于本人属于嵌入式开发范畴,不会将源代码编译成lib文件。这种方式需要使用者用与QT版本相同的编译器去编译,否则QT项目会在编译阶段报错。
使用动态编译也需要自己编译WinRing0的源码,生成DLL文件及相关库文件。
我目前使用的就是这种方法,手头上有个DLL文件以及sys驱动,因为水平有限,不会自己编译库。使用此种方式的原理就是QLibrary类的resolve方法会根据你传的函数名返回DLL中对应的函数指针。你再去执行此函数即可。
#include <QObject>
#include <QLibrary>
class WioBeep : public QObject{
Q_OBJECT;
public:
WioBeep();
~WioBeep();
void beep(uint16_t ms);
private:
QLibrary mylib; //声明所用到的dll文件
};
#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);
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); } } }
WioBeep::~WioBeep(){
DeinitializeWinIoType pFunc = (DeinitializeWinIoType)mylib.resolve("DeinitializeOls");
if (pFunc != NULL)
{
pFunc();
}
}
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); }
WioBeep *beep = new WioBeep();
beep->beep(200);
确保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文件。
百度云网盘链接
提取码:euf3
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。