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
,当我试图改变显示器的个别设置时也出现了(在 pathArray
和 modeArray
中)。
如果您愿意在您的机器上尝试这个,这里有一些可用的 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 :)
我正在使用 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
,当我试图改变显示器的个别设置时也出现了(在 pathArray
和 modeArray
中)。
如果您愿意在您的机器上尝试这个,这里有一些可用的 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 :)