近来在做多显示器下显示窗口到特定显示器上,并且有F11切换全屏和非全屏的功能。现总结如下:
1、先来看如何解决多显示器显示特定窗口的问题。
我们分如下几步走:
第一步,创建一个win32窗口。创建一个窗口是有套路可循的,我将代码贴出来,并加以注释了。
第二步,找出所有的显示器,然后将窗口显示在特定的显示器上。
HINSTANCE _hInst; // 当前实例
TCHAR* szTitle; // 标题栏文本
TCHAR* szWindowClass; // 主窗口类名
//生成的窗口句柄
HWND _hWnd;
UINT width;
UINT height;
int startx;
int starty;
vector<ALLMONITORINFO> mInfo;
BOOL Create(WNDPROC proc)
{
//如果已经创建了一个窗口那么不再重复创建
if (_hWnd != NULL)
return FALSE;
//返回模块的句柄
_hInst = GetModuleHandle(NULL);
/*--------------注册一个窗口类------------------------*/
WNDCLASSEX wcex;
memset(&wcex, 0, sizeof(wcex));
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS | CS_OWNDC;
//设置其回调函数
if (proc != NULL)
wcex.lpfnWndProc = proc;
else
wcex.lpfnWndProc = this->MyWndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = _hInst;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
//wcex.lpszMenuName = MAKEINTRESOURCE(IDC_WIN01);//不要菜单栏
wcex.lpszClassName = szWindowClass;//主窗口类名
//注册窗口类,这个是必须的,要不然下面的CreateWindow函数总返回NULL
ATOM atom = RegisterClassEx(&wcex);
/*-----------------------------------------------*/
/*--------------获取所有显示器信息------------------------*/
mInfo.clear();
//get number of monitors
mInfo.reserve(GetSystemMetrics(SM_CMONITORS));
//这个函数很重要,然后回调函数MonitorEnumProc是获取所有显示器信息的重要函数。
EnumDisplayMonitors(NULL, NULL, this->MonitorEnumProc, reinterpret_cast<LPARAM>(&mInfo));
//如果只有一个显示器的话,那就在这唯一的显示器上显示窗口
if (mInfo.size() == 1)
{
RECT rect = mInfo[0].rect;
width = rect.right - rect.left;
height = rect.bottom - rect.top;
startx = rect.left;
starty = rect.top;
}
for (int i = 0; i < mInfo.size(); i++)
{
//如果有多个显示器的话,就在第一个非主屏的显示器上显示。
if (!mInfo[i].isPrimary)
{
RECT rect = mInfo[0].rect;
width = rect.right - rect.left;
height = rect.bottom - rect.top;
startx = rect.left;
starty = rect.top;
break;
}
}
/*-----------------------------------------------*/
_hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
startx,starty, width, height, NULL, NULL, _hInst, NULL);
if (!_hWnd)
{
return FALSE;
}
//ToggleFullScreen();
ShowWindow(_hWnd, SW_SHOW);
UpdateWindow(_hWnd);
return TRUE;
}
关于ALLMONITORINFO结构体定义,如下:
struct ALLMONITORINFO
{
HMONITOR hMonitor;
RECT rect;
bool isPrimary;
};
BOOL CALLBACK MonitorEnumProc(__in HMONITOR hMonitor, __in HDC hdcMonitor, __in LPRECT lprcMonitor, __in LPARAM dwData)
{
vector<ALLMONITORINFO>& infoArray = *reinterpret_cast<vector<ALLMONITORINFO>* >(dwData);
ALLMONITORINFO monitorInfo;
monitorInfo.hMonitor = hMonitor;
monitorInfo.rect = *lprcMonitor;
HMONITOR priMonitor = MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY);
if (priMonitor == hMonitor)
monitorInfo.isPrimary = true;
else
monitorInfo.isPrimary = false;
infoArray.push_back(monitorInfo);
return TRUE;
}
2、然后是全屏和非全屏切换的问题。
在网上搜索的时候,给出的答案很多是说用ChangeDisplaySettings这个函数。但是经测试,这个函数不起作用。好象是微软已经摒弃了这个方法了,在新的系统上已经不起作用了,取而代之的是SetWindowLong这个函数。
代码如下所示:
void ToggleFullScreen(int width, int height, int startx, int starty)
{
if(isFullScreen)
{
SetWindowLong(_hWnd, GWL_STYLE, GetWindowLong(_hWnd, GWL_STYLE) | WS_OVERLAPPEDWINDOW);
//GWL_EXSTYLE extended style扩展格式。WS_EX_WINDOWEDGE 使得窗口显示,并不覆盖下面的任务栏。
SetWindowLong(_hWnd, GWL_EXSTYLE, GetWindowLong(_hWnd, GWL_EXSTYLE) | WS_EX_WINDOWEDGE);
//SetWindowPos(_hWnd, NULL, startx, starty, width, height, SWP_SHOWWINDOW);// SWP_FRAMECHANGED);
}
else
{
LONG curWinStyle = GetWindowLong(_hWnd, GWL_STYLE);
curWinStyle = curWinStyle & ~WS_OVERLAPPEDWINDOW;
SetWindowLong(_hWnd, GWL_STYLE, curWinStyle | WS_POPUP);
MoveWindow(_hWnd, startx, starty, width, height, TRUE);
}
isFullScreen = !isFullScreen;
}