Windows分辨率及缩放倍数

xingyun86 2022-4-18 2086

Windows分辨率及缩放倍数

// WindowsSetting.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
#include <iostream>
#include <shobjidl_core.h>
#include <vector>
#include <string>
class DpiApi
{
    const std::vector<DEVICE_SCALE_FACTOR> DpiVals = 
    {
        SCALE_100_PERCENT ,
        SCALE_120_PERCENT,
        SCALE_125_PERCENT,
        SCALE_140_PERCENT,
        SCALE_150_PERCENT,
        SCALE_160_PERCENT,
        SCALE_175_PERCENT,
        SCALE_180_PERCENT,
        SCALE_200_PERCENT,
        SCALE_225_PERCENT,
        SCALE_250_PERCENT,
        SCALE_300_PERCENT,
        SCALE_350_PERCENT,
        SCALE_400_PERCENT,
        SCALE_450_PERCENT,
        SCALE_500_PERCENT,
    };
public:
    /*
    * @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
    */
    bool GetPathModeInfoList(std::vector<DISPLAYCONFIG_PATH_INFO>& vPathInfo, std::vector<DISPLAYCONFIG_MODE_INFO>& vModeInfo, int flags = QDC_ONLY_ACTIVE_PATHS)
    {
        UINT32 numPaths = 0, numModes = 0;
        LONG lStatus = GetDisplayConfigBufferSizes(flags, &numPaths, &numModes);
        if (lStatus != ERROR_SUCCESS)
        {
            return false;
        }
        vPathInfo.resize(numPaths, {});
        vModeInfo.resize(numModes, {});
        lStatus = ::QueryDisplayConfig(flags, &numPaths, vPathInfo.data(), &numModes, vModeInfo.data(), NULL);
        if (lStatus != ERROR_SUCCESS)
        {
            return false;
        }
        return true;
    }
    //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;
    };
    bool GetDPIScalingInfo(struct DPIScalingInfo& dpiInfo, LUID adapterID, UINT32 sourceID)
    {
        DISPLAYCONFIG_SOURCE_DPI_SCALE_GET requestPacket = {};
        requestPacket.header.type = (DISPLAYCONFIG_DEVICE_INFO_TYPE)DISPLAYCONFIG_DEVICE_INFO_TYPE_CUSTOM::DISPLAYCONFIG_DEVICE_INFO_GET_DPI_SCALE;
        requestPacket.header.size = sizeof(requestPacket);
        if(0x20 != sizeof(requestPacket)) return false;//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 ((int)DpiVals.size() >= (int)(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;
                return true;
            }
            else
            {
                //Error! Probably DpiVals array is outdated
                return false;
            }
        }
        else
        {
            //DisplayConfigGetDeviceInfo() failed
            return false;
        }
        return false;
    }
    bool SetDPIScaling(LUID adapterID, UINT32 sourceID, UINT32 dpiPercentToSet)
    {
        DPIScalingInfo dPIScalingInfo = {};
        bool bRet = GetDPIScalingInfo(dPIScalingInfo, adapterID, sourceID);
        if (bRet == false)
        {
            return false;
        }
        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;
        DISPLAYCONFIG_SOURCE_DPI_SCALE_SET setPacket = {};
        setPacket.header.adapterId = adapterID;
        setPacket.header.id = sourceID;
        setPacket.header.size = sizeof(setPacket);
        if(0x18 != sizeof(setPacket)) return false;//if this fails => OS has changed somthing, and our reverse enginnering knowledge about the API is outdated
        setPacket.header.type = (DISPLAYCONFIG_DEVICE_INFO_TYPE)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;
    }
    //Get default DPI scaling percentage.The OS recommented value.
    DEVICE_SCALE_FACTOR GetDPIScalingInfoPrimary()
    {
        int dpi = 0;
        BOOL bRet = ::SystemParametersInfoW(SPI_GETLOGICALDPIOVERRIDE, 0, (LPVOID)&dpi, 1);
        if (bRet == TRUE)
        {
            return DpiVals[dpi * -1];
        }
        return DEVICE_SCALE_FACTOR_INVALID;
    }
    bool SetDpiScalingPrimary(DEVICE_SCALE_FACTOR newDeviceScaleFactor)
    {
        bool bRet = FALSE;
        DEVICE_SCALE_FACTOR deviceScaleFactor = GetDPIScalingInfoPrimary();
        if (deviceScaleFactor != DEVICE_SCALE_FACTOR_INVALID)
        {
            int index = 0, recIndex = 0, setIndex = 0;
            for (const auto& scale : DpiVals)
            {
                if (deviceScaleFactor == scale)
                {
                    recIndex = index;
                }
                if (newDeviceScaleFactor == scale)
                {
                    setIndex = index;
                }
                index++;
            }
            int relativeIndex = setIndex - recIndex;
            bRet = (::SystemParametersInfoW(SPI_SETLOGICALDPIOVERRIDE, relativeIndex, (LPVOID)0, 1) == TRUE);
        }
        return bRet;
    }
    bool ModifyWindows()
    {
        //检测当前分辨率
        int Width = GetSystemMetrics(SM_CXSCREEN);
        int Height = GetSystemMetrics(SM_CYSCREEN);
        DEVMODE lpDevMode0;
        lpDevMode0.dmBitsPerPel = 32;
        lpDevMode0.dmPelsWidth = Width;
        lpDevMode0.dmPelsHeight = Height;
        lpDevMode0.dmSize = sizeof(lpDevMode0);
        lpDevMode0.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;
        //修改
        DEVMODE lpDevMode;
        lpDevMode.dmBitsPerPel = 32;
        lpDevMode.dmPelsWidth = 1024;
        lpDevMode.dmPelsHeight = 768;
        lpDevMode.dmSize = sizeof(lpDevMode);
        lpDevMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;
        LONG result = 0;
        result = ::ChangeDisplaySettingsW(&lpDevMode, 0);
        if (result == DISP_CHANGE_SUCCESSFUL)
        {
            ::ChangeDisplaySettingsW(&lpDevMode, CDS_UPDATEREGISTRY);
            //使用CDS_UPDATEREGISTRY表示次修改是持久的,并在注册表中写入了相关的数据
            ::ChangeDisplaySettingsW(&lpDevMode0, CDS_UPDATEREGISTRY);
        }
        else
        {
            //MessageBox("修改失败,恢复原有设置!");
            ::ChangeDisplaySettingsW(NULL, 0);
        }
    }
    INT GetCurrentScaleFactor()
    {
        // 获取窗口当前显示的监视器
        // 使用桌面的句柄.
        HWND hWnd = GetDesktopWindow();
        HMONITOR hMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
        // 获取监视器逻辑宽度与高度
        MONITORINFOEXW miex = {};
        miex.cbSize = sizeof(miex);
        ::GetMonitorInfoW(hMonitor, &miex);
        int cxLogical = (miex.rcMonitor.right - miex.rcMonitor.left);
        int cyLogical = (miex.rcMonitor.bottom - miex.rcMonitor.top);
        // 获取监视器物理宽度与高度
        DEVMODE dm;
        dm.dmSize = sizeof(dm);
        dm.dmDriverExtra = 0;
        ::EnumDisplaySettingsW(miex.szDevice, ENUM_CURRENT_SETTINGS, &dm);
        int cxPhysical = dm.dmPelsWidth;
        int cyPhysical = dm.dmPelsHeight;
        // 缩放比例计算  实际上使用任何一个即可
        double horzScale = ((double)cxPhysical / (double)cxLogical);
        double vertScale = ((double)cyPhysical / (double)cyLogical);
        return horzScale * 100;
    }
    std::string GetCurrentResolution()
    {
        int width = GetSystemMetrics(SM_CXSCREEN);
        int height = GetSystemMetrics(SM_CYSCREEN);
        return std::to_string(width) + "x" + std::to_string(height);
    }
};
int main(int argc, char ** argv)
{
    std::cout << "Hello World!\n";
    
    std::cout << DpiApi().GetCurrentScaleFactor() << std::endl;
    std::cout << DpiApi().GetCurrentResolution() << std::endl;
    return 0;
    std::vector<DISPLAYCONFIG_PATH_INFO> vPathInfo;
    std::vector<DISPLAYCONFIG_MODE_INFO> vModeInfo;
    DpiApi().GetPathModeInfoList(vPathInfo, vModeInfo);
    DpiApi().SetDpiScalingPrimary(SCALE_100_PERCENT);
    return 0;
}
// Run program: Ctrl + F5 or Debug > Start Without Debugging menu
// Debug program: F5 or Debug > Start Debugging menu
// Tips for Getting Started: 
//   1. Use the Solution Explorer window to add/manage files
//   2. Use the Team Explorer window to connect to source control
//   3. Use the Output window to see build output and other messages
//   4. Use the Error List window to view errors
//   5. Go to Project > Add New Item to create new code files, or Project > Add Existing Item to add existing code files to the project
//   6. In the future, to open this project again, go to File > Open > Project and select the .sln file


×
打赏作者
最新回复 (0)
只看楼主
全部楼主
返回