using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Windows;
namespace TestProject
{
/// <summary>
/// 화면
/// </summary>
public class Screen
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Class
////////////////////////////////////////////////////////////////////////////////////////// Private
#region 모니터 열거 콜백 - MonitorEnumCallback
/// <summary>
/// 모니터 열거 콜백
/// </summary>
private class MonitorEnumCallback
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 화면 리스트 - ScreenList
/// <summary>
/// 화면 리스트
/// </summary>
public List<Screen> ScreenList { get; private set; }
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - MonitorEnumCallback()
/// <summary>
/// 생성자
/// </summary>
public MonitorEnumCallback()
{
ScreenList = new List<Screen>();
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 콜백 처리하기 - Callback(monitorHandle, deviceContextHandle, monitorRectangleHandle, dataHandle)
/// <summary>
/// 콜백 처리하기
/// </summary>
/// <param name="monitorHandle">모니터 핸들</param>
/// <param name="deviceContextHandle">디바이스 컨텍스트 핸들</param>
/// <param name="monitorRectangleHandle">모니터 사각형 핸들</param>
/// <param name="dataHandle">데이터 핸들</param>
/// <returns>처리 결과</returns>
public bool Callback(IntPtr monitorHandle, IntPtr deviceContextHandle, IntPtr monitorRectangleHandle, IntPtr dataHandle)
{
ScreenList.Add(new Screen(monitorHandle, deviceContextHandle));
return true;
}
#endregion
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Delegate
////////////////////////////////////////////////////////////////////////////////////////// Private
#region 모니터 열거 프로시저 대리자 - MonitorEnumProc(monitorHandle, deviceContextHandle, monitorRectangleHandle, dataHandle)
/// <summary>
/// 모니터 열거 프로시저 대리자
/// </summary>
/// <param name="monitorHandle">모니터 핸들</param>
/// <param name="deviceContextHandle">디바이스 컨텍스트 핸들</param>
/// <param name="monitorRectangleHandle">모니터 사각형 핸들</param>
/// <param name="dataHandle">데이터 핸들</param>
/// <returns>처리 결과</returns>
private delegate bool MonitorEnumProc
(
IntPtr monitorHandle,
IntPtr deviceContextHandle,
IntPtr monitorRectangleHandle,
IntPtr dataHandle
);
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Import
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Private
#region 시스템 구성 설정 구하기 - GetSystemMetrics(index)
/// <summary>
/// 시스템 구성 설정 구하기
/// </summary>
/// <param name="index">인덱스</param>
/// <returns>시스템 구성 설정</returns>
[DllImport("user32", ExactSpelling = true, CharSet = CharSet.Auto)]
[ResourceExposure(ResourceScope.None)]
private static extern int GetSystemMetrics(int index);
#endregion
#region 모니터 정보 구하기 - GetMonitorInfo(monitorHandleRef, monitorInformation)
/// <summary>
/// 모니터 정보 구하기
/// </summary>
/// <param name="monitorHandleRef">모니터 핸들 참조</param>
/// <param name="monitorInformation">모니터 정보</param>
/// <returns>처리 결과</returns>
[DllImport("user32", CharSet = CharSet.Auto)]
[ResourceExposure(ResourceScope.None)]
private static extern bool GetMonitorInfo(HandleRef monitorHandleRef, [In, Out] MONITOR_INFORMATION monitorInformation);
#endregion
#region 디스플레이 모니터 열거하기 - EnumDisplayMonitors(deviceContextHandle, clippingRectangleHandle, monitorEnumProc, dataHandle
/// <summary>
/// 디스플레이 모니터 열거하기
/// </summary>
/// <param name="deviceContextHandle">디바이스 컨텍스트 핸들</param>
/// <param name="clippingRectangleHandle">클리핑 사각형 핸들</param>
/// <param name="monitorEnumProc">모니터 열거 프로시저</param>
/// <param name="dataHandle">데이터 핸들</param>
/// <returns>처리 결과</returns>
[DllImport("user32", ExactSpelling = true)]
[ResourceExposure(ResourceScope.None)]
private static extern bool EnumDisplayMonitors
(
HandleRef deviceContextHandle,
IntPtr clippingRectangleHandle,
MonitorEnumProc monitorEnumProc,
IntPtr dataHandle
);
#endregion
#region 윈도우에서 모니터 핸들 구하기 - MonitorFromWindow(windowHandleRef, flag)
/// <summary>
/// 윈도우에서 모니터 핸들 구하기
/// </summary>
/// <param name="windowHandleRef">윈도우 핸들 참조</param>
/// <param name="flag">플래그</param>
/// <returns>윈도우 핸들</returns>
[DllImport("user32", ExactSpelling = true)]
[ResourceExposure(ResourceScope.None)]
private static extern IntPtr MonitorFromWindow(HandleRef windowHandleRef, int flag);
#endregion
#region 포인트에서 모니터 핸들 구하기 - MonitorFromPoint(point, flag)
/// <summary>
/// 포인트에서 모니터 핸들 구하기
/// </summary>
/// <param name="point">포인트</param>
/// <param name="flag">플래그</param>
/// <returns>모니터 핸들</returns>
[DllImport("user32", ExactSpelling = true)]
[ResourceExposure(ResourceScope.None)]
private static extern IntPtr MonitorFromPoint(POINT point, int flag);
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// NULL 핸들 참조
/// </summary>
private static readonly HandleRef _nullHandleRef = new HandleRef(null, IntPtr.Zero);
/// <summary>
/// 멀티 모니터 지원 여부
/// </summary>
private static bool _multiMonitorSupport;
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Instance
//////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// SM_CMONITORS
/// </summary>
private const int SM_CMONITORS = 80;
/// <summary>
/// PRIMARY_MONITOR
/// </summary>
private const int PRIMARY_MONITOR = unchecked((int)0xBAADF00D);
/// <summary>
/// MONITORINFOF_PRIMARY
/// </summary>
private const int MONITORINFOF_PRIMARY = 0x00000001;
/// <summary>
/// MONITOR_DEFAULTTONEAREST
/// </summary>
private const int MONITOR_DEFAULTTONEAREST = 0x00000002;
/// <summary>
/// 모니터 핸들
/// </summary>
private readonly IntPtr monitorHandle;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region 모든 화면 열거 가능형 - AllScreenEnumerable
/// <summary>
/// 모든 화면 열거 가능형
/// </summary>
public static IEnumerable<Screen> AllScreenEnumerable
{
get
{
if(_multiMonitorSupport)
{
MonitorEnumCallback monitorEnumCallback = new MonitorEnumCallback();
MonitorEnumProc monitorEnumProc = new MonitorEnumProc(monitorEnumCallback.Callback);
EnumDisplayMonitors(_nullHandleRef, IntPtr.Zero, monitorEnumProc, IntPtr.Zero);
if(monitorEnumCallback.ScreenList.Count > 0)
{
return monitorEnumCallback.ScreenList.Cast<Screen>();
}
}
return new[] { new Screen((IntPtr)PRIMARY_MONITOR) };
}
}
#endregion
#region 기본 화면 - PrimaryScreen
/// <summary>
/// 기본 화면
/// </summary>
public static Screen PrimaryScreen
{
get
{
if(_multiMonitorSupport)
{
return AllScreenEnumerable.FirstOrDefault(screen => screen.Primary);
}
return new Screen((IntPtr)PRIMARY_MONITOR);
}
}
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Instance
//////////////////////////////////////////////////////////////////////////////// Public
#region 장치명 - DeviceName
/// <summary>
/// 장치명
/// </summary>
public string DeviceName { get; private set; }
#endregion
#region 기본 장치 여부 - Primary
/// <summary>
/// 기본 장치 여부
/// </summary>
public bool Primary { get; private set; }
#endregion
#region 테두리 사각형 - BoundRectangle
/// <summary>
/// 테두리 사각형
/// </summary>
public Rect BoundRectangle { get; private set; }
#endregion
#region 작업 영역 사각형 - WorkingAreaRectangle
/// <summary>
/// 작업 영역 사각형
/// </summary>
public Rect WorkingAreaRectangle
{
get
{
if(!_multiMonitorSupport || this.monitorHandle == (IntPtr)PRIMARY_MONITOR)
{
return SystemInformation.WorkingAreaRectangle;
}
MONITOR_INFORMATION monitorInformation = new MONITOR_INFORMATION();
GetMonitorInfo(new HandleRef(null, this.monitorHandle), monitorInformation);
return new Rect
(
monitorInformation.WorkingAreaRectangle.Left,
monitorInformation.WorkingAreaRectangle.Top,
monitorInformation.WorkingAreaRectangle.Right - monitorInformation.WorkingAreaRectangle.Left,
monitorInformation.WorkingAreaRectangle.Bottom - monitorInformation.WorkingAreaRectangle.Top
);
}
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Static
#region 생성자 - Screen()
/// <summary>
/// 생성자
/// </summary>
static Screen()
{
_multiMonitorSupport = GetSystemMetrics(SM_CMONITORS) != 0;
}
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Instance
//////////////////////////////////////////////////////////////////////////////// Private
#region 생성자 - Screen(monitorHandle, deviceContextHandle)
/// <summary>
/// 생성자
/// </summary>
/// <param name="monitorHandle"></param>
/// <param name="deviceContextHandle"></param>
private Screen(IntPtr monitorHandle, IntPtr deviceContextHandle)
{
if(!_multiMonitorSupport || monitorHandle == (IntPtr)PRIMARY_MONITOR)
{
DeviceName = "DISPLAY";
Primary = true;
BoundRectangle = SystemInformation.VirtualScreenRectangle;
}
else
{
MONITOR_INFORMATION monitorInformation = new MONITOR_INFORMATION();
GetMonitorInfo(new HandleRef(null, monitorHandle), monitorInformation);
DeviceName = new string(monitorInformation.DeviceName).TrimEnd((char)0);
Primary = ((monitorInformation.Flag & MONITORINFOF_PRIMARY) != 0);
BoundRectangle = new Rect
(
monitorInformation.DisplayRectangle.Left,
monitorInformation.DisplayRectangle.Top,
monitorInformation.DisplayRectangle.Right - monitorInformation.DisplayRectangle.Left,
monitorInformation.DisplayRectangle.Bottom - monitorInformation.DisplayRectangle.Top
);
}
this.monitorHandle = monitorHandle;
}
#endregion
#region 생성자 - Screen(monitorHandle)
/// <summary>
/// 생성자
/// </summary>
/// <param name="monitorHandle">모니터 핸들</param>
private Screen(IntPtr monitorHandle) : this(monitorHandle, IntPtr.Zero)
{
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region 화면 구하기 - GetScreen(windowHandle)
/// <summary>
/// 화면 구하기
/// </summary>
/// <param name="windowHandle">윈도우 핸들</param>
/// <returns>화면</returns>
public static Screen GetScreen(IntPtr windowHandle)
{
if(_multiMonitorSupport)
{
return new Screen(MonitorFromWindow(new HandleRef(null, windowHandle), 2));
}
return new Screen((IntPtr)PRIMARY_MONITOR);
}
#endregion
#region 화면 구하기 - GetScreen(point)
/// <summary>
/// 화면 구하기
/// </summary>
/// <param name="point">포인트</param>
/// <returns>화면</returns>
public static Screen GetScreen(Point point)
{
if(_multiMonitorSupport)
{
POINT temporaryPoint = new POINT((int)point.X, (int)point.Y);
return new Screen(MonitorFromPoint(temporaryPoint, MONITOR_DEFAULTTONEAREST));
}
return new Screen((IntPtr)PRIMARY_MONITOR);
}
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Method
//////////////////////////////////////////////////////////////////////////////// Public
#region 동일 여부 구하기 - Equals(source)
/// <summary>
/// 동일 여부 구하기
/// </summary>
/// <param name="source">소스 객체</param>
/// <returns>동일 여부</returns>
public override bool Equals(object source)
{
Screen screen = source as Screen;
if(screen != null)
{
if(this.monitorHandle == screen.monitorHandle)
{
return true;
}
}
return false;
}
#endregion
#region 해시 코드 구하기 - GetHashCode()
/// <summary>
/// 해시 코드 구하기
/// </summary>
/// <returns>해시 코드</returns>
public override int GetHashCode()
{
return (int)this.monitorHandle;
}
#endregion
}
}