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