windows 在 Delphi 中使用 ChangeDisplaySettingsEx 设置主显示器
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/956870/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Using ChangeDisplaySettingsEx in Delphi to set primary monitor
提问by tim11g
I'm trying to use ChangeDisplaySettingsEx in Delphi 7 to set a specific monitor as Primary. In Windows.pas, it is defined as
我正在尝试在 Delphi 7 中使用 ChangeDisplaySettingsEx 将特定监视器设置为主要监视器。在 Windows.pas 中,它被定义为
function ChangeDisplaySettingsEx(lpszDeviceName: PChar; var lpDevMode: TDeviceMode;
wnd: HWND; dwFlags: DWORD; lParam: Pointer): Longint; stdcall;
In MSDN, the documentation for ChangeDisplaySettingsEx has the following comment for lpDevMode: "If lpDevMode is NULL, all the values currently in the registry will be used for the display setting."
在MSDN 中,ChangeDisplaySettingsEx 的文档对 lpDevMode 有以下注释:“如果 lpDevMode 为 NULL,则当前在注册表中的所有值都将用于显示设置。”
My objective is to change the Primary monitor on a system with two active monitors, without changing anything else - resolution, bit depth, etc, should all remain the same. It seems like passing lpDevMode as null (nil) is the method that is provided to accomplish this.
我的目标是更改具有两个活动监视器的系统上的主监视器,而不更改任何其他内容 - 分辨率、位深度等都应保持不变。似乎将 lpDevMode 传递为 null (nil) 是为实现此目的而提供的方法。
However, lpDevMode is defined as a packed record type (TDeviceMode), not a pointer type, in Delphi's Windows.pas. Apparently, the Delphi interface to the Windows API performs the translation to the pointers used by Windows API 'behind the scenes'.
但是,lpDevMode 在 Delphi 的 Windows.pas 中被定义为打包记录类型 (TDeviceMode),而不是指针类型。显然,Windows API 的 Delphi 接口执行转换为 Windows API“幕后”使用的指针。
I tried calling it like this:
我试着这样称呼它:
var
alldevs : array[0..maxdev] of TDisplayDevice;
lpDevMode : pointer;
begin
lpDevMode := nil;
lparam := nil;
my_hwnd := nil;
{... snip....}
with alldevs[NewPri] do
ChangeDisplaySettingsEx(devicename,TDeviceMode(lpDevMode),my_hwnd,CDS_SET_PRIMARY,lparam);
That gives me an invalid typecast error on "TDeviceMode(lpDevMode)". How can I pass a null pointer to ChangeDisplaySettingsEx? Or is there a better way to do this?
这给了我一个关于“TDeviceMode(lpDevMode)”的无效类型转换错误。如何将空指针传递给 ChangeDisplaySettingsEx?或者有没有更好的方法来做到这一点?
回答by mghie
You could try
你可以试试
ChangeDisplaySettingsEx(devicename, PDeviceMode(0)^, my_hwnd, CDS_SET_PRIMARY,
lparam);
it does at least compile on Delphi 2009. I can't test it though.
它至少可以在 Delphi 2009 上编译。不过我无法对其进行测试。
Edit:
编辑:
According to the scarce information on the net (this is the most detailed I could find) changing the primary display isn't a simple process, so you may be missing a step along the way. I have two monitors, but can't change the primary device at all, not even with the control panel - looks like the dual-head display card driver doesn't allow it. The following therefore isn't tested, but maybe it will help you:
根据网络上的稀缺信息(这是我能找到的最详细的信息),更改主显示器不是一个简单的过程,因此您可能会错过一个步骤。我有两台显示器,但根本无法更改主设备,甚至无法更改控制面板 - 看起来双头显卡驱动程序不允许这样做。因此,以下内容未经测试,但也许会对您有所帮助:
In order to set a new primary display you have to move the current primary display away from the (0, 0) position first. This is more tricky than it needs to be, because the Delphi Windows.pas
file has an incomplete TDeviceMode type. It is given as
为了设置新的主显示器,您必须先将当前的主显示器从 (0, 0) 位置移开。这比它需要的更棘手,因为 DelphiWindows.pas
文件有一个不完整的 TDeviceMode 类型。它被给出为
_devicemodeA = record
dmDeviceName: array[0..CCHDEVICENAME - 1] of AnsiChar;
...
dmOrientation: SHORT;
dmPaperSize: SHORT;
dmPaperLength: SHORT;
dmPaperWidth: SHORT;
dmScale: SHORT;
dmCopies: SHORT;
dmDefaultSource: SHORT;
dmPrintQuality: SHORT;
dmColor: SHORT;
...
end;
when it should instead be
什么时候应该是
_devicemodeA = record
dmDeviceName: array[0..CCHDEVICENAME - 1] of AnsiChar;
...
case boolean of
FALSE: (
dmOrientation: SHORT;
dmPaperSize: SHORT;
dmPaperLength: SHORT;
dmPaperWidth: SHORT;
dmScale: SHORT;
dmCopies: SHORT;
dmDefaultSource: SHORT;
dmPrintQuality: SHORT;
);
TRUE: (
dmPosition: TPoint;
dmDisplayOrientation: DWORD;
dmDisplayFixedOutput: DWORD;
);
dmColor: SHORT;
...
end;
You should add the fixed record type to your sources, as you need dmPosition
to adjust the origin of the displays. It should go something like this:
您应该将固定记录类型添加到源中,因为您需要dmPosition
调整显示的原点。它应该是这样的:
// get current display settings
EnumDisplaySettings(PChar(AOldPrimaryDevice), ENUM_REGISTRY_SETTINGS, DevMode1);
EnumDisplaySettings(PChar(ANewPrimaryDevice), ENUM_REGISTRY_SETTINGS, DevMode2);
// move old primary display to new position
DevMode1.dmFields := DM_POSITION;
DevMode1.dmPosition.x := DevMode2.dmPelsWidth;
DevMode1.dmPosition.y := 0;
Win32Check(ChangeDisplaySettingsEx(PChar(AOldPrimaryDevice), DevMode1, 0,
CDS_UPDATEREGISTRY or CDS_NORESET, nil)):
// move old secondary display to (0, 0) and make the primary display
DevMode2.dmFields := DM_POSITION;
DevMode2.dmPosition.x := 0;
DevMode2.dmPosition.y := 0;
Win32Check(ChangeDisplaySettingsEx(PChar(ANewPrimaryDevice), DevMode2, 0,
CDS_SET_PRIMARY or CDS_UPDATEREGISTRY or CDS_NORESET or DM_DISPLAYFLAGS, nil)):
// magic ???
Win32Check(ChangeDisplaySettingsEx(nil, PDeviceMode(0)^, 0, 0, nil));
回答by Zo? Peterson
I haven't verified it, but there's a post in the MSDN forumsthat covers this and includes C++ code. The "primary" monitor is the one at position 0,0, so you need to rearrange the positions of the monitors to make that happen.
我还没有验证它,但MSDN 论坛中有一篇文章涵盖了这一点并包括 C++ 代码。“主要”监视器是位置 0,0 的监视器,因此您需要重新排列监视器的位置以实现这一点。
回答by Whome
This is my findings after browsing numerous C++ and Delphi posts around the internet.
这是我在网上浏览了大量 C++ 和 Delphi 帖子后的发现。
- did not want to declare a new TMyDevMode type with union options for missing fields
- use existing TDevMode type but set missing fields with Move() memcopy command and 32bit signed temporary variable
- move away current primary desktop from 0,0 pos, do not apply changes yet
- set new current primary desktop to 0,0 with CDS_SET_PRIMARY flag, do not apply changes yet
- xy position and width,height should not overlap but it seems Win7 can resolve some problems by itself
- call ChangeDisplaySettingsEx with null parameters to apply all pending changes
- 不想为缺少的字段声明一个带有联合选项的新 TMyDevMode 类型
- 使用现有的 TDevMode 类型,但使用 Move() memcopy 命令和 32 位有符号临时变量设置缺失的字段
- 将当前主桌面从 0,0 位置移开,暂不应用更改
- 使用 CDS_SET_PRIMARY 标志将新的当前主桌面设置为 0,0,暂不应用更改
- xy位置和宽度,高度不应重叠,但似乎Win7可以自行解决一些问题
- 使用 null 参数调用 ChangeDisplaySettingsEx 以应用所有挂起的更改
Set dmPosition.x and dmPosition.y values, use memory offset:
设置 dmPosition.x 和 dmPosition.y 值,使用内存偏移:
var dm: TDevMode;
var tempx, tempy: Integer;
Move(tempx, dm.dmOrientation, sizeOf(tempx));
Move(tempy, dm.dmPaperLength, sizeOf(tempy));
Get dmPosition.x and dmPosition.y values, use memory offset:
获取 dmPosition.x 和 dmPosition.y 值,使用内存偏移:
var dm: TDevMode;
var tempx, tempy: Integer;
Move(dm.dmOrientation, tempx, sizeOf(tempx));
Move(dm.dmPaperLength, tempy, sizeOf(tempy));
Set primary desktop from display1 to display2 instance. Make changes then apply all pending changes:
将主桌面从 display1 设置为 display2 实例。进行更改然后应用所有挂起的更改:
flags := CDS_UPDATEREGISTRY or CDS_NORESET;
ChangeDisplaySettingsEx('\.\DISPLAY1', devMode1, 0, flags, nil);
flags := CDS_UPDATEREGISTRY or CDS_SET_PRIMARY or CDS_NORESET;
ChangeDisplaySettingsEx('\.\DISPLAY2', devMode2, 0, flags, nil);
ChangeDisplaySettingsEx(nil, PDeviceMode(0)^, 0, 0, nil);