我正在使用WINAPI通过调用方法来启用/禁用电视监视器。
我发现可以使用的方法是保存两种状态的整个pathInfo和modeInfo数组
(启用/禁用),并使用它们恢复到该状态。
该方法正常运行,但是在重新启动计算机后,设置已过时。
如果我只是重新启动程序,它会很好地工作,因此序列化是正确的。
目标是在计算机上一次保存/序列化这些设置,并能够永久使用它们。
我尝试仅保存我感兴趣的单个监视器的路径和模式(仅通过过滤活动显示),但是事实证明,当有一个屏幕出现时,其他屏幕的模式也会受到影响。在设置中额外显示。
主要方法是以下方法,但是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
中)时,也会出现该错误。
如果您足够在计算机上尝试使用此功能,请here's some usable Save/Load functionality, together with a simple context menu for WPF
(您必须通过上下文菜单上的“退出”按钮退出才能进行序列化)
(ps:您必须保存两次-一次启用第二个屏幕,一次禁用)
任何帮助将不胜感激! :)
答案 0 :(得分:0)
我发现出现此问题是因为随着每台计算机的重新启动,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 :)