この内容はDelphi-ML「[Delphi:90115] ウインドウを最大化したまま最大化サイズを調節する」で付いたレスを参考にしています。 教えてくださる皆様、感謝感謝です。
モニタの範囲を知るためにはWindows APIの「GetMonitorInfo」を使う。
このAPIはマルチモニタ対応のAPIで、ディスプレイモニタのハンドルを渡してあげる事によって、そのディスプレイモニタに関する情報を取得する事が出来ます。
情報はMONITORINFO 構造体によって返され、以下のような情報が帰ってきます。
MonitorInfo.rcMonitor.Bottom | 範囲の下側Y座標 |
MonitorInfo.rcMonitor.Top | 範囲の上側Y座標 |
MonitorInfo.rcMonitor.Left | 範囲の左側X座標 |
MonitorInfo.rcMonitor.Right | 範囲の右側X座標 |
MonitorInfo.rcWork.Bottom | 範囲の下側Y座標 |
MonitorInfo.rcWork.Top | 範囲の上側Y座標 |
MonitorInfo.rcWork.Left | 範囲の左側X座標 |
MonitorInfo.rcWork.Right | 範囲の右側X座標 |
var FMonitorInfo: TMonitorInfo; begin ZeroMemory(@FMonitorInfo, sizeof(FMonitorInfo)); FMonitorInfo.cbSize := SizeOf(FMonitorInfo); GetMonitorInfo(FTargetForm.Monitor.Handle, @FMonitorInfo);
最大化の時に、フォームの大きさと位置の制限をするにはWMGetMinMaxInfoメッセージを使います
procedure WMGetMinMaxInfo(var msg : TWMGetMinMaxInfo); message WM_GETMINMAXINFO;
設定できる値は、以下の通りです。
msg.MinMaxInfo.ptMaxPosition.X | フォームの左上のX座標を指定します |
msg.MinMaxInfo.ptMaxPosition.Y | フォームの左上のY座標を指定します |
msg.MinMaxInfo.ptMaxSize.X | フォームの横幅を指定します |
msg.MinMaxInfo.ptMaxSize.Y | フォームの縦幅を指定します |
「msg.MinMaxInfo.ptMaxPosition.X」「msg.MinMaxInfo.ptMaxPosition.Y」は各種モニタの左上を原点として数値が決まります。
さらに、フォームのBorderStyleによって、原点位置が変わります
BorderStyle | 座標の原点 |
bsDialog | モニタ全体の範囲から(0,0) |
bsNone | モニタ全体の範囲から(0,0) |
bsSingle | モニタ有効面の範囲から(0,0) |
bsSizeable | モニタ有効面の範囲から(0,0) |
bsSizeToolWin | モニタ全体の範囲から(0,0) |
bsToolWindow | モニタ全体の範囲から(0,0) |
※2009/5/7の時点では「2000・XP」と「Vista・7」では原点位置が変わる様に書いていたのですが、いつの間にか「2000・XP」と同じ動作になる様に「Vista・7」が修正された様です。
マルチディスプレイ対応です。
この内容はDelphi-ML「[Delphi:90115] ウインドウを最大化したまま最大化サイズを調節する」で付いたレスを参考にしています。
教えてくださる皆様、感謝感謝です。
2008/1/24修正 ウインドウがbsSizeable・bsSingle以外のモードの場合が動きおかしかったのを修正
2009/5/7修正 WMGetMinMaxInfo2関数で、ちらつきを抑えつつ変更されるように作っていましたが、
色々な場面(OS・フォームBorderStyle・OSテーマ対応)で動作が不安定なので断念…
2010/9/17修正 Vista・7だけ動作が違っていた部分があったが、いつの間にか修正されていた様なので2000・XPと同じ動作をする様に修正する
ダウンロード
この作品はクリエイティブ・コモンズ・ライセンスの下でライセンスされています。
{*****************************************************************************} { } { 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, UxTheme; type // TargetFormが指定されていないか存在しないエラー ETargetFormIsNotFound = class(Exception); // MonitorInfo保持クラス TMonitorInfoParam = class(TComponent) private FMonitorInfo : TMonitorInfo; FDisplayMonitorInfo: TMonitorInfo; FTargetForm: TForm; procedure SetTargetForm(const Value: TForm); protected // MonitorInfoの取得 procedure GetMonitorInfoParam; public //TComponent等 Owner付コンストラクタ定義 constructor Create(AOwner: TComponent); override; // ディスプレイモニタに関する情報を取得 // プライマリモニタの左上を原点(0,0)として座標値を返す function MonitorInfo: TMonitorInfo; // ディスプレイモニタに関する情報を取得 // 各モニタの左上を原点(0,0)として座標値を返す function DisplayMonitorInfo: TMonitorInfo; // モニタの数を取得 function MonitorCount: Integer; // モニタ番号の取得 function MonitorNum: Integer; published // 対象のウインドウを設定 property TargetForm: TForm read FTargetForm write SetTargetForm; end; // 最大化の管理クラス TMultiMonitorMaxInfoCalc = class(TMonitorInfoParam) private FMaxSizeY: Integer; FMaxSizeX: Integer; FMaxPositionY: Integer; FMaxPositionX: Integer; FMaxAlign: TAlign; 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 // フォームの大きさと位置の制限(WMGetMinMaxInfo) procedure WMNoneGetMinMaxInfo(var msg : TWMGetMinMaxInfo); 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 //TComponent等 Owner付コンストラクタ定義 constructor Create(AOwner: TComponent); override; //デストラクタ定義 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; // ウインドウハンドルからモニタ番号の取得 function GetHWNDToMonitorNum(hWnd: HWND): Integer; // Windows XP/Server2003で // なおかつ「Windows XP スタイル」が設定されているかチェック function isWindowsXPUseThemes: Boolean; implementation function GetHWNDToMonitorNum(hWnd: HWND): Integer; var wHMonitor: HMonitor; wHMonitorIndex: Integer; wMonitor: TMonitor; begin Result := -1; wHMonitor := MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST); for wHMonitorIndex := 0 to Screen.MonitorCount - 1 do begin if Screen.Monitors[wHMonitorIndex].Handle = wHMonitor then begin wMonitor := Screen.Monitors[wHMonitorIndex]; result := wMonitor.MonitorNum; Exit; end; end; end; function isWindowsXPUseThemes: Boolean; var WkMajor, WkMinor: LongInt; WkInfo: TOSVersionInfo; begin WkInfo.dwOSVersionInfoSize := SizeOf(WkInfo); GetVersionEx(WkInfo); WkMajor := WkInfo.dwMajorVersion ; WkMinor := WkInfo.dwMinorVersion ; // dwMajorVersionが5は2000・XP・Server2003 if WkMajor = 5 then begin // dwMinorVersionが1はXP // dwMinorVersionが2はServer2003 if (WkMinor = 1) or (WkMinor = 2) then begin // UxThemeのUseThemesは // OSがテーマ対応かクラシックモードかを判別する // アプリケーションは関係なし result := UseThemes; end else begin result := False; end; end else begin result := False; end; end; { TMultiMonitorMaxInfoCalc } constructor TMultiMonitorMaxInfoCalc.Create(AOwner: TComponent); begin inherited; //上位のクリエイトを呼び出す(Create表記を省略) //↓Createを呼出し後に別の処理を書くのが一般的 FMaxAlign := alCustom; FMaxSizeY := 0; FMaxSizeX := 0; FMaxPositionY := 0; FMaxPositionX := 0; 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 = nil then begin raise ETargetFormIsNotFound.Create('TargetFormが指定されていません'); end; if IsWindow(FTargetForm.Handle) = False then begin raise ETargetFormIsNotFound.Create('TargetFormで指定したフォームが存在されていません'); end; 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; WkPositionX, WkPositionY: Integer; WkBoundaryX, WkBoundaryY: Integer; begin // ディスプレイモニタに関する情報を取得 // プライマリモニタの左上を原点(0,0)とした値が帰ってくる WkMonitorInfo := Self.MonitorInfo; // フォームのサイズをmsg.MinMaxInfo.ptMaxSizeに指定 msg.MinMaxInfo.ptMaxSize.X := WkMonitorInfo.rcWork.Right - WkMonitorInfo.rcWork.Left; msg.MinMaxInfo.ptMaxSize.Y := Min(FMaxSizeY, WkMonitorInfo.rcWork.Bottom - WkMonitorInfo.rcWork.Top); // フォームの位置を保持 WkPositionX := WkMonitorInfo.rcWork.Left; WkPositionY := WkMonitorInfo.rcWork.Bottom - msg.MinMaxInfo.ptMaxSize.Y; // フォームの位置をmsg.MinMaxInfo.ptMaxPositionに指定 // msg.MinMaxInfo.ptMaxPositionは // BorderStyleがbsSizeableおよびbsSingleの場合は // 各モニタの有効面の左上を原点(0,0)とした座標を指定する。 // それ以外のBorderStyleでは // 各モニタの左上を原点(0,0)として座標値を返す if (TargetForm.BorderStyle = bsSizeable) or (TargetForm.BorderStyle = bsSingle) then begin msg.MinMaxInfo.ptMaxPosition.X := WkPositionX - WkMonitorInfo.rcWork.Left; msg.MinMaxInfo.ptMaxPosition.Y := WkPositionY - WkMonitorInfo.rcWork.Top; end else begin msg.MinMaxInfo.ptMaxPosition.X := WkPositionX - WkMonitorInfo.rcMonitor.Left; msg.MinMaxInfo.ptMaxPosition.Y := WkPositionY - WkMonitorInfo.rcMonitor.Top; end; // WindowsXP・Server2003でOSがテーマ対応の場合 // BorderStyleがbsDialogが設定されると // 境界線が表示されないので、その調整のために境界線の幅だけ調整する if (isWindowsXPUseThemes = True) and (TargetForm.BorderStyle = bsDialog) then begin // 立体効果付きのウィンドウの境界線の幅 + // 立体効果のないウィンドウの境界の幅 を取得 WkBoundaryX := GetSystemMetrics(SM_CXEDGE) + GetSystemMetrics(SM_CXBORDER); WkBoundaryY := GetSystemMetrics(SM_CYEDGE) + GetSystemMetrics(SM_CXBORDER); msg.MinMaxInfo.ptMaxSize.X := msg.MinMaxInfo.ptMaxSize.X + (WkBoundaryX * 2); msg.MinMaxInfo.ptMaxSize.Y := msg.MinMaxInfo.ptMaxSize.Y + (WkBoundaryY * 2); msg.MinMaxInfo.ptMaxPosition.X := msg.MinMaxInfo.ptMaxPosition.X - WkBoundaryX; msg.MinMaxInfo.ptMaxPosition.Y := msg.MinMaxInfo.ptMaxPosition.Y - WkBoundaryY; end; { // デバッグ用 ShowMessage( 'msg.MinMaxInfo.ptMaxPosition.X:' + IntToStr(msg.MinMaxInfo.ptMaxPosition.X) + #13#10 + 'msg.MinMaxInfo.ptMaxPosition.Y:' + IntToStr(msg.MinMaxInfo.ptMaxPosition.Y) + #13#10 + 'msg.MinMaxInfo.ptMaxSize.X:' + IntToStr(msg.MinMaxInfo.ptMaxSize.X) + #13#10 + 'msg.MinMaxInfo.ptMaxSize.Y:' + IntToStr(msg.MinMaxInfo.ptMaxSize.Y) ); } end; procedure TMultiMonitorMaxInfoCalc.WMCustomGetMinMaxInfo( var msg: TWMGetMinMaxInfo); var WkMonitorInfo : TMonitorInfo; WkPositionX, WkPositionY: Integer; WkBoundaryX, WkBoundaryY: Integer; begin // ディスプレイモニタに関する情報を取得 // プライマリモニタの左上を原点(0,0)とした値が帰ってくる WkMonitorInfo := Self.MonitorInfo; // フォームの位置を保持 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; // フォームのサイズをmsg.MinMaxInfo.ptMaxSizeに指定 // 有効面以上の大きさにならないように大きさを修正する msg.MinMaxInfo.ptMaxSize.X := Min(FMaxSizeX, WkMonitorInfo.rcWork.Right - WkMonitorInfo.rcWork.Left - FMaxPositionX); msg.MinMaxInfo.ptMaxSize.Y := Min(FMaxSizeY, WkMonitorInfo.rcWork.Bottom - WkMonitorInfo.rcWork.Top - FMaxPositionY); // フォームの位置をmsg.MinMaxInfo.ptMaxPositionに指定 // msg.MinMaxInfo.ptMaxPositionは // BorderStyleがbsSizeableおよびbsSingleの場合は // 各モニタの有効面の左上を原点(0,0)とした座標を指定する。 // それ以外のBorderStyleでは // 各モニタの左上を原点(0,0)として座標値を返す if (TargetForm.BorderStyle = bsSizeable) or (TargetForm.BorderStyle = bsSingle) then begin msg.MinMaxInfo.ptMaxPosition.X := WkPositionX - WkMonitorInfo.rcWork.Left; msg.MinMaxInfo.ptMaxPosition.Y := WkPositionY - WkMonitorInfo.rcWork.Top; end else begin msg.MinMaxInfo.ptMaxPosition.X := WkPositionX - WkMonitorInfo.rcMonitor.Left; msg.MinMaxInfo.ptMaxPosition.Y := WkPositionY - WkMonitorInfo.rcMonitor.Top; end; // WindowsXP・Server2003でOSがテーマ対応の場合 // BorderStyleがbsDialogが設定されると // 境界線が表示されないので、その調整のために境界線の幅だけ調整する if (isWindowsXPUseThemes = True) and (TargetForm.BorderStyle = bsDialog) then begin // 立体効果付きのウィンドウの境界線の幅 + // 立体効果のないウィンドウの境界の幅 を取得 WkBoundaryX := GetSystemMetrics(SM_CXEDGE) + GetSystemMetrics(SM_CXBORDER); WkBoundaryY := GetSystemMetrics(SM_CYEDGE) + GetSystemMetrics(SM_CXBORDER); msg.MinMaxInfo.ptMaxSize.X := msg.MinMaxInfo.ptMaxSize.X + (WkBoundaryX * 2); msg.MinMaxInfo.ptMaxSize.Y := msg.MinMaxInfo.ptMaxSize.Y + (WkBoundaryY * 2); msg.MinMaxInfo.ptMaxPosition.X := msg.MinMaxInfo.ptMaxPosition.X - WkBoundaryX; msg.MinMaxInfo.ptMaxPosition.Y := msg.MinMaxInfo.ptMaxPosition.Y - WkBoundaryY; end; { // デバッグ用 ShowMessage( 'msg.MinMaxInfo.ptMaxPosition.X:' + IntToStr(msg.MinMaxInfo.ptMaxPosition.X) + #13#10 + 'msg.MinMaxInfo.ptMaxPosition.Y:' + IntToStr(msg.MinMaxInfo.ptMaxPosition.Y) + #13#10 + 'msg.MinMaxInfo.ptMaxSize.X:' + IntToStr(msg.MinMaxInfo.ptMaxSize.X) + #13#10 + 'msg.MinMaxInfo.ptMaxSize.Y:' + IntToStr(msg.MinMaxInfo.ptMaxSize.Y) ); } end; procedure TMultiMonitorMaxInfoCalc.WMGetMinMaxInfo( var msg: TWMGetMinMaxInfo); begin // ウインドウが存在しない場合は終了 if FTargetForm = nil then begin Exit; end; if IsWindow(FTargetForm.Handle) = False then begin Exit; end; case FMaxAlign of alNone: begin WMNoneGetMinMaxInfo(msg); 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 raise Exception.Create('MaxAlignのエラーです' + #13#10 + 'プログラミングエラーです、開発者にお問い合わせください'); end; end; procedure TMultiMonitorMaxInfoCalc.WMGetMinMaxInfo2; begin // ウインドウが存在しない場合は終了 if FTargetForm = nil then begin Exit; end; if IsWindow(FTargetForm.Handle) = False then begin Exit; end; // 最大化の時のみ実行される if IsZoomed(FTargetForm.Handle) then begin // 最小化して最大化 ShowWindow(FTargetForm.Handle, SW_NORMAL); ShowWindow(FTargetForm.Handle, SW_MAXIMIZE); // 以前は、ちらつきを抑えつつ変更されるように作っていましたが、 // 色々な場面(OS・フォームBorderStyle・OSテーマ対応) // で動作が不安定なので断念… // Delphi-MLで教えていただいた方申し訳ありません。 end; end; procedure TMultiMonitorMaxInfoCalc.WMLeftGetMinMaxInfo( var msg: TWMGetMinMaxInfo); var WkMonitorInfo : TMonitorInfo; WkPositionX, WkPositionY: Integer; WkBoundaryX, WkBoundaryY: Integer; begin // ディスプレイモニタに関する情報を取得 // プライマリモニタの左上を原点(0,0)とした値が帰ってくる WkMonitorInfo := Self.MonitorInfo; // フォームの位置を保持 WkPositionX := WkMonitorInfo.rcWork.Left; WkPositionY := WkMonitorInfo.rcWork.Top; // フォームのサイズをmsg.MinMaxInfo.ptMaxSizeに指定 msg.MinMaxInfo.ptMaxSize.X := Min(FMaxSizeX, WkMonitorInfo.rcWork.Right - WkMonitorInfo.rcWork.Left); msg.MinMaxInfo.ptMaxSize.Y := WkMonitorInfo.rcWork.Bottom - WkMonitorInfo.rcWork.Top; // フォームの位置をmsg.MinMaxInfo.ptMaxPositionに指定 // msg.MinMaxInfo.ptMaxPositionは // BorderStyleがbsSizeableおよびbsSingleの場合は // 各モニタの有効面の左上を原点(0,0)とした座標を指定する。 // それ以外のBorderStyleでは // 各モニタの左上を原点(0,0)として座標値を返す if (TargetForm.BorderStyle = bsSizeable) or (TargetForm.BorderStyle = bsSingle) then begin msg.MinMaxInfo.ptMaxPosition.X := WkPositionX - WkMonitorInfo.rcWork.Left; msg.MinMaxInfo.ptMaxPosition.Y := WkPositionY - WkMonitorInfo.rcWork.Top; end else begin msg.MinMaxInfo.ptMaxPosition.X := WkPositionX - WkMonitorInfo.rcMonitor.Left; msg.MinMaxInfo.ptMaxPosition.Y := WkPositionY - WkMonitorInfo.rcMonitor.Top; end; // WindowsXP・Server2003でOSがテーマ対応の場合 // BorderStyleがbsDialogが設定されると // 境界線が表示されないので、その調整のために境界線の幅だけ調整する if (isWindowsXPUseThemes = True) and (TargetForm.BorderStyle = bsDialog) then begin // 立体効果付きのウィンドウの境界線の幅 + // 立体効果のないウィンドウの境界の幅 を取得 WkBoundaryX := GetSystemMetrics(SM_CXEDGE) + GetSystemMetrics(SM_CXBORDER); WkBoundaryY := GetSystemMetrics(SM_CYEDGE) + GetSystemMetrics(SM_CXBORDER); msg.MinMaxInfo.ptMaxSize.X := msg.MinMaxInfo.ptMaxSize.X + (WkBoundaryX * 2); msg.MinMaxInfo.ptMaxSize.Y := msg.MinMaxInfo.ptMaxSize.Y + (WkBoundaryY * 2); msg.MinMaxInfo.ptMaxPosition.X := msg.MinMaxInfo.ptMaxPosition.X - WkBoundaryX; msg.MinMaxInfo.ptMaxPosition.Y := msg.MinMaxInfo.ptMaxPosition.Y - WkBoundaryY; end; { // デバッグ用 ShowMessage( 'msg.MinMaxInfo.ptMaxPosition.X:' + IntToStr(msg.MinMaxInfo.ptMaxPosition.X) + #13#10 + 'msg.MinMaxInfo.ptMaxPosition.Y:' + IntToStr(msg.MinMaxInfo.ptMaxPosition.Y) + #13#10 + 'msg.MinMaxInfo.ptMaxSize.X:' + IntToStr(msg.MinMaxInfo.ptMaxSize.X) + #13#10 + 'msg.MinMaxInfo.ptMaxSize.Y:' + IntToStr(msg.MinMaxInfo.ptMaxSize.Y) ); } end; procedure TMultiMonitorMaxInfoCalc.WMClientGetMinMaxInfo( var msg: TWMGetMinMaxInfo); var WkMonitorInfo : TMonitorInfo; WkPositionX, WkPositionY: Integer; WkBoundaryX, WkBoundaryY: Integer; begin // ディスプレイモニタに関する情報を取得 // プライマリモニタの左上を原点(0,0)とした値が帰ってくる WkMonitorInfo := Self.MonitorInfo; // フォームの位置を保持 WkPositionX := WkMonitorInfo.rcWork.Left; WkPositionY := WkMonitorInfo.rcWork.Top; // フォームのサイズをmsg.MinMaxInfo.ptMaxSizeに指定 msg.MinMaxInfo.ptMaxSize.X := WkMonitorInfo.rcWork.Right - WkMonitorInfo.rcWork.Left; msg.MinMaxInfo.ptMaxSize.Y := WkMonitorInfo.rcWork.Bottom - WkMonitorInfo.rcWork.Top; // フォームの位置をmsg.MinMaxInfo.ptMaxPositionに指定 // msg.MinMaxInfo.ptMaxPositionは // BorderStyleがbsSizeableおよびbsSingleの場合は // 各モニタの有効面の左上を原点(0,0)とした座標を指定する。 // それ以外のBorderStyleでは // 各モニタの左上を原点(0,0)として座標値を返す if (TargetForm.BorderStyle = bsSizeable) or (TargetForm.BorderStyle = bsSingle) then begin msg.MinMaxInfo.ptMaxPosition.X := WkPositionX - WkMonitorInfo.rcWork.Left; msg.MinMaxInfo.ptMaxPosition.Y := WkPositionY - WkMonitorInfo.rcWork.Top; end else begin msg.MinMaxInfo.ptMaxPosition.X := WkPositionX - WkMonitorInfo.rcMonitor.Left; msg.MinMaxInfo.ptMaxPosition.Y := WkPositionY - WkMonitorInfo.rcMonitor.Top; end; // WindowsXP・Server2003でOSがテーマ対応の場合 // BorderStyleがbsDialogが設定されると // 境界線が表示されないので、その調整のために境界線の幅だけ調整する if (isWindowsXPUseThemes = True) and (TargetForm.BorderStyle = bsDialog) then begin // 立体効果付きのウィンドウの境界線の幅 + // 立体効果のないウィンドウの境界の幅 を取得 WkBoundaryX := GetSystemMetrics(SM_CXEDGE) + GetSystemMetrics(SM_CXBORDER); WkBoundaryY := GetSystemMetrics(SM_CYEDGE) + GetSystemMetrics(SM_CXBORDER); msg.MinMaxInfo.ptMaxSize.X := msg.MinMaxInfo.ptMaxSize.X + (WkBoundaryX * 2); msg.MinMaxInfo.ptMaxSize.Y := msg.MinMaxInfo.ptMaxSize.Y + (WkBoundaryY * 2); msg.MinMaxInfo.ptMaxPosition.X := msg.MinMaxInfo.ptMaxPosition.X - WkBoundaryX; msg.MinMaxInfo.ptMaxPosition.Y := msg.MinMaxInfo.ptMaxPosition.Y - WkBoundaryY; end; { // デバッグ用 ShowMessage( 'msg.MinMaxInfo.ptMaxPosition.X:' + IntToStr(msg.MinMaxInfo.ptMaxPosition.X) + #13#10 + 'msg.MinMaxInfo.ptMaxPosition.Y:' + IntToStr(msg.MinMaxInfo.ptMaxPosition.Y) + #13#10 + 'msg.MinMaxInfo.ptMaxSize.X:' + IntToStr(msg.MinMaxInfo.ptMaxSize.X) + #13#10 + 'msg.MinMaxInfo.ptMaxSize.Y:' + IntToStr(msg.MinMaxInfo.ptMaxSize.Y) ); } end; procedure TMultiMonitorMaxInfoCalc.WMRightGetMinMaxInfo( var msg: TWMGetMinMaxInfo); var WkMonitorInfo : TMonitorInfo; WkPositionX, WkPositionY: Integer; WkBoundaryX, WkBoundaryY: Integer; begin // ディスプレイモニタに関する情報を取得 // プライマリモニタの左上を原点(0,0)とした値が帰ってくる WkMonitorInfo := Self.MonitorInfo; // フォームのサイズをmsg.MinMaxInfo.ptMaxSizeに指定 msg.MinMaxInfo.ptMaxSize.X := Min(FMaxSizeX, WkMonitorInfo.rcWork.Right - WkMonitorInfo.rcWork.Left); msg.MinMaxInfo.ptMaxSize.Y := WkMonitorInfo.rcWork.Bottom - WkMonitorInfo.rcWork.Top; // フォームの位置を保持 WkPositionX := WkMonitorInfo.rcWork.Right - msg.MinMaxInfo.ptMaxSize.X; WkPositionY := WkMonitorInfo.rcWork.Top; // フォームの位置をmsg.MinMaxInfo.ptMaxPositionに指定 // msg.MinMaxInfo.ptMaxPositionは // BorderStyleがbsSizeableおよびbsSingleの場合は // 各モニタの有効面の左上を原点(0,0)とした座標を指定する。 // それ以外のBorderStyleでは // 各モニタの左上を原点(0,0)として座標値を返す if (TargetForm.BorderStyle = bsSizeable) or (TargetForm.BorderStyle = bsSingle) then begin msg.MinMaxInfo.ptMaxPosition.X := WkPositionX - WkMonitorInfo.rcWork.Left; msg.MinMaxInfo.ptMaxPosition.Y := WkPositionY - WkMonitorInfo.rcWork.Top; end else begin msg.MinMaxInfo.ptMaxPosition.X := WkPositionX - WkMonitorInfo.rcMonitor.Left; msg.MinMaxInfo.ptMaxPosition.Y := WkPositionY - WkMonitorInfo.rcMonitor.Top; end; // WindowsXP・Server2003でOSがテーマ対応の場合 // BorderStyleがbsDialogが設定されると // 境界線が表示されないので、その調整のために境界線の幅だけ調整する if (isWindowsXPUseThemes = True) and (TargetForm.BorderStyle = bsDialog) then begin // 立体効果付きのウィンドウの境界線の幅 + // 立体効果のないウィンドウの境界の幅 を取得 WkBoundaryX := GetSystemMetrics(SM_CXEDGE) + GetSystemMetrics(SM_CXBORDER); WkBoundaryY := GetSystemMetrics(SM_CYEDGE) + GetSystemMetrics(SM_CXBORDER); msg.MinMaxInfo.ptMaxSize.X := msg.MinMaxInfo.ptMaxSize.X + (WkBoundaryX * 2); msg.MinMaxInfo.ptMaxSize.Y := msg.MinMaxInfo.ptMaxSize.Y + (WkBoundaryY * 2); msg.MinMaxInfo.ptMaxPosition.X := msg.MinMaxInfo.ptMaxPosition.X - WkBoundaryX; msg.MinMaxInfo.ptMaxPosition.Y := msg.MinMaxInfo.ptMaxPosition.Y - WkBoundaryY; end; { // デバッグ用 ShowMessage( 'msg.MinMaxInfo.ptMaxPosition.X:' + IntToStr(msg.MinMaxInfo.ptMaxPosition.X) + #13#10 + 'msg.MinMaxInfo.ptMaxPosition.Y:' + IntToStr(msg.MinMaxInfo.ptMaxPosition.Y) + #13#10 + 'msg.MinMaxInfo.ptMaxSize.X:' + IntToStr(msg.MinMaxInfo.ptMaxSize.X) + #13#10 + 'msg.MinMaxInfo.ptMaxSize.Y:' + IntToStr(msg.MinMaxInfo.ptMaxSize.Y) ); } end; procedure TMultiMonitorMaxInfoCalc.WMTopGetMinMaxInfo( var msg: TWMGetMinMaxInfo); var WkMonitorInfo : TMonitorInfo; WkPositionX, WkPositionY: Integer; WkBoundaryX, WkBoundaryY: Integer; begin // ディスプレイモニタに関する情報を取得 // プライマリモニタの左上を原点(0,0)とした値が帰ってくる WkMonitorInfo := Self.MonitorInfo; // フォームの位置を保持 WkPositionX := WkMonitorInfo.rcWork.Left; WkPositionY := WkMonitorInfo.rcWork.Top; // フォームのサイズをmsg.MinMaxInfo.ptMaxSizeに指定 msg.MinMaxInfo.ptMaxSize.X := WkMonitorInfo.rcWork.Right - WkMonitorInfo.rcWork.Left; msg.MinMaxInfo.ptMaxSize.Y := Min(FMaxSizeY, WkMonitorInfo.rcWork.Bottom - WkMonitorInfo.rcWork.Top); // フォームの位置をmsg.MinMaxInfo.ptMaxPositionに指定 // msg.MinMaxInfo.ptMaxPositionは // BorderStyleがbsSizeableおよびbsSingleの場合は // 各モニタの有効面の左上を原点(0,0)とした座標を指定する。 // それ以外のBorderStyleでは // 各モニタの左上を原点(0,0)として座標値を返す if (TargetForm.BorderStyle = bsSizeable) or (TargetForm.BorderStyle = bsSingle) then begin msg.MinMaxInfo.ptMaxPosition.X := WkPositionX - WkMonitorInfo.rcWork.Left; msg.MinMaxInfo.ptMaxPosition.Y := WkPositionY - WkMonitorInfo.rcWork.Top; end else begin msg.MinMaxInfo.ptMaxPosition.X := WkPositionX - WkMonitorInfo.rcMonitor.Left; msg.MinMaxInfo.ptMaxPosition.Y := WkPositionY - WkMonitorInfo.rcMonitor.Top; end; // WindowsXP・Server2003でOSがテーマ対応の場合 // BorderStyleがbsDialogが設定されると // 境界線が表示されないので、その調整のために境界線の幅だけ調整する if (isWindowsXPUseThemes = True) and (TargetForm.BorderStyle = bsDialog) then begin // 立体効果付きのウィンドウの境界線の幅 + // 立体効果のないウィンドウの境界の幅 を取得 WkBoundaryX := GetSystemMetrics(SM_CXEDGE) + GetSystemMetrics(SM_CXBORDER); WkBoundaryY := GetSystemMetrics(SM_CYEDGE) + GetSystemMetrics(SM_CXBORDER); msg.MinMaxInfo.ptMaxSize.X := msg.MinMaxInfo.ptMaxSize.X + (WkBoundaryX * 2); msg.MinMaxInfo.ptMaxSize.Y := msg.MinMaxInfo.ptMaxSize.Y + (WkBoundaryY * 2); msg.MinMaxInfo.ptMaxPosition.X := msg.MinMaxInfo.ptMaxPosition.X - WkBoundaryX; msg.MinMaxInfo.ptMaxPosition.Y := msg.MinMaxInfo.ptMaxPosition.Y - WkBoundaryY; end; { // デバッグ用 ShowMessage( 'msg.MinMaxInfo.ptMaxPosition.X:' + IntToStr(msg.MinMaxInfo.ptMaxPosition.X) + #13#10 + 'msg.MinMaxInfo.ptMaxPosition.Y:' + IntToStr(msg.MinMaxInfo.ptMaxPosition.Y) + #13#10 + 'msg.MinMaxInfo.ptMaxSize.X:' + IntToStr(msg.MinMaxInfo.ptMaxSize.X) + #13#10 + 'msg.MinMaxInfo.ptMaxSize.Y:' + IntToStr(msg.MinMaxInfo.ptMaxSize.Y) ); } end; procedure TMultiMonitorMaxInfoCalc.WMNoneGetMinMaxInfo( var msg: TWMGetMinMaxInfo); var WkMonitorInfo : TMonitorInfo; WkPositionX, WkPositionY: Integer; WkBoundaryX, WkBoundaryY: Integer; begin // ディスプレイモニタに関する情報を取得 // プライマリモニタの左上を原点(0,0)とした値が帰ってくる WkMonitorInfo := Self.MonitorInfo; // フォームの位置を保持 if (TargetForm.BorderStyle = bsSizeable) or (TargetForm.BorderStyle = bsSingle) then begin // bsSizeable, bsSingle は // 最大化はモニタ有効面の範囲で広がる WkPositionX := WkMonitorInfo.rcWork.Left; WkPositionY := WkMonitorInfo.rcWork.Top; end else begin // bsDialog, bsNone, bsSizeToolWin, bsToolWindow は // 最大化はモニタ全体の範囲で広がる WkPositionX := WkMonitorInfo.rcMonitor.Left; WkPositionY := WkMonitorInfo.rcMonitor.Top; end; // 境界線の幅を取得 case FTargetForm.BorderStyle of bsNone: begin // ウィンドウの境界線が無い WkBoundaryX := 0; WkBoundaryY := 0; end; bsDialog, bsSingle, bsToolWindow: begin // 立体効果付きのウィンドウの境界線の幅 + // 立体効果のないウィンドウの境界の幅 を取得 WkBoundaryX := GetSystemMetrics(SM_CXEDGE) + GetSystemMetrics(SM_CXBORDER); WkBoundaryY := GetSystemMetrics(SM_CYEDGE) + GetSystemMetrics(SM_CYBORDER); end; else //bsSizeable, bsSizeToolWin // サイズ変更可能なウィンドウの周囲を囲む枠の幅 を取得 WkBoundaryX := GetSystemMetrics(SM_CXFRAME); WkBoundaryY := GetSystemMetrics(SM_CYFRAME); end; // フォームのサイズをmsg.MinMaxInfo.ptMaxSizeに指定 // 境界線の幅をフォームのサイズに反映させる if (TargetForm.BorderStyle = bsSizeable) or (TargetForm.BorderStyle = bsSingle) then begin // bsSizeable, bsSingle は // 最大化はモニタ有効面の範囲で広がる msg.MinMaxInfo.ptMaxSize.X := WkMonitorInfo.rcWork.Right - WkMonitorInfo.rcWork.Left + (WkBoundaryX * 2); msg.MinMaxInfo.ptMaxSize.Y := WkMonitorInfo.rcWork.Bottom - WkMonitorInfo.rcWork.Top + (WkBoundaryY * 2); end else begin // bsDialog, bsNone, bsSizeToolWin, bsToolWindow は // 最大化はモニタ全体の範囲で広がる msg.MinMaxInfo.ptMaxSize.X := WkMonitorInfo.rcMonitor.Right - WkMonitorInfo.rcMonitor.Left + (WkBoundaryX * 2); msg.MinMaxInfo.ptMaxSize.Y := WkMonitorInfo.rcMonitor.Bottom - WkMonitorInfo.rcMonitor.Top + (WkBoundaryY * 2); // Windows Vista(SP1)では、 // bsSizeToolWin, bsToolWindowの時に、縦のサイズがおかしく表示される // 現象が起こる // Windows Vista(無印/SP2)は未確認、他のWindowsではこの現象は起きない end; // フォームの位置をmsg.MinMaxInfo.ptMaxPositionに指定 // 境界線の幅をフォームの位置に反映させる // msg.MinMaxInfo.ptMaxPositionは // BorderStyleがbsSizeableおよびbsSingleの場合は // 各モニタの有効面の左上を原点(0,0)とした座標を指定する。 // それ以外のBorderStyleでは // 各モニタの左上を原点(0,0)として座標値を返す if (TargetForm.BorderStyle = bsSizeable) or (TargetForm.BorderStyle = bsSingle) then begin msg.MinMaxInfo.ptMaxPosition.X := WkPositionX - WkMonitorInfo.rcWork.Left - WkBoundaryX; msg.MinMaxInfo.ptMaxPosition.Y := WkPositionY - WkMonitorInfo.rcWork.Top - WkBoundaryY; end else begin msg.MinMaxInfo.ptMaxPosition.X := WkPositionX - WkMonitorInfo.rcMonitor.Left - WkBoundaryX; msg.MinMaxInfo.ptMaxPosition.Y := WkPositionY - WkMonitorInfo.rcMonitor.Top - WkBoundaryY; end; { // デバッグ用 ShowMessage( 'msg.MinMaxInfo.ptMaxPosition.X:' + IntToStr(msg.MinMaxInfo.ptMaxPosition.X) + #13#10 + 'msg.MinMaxInfo.ptMaxPosition.Y:' + IntToStr(msg.MinMaxInfo.ptMaxPosition.Y) + #13#10 + 'msg.MinMaxInfo.ptMaxSize.X:' + IntToStr(msg.MinMaxInfo.ptMaxSize.X) + #13#10 + 'msg.MinMaxInfo.ptMaxSize.Y:' + IntToStr(msg.MinMaxInfo.ptMaxSize.Y) ); } end; { TMonitorInfoParam } constructor TMonitorInfoParam.Create(AOwner: TComponent); begin inherited; if AOwner is TForm then begin FTargetForm := TForm(AOwner) end else begin FTargetForm := nil; end; end; function TMonitorInfoParam.DisplayMonitorInfo: TMonitorInfo; begin // ウインドウが存在しない場合は例外を発生させる if FTargetForm = nil then begin raise ETargetFormIsNotFound.Create('TargetFormが指定されていません'); end; if IsWindow(FTargetForm.Handle) = False then begin raise ETargetFormIsNotFound.Create('TargetFormで指定したフォームが存在されていません'); end; 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 // ウインドウが存在しない場合は例外を発生させる if FTargetForm = nil then begin raise ETargetFormIsNotFound.Create('TargetFormが指定されていません'); end; if IsWindow(FTargetForm.Handle) = False then begin raise ETargetFormIsNotFound.Create('TargetFormで指定したフォームが存在されていません'); end; 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.