Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/joomla/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何使用C语言以编程方式更改Windows 10的显示比例#_C#_Screen Resolution_Dpi - Fatal编程技术网

C# 如何使用C语言以编程方式更改Windows 10的显示比例#

C# 如何使用C语言以编程方式更改Windows 10的显示比例#,c#,screen-resolution,dpi,C#,Screen Resolution,Dpi,我正试图找到一种方法,使用C#以编程方式更改Windows 10中的显示比例 我还要说的是,我并没有试图创建一个自动强制用户屏幕改变分辨率/缩放的应用程序。这只是一个工具,我可以从托盘上切换天平,因为这是我经常要做的测试。所以是特意为这次行动设计的 因此,当用户通过下面的官方对话框手动执行此操作时,我能够跟踪设置了哪些注册表项(HKEY\U CURRENT\U USER\Control Panel\Desktop): 然而,显然,直接使用注册表意味着我需要重新启动机器才能生效 我知道您可以使用

我正试图找到一种方法,使用C#以编程方式更改Windows 10中的显示比例

我还要说的是,我并没有试图创建一个自动强制用户屏幕改变分辨率/缩放的应用程序。这只是一个工具,我可以从托盘上切换天平,因为这是我经常要做的测试。所以是特意为这次行动设计的

因此,当用户通过下面的官方对话框手动执行此操作时,我能够跟踪设置了哪些注册表项(HKEY\U CURRENT\U USER\Control Panel\Desktop):

然而,显然,直接使用注册表意味着我需要重新启动机器才能生效

我知道您可以使用Pinvoke更改屏幕分辨率:


我想知道是否有一种方法也可以为给定屏幕更改此“%”?i、 我的屏幕上方显示150%,我希望能够通过编程在100-500%的范围内对其进行更改。

在搜索完全相同的内容时,我找到了您的问题并找到了可能的解决方案

我发现此%值的每监视器切换位于注册表中的
Computer\HKEY\u CURRENT\u USER\Control Panel\Desktop\PerMonitorSettings\*monitorId*\DpiValue
。该值的含义似乎取决于屏幕(大小和dpi),有关详细信息,请参阅

对于我的24英寸1080p屏幕,
0
表示100%,而
1
表示125%。似乎有点解释了这些值

遗憾的是,仅更改注册表值是不够的。但您可以在写入注册表后通过更改分辨率来刷新dpi

以下代码设置dpi,然后将分辨率切换为低,再切换为高,以触发dpi更新

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using Microsoft.Win32;

namespace SetDpiScale
{
    public partial class Form1 : Form
    {
        public enum DMDO
        {
            DEFAULT = 0,
            D90 = 1,
            D180 = 2,
            D270 = 3
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        struct DEVMODE
        {
            public const int DM_PELSWIDTH = 0x80000;
            public const int DM_PELSHEIGHT = 0x100000;
            private const int CCHDEVICENAME = 32;
            private const int CCHFORMNAME = 32;

            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)]
            public string dmDeviceName;
            public short dmSpecVersion;
            public short dmDriverVersion;
            public short dmSize;
            public short dmDriverExtra;
            public int dmFields;

            public int dmPositionX;
            public int dmPositionY;
            public DMDO dmDisplayOrientation;
            public int dmDisplayFixedOutput;

            public short dmColor;
            public short dmDuplex;
            public short dmYResolution;
            public short dmTTOption;
            public short dmCollate;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHFORMNAME)]
            public string dmFormName;
            public short dmLogPixels;
            public int dmBitsPerPel;
            public int dmPelsWidth;
            public int dmPelsHeight;
            public int dmDisplayFlags;
            public int dmDisplayFrequency;
            public int dmICMMethod;
            public int dmICMIntent;
            public int dmMediaType;
            public int dmDitherType;
            public int dmReserved1;
            public int dmReserved2;
            public int dmPanningWidth;
            public int dmPanningHeight;
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        static extern int ChangeDisplaySettings([In] ref DEVMODE lpDevMode, int dwFlags);

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            ChangeDPI(0); // 100%
        }
        private void button2_Click(object sender, EventArgs e)
        {
            ChangeDPI(1); // 125%
        }

        void ChangeDPI(int dpi)
        {
            RegistryKey key = Registry.CurrentUser.OpenSubKey("Control Panel", true);

            key = key.OpenSubKey("Desktop", true);
            key = key.OpenSubKey("PerMonitorSettings", true);
            key = key.OpenSubKey("*monitor id where to change the dpi*", true); // my second monitor here

            key.SetValue("DpiValue", dpi);

            SetResolution(1920, 1080); // this sets the resolution on primary screen
            SetResolution(2560, 1440); // returning back to my primary screens default resolution
        }

        private static void SetResolution(int w, int h)
        {
            long RetVal = 0;

            DEVMODE dm = new DEVMODE();

            dm.dmSize = (short)Marshal.SizeOf(typeof(DEVMODE));

            dm.dmPelsWidth = w;
            dm.dmPelsHeight = h;

            dm.dmFields = DEVMODE.DM_PELSWIDTH | DEVMODE.DM_PELSHEIGHT;


            RetVal = ChangeDisplaySettings(ref dm, 0);
        }
    }
}

这是我从RND学习的,我在系统设置应用程序(沉浸式控制面板)上做的。(强烈地看到我的另一个答案,我从这个学习中创建了一个简单的C++ API。

  • 系统设置应用程序(Windows 10附带的新沉浸式控制面板)能够做到这一点。这意味着肯定有一个API,只是Microsoft尚未将其公开。
  • 系统设置应用程序是一个UWP应用程序,但可以与调试器WinDbg挂钩
  • 我使用WinDbg检查了此应用程序的调用。我发现,只要执行了特定的函数-
    user32!\u imp\u NtUserDisplayConfigSetDeviceInfo
    ,新的DPI设置就会在我的机器上生效

    我无法在此函数上设置断点,但可以在
    DisplayConfigSetDeviceInfo()
    (bp user32!DisplayConfigSetDeviceInfo)
    上设置断点

    DisplayConfigSetDeviceInfo()是一个公共函数,但似乎设置应用程序正在向其发送未记录的参数。 以下是我在调试会话期间找到的参数

    ((user32!DISPLAYCONFIG_DEVICE_INFO_HEADER *)0x55df8fba30)                 : 0x55df8fba30 [Type: DISPLAYCONFIG_DEVICE_INFO_HEADER *]
        [+0x000] type             : -4 [Type: DISPLAYCONFIG_DEVICE_INFO_TYPE]
        [+0x004] size             : 0x18 [Type: unsigned int]
        [+0x008] adapterId        [Type: _LUID]
        [+0x010] id               : 0x0 [Type: unsigned int]
    0:003> dx -r1 (*((user32!_LUID *)0x55df8fba38))
    (*((user32!_LUID *)0x55df8fba38))                 [Type: _LUID]
        [+0x000] LowPart          : 0xcbae [Type: unsigned long]
        [+0x004] HighPart         : 0 [Type: long]
    
    基本上,
    DISPLAYCONFIG\u DEVICE\u INFO\u HEADER
    struct的成员的值传递给
    displayconfigusetdeviceinfo()

    type : -4
    size : 0x18
    adapterId : LowPart : 0xcbae HighPart :0
    
    wingdi.h中定义的枚举类型为:

    typedef enum
    {
          DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME                 = 1,
          DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME                 = 2,
          DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE       = 3,
          DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME                = 4,
          DISPLAYCONFIG_DEVICE_INFO_SET_TARGET_PERSISTENCE          = 5,
          DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_BASE_TYPE            = 6,
          DISPLAYCONFIG_DEVICE_INFO_GET_SUPPORT_VIRTUAL_RESOLUTION  = 7,
          DISPLAYCONFIG_DEVICE_INFO_SET_SUPPORT_VIRTUAL_RESOLUTION  = 8,
          DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO         = 9,
          DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE        = 10,
          DISPLAYCONFIG_DEVICE_INFO_FORCE_UINT32                = 0xFFFFFFFF
    } DISPLAYCONFIG_DEVICE_INFO_TYPE;
    
    当设置应用程序尝试发送类型为-4时,我们可以看到枚举没有负值

    如果我们能够完全反向工程,我们将有一个工作API来设置监视器的DPI

    微软为自己的应用程序提供了一些其他人无法使用的特殊API,这似乎极不公平

    更新1: 为了验证我的理论,我复制了(使用WinDbg)发送到
    DISPLAYCONFIG\u DEVICE\u INFO\u头的
    struct的字节,作为参数发送到
    displayconfigusetdeviceinfo()
    ;当DPI缩放从系统设置应用程序更改时(尝试设置150%的DPI缩放)

    然后,我编写了一个简单的C程序,将这些字节(24字节-0x18字节)发送到
    displayconfigusetdeviceinfo()

    然后我将我的DPI比例改回100%,并运行了我的代码。果然,在运行代码时DPI比例确实发生了变化

    BYTE buf[] = { 0xFC,0xFF,0xFF,0xFF,0x18,0x00,0x00,0x00,0xAE,0xCB,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00 };
    DISPLAYCONFIG_DEVICE_INFO_HEADER* packet = (DISPLAYCONFIG_DEVICE_INFO_HEADER*)buf;
        DisplayConfigSetDeviceInfo(packet);
    
    请注意,与LUID和id参数相同的代码可能不适用于您,它们指向系统上的显示可能不同(LUID通常用于GPU,id可以是源id、目标id或其他id,此参数取决于DISPLAYCONFIG\u DEVICE\u INFO\u HEADER::type)

    我现在必须弄清楚这24个字节的含义

    更新2: 下面是我在尝试设置175%dpi缩放时得到的字节

    BYTE buf[] = { 0xFC,0xFF,0xFF,0xFF,0x18,0x00,0x00,0x00,0xAE,0xCB,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00 };
    
    如果我们比较两个字节的缓冲区,我们可以得出以下结论

  • 字节号21用于指定DPI缩放,因为所有其他字节在150%和175%之间相同
  • 对于150%缩放,字节21的值为1,而对于175%缩放,字节21的值为2。此监视器的默认(推荐)DPI缩放为125%
  • 根据@Dodge提到的,用Windows的说法,0对应于建议的DPI缩放值。其他整数对应于相对于此建议值的相对DPI缩放。1表示缩放提前一步,-1表示降低一步。例如,如果建议为125%,则值1表示缩放150%。这确实是我们看到的。 现在剩下的唯一一件事是找出如何获得显示器的建议DPI缩放值,然后我们将能够编写以下形式的API-
    SetDPIScaling(monitor\u LUID,DPIScale\u percent)

    更新3: 如果我们检查@Dodge的答案中提到的注册表项,我们就会知道这些整数存储为DWORD,并且由于我的计算机是little endian,这意味着最后4个字节(字节21到24)因此,要发送负数,我们必须使用DWORD的2的补码,并将字节写为小端

    更新4: 我还一直在研究Windows如何尝试使用ge
    HKEY_CURRENT_USER\Control Panel\Desktop\PerMonitorSettings\
    *MonitorID*
    
    DpiHelper::DPIScalingInfo DpiHelper::GetDPIScalingInfo(LUID adapterID, UINT32 sourceID)
    
    bool DpiHelper::SetDPIScaling(LUID adapterID, UINT32 sourceID, UINT32 dpiPercentToSet)
    
    #pragma once
    
    #include "targetver.h"
    
    #define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
    // Windows Header Files
    #include <windows.h>
    
    // reference additional headers your program requires here
    #ifdef __cplusplus
    extern "C" {
    #endif
        extern __declspec(dllexport) void PrintDpiInfo();
        extern __declspec(dllexport) void SetDPIScaling(INT32 adapterIDHigh, UINT32 adapterIDlow, UINT32 sourceID, UINT32 dpiPercentToSet);
        extern __declspec(dllexport) void RestoreDPIScaling();
    #ifdef __cplusplus
    }
    #endif
    
    // DpiHelper.cpp : Defines the exported functions for the DLL application.
    //
    
    #include "stdafx.h"
    #include "DpiHelper.h"
    #include <memory>
    #include <cassert>
    #include <string>
    #include <map>
    
    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;
    }
    
    std::wstring GetTargetName(LUID adapterLUID, UINT32 sourceId)
    {
        std::vector<DISPLAYCONFIG_PATH_INFO> pathsV;
        std::vector<DISPLAYCONFIG_MODE_INFO> modesV;
        int flags = QDC_ONLY_ACTIVE_PATHS;
        if (false == DpiHelper::GetPathsAndModes(pathsV, modesV, flags))
        {
            wprintf(L"DpiHelper::GetPathsAndModes() failed\r\n");
        }
    
        for (const auto& path : pathsV)
        {
    
            if (adapterLUID.LowPart == path.targetInfo.adapterId.LowPart
                && adapterLUID.HighPart == path.targetInfo.adapterId.HighPart
                && sourceId == path.sourceInfo.id)
            {
                DISPLAYCONFIG_TARGET_DEVICE_NAME deviceName;
                deviceName.header.size = sizeof(deviceName);
                deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
                deviceName.header.adapterId = adapterLUID;
                deviceName.header.id = path.targetInfo.id;
                if (ERROR_SUCCESS != DisplayConfigGetDeviceInfo(&deviceName.header))
                {
                    wprintf(L"DisplayConfigGetDeviceInfo() failed\r\n");
                }
                else
                {
    
                    std::wstring nameString = deviceName.monitorFriendlyDeviceName;
                    if (DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL == deviceName.outputTechnology)
                    {
                        nameString += L"(internal display)";
                    }
                    return nameString;
                }
            }
    
        }
        return L"N/A";
    
    }
    
    
    
    void printOne(LUID adapterLUID, UINT32 sourceID) {
        wprintf(L"GPU=%ld.%u,Desktop_Index_In_GPU=%d,Monitor=%ls\r\n"
            ,adapterLUID.HighPart
            , adapterLUID.LowPart
            , sourceID
            , GetTargetName(adapterLUID, sourceID).data());
    }
    
    
    
    bool DpiHelper::SetDPIScaling(LUID adapterID, UINT32 sourceID, UINT32 dpiPercentToSet)
    {
    
        wprintf(L"setting dpi scale to %d: ", dpiPercentToSet);
        printOne(adapterID, sourceID);
        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;
    }
    
    
    #define MAX_ID  10
    LUID GpuId[MAX_ID];
    UINT32 DesktopIndexInGpu[MAX_ID];
    UINT32 oldDPI[MAX_ID];
    
    
    void PrintDpiInfo() {
    
    
    
        std::vector<DISPLAYCONFIG_PATH_INFO> pathsV;
        std::vector<DISPLAYCONFIG_MODE_INFO> modesV;
        int flags = QDC_ONLY_ACTIVE_PATHS;
        if (false == DpiHelper::GetPathsAndModes(pathsV, modesV, flags))
        {
            wprintf(L"DpiHelper::GetPathsAndModes() failed");
        }
    
        int i = 0;
        for (const auto& path : pathsV)
        {
            //get display name
            auto adapterLUID = path.targetInfo.adapterId;       
            auto sourceID = path.sourceInfo.id;
            std::wstring monitor_name = GetTargetName(adapterLUID, sourceID);
            printOne(adapterLUID, sourceID);
    
            DpiHelper::DPIScalingInfo dpiInfo = DpiHelper::GetDPIScalingInfo(adapterLUID, sourceID);
    
            GpuId[i] = adapterLUID;
            DesktopIndexInGpu[i] = sourceID;
            oldDPI[i] = dpiInfo.current;
    
    
            wprintf(L"Available DPI:\r\n");
            int curdpi = 0;
            for (const auto& dpi : DpiVals)
            {
                if ((dpi >= dpiInfo.mininum) && (dpi <= dpiInfo.maximum))
                    wprintf(L"    %d\r\n",dpi);
            }
            wprintf(L"    current DPI: %d\r\n",dpiInfo.current);
    
            i++;
            if (i >= MAX_ID) {
                wprintf(L"To many desktops\r\n");
                break;
            }
        }
    
    
    }
    
    void SetDPIScaling(INT32 adapterIDHigh, UINT32 adapterIDlow, UINT32 sourceID, UINT32 dpiPercentToSet) {
        LUID adapterId;
        adapterId.HighPart = adapterIDHigh;
        adapterId.LowPart = adapterIDlow;   
        DpiHelper::SetDPIScaling(adapterId, sourceID, dpiPercentToSet);
    }
    
    void RestoreDPIScaling() 
    {
        wprintf(L"Now restore DPI settings...\r\n");
        for (int i = 0;i < MAX_ID;i++) {
            if (GpuId[i].LowPart == 0 && GpuId[i].HighPart==0) break;
            DpiHelper::SetDPIScaling(GpuId[i], DesktopIndexInGpu[i], oldDPI[i]);
        }
    
    }
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace DispSetEx
    {
        class Program
        {
    
    
    
            [DllImport("DpiHelper.dll")]
            static public extern void PrintDpiInfo();
    
            [DllImport("DpiHelper.dll")]
            static public extern int SetDPIScaling(Int32 adapterIDHigh, UInt32 adapterIDlow, UInt32 sourceID, UInt32 dpiPercentToSet);
            [DllImport("DpiHelper.dll")]
            static public extern void RestoreDPIScaling();
    
            static void Main(string[] args)
            {
                if ((args.Length % 3) != 0)
                {
                    Console.WriteLine("wrong parameters");
                    return;
                }
    
    //print the DPI info, you need to set the command line parameters
    //according to this
                PrintDpiInfo();
    
        //commandline parameters should be of groups of three
        //each groups's tree paramters control a desktop's setting
        //in each group:
        //GPUIdhigh.GPUIdlow DesktopIndexInGPU DPIScalingValue
        //for example:
        //    0.1234 0 100 //set the DPI scaling to 100 for desktop 0 on GPU 0.1234
        //    0.4567 0 125 //set the DPI scaling to 125 for desktop 0 on GPU 0.5678
        //    0.4567 1 150 //set the DPI scaling to 150 for desktop 1 on GPU 0.5678
        //in most cases GPUIdhigh is 0.
        //you can use the monitor name to identify which is which easily
        //you need to set the command line parameters according to the result of PrintDpiInfo
        //e.g. you should only set the DPI scaling to a value that is supported by 
        //that desktop. 
    
    
                for (int i = 0; i < args.Length / 3; i++)
                {
                    string[] sa = args[i * 3].Split(new char[] { '.' });
    
                    Int32 adapterHigh = Int32.Parse(sa[0]);
                    UInt32 adapterLow = UInt32.Parse(sa[1]);
                    UInt32 source = UInt32.Parse(args[i * 3 + 1]);
                    UInt32 dpiscale = UInt32.Parse(args[i * 3 + 2]);
    
                    SetDPIScaling(adapterHigh, adapterLow, source,dpiscale);
                }
    
                Console.WriteLine("Press any key to resotre the settings...");
                Console.ReadKey();
    
                RestoreDPIScaling();  
            }
        }
    }
    
    SPI_SETLOGICALDPIOVERRIDE   Do not use. 
    0x009F
    
    SystemParametersInfo(SPI_SETLOGICALDPIOVERRIDE, relativeIndex, (LPVOID)0, 1);
    
    100,125,150,175,200,225,250,300,350, 400, 450, 500
    
    SystemParametersInfo(SPI_GETLOGICALDPIOVERRIDE, 0, (LPVOID)&dpi, 1);
    
    #include <iostream>
    #include <Windows.h>
    
    using namespace std;
    
    
    static const UINT32 DpiVals[] = { 100,125,150,175,200,225,250,300,350, 400, 450, 500 };
    
    /*Get default DPI scaling percentage.
    The OS recommented value.
    */
    int GetRecommendedDPIScaling()
    {
        int dpi = 0;
        auto retval = SystemParametersInfo(SPI_GETLOGICALDPIOVERRIDE, 0, (LPVOID)&dpi, 1);
    
        if (retval != 0)
        {
            int currDPI = DpiVals[dpi * -1];
            return currDPI;
        }
    
        return -1;
    }
    
    void SetDpiScaling(int percentScaleToSet)
    {
        int recommendedDpiScale = GetRecommendedDPIScaling();
    
        if (recommendedDpiScale > 0)
        {
            int index = 0, recIndex = 0, setIndex = 0 ;
            for (const auto& scale : DpiVals)
            {
                if (recommendedDpiScale == scale)
                {
                    recIndex = index;
                }
                if (percentScaleToSet == scale)
                {
                    setIndex = index;
                }
                index++;
            }
            
            int relativeIndex = setIndex - recIndex;
            SystemParametersInfo(SPI_SETLOGICALDPIOVERRIDE, relativeIndex, (LPVOID)0, 1);
        }
    }
    
    int main()
    {
        for (;;)
        {
            int n = 0, dpiToSet = 0;
            cout << R"(
                1. Show Recommended DPI
                2. Set DPI
                Anything else to exit
    )";
            cin >> n;
            switch (n)
            {
            case 1:
                cout << "recommened scaling: " << GetRecommendedDPIScaling() << "%" << endl;
                break;
            case 2:
                cout << "enter scaling to set in percentage" << endl;
                cin >> dpiToSet;
                SetDpiScaling(dpiToSet);
                break;
            default:
                exit(0);
                break;
            }
        }
        return 0;
    }