将dmDisplayFixedOutput 设置为DMDFO_CENTER(docs) 可能会奏效。
但您也可能会在所有四个侧面都看到黑条。
它也可能没有任何区别。尝试此操作时,我在不同显示器上的结果确实不一致。您的显示器设置肯定也会在这里发挥作用。
但无论如何,我想值得一试。
让我们从 _devicemodeA(docs) 结构开始。
我添加了每个成员的大小(以字节为单位)及其偏移量:
typedef struct _devicemodeA { // size | offset
BYTE dmDeviceName[CCHDEVICENAME]; // 32 0
WORD dmSpecVersion; // 2 32
WORD dmDriverVersion; // 2 34
WORD dmSize; // 2 36
WORD dmDriverExtra; // 2 38
DWORD dmFields; // 4 40
union {
/* printer only fields */
struct {
short dmOrientation; // 2 44
short dmPaperSize; // 2 46
short dmPaperLength; // 2 48
short dmPaperWidth; // 2 50
short dmScale; // 2 52
short dmCopies; // 2 54
short dmDefaultSource; // 2 56
short dmPrintQuality; // 2 58
} DUMMYSTRUCTNAME;
/* display only fields */
struct {
POINTL dmPosition; // 8 44
DWORD dmDisplayOrientation; // 4 52
DWORD dmDisplayFixedOutput; // 4 56
} DUMMYSTRUCTNAME2;
} DUMMYUNIONNAME;
short dmColor; // 2 60
short dmDuplex; // 2 62
short dmYResolution; // 2 64
short dmTTOption; // 2 66
short dmCollate; // 2 68
BYTE dmFormName[CCHFORMNAME]; // 32 70
WORD dmLogPixels; // 2 102
DWORD dmBitsPerPel; // 4 104
DWORD dmPelsWidth; // 4 108
DWORD dmPelsHeight; // 4 112
union {
DWORD dmDisplayFlags; // 4 116
DWORD dmNup; // 4 116
} DUMMYUNIONNAME2;
DWORD dmDisplayFrequency; // 4 120
#if(WINVER >= 0x0400)
DWORD dmICMMethod; // 4 124
DWORD dmICMIntent; // 4 128
DWORD dmMediaType; // 4 132
DWORD dmDitherType; // 4 136
DWORD dmReserved1; // 4 140
DWORD dmReserved2; // 4 144
#if (WINVER >= 0x0500) || (_WIN32_WINNT >= _WIN32_WINNT_NT4)
DWORD dmPanningWidth; // 4 148
DWORD dmPanningHeight; // 4 152
#endif //-----
#endif /* WINVER >= 0x0400 */ // 156 (total)
} DEVMODEA, *PDEVMODEA, *NPDEVMODEA, *LPDEVMODEA;
我们对dmSize、dmFields、dmDisplayFixedOutput、dmPelsWidth 和 dmPelsHeight 成员感兴趣。
在其他实现上,例如 dmDeviceName、dmPosition、dmDisplayOrientation 和 dmDisplayFrequency 成员也可能非常有趣。
所以我们首先创建一个 DEVMODE 结构并用我们的显示设备的当前信息填充它。这样我们就可以只改变我们想要的,而不是在那里设置每一条所需的信息。
所以我们创建了一个变量 _DEVMODE 并为其保留 156 个字节。
156 来自我们上面计算的大小。
保留的东西只是你为AHK DllCalling做的事情,没什么特别的。
VarSetCapacity(_DEVMODE, 156)
然后我们可以使用EnumDisplaySettingsA(docs)函数来填充结构。
但首先我们注意到文档状态:
在调用 EnumDisplaySettings 之前,将 dmSize 成员设置为 sizeof(DEVMODE)
让我们这样做吧,我们知道大小为 156 字节,并且我们知道 dmSize 成员的偏移量为 36 字节。
NumPut()(docs) 被使用在某个内存地址操作内存:
NumPut(156, _DEVMODE, 36)
那么我们就可以打电话给EnumDisplaySettingsA了:
DllCall("EnumDisplaySettingsA", Ptr, 0, UInt, ENUM_CURRENT_SETTINGS := -1, UInt, &_DEVMODE)
参数:
-
lpszDeviceName:Ptr 0 传入表示NULL(docs),表示使用当前显示设备。更高级的解决方案可以在这里选择要使用的显示器。
-
iModeNum:UInt ENUM_CURRENT_SETTINGS(其值为-1)传入以指示使用当前使用的设置,而不是存储在注册表中的设置。
-
*lpDevMode:UInt &_DEVMODE传入,表示我们的_DEVMODE结构的指针。
&(docs)用来获取指针。
现在应该成功填充结构。
测试它可能很好,所以让我们尝试使用NumGet()(docs) 来获得刷新率作为测试:
MsgBox, % NumGet(_DEVMODE, 120) " Hz"
现在我们可以开始更改分辨率并设置DMDFO_CENTER 标志以希望获得所需的黑条。
首先,我们将标志设置为dmFields 成员,以表明我们正在使用一些成员。
我们需要设置DM_PELSHEIGHT、DM_PELSWIDTH 和DM_DISPLAYFIXEDOUTPUT 标志,因为我们要更改高度、宽度和“固定输出”状态。
DM_PELSHEIGHT := 0x00100000
DM_PELSWIDTH := 0x00080000
DM_DISPLAYFIXEDOUTPUT := 0x20000000
; technically + instead of | (bitwise or) would work the same
flags := DM_PELSHEIGHT | DM_PELSWIDTH | DM_DISPLAYFIXEDOUTPUT
那我们可以NumPut。我们知道dmFields 的偏移量为 40。
NumPut(flags, _DEVMODE, 40)
然后我们可以将NumPut我们想要的分辨率设置为dmPelsWidth(偏移量108)和dmPelsHeight(偏移量112):
NumPut(1920, _DEVMODE, 108)
NumPut(1080, _DEVMODE, 112)
然后NumPut 将DMDFO_CENTER 标志设置为dmDisplayFixedOutput(偏移量56):
NumPut(DMDFO_CENTER := 2, _DEVMODE, 56)
现在我们的结构应该被我们想要的东西填充了。
注意:如果您要改回显示器的原始分辨率,请不要尝试使用DMDFO_CENTER 或DMDFO_STRETCH 标志。使用DMDFO_DEFAULT,或者根本不使用dmDisplayFixedOutput 成员。
现在剩下的就是使用ChangeDisplaySettingsA(docs) 函数来设置我们的更改:
DllCall("ChangeDisplaySettingsA", UInt, &_DEVMODE, UInt, 0)
同样,传入我们结构的指针,然后是 UInt 0 标志,以指示立即更改显示设置。
完整示例脚本
#NoEnv
ENUM_CURRENT_SETTINGS := -1
DM_PELSHEIGHT := 0x00100000
DM_PELSWIDTH := 0x00080000
DM_DISPLAYFIXEDOUTPUT := 0x20000000
DMDFO_DEFAULT := 0
DMDFO_STRETCH := 1
DMDFO_CENTER := 2
VarSetCapacity(_DEVMODE, 156)
NumPut(156, _DEVMODE, 36) ;dmSize
DllCall("EnumDisplaySettingsA", Ptr, 0, UInt, ENUM_CURRENT_SETTINGS, UInt, &_DEVMODE)
MsgBox, % NumGet(_DEVMODE, 120) " Hz"
; technically + instead of | (bitwise or) would work the same
flags := DM_PELSHEIGHT | DM_PELSWIDTH | DM_DISPLAYFIXEDOUTPUT
NumPut(flags, _DEVMODE, 40) ;dmFields
NumPut(1920, _DEVMODE, 108) ;dmPelsWidth
NumPut(1080, _DEVMODE, 112) ;dmPelsHeight
NumPut(DMDFO_CENTER, _DEVMODE, 56) ;dmDisplayFixedOutput
DllCall("ChangeDisplaySettingsA", UInt, &_DEVMODE, UInt, 0)
作为函数的完整示例脚本
#NoEnv
DMDFO_DEFAULT := 0
DMDFO_STRETCH := 1
DMDFO_CENTER := 2
F1::ChangeDisplaySettings(1920, 1080, DMDFO_CENTER)
F2::ChangeDisplaySettings(3840, 1600, DMDFO_DEFAULT)
ChangeDisplaySettings(width, height, DMDFO)
{
static ENUM_CURRENT_SETTINGS := -1
, DM_PELSHEIGHT := 0x00100000
, DM_PELSWIDTH := 0x00080000
, DM_DISPLAYFIXEDOUTPUT := 0x20000000
VarSetCapacity(_DEVMODE, 156)
NumPut(156, _DEVMODE, 36) ;dmSize
DllCall("EnumDisplaySettingsA", Ptr, 0, UInt, ENUM_CURRENT_SETTINGS, UInt, &_DEVMODE)
; technically + instead of | (bitwise or) would work the same
flags := DM_PELSHEIGHT | DM_PELSWIDTH | DM_DISPLAYFIXEDOUTPUT
NumPut(flags, _DEVMODE, 40) ;dmFields
NumPut(width, _DEVMODE, 108) ;dmPelsWidth
NumPut(height, _DEVMODE, 112) ;dmPelsHeight
NumPut(DMDFO, _DEVMODE, 56) ;dmDisplayFixedOutput
return DllCall("ChangeDisplaySettingsA", UInt, &_DEVMODE, UInt, 0)
}
对于您可能最终不会使用的东西,相当长且可能过于详细的帖子,但是,我正在避免学校作业。
写起来很有趣,也许至少有人可以从中学到一些东西。