利用 NvAPI 设置数字振动数值

开始是一位朋友有这个需求,他给了我一个英伟达官网的开发包,名字是:R410-developer.zip(诸位可以自己到英伟达官网下载),里面提供了一些示例,包含自定义分辨率、显示器颜色设置等,但是显示器色彩设置的例子一致没有跑通,而且我也没有找到哪个参数是可以设置数字振动值的,所以憋屈了很多天。但直到看到了一个 AHK 版本实现的设置工具通过代码发现,其实实现方法是通过 nvapi.dll 动态库导出的一个查询函数地址的方法,将指定接口导出来执行具体业务。在加上在 github 上搜索的各类示例,最终实现了这个功能,下面详细介绍实现步骤。

1)需要的方法和结构

设置数字振动数值需要先通过 nvapi.dll 导出的 NvAPI_QueryInterface_t 方法获取 NvAPI_Initialize_t 方法来初始化 NvAPI。然后依次获取显示器句柄、获取当前显示器数字振动值、设置数字振动值的函数地址,他们的声明分别对应如下:

// 查询在 nvapi.dll 中函数的地址方法函数声明
typedef int*(*NvAPI_QueryInterface_t)(unsigned int offset);
// 初始化 NvAPI 的方法
typedef int(*NvAPI_Initialize_t)();
// 根据 ID 枚举显示器句柄的方法
typedef int(*NvAPI_EnumNvidiaDisplayHandle_t)(int thisEnum, int* pNvDispHandle);
// 获取数字振动当前值
typedef int(*NvAPI_GetDVCInfoEx_t)(int hNvDisplay, int outputId, NV_DISPLAY_DVC_INFO_EX* pDVCInfo);
// 设置数字振动值
typedef int(*NvAPI_SetDVCLevelEx_t)(int hNvDisplay, int outputId, NV_DISPLAY_DVC_INFO_EX* pDVCInfo);

其中设置和获取数字振动值需要一个结构体 NV_DISPLAY_DVC_INFO_EX,其声明如下:

typedef struct
{
    unsigned int version;   // 结构体版本
    int currentLevel;       // 当前级别
    int minLevel;           // 最低级别
    int maxLevel;           // 最高级别
    int defaultLevel;       // 默认级别
} NV_DISPLAY_DVC_INFO_EX, *PNV_DISPLAY_DVC_INFO_EX;

2)获取各个接口地址

我们首先 Load nvapi.dll 然后得到 nvapi_QueryInterface 方法的地址,然后通过 nvapi_QueryInterface 方法查询另外一些接口的地址。如下所示:

bool NvController::Initialize()
{
    hModule = LoadLibraryW(TEXT("nvapi.dll"));
    if (hModule == nullptr)
    {
        std::cerr << "Failed to load nvapi.dll." << std::endl;
        return false;
    }

    NvAPI_QueryInterface = (NvAPI_QueryInterface_t)GetProcAddress(hModule, "nvapi_QueryInterface");
    NvAPI_Initialize = (NvAPI_Initialize_t)(*NvAPI_QueryInterface)(_NvAPI_Initialize);
    NvAPI_EnumNvidiaDisplayHandle = (NvAPI_EnumNvidiaDisplayHandle_t)(*NvAPI_QueryInterface)(_NvAPI_EnumNvidiaDisplayHandle);
    NvAPI_GetDVCInfoEx = (NvAPI_GetDVCInfoEx_t)(*NvAPI_QueryInterface)(_NvAPI_GetDVCInfoEx);
    NvAPI_SetDVCLevelEx = (NvAPI_SetDVCLevelEx_t)(*NvAPI_QueryInterface)(_NvAPI_SetDVCLevelEx);

    _NvAPI_Status status = (_NvAPI_Status)(*NvAPI_Initialize)();
    if (status != NVAPI_OK)
    {
        std::cerr << "NvAPI initialization failed." << std::endl;
        return false;
    }

    return true;
}

其中以下划线开头的枚举名字是每个函数在 dll 中的对应地址,这些是写死的,如下所示:

enum NvAPIs
{
    _NvAPI_Initialize = 0x150E828,
    _NvAPI_EnumNvidiaDisplayHandle = 0x9ABDD40D,
    _NvAPI_GetAssociatedNvidiaDisplayName = 0x22A78B05,
    _NvAPI_GetDVCInfoEx = 0x0E45002D,
    _NvAPI_SetDVCLevelEx = 0x4A82C2B1
};

你可能会问,你怎么知道这些函数地址的?其实我也是搜索到的,也看了以前 NvAPI 老版本提供的代码,可以搜索到相关痕迹。点击查看此文件里面有完整的所有函数地址。

3)获取和设置数字振动

得到了各个函数的地址,我们就可以设置数字振动值了,代码如下:

bool NvController::SetDVCLevelEx(int nDisp, int level)
{
    int NvDispHandle;
    if (EnumNvidiaDisplayHandle(nDisp, &NvDispHandle) != 0)
    {
        NV_DISPLAY_DVC_INFO_EX oldInfo = GetDvcInfoEx(nDisp);

        NV_DISPLAY_DVC_INFO_EX info;
        info.version = oldInfo.version;
        info.currentLevel = level;
        info.minLevel = oldInfo.minLevel;
        info.maxLevel = oldInfo.maxLevel;
        info.defaultLevel = oldInfo.defaultLevel;

        _NvAPI_Status status = (_NvAPI_Status)(*NvAPI_SetDVCLevelEx)(NvDispHandle, 0, &info);
        if (status != NVAPI_OK)
        {
            return false;
        }

        return true;
    }

    return false;
}

我们首先获得用户传入的显示器编号所对应的句柄,然后根据这个句柄获取当前数字振动的数值,然后修改其 currentLevel 成员数值来设置数字振动效果。这样处理后就可以使用了。代码参考地址:https://github.com/nmgwddj/nvapi-example

Post Views: 6

相关

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏進无尽的文章

简述OC语言

对于一门语言的学习是需要时间领悟的,而对于一些原理性的问题,我们需要清楚其核心思想,知其然而知其所以然,这样才能有利于自己的后续发展。本文只是简述,没有面面具到...

1882
来自专栏Java呓语

单元测试以及JUnit框架解析

我们都有个习惯,常常不乐意去写个简单的单元测试程序来验证自己的代码。对自己的程序一直非常有自信,或存在侥幸心理每次运行通过后就直接扔给测试组测试了。然而每次测试...

1292
来自专栏一个会写诗的程序员的博客

CodeMirror 实现 JavaScript、 MySql 关键字的变色和自动实时提示 autocomplete

https://codemirror.net/doc/manual.html#config

1.1K2
来自专栏大内老A

这算是ASP.NET MVC的一个大BUG吗?

这是昨天一个同事遇到的问题,我觉得这是一个蛮大的问题,而且不像是ASP.NET MVC的设计者有意为之,换言之,这可能是ASP.NET MVC的一个Bug(不过...

2128
来自专栏软件工程师成长笔记

[干货]springboot集成rocketmq

1.6K2
来自专栏青玉伏案

iOS逆向工程之Hopper中的ARM指令

虽然前段时间ARM被日本软银收购了,但是科技是无国界的,所以呢ARM相关知识该学的学。现在看ARM指令集还是倍感亲切的,毕竟大学里开了ARM这门课,并且做了不少...

3077
来自专栏CRPER折腾记

2018春招前端面试: 闯关记(精排精校)

box-sizing有两个值:content-box(W3C标准盒模型),border-box(怪异模型),

1892
来自专栏SDNLAB

Open vSwitch系列之openflow版本兼容

众所周知Open vSwitch支持的openflow版本从1.0到1.5版本(当前Open vSwitch版本是2.3.2)通过阅读代码,处理openflow...

55613
来自专栏haifeiWu与他朋友们的专栏

造个轮子之基于 Netty 实现自己的 RPC 框架

服务端开发都会或多或少的涉及到 RPC 的使用,当然如果止步于会用,对自己的成长很是不利,所以楼主今天本着知其然,且知其所以然的精神来探讨一下 RPC 这个东西...

1513
来自专栏武培轩的专栏

搜狗面经汇总

volatile是变量修饰符,其修饰的变量具有可见性,Java的做法是将该变量的操作放在寄存器或者CPU缓存上进行,之后才会同步到主存,使用volatile修饰...

2235

扫码关注云+社区

领取腾讯云代金券