DPI 设置和获取

news2025/1/19 23:07:30

DPI设置与获取之前请保证程序已经打开DPI感知或者在清单文件嵌入DPI感知,否则API可能获取的值不准确
在这里插入图片描述

方法一:GetScaleFactorForMonitor

通过枚举显示器获取不同设备的DPI,获取的是实际DPI

static BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor,HDC hdcMonitor,LPRECT lprcMonitor,LPARAM dwData)
{
    std::vector<int> *pvec_ret = (std::vector<int> *)(dwData);
    DEVICE_SCALE_FACTOR val = DEVICE_SCALE_FACTOR_INVALID;
    GetScaleFactorForMonitor(hMonitor, &val);
    if (val != DEVICE_SCALE_FACTOR_INVALID)
        pvec_ret->push_back(val);
    return TRUE;
}

std::vector<int> DpiHelper::GetDpiFromMonitors()
{
    std::vector<int> vec_ret;
    do
    {
        HDC hdc = GetDC(NULL);
        if (hdc == nullptr)
        {
            OutputDebugStringA("DpiHelper::GetDpiFromMonitors GetDC failed");
            break;
        }

        EnumDisplayMonitors(hdc, NULL, MonitorEnumProc, (LPARAM)&vec_ret);
        ReleaseDC(NULL,hdc);
    } while (false);
   
    if (vec_ret.empty())
        vec_ret.push_back(100);

    return vec_ret;
}

方法二:DisplayConfigGetDeviceInfo

实测过程中发现有时候该值返回的与实际值不一致。例如当系统锁定缩放200%,通过显示设置查看的值200%,但当打开自定义缩放的时候发现实际值是100%。此时该接口返回的是200%

具体代码如下:
DpiHelper.h

#pragma once
#include <Windows.h>
#include <vector>


/*
* OS reports DPI scaling values in relative terms, and not absolute terms.
* eg. if current DPI value is 250%, and recommended value is 200%, then
* OS will give us integer 2 for DPI scaling value (starting from recommended
* DPI scaling move 2 steps to the right in this list).
* values observed (and extrapolated) from system settings app (immersive control panel).
*/
static const UINT32 DpiVals[] = { 100,125,150,175,200,225,250,300,350, 400, 450, 500 };

class DpiHelper
{
public:
    template<class T, size_t sz>
    static size_t CountOf(const T (&arr)[sz])
    {
        UNREFERENCED_PARAMETER(arr);
        return sz;
    }

    /*
    * @brief : Use QueryDisplayConfig() to get paths, and modes.
    * @param[out] pathsV : reference to a vector which will contain paths
    * @param[out] modesV : reference to a vector which will contain modes
    * @param[in] flags : determines the kind of paths to retrieve (only active paths by default)
    * return : false in case of failure, else true
    */
    static bool GetPathsAndModes(std::vector<DISPLAYCONFIG_PATH_INFO>& pathsV, std::vector<DISPLAYCONFIG_MODE_INFO>& modesV, int flags = QDC_ONLY_ACTIVE_PATHS);

    //out own enum, similar to DISPLAYCONFIG_DEVICE_INFO_TYPE enum in wingdi.h
    enum class DISPLAYCONFIG_DEVICE_INFO_TYPE_CUSTOM : int
    {
        DISPLAYCONFIG_DEVICE_INFO_GET_DPI_SCALE = -3, //returns min, max, suggested, and currently applied DPI scaling values.
        DISPLAYCONFIG_DEVICE_INFO_SET_DPI_SCALE = -4, //set current dpi scaling value for a display
    };

    /*
    * struct DISPLAYCONFIG_SOURCE_DPI_SCALE_GET
    * @brief used to fetch min, max, suggested, and currently applied DPI scaling values.
    * All values are relative to the recommended DPI scaling value
    * Note that DPI scaling is a property of the source, and not of target.
    */
    struct DISPLAYCONFIG_SOURCE_DPI_SCALE_GET
    {
        DISPLAYCONFIG_DEVICE_INFO_HEADER            header;
        /*
        * @brief min value of DPI scaling is always 100, minScaleRel gives no. of steps down from recommended scaling
        * eg. if minScaleRel is -3 => 100 is 3 steps down from recommended scaling => recommended scaling is 175%
        */
        std::int32_t minScaleRel;

        /*
        * @brief currently applied DPI scaling value wrt the recommended value. eg. if recommended value is 175%,
        * => if curScaleRel == 0 the current scaling is 175%, if curScaleRel == -1, then current scale is 150%
        */
        std::int32_t curScaleRel;

        /*
        * @brief maximum supported DPI scaling wrt recommended value
        */
        std::int32_t maxScaleRel;
    };

    /*
    * struct DISPLAYCONFIG_SOURCE_DPI_SCALE_SET
    * @brief set DPI scaling value of a source
    * Note that DPI scaling is a property of the source, and not of target.
    */
    struct DISPLAYCONFIG_SOURCE_DPI_SCALE_SET
    {
        DISPLAYCONFIG_DEVICE_INFO_HEADER            header;
        /*
        * @brief The value we want to set. The value should be relative to the recommended DPI scaling value of source.
        * eg. if scaleRel == 1, and recommended value is 175% => we are trying to set 200% scaling for the source
        */
        int32_t scaleRel;
    };

    /*
    * struct DPIScalingInfo
    * @brief DPI info about a source
    * mininum :     minumum DPI scaling in terms of percentage supported by source. Will always be 100%.
    * maximum :     maximum DPI scaling in terms of percentage supported by source. eg. 100%, 150%, etc.
    * current :     currently applied DPI scaling value
    * recommended : DPI scaling value reommended by OS. OS takes resolution, physical size, and expected viewing distance
    *               into account while calculating this, however exact formula is not known, hence must be retrieved from OS
    *               For a system in which user has not explicitly changed DPI, current should eqaul recommended.
    * bInitDone :   If true, it means that the members of the struct contain values, as fetched from OS, and not the default
    *               ones given while object creation.
    */
    struct DPIScalingInfo
    {
        UINT32 mininum = 100;
        UINT32 maximum = 100;
        UINT32 current = 100;
        UINT32 recommended = 100;
        bool bInitDone = false;
    };

    DpiHelper();
    ~DpiHelper();
    //当锁定的时候获取的值可能不对,例如dpi锁定200%,该函数获取的是200,但是实际是100
    static DpiHelper::DPIScalingInfo GetDPIScalingInfo(LUID adapterID, UINT32 sourceID);
    static bool SetDPIScaling(LUID adapterID, UINT32 sourceID, UINT32 dpiPercentToSet);

    //当锁定的时候获取的值可能不对,例如dpi锁定200% 但是实际是100,则获取实际值100
    static std::vector<int> GetDpiFromMonitors();
};


DpiHelper.cpp

#include "DpiHelper.h"
#include <memory>
#include <cassert>
#include<Windows.h>
#include<shtypes.h>
#include<shellscalingapi.h>

bool DpiHelper::GetPathsAndModes(std::vector<DISPLAYCONFIG_PATH_INFO>& pathsV, std::vector<DISPLAYCONFIG_MODE_INFO>& modesV, int flags)
{
    UINT32 numPaths = 0, numModes = 0;
    auto status = GetDisplayConfigBufferSizes(flags, &numPaths, &numModes);
    if (ERROR_SUCCESS != status)
    {
        return false;
    }

    std::unique_ptr<DISPLAYCONFIG_PATH_INFO[]> paths(new DISPLAYCONFIG_PATH_INFO[numPaths]);
    std::unique_ptr<DISPLAYCONFIG_MODE_INFO[]> modes(new DISPLAYCONFIG_MODE_INFO[numModes]);
    status = QueryDisplayConfig(flags, &numPaths, paths.get(), &numModes, modes.get(), nullptr);
    if (ERROR_SUCCESS != status)
    {
        return false;
    }

    for (unsigned int i = 0; i < numPaths; i++)
    {
        pathsV.push_back(paths[i]);
    }

    for (unsigned int i = 0; i < numModes; i++)
    {
        modesV.push_back(modes[i]);
    }

    return true;
}


DpiHelper::DpiHelper()
{
}


DpiHelper::~DpiHelper()
{
}


DpiHelper::DPIScalingInfo DpiHelper::GetDPIScalingInfo(LUID adapterID, UINT32 sourceID)
{
    DPIScalingInfo dpiInfo = {};

    DpiHelper::DISPLAYCONFIG_SOURCE_DPI_SCALE_GET requestPacket = {};
    requestPacket.header.type = (DISPLAYCONFIG_DEVICE_INFO_TYPE)DpiHelper::DISPLAYCONFIG_DEVICE_INFO_TYPE_CUSTOM::DISPLAYCONFIG_DEVICE_INFO_GET_DPI_SCALE;
    requestPacket.header.size = sizeof(requestPacket);
    assert(0x20 == sizeof(requestPacket));//if this fails => OS has changed somthing, and our reverse enginnering knowledge about the API is outdated
    requestPacket.header.adapterId = adapterID;
    requestPacket.header.id = sourceID;

    auto res = ::DisplayConfigGetDeviceInfo(&requestPacket.header);
    if (ERROR_SUCCESS == res)
    {//success
        if (requestPacket.curScaleRel < requestPacket.minScaleRel)
        {
            requestPacket.curScaleRel = requestPacket.minScaleRel;
        }
        else if (requestPacket.curScaleRel > requestPacket.maxScaleRel)
        {
            requestPacket.curScaleRel = requestPacket.maxScaleRel;
        }

        std::int32_t minAbs = abs((int)requestPacket.minScaleRel);
        if (DpiHelper::CountOf(DpiVals) >= (size_t)(minAbs + requestPacket.maxScaleRel + 1))
        {//all ok
            dpiInfo.current = DpiVals[minAbs + requestPacket.curScaleRel];
            dpiInfo.recommended = DpiVals[minAbs];
            dpiInfo.maximum = DpiVals[minAbs + requestPacket.maxScaleRel];
            dpiInfo.bInitDone = true;
        }
        else
        {
            //Error! Probably DpiVals array is outdated
            return dpiInfo;
        }
    }
    else
    {
        //DisplayConfigGetDeviceInfo() failed
        return dpiInfo;
    }

    return dpiInfo;
}


bool DpiHelper::SetDPIScaling(LUID adapterID, UINT32 sourceID, UINT32 dpiPercentToSet)
{
    DPIScalingInfo dPIScalingInfo = GetDPIScalingInfo(adapterID, sourceID);

    if (dpiPercentToSet == dPIScalingInfo.current)
    {
        return true;
    }

    if (dpiPercentToSet < dPIScalingInfo.mininum)
    {
        dpiPercentToSet = dPIScalingInfo.mininum;
    }
    else if (dpiPercentToSet > dPIScalingInfo.maximum)
    {
        dpiPercentToSet = dPIScalingInfo.maximum;
    }

    int idx1 = -1, idx2 = -1;

    int i = 0;
    for (const auto& val : DpiVals)
    {
        if (val == dpiPercentToSet)
        {
            idx1 = i;
        }

        if (val == dPIScalingInfo.recommended)
        {
            idx2 = i;
        }
        i++;
    }

    if ((idx1 == -1) || (idx2 == -1))
    {
        //Error cannot find dpi value
        return false;
    }

    int dpiRelativeVal = idx1 - idx2;

    DpiHelper::DISPLAYCONFIG_SOURCE_DPI_SCALE_SET setPacket = {};
    setPacket.header.adapterId = adapterID;
    setPacket.header.id = sourceID;
    setPacket.header.size = sizeof(setPacket);
    assert(0x18 == sizeof(setPacket));//if this fails => OS has changed somthing, and our reverse enginnering knowledge about the API is outdated
    setPacket.header.type = (DISPLAYCONFIG_DEVICE_INFO_TYPE)DpiHelper::DISPLAYCONFIG_DEVICE_INFO_TYPE_CUSTOM::DISPLAYCONFIG_DEVICE_INFO_SET_DPI_SCALE;
    setPacket.scaleRel = (UINT32)dpiRelativeVal;

    auto res = ::DisplayConfigSetDeviceInfo(&setPacket.header);
    if (ERROR_SUCCESS == res)
    {
        return true;
    }
    else
    {
        return false;
    }
    return true;
}

static BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor,HDC hdcMonitor,LPRECT lprcMonitor,LPARAM dwData)
{
    std::vector<int> *pvec_ret = (std::vector<int> *)(dwData);
    DEVICE_SCALE_FACTOR val = DEVICE_SCALE_FACTOR_INVALID;
    GetScaleFactorForMonitor(hMonitor, &val);
    if (val != DEVICE_SCALE_FACTOR_INVALID)
        pvec_ret->push_back(val);
    return TRUE;
}

std::vector<int> DpiHelper::GetDpiFromMonitors()
{
    std::vector<int> vec_ret;
    do
    {
        HDC hdc = GetDC(NULL);
        if (hdc == nullptr)
        {
            OutputDebugStringA("DpiHelper::GetDpiFromMonitors GetDC failed");
            break;
        }

        EnumDisplayMonitors(hdc, NULL, MonitorEnumProc, (LPARAM)&vec_ret);
        ReleaseDC(NULL,hdc);
    } while (false);
   
    if (vec_ret.empty())
        vec_ret.push_back(100);

    return vec_ret;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/943608.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

11.2.1-通货膨胀CPI

文章目录 1. 什么是CPI2. 在哪里获取CPI数据3. CPI的同比、环比到底是什么意思&#xff1f;4. 计算购买力侵蚀5. 复利计算 微不足道的小事也会引发惊人的结果&#xff0c; 每念及此&#xff0c; 我就认为世上无小事。——布鲁斯巴登&#xff08;Bruce Barton&#xff09; 核心内…

2023年9月DAMA-CDGA/CDGP数据治理认证考试,火热报名

据DAMA中国官方网站消息&#xff0c;2023年度第三期DAMA中国CDGA和CDGP认证考试定于2023年9月23日举行。 报名通道现已开启&#xff0c;相关事宜通知如下&#xff1a; 考试科目: 数据治理工程师(CertifiedDataGovernanceAssociate,CDGA) 数据治理专家(CertifiedDataGovernanc…

[NSSCTF 2nd] NSS两周年纪念赛。

都说开卷有益&#xff0c;其实作题也有益&#xff0c;每打一次总能学到点东西。 PWN NewBottleOldWine 这个没作出来&#xff0c;一时还不明白RISC-V64怎么弄本地环境&#xff0c;不过看了WP感觉很简单&#xff0c;取flag用不着环境。 IDA不给翻译了&#xff0c;一点点看汇…

资源分享| 4种聚类算法及可视化(Python)

在这篇文章中&#xff0c;基于20家公司的股票价格时间序列数据。根据股票价格之间的相关性&#xff0c;看一下对这些公司进行聚类的四种不同方式。 苹果&#xff08;AAPL&#xff09;&#xff0c;亚马逊&#xff08;AMZN&#xff09;&#xff0c;Facebook&#xff08;META&…

035 - datetime和timedstamp

该DATETIME类型用于包含日期和时间部分的值。MySQL检索并DATETIME以格式显示 值 。支持的范围是 到。 YYYY-MM-DD hh:mm:ss1000-01-01 00:00:009999-12-31 23:59:59 该TIMESTAMP数据类型被用于同时包含日期和时间部分的值。 UTCTIMESTAMP的范围是UTC。 1970-01-01 00:00:01203…

gitlab提交项目Log in with Access Token错误

目录 报错信息 问题描述 解决方案 报错信息 问题描述 在提交项目到gitlab时&#xff0c;需要添加账户信息 &#xff0c;但是报了这样一个错&#xff0c;原因应该就是路径问题&#xff0c;我在填写server地址的时候&#xff0c;就出现了路径问题&#xff0c;我把多余的几个/…

为啥这么多公司用 ZooKeeper?它到底解决了什么问题?

ZooKeeper 介绍 ZooKeeper 很流行&#xff0c;有个基本的疑问&#xff1a; ZooKeeper 是用来做什么的&#xff1f; 之前没有ZK&#xff0c;为什么会诞生 ZK&#xff1f; OK&#xff0c;解答一下上面的疑问&#xff1a;&#xff08;下面是凭直觉说的&#xff09; ZooKeeper …

前端项目部署,阿里云服务器部署前端项目,超详细

需求背景&#xff1a;作为一个前端&#xff0c;特别身处于中小公司时&#xff0c;对于部署也需要有所了解。本次就介绍前端基础的项目部署。本次使用的是阿里云服务器进行的部署 部署核心步骤 1.准备打包好的前端代码&#xff08;dist包&#xff09;或者是一个html文件 2.购买…

Day49|动态规划part10:188.买卖股票的最佳时机IV、121. 买卖股票的最佳时机、122.买卖股票的最佳时机II

188. 买卖股票的最佳时机IV leetcode链接&#xff1a;188 题「买卖股票的最佳时机 IVopen in new window」 视频链接&#xff1a;动态规划来决定最佳时机&#xff0c;至多可以买卖K次&#xff01;| LeetCode&#xff1a;188.买卖股票最佳时机4 给你一个整数数组 prices 和一…

【考研数学】概率论与数理统计 —— 第二章 | 一维随机变量及其分布(2,常见随机变量及其分布 | 随机变量函数的分布)

文章目录 引言三、常见的随机变量及其分布3.1 常见的离散型随机变量及其分布律&#xff08;一&#xff09;&#xff08;0-1&#xff09;分布&#xff08;二&#xff09;二项分布&#xff08;三&#xff09;泊松分布&#xff08;四&#xff09;几何分布&#xff08;五&#xff0…

利用R作圆环条形图

从理念上看&#xff0c;本质就是增加了圆环弧度的条形图。如上图2。 需要以下步骤&#xff1a; 数据处理&#xff0c;将EXCEL中的数据做成3*N的表格导入系统&#xff0c;代码如下&#xff1a;library(tidyverse) library(stringr)library(ggplot2)library(viridis) stuper &…

电脑连不上网?学会这5个方法,不用愁!

“怎么回事呢&#xff1f;我的电脑一直连不上网。尝试了好多个方法都还是不行。大家遇到这种情况的时候都是怎么处理的呀&#xff1f;” 在现代生活中&#xff0c;电脑连接到网络已经成为必不可少的一部分。如果电脑无法联网&#xff0c;可能会影响我们的工作和娱乐。那么&…

Scrum敏捷研发迭代式开发

Scrum是一个迭代式增量软件开发过程&#xff0c;是敏捷方法论中的重要框架之一。它通常用于敏捷软件开发&#xff0c;包括了一系列实践和预定义角色的过程骨架。Scrum中的主要角色包括Scrum主管&#xff08;Scrum Master&#xff09;、产品负责人&#xff08;Product Owner&…

【已解决】ZooKeeper配置中出现Error contacting service. It is probably not running

ZooKeeper配置中出现Error contacting service. It is probably not running 问题 安装zookeeper&#xff0c;启动报错了 Error contacting service. It is probably not running 思路 tail -100f logs/zookeeper-root-server-node1.itcast.cn.out 查看日志报错 zoo.cfg没…

LinuxShell变量

变量&#xff1a; 命名规则&#xff1a; 在Shell中&#xff0c;变量名可以由字母、数字或者下划线组成&#xff0c;并且只能以字母或者下划线开头。对于变量名的长度&#xff0c;Shell并没有做出明确的规定。因此&#xff0c;用户可以使用任意长度的字符串来作为变量名。但是…

算法:模拟思想算法

文章目录 实现原理算法思路典型例题替换所有问号提莫攻击N字型变换外观序列 总结 本篇总结的是模拟算法 实现原理 模拟算法的实现原理很简单&#xff0c;就是依据题意实现题意的目的即可&#xff0c;考察的是你能不能实现题目题意的代码能力 算法思路 没有很明显的算法思路…

模拟集成电路设计:Bandgap电路设计及版图实现

模拟集成电路设计 Bandgap电路设计及版图实现 一、目的&#xff1a; 1、熟悉模拟集成电路设计的基本流程&#xff0c;实现Bandgap电路设计&#xff1b; 2、熟悉Linux系统及Cadence Virtuoso icfb设计、仿真软件的使用方法。 二、原理&#xff1a; 1、设计目标&#xff1a;…

【HSPCIE仿真】输入网表文件(2)电路拓扑描述

电路拓扑描述 1.元器件描述语句(Elements)电阻(Resistor)电容(Capacitors)电感(Inductors)二极管(Diode)MOSFET 2. 模型(.MODEL)2.1 基本概念2.2 模型的使用.model 描述语句MOSFET的 .model 语句 3. 电源和激励描述语句3.1 独立源直流源梯形脉冲源Pattern Source 4. 库文件的调…

SpringBoot初级开发--加入Swagger展现接口文档(5)

Swagger 是一个规范且完整的框架&#xff0c;用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。作为web开发&#xff0c;它已经成了接口文档服务的代名词了。现在很多协作型项目&#xff0c;都用它生成api文档&#xff0c;让大家能够很好的协作开发。紧接上一章&#xf…

液冷技术之液冷连接器快速接头

文章目录 系列文章目录前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 热能在液冷技术的研发不断加大&#xff0c;特别是在水冷产品生产工艺上不断革新&#xff0c;在铜管自动折弯、挤型模、压管、粘连焊接等技术工艺获得了多个技术专利&#xff0…