В статье рассказывается, как осуществить поддержку мультиоконного режима в операционной системе семейства Windows.
Перед тем, как приступить к работе с множеством мониторов необходимо их перечислить. Для этого можно воспользоваться функцией EnumDisplayMonitors. Эта функция позволит получить описатели ( HMONITOR ) всех мониторов из, которых можно достать необходимую информацию.
BOOL EnumDisplayMonitors(
__in HDC hdc,
__in LPCRECT lprcClip,
__in MONITORENUMPROC lpfnEnum,
__in LPARAM dwData);
Для перечисления мониторов в системе все параметры, кроме функции обратного вызова, достаточно установить в NULL. Для удобства можно сложить информацию о мониторах в вектор, который будет заполняться по мере вызова CALLBACK- функции.
BOOL CALLBACK EnumMonitorsProc(HMONITOR hMonitor, HDC, LPRECT rect, LPARAM lParam)
{
m_vectAllMonitors.push_back( hMonitor );
return true;
}
Хочу заметить, что при первом вызове этой функции мы получаем описатель первичного (основного) монитора.
Информацию о координатах относительно первичного монитора и названий мониторов можно узнать с помощью функции:
BOOL GetMonitorInfo(
__in HMONITOR hMonitor,
__out LPMONITORINFO lpmi);
Первый параметр – описатель монитора.
Второй параметр – структура данных с информацией о мониторе.
Расмотрим структуру MONITORINFO:
typedef struct tagMONITORINFO {
DWORD cbSize;
RECT rcMonitor;
RECT rcWork;
DWORD dwFlags;
} MONITORINFO, *LPMONITORINFO;
cbSize — размер структуры в байтах.
rcMonitor – определяет RECT монитора, выраженные в виртуальных экранных координатах. Если монитор не является основным монитором, некоторые из координат монитора могут быть отрицательными.
rcWork – определяет рабочей область монитора, выраженные в виртуальных экранных координатах. Если монитор не является основным, некоторые из координат монитора могут быть отрицательными.
dwFlags — атрибуты монитора. MONITORINFOF_PRIMARY – первичный монитор.
Имеется расширенная структура данных MONITORINFOEX, из которой можно узнать имя монитора.
Имея координаты областей различных мониторов, можно осуществлять работу с ними.
Рассмотрим пример. Необходимо создать окно на два монитора, каждую область окна ( соответствующую области отдельного монитора )залить разным цветом и вывести имя дисплея. Для реализации поставленной задачи необходимо сделать окно размер, которого будет соответствовать двум мониторам. Используя методы описанные выше можно узнать координаты двух мониторов и создать окно по их крайним значениям.
hwndCover = ::CreateWindowEx(0, TEXT("Static"), TEXT("Window"), SS_OWNERDRAW | WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, lpmi1.rcWork.left, lpmi1.rcWork.top , lpmi2.rcWork.right ,lpmi2.rcWork.bottom, ss.fHideIcons?hBack:hSysLvw, NULL, NULL, NULL);
Здесь lpmi1 и lpmi2 – структуры типа MONITORINFO в которых хранится информация о первом и втором мониторе.
Отрисовка областей окна будет реализована через функцию Rectangle с использованием кисти. Благодаря тому, что мы имеем описатели мониторов, мы можем узнать необходимые координаты, соответствующие координатам мониторов, для заливки областей окна и вывода текста.
case WM_PAINT:
{
hdc = BeginPaint(hWnd, &ps);
RECT rcWorck;
GetClientRect( hWnd, &rcWorck );
int nCorrectX = (m_veclpmi[1].rcWork.right - rcWorck.right) / 2; // correct size for left window border
// Draw first rectangle on first Monitor
HBRUSH hbrushFirstMonitor;
hbrushFirstMonitor = CreateSolidBrush( RGB( 0, 255, 0) );
SelectObject( hdc, hbrushFirstMonitor );
int nsize = 0;
while( m_veclpmi[0].szDevice[nsize] != ' ' )
nsize++;
Rectangle( hdc, m_veclpmi[0].rcWork.left, m_veclpmi[0].rcWork.top, m_veclpmi[0].rcWork.right - nCorrectX, m_veclpmi[0].rcWork.bottom );
TextOut( hdc, (m_veclpmi[0].rcWork.right - nCorrectX) / 2, m_veclpmi[0].rcWork.bottom /2 , m_veclpmi[0].szDevice, nsize);
DeleteObject( hbrushFirstMonitor );
// Draw first rectangle on secod Monitor
HBRUSH hbrushSecondMonitor;
hbrushSecondMonitor = CreateSolidBrush( RGB( 0, 255, 255 ) );
SelectObject( hdc, hbrushSecondMonitor );
nsize = 0;
while( m_veclpmi[0].szDevice[nsize] != ' ' )
nsize++;
Rectangle( hdc, m_veclpmi[1].rcWork.left - nCorrectX, m_veclpmi[1].rcWork.top, m_veclpmi[1].rcWork.right , m_veclpmi[1].rcWork.bottom );
TextOut( hdc, (m_veclpmi[1].rcWork.right - nCorrectX) / 2 + (m_veclpmi[0].rcWork.right - nCorrectX) / 2, m_veclpmi[1].rcWork.bottom / 2, m_veclpmi[1].szDevice, nsize);
DeleteObject( hbrushSecondMonitor );
EndPaint(hWnd, &ps);
}
break;