この内容はDelphi-ML「[Delphi:90115] ウインドウを最大化したまま最大化サイズを調節する」で付いたレスを参考にしています。 教えてくださる皆様、感謝感謝です。
モニタの範囲を知るためにはWindows APIの「GetMonitorInfo」を使う。
このAPIはマルチモニタ対応のAPIで、ディスプレイモニタのハンドルを渡してあげる事によって、そのディスプレイモニタに関する情報を取得する事が出来ます。
情報はMONITORINFO 構造体によって返され、以下のような情報が帰ってきます。
モニタの範囲
MonitorInfo.rcMonitor.Bottom
MonitorInfo.rcMonitor.Top
MonitorInfo.rcMonitor.Left
MonitorInfo.rcMonitor.Right
モニタの有効面の範囲
MonitorInfo.rcWork.Bottom
MonitorInfo.rcWork.Top
MonitorInfo.rcWork.Left
MonitorInfo.rcWork.Right
以下の例では赤い四角がモニタの範囲、緑色の範囲がモニタの有効面の範囲になってます。
以下の例ではタスクバーの分が有効面から除外されています。
これは、ウインドウを最大化したときに使える範囲=有効面だからです。
なので、他のソフトでデスクトップを占有するタイプのアプリケーションがある場合はその分も
有効面から除外されます
#ref(): File not found: "Monitor1.jpg" at page "Delphiでウインドウの最大化時の大きさをコントロール(マルチモニタ対応)"
取得出来る値は、プライマリモニタの左上を原点として数値が決まります。
2台目以降のモニタの位置はある程度好きなように置ける様です。
プライマリモニタより左・上のモニタは取得出来る値が当然マイナスで帰って来ます。
※ぜんぜん関係ない話ですが、上の図はExcelで作っている物を、セル範囲のコピーってそのままペイント等に画像として貼り付けしてます。
知らなかったです…なんとなく「出来るかな?」と思ってやって出来てびっくり。
Delphiのコードだとこんな感じです
var FMonitorInfo: TMonitorInfo; begin ZeroMemory(@FMonitorInfo, sizeof(FMonitorInfo)); FMonitorInfo.cbSize := SizeOf(FMonitorInfo); GetMonitorInfo(FTargetForm.Monitor.Handle, @FMonitorInfo);
procedure WMSysCommand(var Message: TWMSysCommand); message WM_SYSCOMMAND;
の関数の中で使われている「msg.MinMaxInfo.ptMaxPosition.X」「msg.MinMaxInfo.ptMaxPosition.Y」なのですが、よくよく調べてみるとココに入れる値は
// GetMonitorInfo,Self.Top,Self.Leftで帰ってくる値は // プライマリモニタの左上を原点(0,0)とした値が帰ってくるが、 // msg.MinMaxInfo.ptMaxPositionは // BorderStyleがbsSizeableおよびbsSingleの場合のみ // 各種モニタごとの有効面の左上を原点(0,0) // とした座標を指定しなければいけない // それ以外のBorderStyleでは // 各種モニタごとの左上を原点(0,0)とした座標を指定する。
という事らしい。
ウインドウのモードによって変わるなんて…あああ大混乱する…。
マルチディスプレイ対応です。
この内容はDelphi-ML「[Delphi:90115] ウインドウを最大化したまま最大化サイズを調節する」で付いたレスを参考にしています。
教えてくださる皆様、感謝感謝です。
2008/1/24修正 ウインドウがbsSizeable・bsSingle以外のモードの場合が動きおかしかったのを修正
ダウンロード
#ref(): File not found: "MultiMonitorParam20080127.ZIP" at page "Delphiでウインドウの最大化時の大きさをコントロール(マルチモニタ対応)"
{*****************************************************************************} { } { MultiMonitorParam } { } { Copyright (c) 2007 Moon Doldo } { } {この内容はDelphi-ML } {[Delphi:90115] ウインドウを最大化したまま最大化サイズを調節する } {で付いたレスを参考にしています。 } { } {*****************************************************************************} unit MultiMonitorParam; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, Math, MultiMon; type // MonitorInfo保持クラス TMonitorInfoParam = class private FMonitorInfo : TMonitorInfo; FDisplayMonitorInfo: TMonitorInfo; FTargetForm: TForm; procedure SetTargetForm(const Value: TForm); protected // MonitorInfoの取得 procedure GetMonitorInfoParam; public // 対象のウインドウを設定 property TargetForm: TForm read FTargetForm write SetTargetForm; function MonitorInfo: TMonitorInfo; function DisplayMonitorInfo: TMonitorInfo; // モニタの数を取得 function MonitorCount: Integer; // モニタ番号の取得 function MonitorNum: Integer; end; // 最大化の管理クラス TMultiMonitorMaxInfoCalc = class(TMonitorInfoParam) private FMaxSizeY: Integer; FMaxSizeX: Integer; FMaxPositionY: Integer; FMaxPositionX: Integer; FMaxAlign: TAlign; FSetWMGetMinMaxInfo2: Boolean; procedure SetMaxSizeX(const Value: Integer); procedure SetMaxSizeY(const Value: Integer); procedure SetMaxPositionX(const Value: Integer); procedure SetMaxPositionY(const Value: Integer); procedure SetMaxAlign(const Value: TAlign); protected // フォームの大きさと位置の制限 procedure WMClientGetMinMaxInfo(var msg : TWMGetMinMaxInfo); procedure WMCustomGetMinMaxInfo(var msg : TWMGetMinMaxInfo); procedure WMTopGetMinMaxInfo(var msg : TWMGetMinMaxInfo); procedure WMBottomGetMinMaxInfo(var msg : TWMGetMinMaxInfo); procedure WMLeftGetMinMaxInfo(var msg : TWMGetMinMaxInfo); procedure WMRightGetMinMaxInfo(var msg : TWMGetMinMaxInfo); public //TObjectのコンストラクタ定義 constructor Create; //デストラクタ定義 destructor Destroy; override; // フォームの大きさと位置の制限 procedure WMGetMinMaxInfo(var msg : TWMGetMinMaxInfo); // フォームの大きさと位置の制限(既に最大化している場合) procedure WMGetMinMaxInfo2; // 通常の状態のフォームを適切な位置へ移動する procedure SetFormSafePosition; published // 設定したいウインドウサイズを指定する // alLeft, alRight, alCustomの場合のみ実行される property MaxSizeX: Integer read FMaxSizeX write SetMaxSizeX; // alTop, alBottom, alCustomの場合のみ実行される property MaxSizeY: Integer read FMaxSizeY write SetMaxSizeY; // 設定したい位置を指定する(各種モニタの有効面の左上を原点(0,0)) // alCustomの場合のみ実行される property MaxPositionX: Integer read FMaxPositionX write SetMaxPositionX; property MaxPositionY: Integer read FMaxPositionY write SetMaxPositionY; // 最大化の際に、モニタ内でコントロールを揃える方法を指定します。 property MaxAlign: TAlign read FMaxAlign write SetMaxAlign; end; implementation { TMultiMonitorMaxInfoCalc } constructor TMultiMonitorMaxInfoCalc.Create; begin FMaxAlign := alCustom; FMaxSizeY := 0; FMaxSizeX := 0; FMaxPositionY := 0; FMaxPositionX := 0; FSetWMGetMinMaxInfo2 := False; end; destructor TMultiMonitorMaxInfoCalc.Destroy; begin inherited; end; procedure TMultiMonitorMaxInfoCalc.SetFormSafePosition; var WkMonitorInfo : TMonitorInfo; WkMonitorInfoWidth, WkMonitorInfoHeight: Integer; WkTargetFormRight, WkTargetFormBottom: Integer; WkLeft, WkTop: Integer; begin if FTargetForm.WindowState = wsNormal then begin // マルチモニタ対応の有効面の取得 WkMonitorInfo := Self.MonitorInfo; WkLeft := FTargetForm.Left; WkTop := FTargetForm.Top; // 左上が有効面からはみ出ていたら直す if WkLeft < WkMonitorInfo.rcWork.Left then begin WkLeft := WkMonitorInfo.rcWork.Left; end; if WkTop < WkMonitorInfo.rcWork.Top then begin WkTop := WkMonitorInfo.rcWork.Top; end; // 右下が有効面からはみ出ていたら直す // ただし、有効面のよりフォームが小さい場合のみ行う(左上を優先にするため) WkMonitorInfoWidth := WkMonitorInfo.rcWork.Right - WkMonitorInfo.rcWork.Left; if FTargetForm.Width <= WkMonitorInfoWidth then begin WkTargetFormRight := WkLeft + FTargetForm.Width; if WkTargetFormRight > WkMonitorInfo.rcWork.Right then begin WkLeft := WkMonitorInfo.rcWork.Right - FTargetForm.Width; end; end; WkMonitorInfoHeight := WkMonitorInfo.rcWork.Bottom - WkMonitorInfo.rcWork.Top; if FTargetForm.Height <= WkMonitorInfoHeight then begin WkTargetFormBottom := WkTop + FTargetForm.Height; if WkTargetFormBottom > WkMonitorInfo.rcWork.Bottom then begin WkTop := WkMonitorInfo.rcWork.Bottom - FTargetForm.Height; end; end; FTargetForm.Left := WkLeft; FTargetForm.Top := WkTop; end; end; procedure TMultiMonitorMaxInfoCalc.SetMaxAlign(const Value: TAlign); begin FMaxAlign := Value; WMGetMinMaxInfo2; end; procedure TMultiMonitorMaxInfoCalc.SetMaxPositionX(const Value: Integer); begin FMaxPositionX := Value; end; procedure TMultiMonitorMaxInfoCalc.SetMaxPositionY(const Value: Integer); begin FMaxPositionY := Value; end; procedure TMultiMonitorMaxInfoCalc.SetMaxSizeX(const Value: Integer); begin FMaxSizeX := Value; end; procedure TMultiMonitorMaxInfoCalc.SetMaxSizeY(const Value: Integer); begin FMaxSizeY := Value; end; procedure TMultiMonitorMaxInfoCalc.WMBottomGetMinMaxInfo( var msg: TWMGetMinMaxInfo); var WkMonitorInfo : TMonitorInfo; WkSizeX, WkSizeY: Integer; WkPositionX, WkPositionY: Integer; begin // マルチモニタ対応の有効面の取得 WkMonitorInfo := Self.MonitorInfo; WkSizeX := WkMonitorInfo.rcWork.Right - WkMonitorInfo.rcWork.Left; WkSizeY := Min(FMaxSizeY, WkMonitorInfo.rcWork.Bottom - WkMonitorInfo.rcWork.Top); WkPositionX := WkMonitorInfo.rcWork.Left; WkPositionY := WkMonitorInfo.rcWork.Bottom - WkSizeY; msg.MinMaxInfo.ptMaxSize.X := WkSizeX; msg.MinMaxInfo.ptMaxSize.Y := WkSizeY; // GetMonitorInfo,Self.Top,Self.Leftで帰ってくる値は // プライマリモニタの左上を原点(0,0)とした値が帰ってくるが、 // msg.MinMaxInfo.ptMaxPositionは // BorderStyleがbsSizeableおよびbsSingleの場合のみ // 各種モニタごとの有効面の左上を原点(0,0) // とした座標を指定しなければいけない // それ以外のBorderStyleでは // 各種モニタごとの左上を原点(0,0)とした座標を指定する。 if (TargetForm.BorderStyle = bsSizeable) or (TargetForm.BorderStyle = bsSingle) or (FSetWMGetMinMaxInfo2 = True) then begin WkPositionX := WkPositionX - WkMonitorInfo.rcWork.Left; WkPositionY := WkPositionY - WkMonitorInfo.rcWork.Top; FSetWMGetMinMaxInfo2 := False; end else begin WkPositionX := WkPositionX - WkMonitorInfo.rcMonitor.Left; WkPositionY := WkPositionY - WkMonitorInfo.rcMonitor.Top; end; msg.MinMaxInfo.ptMaxPosition.X := WkPositionX; msg.MinMaxInfo.ptMaxPosition.Y := WkPositionY; end; procedure TMultiMonitorMaxInfoCalc.WMCustomGetMinMaxInfo( var msg: TWMGetMinMaxInfo); var WkMonitorInfo : TMonitorInfo; WkSizeX, WkSizeY: Integer; WkPositionX, WkPositionY: Integer; begin // マルチモニタ対応の有効面の取得 WkMonitorInfo := Self.MonitorInfo; // 位置の値を保持 // プライマリモニタの左上を原点(0,0)とした値に直す WkPositionX := FMaxPositionX + WkMonitorInfo.rcWork.Left; WkPositionY := FMaxPositionY + WkMonitorInfo.rcWork.Top; // 有効面から左上にはみ出ている場合は直す if WkPositionX < WkMonitorInfo.rcWork.Left then begin WkPositionX := WkMonitorInfo.rcWork.Left; end; if WkPositionY < WkMonitorInfo.rcWork.Top then begin WkPositionY := WkMonitorInfo.rcWork.Top; end; // 有効面以上の大きさにならないように大きさを修正する WkSizeX := Min(FMaxSizeX, WkMonitorInfo.rcWork.Right - WkMonitorInfo.rcWork.Left - FMaxPositionX); WkSizeY := Min(FMaxSizeY, WkMonitorInfo.rcWork.Bottom - WkMonitorInfo.rcWork.Top - FMaxPositionY); msg.MinMaxInfo.ptMaxSize.X := WkSizeX; msg.MinMaxInfo.ptMaxSize.Y := WkSizeY; // GetMonitorInfo,Self.Top,Self.Leftで帰ってくる値は // プライマリモニタの左上を原点(0,0)とした値が帰ってくるが、 // msg.MinMaxInfo.ptMaxPositionは // BorderStyleがbsSizeableおよびbsSingleの場合のみ // 各種モニタごとの有効面の左上を原点(0,0) // とした座標を指定しなければいけない // それ以外のBorderStyleでは // 各種モニタごとの左上を原点(0,0)とした座標を指定する。 if (TargetForm.BorderStyle = bsSizeable) or (TargetForm.BorderStyle = bsSingle) or (FSetWMGetMinMaxInfo2 = True) then begin WkPositionX := WkPositionX - WkMonitorInfo.rcWork.Left; WkPositionY := WkPositionY - WkMonitorInfo.rcWork.Top; FSetWMGetMinMaxInfo2 := False; end else begin WkPositionX := WkPositionX - WkMonitorInfo.rcMonitor.Left; WkPositionY := WkPositionY - WkMonitorInfo.rcMonitor.Top; end; msg.MinMaxInfo.ptMaxPosition.X := WkPositionX; msg.MinMaxInfo.ptMaxPosition.Y := WkPositionY; end; procedure TMultiMonitorMaxInfoCalc.WMGetMinMaxInfo( var msg: TWMGetMinMaxInfo); var WkMonitorInfo : TMonitorInfo; WkSizeX, WkSizeY: Integer; WkPositionX, WkPositionY: Integer; WkPositionRight, WkPositionBottom: Integer; begin case FMaxAlign of alNone: begin // 何もしないデフォルトの動作 Exit; end; alClient: begin WMClientGetMinMaxInfo(msg); end; alCustom: begin WMCustomGetMinMaxInfo(msg); end; alTop: begin WMTopGetMinMaxInfo(msg); end; alBottom: begin WMBottomGetMinMaxInfo(msg); end; alLeft: begin WMLeftGetMinMaxInfo(msg); end; alRight: begin WMRightGetMinMaxInfo(msg); end; else ; end; end; procedure TMultiMonitorMaxInfoCalc.WMGetMinMaxInfo2; var WkMonitorInfo : TMonitorInfo; WkWindowPlacement, WkSaveWindowPlacement : TWindowPlacement; WkSmCxframe, WkSmCyframe: Integer; WkSizeX, WkSizeY: Integer; WkPositionX, WkPositionY: Integer; begin // 最大化の時のみ実行される if IsZoomed(FTargetForm.Handle) then begin if (FMaxAlign <> alNone) and (FMaxAlign <> alClient) then begin // ウィンドウの配置情報を保持 ZeroMemory(@WkSaveWindowPlacement, sizeof(WkSaveWindowPlacement)); WkSaveWindowPlacement.length := sizeof(WkSaveWindowPlacement); GetWindowPlacement(FTargetForm.Handle, @WkSaveWindowPlacement); // フォームの最大化スタイルを無理やり消しちゃう SetWindowLong( FTargetForm.Handle, GWL_STYLE, GetWindowLong(FTargetForm.Handle, GWL_STYLE) and not WS_MAXIMIZE); // さらに最大化 // ただ、この方法だとBorderStyleがbsSizeable・bsSingle以外の場合でも // WMGetMinMaxInfoがbsSizeable・bsSingleとして認識してしまうため、 // その対策のためのフラグを立てて // 必ずBorderStyleがbsSizeable・bsSingleの扱いで動作させる // もっとスマートな方法ないのかな・・・ FSetWMGetMinMaxInfo2 := True; ShowWindow(FTargetForm.Handle, SW_MAXIMIZE); // 新しい位置情報を取得 ZeroMemory(@WkWindowPlacement, sizeof(WkWindowPlacement)); WkWindowPlacement.length := sizeof(WkWindowPlacement); GetWindowPlacement(FTargetForm.Handle, @WkWindowPlacement); // リストア時の情報のみ上書き WkWindowPlacement.rcNormalPosition := WkSaveWindowPlacement.rcNormalPosition; //保持していたウィンドウの配置情報を戻す SetWindowPlacement(FTargetForm.Handle, @WkWindowPlacement); end else begin // 上記のコードで全部まかなえると思ったら通常の最大化だけおかしくなるので // 最大化のコードだけ以下のものを使う // 通常の最大化 // 通常、Windowsでは最大化した際にウインドウの枠は有効面の外にはみ出る // 大きさは有効面より左右2辺、上下2辺の枠の分だけ大きくなる。 // ウインドウの位置も左1辺、上1辺分だけ左上にずれる事になる。 WkMonitorInfo := Self.MonitorInfo; if FTargetForm.BorderStyle <> bsNone then begin // ウインドウの枠の幅を取得 WkSmCxframe := GetSystemMetrics(SM_CXFRAME); WkSmCyframe := GetSystemMetrics(SM_CYFRAME); end else begin // BorderStyleがbsNoneの場合は枠がないため枠の大きさを考慮しない WkSmCxframe := 0; WkSmCyframe := 0; end; WkPositionX := WkMonitorInfo.rcWork.Left - WkSmCxframe; WkPositionY := WkMonitorInfo.rcWork.Top - WkSmCyframe; WkSizeX := WkMonitorInfo.rcWork.Right - WkMonitorInfo.rcWork.Left + (WkSmCxframe * 2); WkSizeY := WkMonitorInfo.rcWork.Bottom - WkMonitorInfo.rcWork.Top + (WkSmCyframe * 2); // 隣のモニタに枠の部分がはみでて見えてしまうのを防止するために // ちらつきを最小限にして元のサイズに戻す→最大化をする // ウィンドウの配置情報を保持 ZeroMemory(@WkSaveWindowPlacement, sizeof(WkSaveWindowPlacement)); WkSaveWindowPlacement.length := sizeof(WkSaveWindowPlacement); GetWindowPlacement(FTargetForm.Handle, @WkSaveWindowPlacement); WkWindowPlacement.length := SizeOf(WkWindowPlacement); WkWindowPlacement.showCmd := SW_SHOWNORMAL; WkWindowPlacement.flags := WPF_RESTORETOMAXIMIZED; WkWindowPlacement.rcNormalPosition := Rect(WkPositionX, WkPositionY, WkPositionX + WkSizeX, WkPositionY + WkSizeY); SetWindowPlacement(FTargetForm.Handle, @WkWindowPlacement); // 再最大化する ShowWindow(FTargetForm.Handle, SW_SHOWMAXIMIZED); // 保持していたウィンドウの配置情報を戻す SetWindowPlacement(FTargetForm.Handle, @WkSaveWindowPlacement); end; end; end; procedure TMultiMonitorMaxInfoCalc.WMLeftGetMinMaxInfo( var msg: TWMGetMinMaxInfo); var WkMonitorInfo : TMonitorInfo; WkSizeX, WkSizeY: Integer; WkPositionX, WkPositionY: Integer; begin // マルチモニタ対応の有効面の取得 WkMonitorInfo := Self.MonitorInfo; WkPositionX := WkMonitorInfo.rcWork.Left; WkPositionY := WkMonitorInfo.rcWork.Top; WkSizeX := Min(FMaxSizeX, WkMonitorInfo.rcWork.Right - WkMonitorInfo.rcWork.Left); WkSizeY := WkMonitorInfo.rcWork.Bottom - WkMonitorInfo.rcWork.Top; msg.MinMaxInfo.ptMaxSize.X := WkSizeX; msg.MinMaxInfo.ptMaxSize.Y := WkSizeY; // GetMonitorInfo,Self.Top,Self.Leftで帰ってくる値は // プライマリモニタの左上を原点(0,0)とした値が帰ってくるが、 // msg.MinMaxInfo.ptMaxPositionは // BorderStyleがbsSizeableおよびbsSingleの場合のみ // 各種モニタごとの有効面の左上を原点(0,0) // とした座標を指定しなければいけない // それ以外のBorderStyleでは // 各種モニタごとの左上を原点(0,0)とした座標を指定する。 if (TargetForm.BorderStyle = bsSizeable) or (TargetForm.BorderStyle = bsSingle) or (FSetWMGetMinMaxInfo2 = True) then begin WkPositionX := WkPositionX - WkMonitorInfo.rcWork.Left; WkPositionY := WkPositionY - WkMonitorInfo.rcWork.Top; FSetWMGetMinMaxInfo2 := False; end else begin WkPositionX := WkPositionX - WkMonitorInfo.rcMonitor.Left; WkPositionY := WkPositionY - WkMonitorInfo.rcMonitor.Top; end; msg.MinMaxInfo.ptMaxPosition.X := WkPositionX; msg.MinMaxInfo.ptMaxPosition.Y := WkPositionY; end; procedure TMultiMonitorMaxInfoCalc.WMClientGetMinMaxInfo( var msg: TWMGetMinMaxInfo); var WkMonitorInfo : TMonitorInfo; WkSizeX, WkSizeY: Integer; WkPositionX, WkPositionY: Integer; begin // マルチモニタ対応の有効面の取得 WkMonitorInfo := Self.MonitorInfo; // BorderStyleがそれ以外の場合は // 最大化した際に有効面を無視してしまう仕様のため、 // 自前で有効面の制限を設定する。 WkSizeX := WkMonitorInfo.rcWork.Right - WkMonitorInfo.rcWork.Left; WkSizeY := WkMonitorInfo.rcWork.Bottom - WkMonitorInfo.rcWork.Top; WkPositionX := WkMonitorInfo.rcWork.Left; WkPositionY := WkMonitorInfo.rcWork.Top; msg.MinMaxInfo.ptMaxSize.X := WkSizeX; msg.MinMaxInfo.ptMaxSize.Y := WkSizeY; // GetMonitorInfo,Self.Top,Self.Leftで帰ってくる値は // プライマリモニタの左上を原点(0,0)とした値が帰ってくるが、 // msg.MinMaxInfo.ptMaxPositionは // BorderStyleがbsSizeableおよびbsSingleの場合のみ // 各種モニタごとの有効面の左上を原点(0,0) // とした座標を指定しなければいけない // それ以外のBorderStyleでは // 各種モニタごとの左上を原点(0,0)とした座標を指定する。 if (TargetForm.BorderStyle = bsSizeable) or (TargetForm.BorderStyle = bsSingle) or (FSetWMGetMinMaxInfo2 = True) then begin WkPositionX := WkPositionX - WkMonitorInfo.rcWork.Left; WkPositionY := WkPositionY - WkMonitorInfo.rcWork.Top; FSetWMGetMinMaxInfo2 := False; end else begin WkPositionX := WkPositionX - WkMonitorInfo.rcMonitor.Left; WkPositionY := WkPositionY - WkMonitorInfo.rcMonitor.Top; end; msg.MinMaxInfo.ptMaxPosition.X := WkPositionX; msg.MinMaxInfo.ptMaxPosition.Y := WkPositionY; end; procedure TMultiMonitorMaxInfoCalc.WMRightGetMinMaxInfo( var msg: TWMGetMinMaxInfo); var WkMonitorInfo : TMonitorInfo; WkSizeX, WkSizeY: Integer; WkPositionX, WkPositionY: Integer; begin // マルチモニタ対応の有効面の取得 WkMonitorInfo := Self.MonitorInfo; WkSizeX := Min(FMaxSizeX, WkMonitorInfo.rcWork.Right - WkMonitorInfo.rcWork.Left); WkSizeY := WkMonitorInfo.rcWork.Bottom - WkMonitorInfo.rcWork.Top; WkPositionX := WkMonitorInfo.rcWork.Right - WkSizeX; WkPositionY := WkMonitorInfo.rcWork.Top; msg.MinMaxInfo.ptMaxSize.X := WkSizeX; msg.MinMaxInfo.ptMaxSize.Y := WkSizeY; // GetMonitorInfo,Self.Top,Self.Leftで帰ってくる値は // プライマリモニタの左上を原点(0,0)とした値が帰ってくるが、 // msg.MinMaxInfo.ptMaxPositionは // BorderStyleがbsSizeableおよびbsSingleの場合のみ // 各種モニタごとの有効面の左上を原点(0,0) // とした座標を指定しなければいけない // それ以外のBorderStyleでは // 各種モニタごとの左上を原点(0,0)とした座標を指定する。 if (TargetForm.BorderStyle = bsSizeable) or (TargetForm.BorderStyle = bsSingle) or (FSetWMGetMinMaxInfo2 = True) then begin WkPositionX := WkPositionX - WkMonitorInfo.rcWork.Left; WkPositionY := WkPositionY - WkMonitorInfo.rcWork.Top; FSetWMGetMinMaxInfo2 := False; end else begin WkPositionX := WkPositionX - WkMonitorInfo.rcMonitor.Left; WkPositionY := WkPositionY - WkMonitorInfo.rcMonitor.Top; end; msg.MinMaxInfo.ptMaxPosition.X := WkPositionX; msg.MinMaxInfo.ptMaxPosition.Y := WkPositionY; end; procedure TMultiMonitorMaxInfoCalc.WMTopGetMinMaxInfo( var msg: TWMGetMinMaxInfo); var WkMonitorInfo : TMonitorInfo; WkSizeX, WkSizeY: Integer; WkPositionX, WkPositionY: Integer; begin // マルチモニタ対応の有効面の取得 WkMonitorInfo := Self.MonitorInfo; WkPositionX := WkMonitorInfo.rcWork.Left; WkPositionY := WkMonitorInfo.rcWork.Top; WkSizeX := WkMonitorInfo.rcWork.Right - WkMonitorInfo.rcWork.Left; WkSizeY := Min(FMaxSizeY, WkMonitorInfo.rcWork.Bottom - WkMonitorInfo.rcWork.Top); msg.MinMaxInfo.ptMaxSize.X := WkSizeX; msg.MinMaxInfo.ptMaxSize.Y := WkSizeY; // GetMonitorInfo,Self.Top,Self.Leftで帰ってくる値は // プライマリモニタの左上を原点(0,0)とした値が帰ってくるが、 // msg.MinMaxInfo.ptMaxPositionは // BorderStyleがbsSizeableおよびbsSingleの場合のみ // 各種モニタごとの有効面の左上を原点(0,0) // とした座標を指定しなければいけない // それ以外のBorderStyleでは // 各種モニタごとの左上を原点(0,0)とした座標を指定する。 if (TargetForm.BorderStyle = bsSizeable) or (TargetForm.BorderStyle = bsSingle) or (FSetWMGetMinMaxInfo2 = True) then begin WkPositionX := WkPositionX - WkMonitorInfo.rcWork.Left; WkPositionY := WkPositionY - WkMonitorInfo.rcWork.Top; FSetWMGetMinMaxInfo2 := False; end else begin WkPositionX := WkPositionX - WkMonitorInfo.rcMonitor.Left; WkPositionY := WkPositionY - WkMonitorInfo.rcMonitor.Top; end; msg.MinMaxInfo.ptMaxPosition.X := WkPositionX; msg.MinMaxInfo.ptMaxPosition.Y := WkPositionY; end; { TMonitorInfoParam } function TMonitorInfoParam.DisplayMonitorInfo: TMonitorInfo; begin GetMonitorInfoParam; result := FDisplayMonitorInfo; end; procedure TMonitorInfoParam.GetMonitorInfoParam; begin // マルチモニタ対応のモニタ情報の取得(プライマリモニタの左上とした座標) ZeroMemory(@FMonitorInfo, sizeof(FMonitorInfo)); FMonitorInfo.cbSize := SizeOf(FMonitorInfo); if GetMonitorInfo(FTargetForm.Monitor.Handle, @FMonitorInfo) = False then begin // 念のためリトライ(いるのかな・・・?) Sleep(100); if GetMonitorInfo(FTargetForm.Monitor.Handle, @FMonitorInfo) = False then begin raise Exception.Create('APIのGetMonitorInfo関数でエラーが発生しました' + #13#10 + 'プログラミングエラーです、開発者にお問い合わせください'); end; end; // 現在のモニタの左上を原点とした座標の取得 FDisplayMonitorInfo.cbSize := SizeOf(FDisplayMonitorInfo); FDisplayMonitorInfo.dwFlags := FMonitorInfo.dwFlags; FDisplayMonitorInfo.rcMonitor.Top := 0; FDisplayMonitorInfo.rcMonitor.Left := 0; FDisplayMonitorInfo.rcMonitor.Bottom := FMonitorInfo.rcMonitor.Bottom - FMonitorInfo.rcMonitor.Top; FDisplayMonitorInfo.rcMonitor.Right := FMonitorInfo.rcMonitor.Right - FMonitorInfo.rcMonitor.Left; FDisplayMonitorInfo.rcWork.Top := FMonitorInfo.rcWork.Top - FMonitorInfo.rcMonitor.Top; FDisplayMonitorInfo.rcWork.Left := FMonitorInfo.rcWork.Left - FMonitorInfo.rcMonitor.Left; FDisplayMonitorInfo.rcWork.Bottom := FMonitorInfo.rcWork.Bottom - FMonitorInfo.rcMonitor.Top; FDisplayMonitorInfo.rcWork.Right := FMonitorInfo.rcWork.Right - FMonitorInfo.rcMonitor.Left; end; function TMonitorInfoParam.MonitorCount: Integer; begin result := Screen.MonitorCount; end; function TMonitorInfoParam.MonitorInfo: TMonitorInfo; begin GetMonitorInfoParam; result := FMonitorInfo; end; function TMonitorInfoParam.MonitorNum: Integer; begin result := FTargetForm.Monitor.MonitorNum; end; procedure TMonitorInfoParam.SetTargetForm(const Value: TForm); begin FTargetForm := Value; end; end. unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, TypInfo, MultiMon, Math, MultiMonitorParam; type TForm1 = class(TForm) Button1: TButton; Panel1: TPanel; MaxAlignRadioGroup: TRadioGroup; Button2: TButton; Button3: TButton; procedure Button1Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure Panel1Resize(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure MaxAlignRadioGroupClick(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); private { Private 宣言 } FMultiMonitorMaxInfoCalc: TMultiMonitorMaxInfoCalc; // 最大化時にWM_GETMINMAXINFOでフォームの大きさを調整した際に // フォームが移動できてしまうのを防止するためのコード procedure WMSysCommand(var Message: TWMSysCommand); message WM_SYSCOMMAND; // フォームの大きさと位置の制限 procedure WMGetMinMaxInfo(var msg : TWMGetMinMaxInfo); message WM_GETMINMAXINFO; public { Public 宣言 } end; var Form1: TForm1; implementation {$R *.dfm} { TForm1 } procedure TForm1.WMSysCommand(var Message: TWMSysCommand); begin if IsZoomed(Self.Handle) and ((Message.CmdType and $FFF0) = SC_MOVE) then begin Exit; end; inherited; end; procedure TForm1.Button1Click(Sender: TObject); var WkMonitorInfo: TMonitorInfo; WkDisplayMonitorInfo: TMonitorInfo; begin //マルチモニタ対応のGetMonitorInfoを使う WkMonitorInfo := FMultiMonitorMaxInfoCalc.MonitorInfo; WkDisplayMonitorInfo := FMultiMonitorMaxInfoCalc.DisplayMonitorInfo; ShowMessage( '●モニタの情報' + #13#10 + 'モニタの数(1~?):' + IntToStr(FMultiMonitorMaxInfoCalc.MonitorCount) + #13#10 + 'フォームが表示されているモニタ番号(0~?):' + IntToStr(Self.Monitor.MonitorNum) + #13#10#13#10 + '●モニタの位置とサイズを表す(プライマリモニタ左上原点)' + #13#10 + 'Left :' + IntToStr(WkMonitorInfo.rcMonitor.Left) + #13#10 + 'Top :' + IntToStr(WkMonitorInfo.rcMonitor.Top) + #13#10 + 'Right :' + IntToStr(WkMonitorInfo.rcMonitor.Right) + #13#10 + 'Bottom:' + IntToStr(WkMonitorInfo.rcMonitor.Bottom) + #13#10#13#10 + '●有効面のモニタの位置とサイズを表す(プライマリモニタ左上原点)' + #13#10 + 'Left :' + IntToStr(WkMonitorInfo.rcWork.Left) + #13#10 + 'Top :' + IntToStr(WkMonitorInfo.rcWork.Top) + #13#10 + 'Right :' + IntToStr(WkMonitorInfo.rcWork.Right) + #13#10 + 'Bottom:' + IntToStr(WkMonitorInfo.rcWork.Bottom) + #13#10#13#10 + '●モニタの位置とサイズを表す(各種モニタ左上原点)' + #13#10 + 'Left :' + IntToStr(WkDisplayMonitorInfo.rcMonitor.Left) + #13#10 + 'Top :' + IntToStr(WkDisplayMonitorInfo.rcMonitor.Top) + #13#10 + 'Right :' + IntToStr(WkDisplayMonitorInfo.rcMonitor.Right) + #13#10 + 'Bottom:' + IntToStr(WkDisplayMonitorInfo.rcMonitor.Bottom) + #13#10#13#10 + '●有効面のモニタの位置とサイズを表す(各種モニタ左上原点)' + #13#10 + 'Left :' + IntToStr(WkDisplayMonitorInfo.rcWork.Left) + #13#10 + 'Top :' + IntToStr(WkDisplayMonitorInfo.rcWork.Top) + #13#10 + 'Right :' + IntToStr(WkDisplayMonitorInfo.rcWork.Right) + #13#10 + 'Bottom:' + IntToStr(WkDisplayMonitorInfo.rcWork.Bottom)); end; procedure TForm1.WMGetMinMaxInfo(var msg: TWMGetMinMaxInfo); begin // OnCreateする前にイベントが来てしまう処置 if FMultiMonitorMaxInfoCalc <> nil then begin FMultiMonitorMaxInfoCalc.WMGetMinMaxInfo(msg); end; end; procedure TForm1.FormCreate(Sender: TObject); begin FMultiMonitorMaxInfoCalc := TMultiMonitorMaxInfoCalc.Create; FMultiMonitorMaxInfoCalc.TargetForm := Self; FMultiMonitorMaxInfoCalc.MaxSizeX := 800; FMultiMonitorMaxInfoCalc.MaxSizeY := 800; FMultiMonitorMaxInfoCalc.MaxPositionX := 10; FMultiMonitorMaxInfoCalc.MaxPositionY := 10; MaxAlignRadioGroup.ItemIndex := Ord(FMultiMonitorMaxInfoCalc.MaxAlign); end; procedure TForm1.Panel1Resize(Sender: TObject); begin Self.Caption := Self.Caption + 'r'; end; procedure TForm1.FormDestroy(Sender: TObject); begin FreeAndNil(FMultiMonitorMaxInfoCalc); end; procedure TForm1.MaxAlignRadioGroupClick(Sender: TObject); begin FMultiMonitorMaxInfoCalc.MaxAlign := TAlign(MaxAlignRadioGroup.ItemIndex); end; procedure TForm1.Button2Click(Sender: TObject); begin ShowMessage( '●フォームの情報' + #13#10 + 'Top:' + IntToStr(Self.Top) + #13#10 + 'Left:' + IntToStr(Self.Left) + #13#10 + 'Width:' + IntToStr(Self.Width) + #13#10 + 'Height:' + IntToStr(Self.Height) + #13#10 + 'WindowState:' + GetEnumName(TypeInfo(TWindowState), Ord(Self.WindowState))); end; procedure TForm1.Button3Click(Sender: TObject); begin FMultiMonitorMaxInfoCalc.SetFormSafePosition; end; end.