■ 參考資料
● Disconnect and Reconnect Displays Programmatically
● How do I enable a second monitor in C#?
● How to disable a secondary monitor (with ChangeDisplaySettingsEx)?
● Use Windows API from C# to set primary monitor
■程式片段
需要下載底下兩個project並build出 dll ,開關外接螢幕需要使用到裡面的function
https://github.com/Jaykul/MultiMonitorHelper/tree/master/MultiMonitorHelper
https://github.com/regueiro/CCDWrapper/tree/master/CCDWrapper
●主程式 ( WPF )
using MultiMonitorHelper;
public MainWindow()
{
InitializeComponent();
IDisplayModel displayModel = DisplayFactory.GetDisplayModel();
List<Display> displayList = displayModel.GetActiveDisplays().ToList();
displayList[0].DisconnectDisplay(1); // 關閉外接螢幕 , 1是外接螢幕的DisplayNumber
//等待12秒,讓外接螢幕有時間進入power save mode(沒有backlight),我使用的外接螢幕
//會有提示將進入power save mode;進入後螢幕的power 鍵轉成呼吸燈號模式
System.Threading.Thread.Sleep(12000);
displayList[0].ReconnectDisplays();
}
--------------------------------------------------------------------------------------------------------------------------
●MultiMonitorHelper-master\MultiMonitorHelper\Display.cs
加上底下兩個method
public void ReconnectDisplays()
{
//底下DisplaySwitch.exe這段方法是網友提供的
//這是windows內建的程式 ,執行後會出現像下圖的東西 ,等同於按 win +p
//但實際上這方法對已經進入powersave mode的外接螢幕沒轍
//執行程式後沒辦法 wake 外接螢幕
//保留在這邊讓其他人少走一點冤枉路
/*Process DisplayChanger = new Process
{
StartInfo =
{
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden,
FileName = @"C:\Windows\Sysnative\DisplaySwitch.exe",
Arguments = "/clone" //extend
}
};
DisplayChanger.Start();*/
//******************************************************************************************
int id = 1;
var device = new DisplayDevice();
var deviceMode = new DevMode();
device.cb = Marshal.SizeOf(device);
XPWrapper.EnumDisplayDevices(null, id, ref device, 0);
XPWrapper.EnumDisplaySettings(device.DeviceName, -1, ref deviceMode);
XPWrapper.ChangeDisplaySettingsEx(
device.DeviceName,
ref deviceMode,
(IntPtr)null,
(CdsFlags.UpdateRegistry | CdsFlags.Noreset),
IntPtr.Zero);
//紅字這段很重要一定要加,這是為了讓ChangeDisplaySettingsEX生效用的
//附上網友這段話,寫得很清楚
//for some weird reason, you must first store the change in the registry
//(with or without CDS_NORESET, it doesn't matter),
//and then use again ChangeDisplaySettingsEx with NULL values to make the changes happen.
//This might have something to do both monitors connected to the same display device, I'm not sure...
//還沒看到這段話之前, survey資料的時候看到其他人這樣使用還不知道原因是什麼咧...
//這也可以解釋為什麼在MultiMonitorHelper-master\MultiMonitorHelper\DisplayModels\XP\XPWrapper.cs
//要宣告兩個 ChangeDisplaySettingsEx (如下)
/*[DllImport("user32.dll", CharSet = CharSet.Ansi)]
public static extern DispChange ChangeDisplaySettingsEx(string deviceName, ref DevMode lpDevMode,
IntPtr hwnd, CdsFlags dwflags, IntPtr lParam);
// A signature for ChangeDisplaySettingsEx with a DevMode struct as the second parameter won't allow you to pass in IntPtr.Zero, so create an overload
[DllImport("user32.dll", CharSet = CharSet.Ansi)]
public static extern DispChange ChangeDisplaySettingsEx(string deviceName, IntPtr devmode,
IntPtr hwnd, CdsFlags dwflags, IntPtr lParam);*/
// Apply settings
XPWrapper.ChangeDisplaySettingsEx(null, IntPtr.Zero, (IntPtr)null, CdsFlags.None, (IntPtr)null);
// ReconnectDisplay
Wrapper.SetDisplayConfig(0, IntPtr.Zero, 0, IntPtr.Zero, (SdcFlags.Apply | SdcFlags.AllowChanges | SdcFlags.UseSuppliedDisplayConfig));
}
public void DisconnectDisplay(int displayNumber)
{
// Get the necessary display information
int numPathArrayElements = -1;
int numModeInfoArrayElements = -1;
StatusCode error = Wrapper.GetDisplayConfigBufferSizes(
QueryDisplayFlags.OnlyActivePaths,
out numPathArrayElements,
out numModeInfoArrayElements);
DisplayConfigPathInfo[] pathInfoArray = new DisplayConfigPathInfo[numPathArrayElements];
DisplayConfigModeInfo[] modeInfoArray = new DisplayConfigModeInfo[numModeInfoArrayElements];
error = Wrapper.QueryDisplayConfig(
QueryDisplayFlags.OnlyActivePaths,
ref numPathArrayElements,
pathInfoArray,
ref numModeInfoArrayElements,
modeInfoArray,
IntPtr.Zero);
if (error != StatusCode.Success)
{
Console.WriteLine("error=" + error);
// QueryDisplayConfig failed
}
// Check the index
if (pathInfoArray[displayNumber].sourceInfo.modeInfoIdx < modeInfoArray.Length)
{
// Disable and reset the display configuration
pathInfoArray[displayNumber].flags = DisplayConfigFlags.Zero;
error = Wrapper.SetDisplayConfig(
pathInfoArray.Length,
pathInfoArray,
modeInfoArray.Length,
modeInfoArray,
(SdcFlags.Apply | SdcFlags.AllowChanges | SdcFlags.UseSuppliedDisplayConfig));
if (error != StatusCode.Success)
{
// SetDisplayConfig failed
}
}
}
--------------------------------------------------------------------------------------------------------------------------
●其他
過程中一定會有build error , 但都是很好解決的問題
比如:
無法存取其他class成員(改成public即可)
找不到變數(因為程式碼我有做一點更動,所以其他地方也要做相對應的修改)
工程師除了要會上網google程式碼複製貼上
另外一定要有解決build error的能力
在這邊就不贅述解法