How can I change Windows 10 Display Scaling Programmatically using C#

前端 未结 6 2076
野的像风
野的像风 2020-11-28 11:39

I\'m trying to find a way to change the Display Scaling in Windows 10 Programmatically using C#.

Let me also say that, I\'m not trying to create a application that a

6条回答
  •  没有蜡笔的小新
    2020-11-28 11:44

    Here is my code based on @Sahil Singh's:

    Dll project to wrap the C++ API:

    stdafx.h:

    #pragma once
    
    #include "targetver.h"
    
    #define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
    // Windows Header Files
    #include 
    
    // 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:

    // DpiHelper.cpp : Defines the exported functions for the DLL application.
    //
    
    #include "stdafx.h"
    #include "DpiHelper.h"
    #include 
    #include 
    #include 
    #include 
    
    bool DpiHelper::GetPathsAndModes(std::vector& pathsV, std::vector& modesV, int flags)
    {
        UINT32 numPaths = 0, numModes = 0;
        auto status = GetDisplayConfigBufferSizes(flags, &numPaths, &numModes);
        if (ERROR_SUCCESS != status)
        {
            return false;
        }
    
        std::unique_ptr paths(new DISPLAYCONFIG_PATH_INFO[numPaths]);
        std::unique_ptr 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 pathsV;
        std::vector 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 pathsV;
        std::vector 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]);
        }
    
    }
    

    DpiHelper.h is the same as the referenced answer. Create an C++ Dll project in Visual studio and add/put in the above code and use the dll in the bellow C# application.

    A C# console application that set DPI according to command line parameters and restore them when press any key:

    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();  
            }
        }
    }
    

提交回复
热议问题