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文件。