DXVA2 直接控制實體螢幕硬體,可操作DDC/CI對外接式螢幕進行溝通
通常對筆電螢幕無效,筆電螢幕必須使用WMI
EXE執行檔下載需要用到的 API
user32.dll
- EnumDisplayMonitors
- GetMonitorInfo
- 取得所有螢幕的 hMonitor 值
- 取得螢幕基本資訊(位置、主螢幕)
dxva2.dll
- GetNumberOfPhysicalMonitorsFromHMONITOR
- GetPhysicalMonitorsFromHMONITOR
- DestroyPhysicalMonitors
- GetMonitorBrightness
- 根據 hMonitor值來取得顯示器的數量
- 將 HMONITOR 轉成「實體螢幕」
- 和GetPhysicalMonitorsFromHMONITOR配套,在程式最後又來銷毀handle
- 用來取得螢幕亮度/最大值/最小值,對筆電螢幕無效
正確流程
DXVA2 不是用來「列舉螢幕」的,它是用來控制實體螢幕(亮度、對比)。
1. 所以要先用 User32 列舉所有 HMONITOR
2. 對每個 HMONITOR 用 Dxva2 取得「實體螢幕(Physical Monitor)」
EnumDisplayMonitors
↓
HMONITOR
↓
GetNumberOfPhysicalMonitorsFromHMONITOR
↓
GetPhysicalMonitorsFromHMONITOR
↓
PHYSICAL_MONITOR[]
這個範例在取得螢幕MonitorData,也把名稱加到Combobox1上,之後的應用可以隨著Combobox1的選擇
反饋comboBox1.SelectedIndex去選取monitors[i].hMonitor
private void button1_Click(object sender, EventArgs e)
{
monitors.Clear();
comboBox1.Items.Clear();
EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, MonitorEnumProc, IntPtr.Zero);
// 示範:列出 List 裡的資料
for (int i = 0; i < monitors.Count; i++)
{
var m = monitors[i];
Console.WriteLine($"[{i}] Handle : 0x{m.hMonitor.ToInt64():X}");
Console.WriteLine($" Device : {m.DeviceName}");
Console.WriteLine($" Primary : {m.IsPrimary}");
Console.WriteLine($" Bounds : {m.Monitor.Left},{m.Monitor.Top},{m.Monitor.Right},{m.Monitor.Bottom}");
Console.WriteLine();
comboBox1.Items.Add(monitors[i].DeviceName);
}
comboBox1.SelectedIndex = 0;
}
// 儲存所有螢幕資料
static List monitors = new List();
struct MonitorData
{
public IntPtr hMonitor;
public RECT Monitor;
public RECT WorkArea;
public bool IsPrimary;
public string DeviceName;
}
// ====== Win32 Struct ======
[StructLayout(LayoutKind.Sequential)]
struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct MONITORINFOEX
{
public int cbSize;
public RECT rcMonitor;
public RECT rcWork;
public uint dwFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string szDevice;
}
// ====== Win32 Delegate ======
delegate bool MonitorEnumDelegate(
IntPtr hMonitor,
IntPtr hdcMonitor,
ref RECT lprcMonitor,
IntPtr dwData
);
// ====== Win32 API ======
[DllImport("user32.dll")]
static extern bool EnumDisplayMonitors(
IntPtr hdc,
IntPtr lprcClip,
MonitorEnumDelegate lpfnEnum,
IntPtr dwData
);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern bool GetMonitorInfo(
IntPtr hMonitor,
ref MONITORINFOEX lpmi
);
// ====== Callback ======
static bool MonitorEnumProc(
IntPtr hMonitor,
IntPtr hdcMonitor,
ref RECT lprcMonitor,
IntPtr dwData)
{
MONITORINFOEX info = new MONITORINFOEX();
info.cbSize = Marshal.SizeOf(typeof(MONITORINFOEX));
if (GetMonitorInfo(hMonitor, ref info))
{
/*
Console.WriteLine("====== Monitor ======");
Console.WriteLine($"hMonitor : 0x{hMonitor.ToInt64():X}");
Console.WriteLine($"Device : {info.szDevice}");
Console.WriteLine($"Primary : {(info.dwFlags == 1)}");
Console.WriteLine($"Bounds : {info.rcMonitor.Left}, {info.rcMonitor.Top}, {info.rcMonitor.Right}, {info.rcMonitor.Bottom}");
Console.WriteLine($"WorkArea : {info.rcWork.Left}, {info.rcWork.Top}, {info.rcWork.Right}, {info.rcWork.Bottom}");
Console.WriteLine();
*/
MonitorData data = new MonitorData
{
hMonitor = hMonitor,
Monitor = info.rcMonitor,
WorkArea = info.rcWork,
IsPrimary = (info.dwFlags & 1) != 0,
DeviceName = info.szDevice
};
monitors.Add(data);
}
return true; // 繼續列舉
}
上個例子把所有螢幕的 hMonitor 儲存在monitors[i].hMonitor裡,接下來就用它來調整亮度
重點先講清楚(很重要)
- HMONITOR 不能直接調亮度
- 必須透過 Dxva2.dll 轉成 Physical Monitor
- 一個 hMonitor 可能對應多個實體螢幕
- 取得亮度API GetMonitorBrightness
- 設定亮度API SetMonitorBrightness
使用流程
DXVA2 不是用來「列舉螢幕」的,它是用來控制實體螢幕(亮度、對比)。
1. 所以要先用 User32 列舉所有 HMONITOR
2. 對每個 HMONITOR 用 Dxva2 取得「實體螢幕(Physical Monitor)」
hMonitor
↓
GetNumberOfPhysicalMonitorsFromHMONITOR
↓
GetPhysicalMonitorsFromHMONITOR
↓
GetMonitorBrightness / SetMonitorBrightness
加上Dxva2 P/Invoke
// ====== DVXA2 ======
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct PHYSICAL_MONITOR
{
public IntPtr hPhysicalMonitor;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string szPhysicalMonitorDescription;
}
[DllImport("dxva2.dll", SetLastError = true)]
static extern bool GetNumberOfPhysicalMonitorsFromHMONITOR(
IntPtr hMonitor,
out uint numberOfPhysicalMonitors
);
[DllImport("dxva2.dll", SetLastError = true)]
static extern bool GetPhysicalMonitorsFromHMONITOR(
IntPtr hMonitor,
uint physicalMonitorArraySize,
[Out] PHYSICAL_MONITOR[] physicalMonitorArray
);
[DllImport("dxva2.dll", SetLastError = true)]
static extern bool DestroyPhysicalMonitors(
uint physicalMonitorArraySize,
PHYSICAL_MONITOR[] physicalMonitorArray
);
[DllImport("dxva2.dll", SetLastError = true)]
static extern bool GetMonitorBrightness(
IntPtr hMonitor,
out uint minimumBrightness,
out uint currentBrightness,
out uint maximumBrightness
);
[DllImport("dxva2.dll", SetLastError = true)]
static extern bool SetMonitorBrightness(
IntPtr hMonitor,
uint newBrightness
);
取得亮度
// ====== Get Brightness ======
static uint GetBrightness(IntPtr hMonitor)
{
uint value = 0;
if (!GetNumberOfPhysicalMonitorsFromHMONITOR(hMonitor, out uint count) || count == 0)
return value;
PHYSICAL_MONITOR[] pms = new PHYSICAL_MONITOR[count];
if (GetPhysicalMonitorsFromHMONITOR(hMonitor, count, pms))
{
for (int i = 0; i < count; i++)
{
if (GetMonitorBrightness(
pms[i].hPhysicalMonitor,
out uint min,
out uint cur,
out uint max))
{
Console.WriteLine($"Brightness: {cur} (Min:{min}, Max:{max})");
value = cur;
}
else
{
Console.WriteLine($"Not Support");
}
}
DestroyPhysicalMonitors(count, pms);
}
return value;
}
設定亮度
// ====== Set Brightness ======
static void SetBrightness(IntPtr hMonitor, int percent)
{
if (!GetNumberOfPhysicalMonitorsFromHMONITOR(hMonitor, out uint count) || count == 0)
return;
PHYSICAL_MONITOR[] pms = new PHYSICAL_MONITOR[count];
if (GetPhysicalMonitorsFromHMONITOR(hMonitor, count, pms))
{
for (int i = 0; i < count; i++)
{
if (GetMonitorBrightness(
pms[i].hPhysicalMonitor,
out uint min,
out uint cur,
out uint max))
{
uint newValue = (uint)(min + (max - min) * percent / 100);
SetMonitorBrightness(pms[i].hPhysicalMonitor, newValue);
}
else
{
Console.WriteLine($"Not Support");
}
}
DestroyPhysicalMonitors(count, pms);
}
}
應用
//取得亮度
private void button3_Click(object sender, EventArgs e)
{
uint brightness = 0;
int index = comboBox1.SelectedIndex;
IntPtr hMonitor = monitors[index].hMonitor;
brightness = GetBrightness(hMonitor);
textBox1.Text = brightness.ToString();
}
//設定亮度
private void button4_Click(object sender, EventArgs e)
{
int brightness = 0;
try
{
brightness = Convert.ToInt32(textBox2.Text);
}
catch (Exception ex)
{
textBox2.Text = 0.ToString();
}
brightness = brightness % 101;
int index = comboBox1.SelectedIndex;
IntPtr hMonitor = monitors[index].hMonitor;
SetBrightness(hMonitor, brightness);
}

留言
張貼留言