SetDisplayConfig 的配置在电脑重启后失效

Configs for SetDisplayConfig become invalid after computer restart

我正在使用 WINAPI 通过调用方法 Enable/Disable 我的电视监视器。

我发现有效的方法是为两种状态保存整个 pathInfo 和 modeInfo 数组
(Enabled/Disabled) 并使用它们恢复到该状态。

该方法工作正常,但在重新启动计算机后,设置已过时。
如果我只是重新启动程序就可以正常工作,所以序列化是正确的。

我们的目标是 Save/Serialize 这些设置在计算机上一次,并且能够永远使用它们。

我尝试只保存我感兴趣的单个显示器的路径和模式(通过仅过滤活动显示器),但事实证明其他屏幕的模式也会受到影响是设置中的额外显示。

主要方法是下面的方法,但是you can find the whole class here!
(我在上面使用 User32 PInvoke lib

[Serializable]
public class TVSettings {
    public DISPLAYCONFIG_PATH_INFO[] Path;
    public DISPLAYCONFIG_MODE_INFO[] Mode;
    public TVSettings(DISPLAYCONFIG_PATH_INFO[] pathArray, DISPLAYCONFIG_MODE_INFO[] modeArray) {
        Path = pathArray;
        Mode = modeArray;
    }
}
// The preset of the settings which I serialize.
public static TVSettings Enabled;
public static TVSettings Disabled;

// The main method. Merged the Save & ChangeState branches to save space.
public static void ChangeTVState(bool ChangeState = false, bool Save = false) {
    uint numPathArrayElements = 0;
    uint numModeInfoArrayElements = 0;
    uint id = QDC_ALL_PATHS; // Searching for ALL PATHS because I want the disabled screen inside the array after the Query.

    // Initialize and Query all the Display Config info.
    int bufferError = GetDisplayConfigBufferSizes(id, ref numPathArrayElements, ref numModeInfoArrayElements);
    DISPLAYCONFIG_PATH_INFO[] pathArray = new DISPLAYCONFIG_PATH_INFO[numPathArrayElements];
    DISPLAYCONFIG_MODE_INFO[] modeArray = new DISPLAYCONFIG_MODE_INFO[numModeInfoArrayElements];

    QueryDisplayConfig(id, ref numPathArrayElements, pathArray, ref numModeInfoArrayElements, modeArray, IntPtr.Zero);

    // Grab the active Screens -- was previously used for tests.
    var active_modeArray = modeArray.Where(x => x.targetMode.targetVideoSignalInfo.activeSize.cx != 0).ToArray();
    var active_pathArray = pathArray.Where(x => x.flags != 0).ToArray();

    bool ThirdScreenIsConnected = active_pathArray.Length >= 3 && active_modeArray.Length >= 3;

    if (Save) {
        // Save on the appropriate Preset field.
        if (ThirdScreenIsConnected) { Enabled = new TVSettings(pathArray, modeArray); }
        else { Disabled = new TVSettings(pathArray, modeArray); }
    }

    if (ChangeState) {
        // Safety measures because I don't wanna mess up the settings too much.
        if (Enabled == null || Disabled == null) {
            Console.WriteLine("Enabled & Disabled Settings are not configured properly.");
            Console.WriteLine("Please save both and try again.");
            return;
        }

        // Use the settings of the other state
        // eg: if 3rd monitor is currently disabled, we use the Disabled preset.
        var Settings = ThirdScreenIsConnected ? Disabled : Enabled;
        pathArray = Settings.Path;
        modeArray = Settings.Mode;

        // Call SetDisplayConfig to update the display config.
        // It works fine on a single windows boot, but the settings are not valid if I reboot.
        uint flag = (SDC_APPLY | SDC_SAVE_TO_DATABASE | SDC_ALLOW_CHANGES | SDC_USE_SUPPLIED_DISPLAY_CONFIG);
        int errorID = SetDisplayConfig((uint)pathArray.Length, pathArray, (uint)modeArray.Length, modeArray, flag);

        if (errorID == 0) { Console.WriteLine("Successfully updated Screen setup!"); }
        else { Console.WriteLine("ERROR: " + errorID); }
    }

}

我希望设置在多个 windows 会话中有效,但它们被视为无效。

重新启动后出现的错误是:87 -- INVALID PARAMETERS,当我试图改变显示器的个别设置时也出现了(在 pathArraymodeArray 中)。

如果您愿意在您的机器上尝试这个,这里有一些可用的 Save/Load 功能,以及 WPF

的简单上下文菜单

(您必须通过上下文菜单上的 "Exit" 按钮退出才能进行序列化)
(ps:您必须保存两次——一次启用第二个屏幕,一次禁用)

如有任何帮助,我们将不胜感激! :)

我发现出现此问题是因为 GPU 和主板的 adapterID 会随着每次计算机重启而发生变化。

我找到了一个很好的方法来解决我的问题,而无需对之前的 adapterID!
进行任何序列化 这是它的代码:

static void UpdateAdapterID() {

    // Cache saved adapterIDs, for later comparison with the current ones.
    LUID savedAdapterID_GPU = Enabled.Path[0].sourceInfo.adapterId;
    LUID savedAdapterID_MB = Enabled.Path.First(x => x.sourceInfo.adapterId.LowPart != savedAdapterID_GPU.LowPart && x.sourceInfo.adapterId.LowPart != 0).sourceInfo.adapterId;

    bool isAdapterUpdated_GPU = savedAdapterID_GPU.LowPart == CurrentAdapterID_GPU.LowPart;
    bool isAdapterUpdated_MB = savedAdapterID_MB.LowPart == CurrentAdapterID_MB.LowPart;

    // Check if our saved states have already been updated.
    if (isAdapterUpdated_GPU && isAdapterUpdated_MB) { return; }

    for (int i = 0; i < Enabled.Path.Length; i++) {
        Update(ref Enabled.Path[i].sourceInfo.adapterId);
        Update(ref Enabled.Path[i].targetInfo.adapterId);
    }
    for (int i = 0; i < Disabled.Path.Length; i++) {
        Update(ref Disabled.Path[i].sourceInfo.adapterId);
        Update(ref Disabled.Path[i].targetInfo.adapterId);
    }

    for (int i = 0; i < Enabled.Mode.Length; i++) { Update(ref Enabled.Mode[i].adapterId); }
    for (int i = 0; i < Disabled.Mode.Length; i++) { Update(ref Disabled.Mode[i].adapterId); }



    void Update(ref LUID adapterID) {
        bool isInvalid = adapterID.LowPart == 0;
        bool isUpdated = adapterID.LowPart == CurrentAdapterID_GPU.LowPart || adapterID.LowPart == CurrentAdapterID_MB.LowPart;

        if (!isInvalid && !isUpdated) {
            bool adapterIsGPU = adapterID.LowPart == savedAdapterID_GPU.LowPart;
            if (adapterIsGPU) { adapterID = CurrentAdapterID_GPU; }
            else { adapterID = CurrentAdapterID_MB; }
        }
    }

    if (!isAdapterUpdated_GPU) { Console.WriteLine("Updated adapterID for GPU."); }
    if (!isAdapterUpdated_MB) { Console.WriteLine("Updated adapterID for MB."); }
}

Here's the whole script in case anyone can find it helpful :)