一、问题
在屏幕分享或者投屏,选择单个应用窗口进行分享或者投屏时,当我们选择了PPT的窗口,如果在分享的中途,点击了放映PPT,此时会新创建一个窗口,句柄HWND也会不一样,但是此时远端看到的还是我们最开始那个PPT窗口
二、要求
1、PPT放映后可以自动切换到PPT幻灯片放映窗口,并且结束放映后可以自动切换到原来的窗口
2、使用 Alt + Tab 键可以自动切换PPT放映窗口和原窗口
三、实现步骤(假设我们这里已经拿到了最开始的那个可以编辑的PPT窗口句柄hwnd)
1、先来看一下使用office和wps时PPT窗口的一些不同属性
office | wps | |||
PPT编辑窗口 | PPT放映窗口 | PPT编辑窗口 | PPT放映窗口 | |
exe名称 |
win10:POWERPNT.EXE win7 :POWERPNT.exe |
win10:POWERPNT.EXE win7 :POWERPNT.exe |
wps.exe |
wpp.exe |
窗口句柄 | 不同 | 不同 | 不同 | 不同 |
进程pid | 相同 | 相同 | 不同 | 不同 |
大小 | 可变化 | 全屏 | 可变化 | 全屏 |
2、根据以上属性找到PPT放映窗口
1、将最开始的窗口句柄保存两份
2、如果当前捕获的创建exe名称为POWERPNT.EXE或者是wps.exe
3、获取当前置顶的窗口exe名称(这一步也可以轮询所有的exe,然后找到exe名字为POWERPNT.EXE或者是wpp.exe的窗口,并且判断是否最大化)
4、判断当前置顶窗口exe名称是不是POWERPNT.EXE、wps.exe、wpp.exe其中的一个
5、如果是再判断当前窗口句柄和上一次捕获的窗口句柄是否相同,如果相同那么则不做处理。
如果不同:
(1) 判断当前置顶的窗口是不是全屏的,如果是全屏并且当前窗口不是全屏的,那么则切换窗口句柄为置顶窗口的句柄
(2) 如果不是全屏并且上一次捕获窗口是全屏的,而且当前置顶窗口和我们最开始捕获的窗口一致,那么也切换窗口句柄为当前置顶的窗口句柄
四、具体代码
static std::vector<string> SpecialProcNameList{ "powerpnt.exe", "wps.exe" };
static std::vector<string> originProcNameList{ "wps.exe" };
static std::vector<string> changeProcNameList{ "wpp.exe" };
// 大写转换成小写
void UpperToLower(std::string& upper)
{
std::string lower;
for (int i = 0; i < upper.length(); ++i)
{
if ('A' <= upper[i] && 'Z' >= upper[i])
{
upper[i] = upper[i] + 32;
}
}
}
// 枚举显示器的大小
BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
{
std::vector<RECT> * pRect = (std::vector<RECT>*)dwData;
MONITORINFO monitorinfo;
monitorinfo.cbSize = sizeof(MONITORINFO);
GetMonitorInfo(hMonitor, &monitorinfo);
pRect->push_back(monitorinfo.rcMonitor);
return true;
}
// 判断是不是全屏的
bool CheckFullscreen(HWND hwnd)
{
std::vector<RECT> monitorRect;
EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)&monitorRect);
RECT rect;
GetWindowRect(hwnd, &rect);
for (int i = 0; i < monitorRect.size(); ++i) {
if (rect.left <= monitorRect[i].left &&
rect.top <= monitorRect[i].top &&
rect.right >= monitorRect[i].right &&
rect.bottom >= monitorRect[i].bottom)
{
return true;
}
}
return false;
}
// 获取当前置顶窗口
HWND GetCurrentTopWindow()
{
return ::GetForegroundWindow();
}
// 获取进程id
DWORD GetProcessId(HWND hwnd)
{
DWORD dwProcId;
::GetWindowThreadProcessId(hwnd, &dwProcId);
return dwProcId;
}
// 获取进程名称,比如wps.exe
std::string GetProcessName(DWORD procId)
{
HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, procId);
char wndName[MAX_PATH] = "";
GetProcessImageFileNameA(hProc, wndName, MAX_PATH);
CloseHandle(hProc);
std::string name = wndName;
auto pos = name.rfind("/");
if (pos == std::string::npos) {
pos = name.rfind("\\");
}
if (pos == std::string::npos) {
return "";
}
return name.substr(pos + 1, name.length() - pos);
}
// 判断exe name是不是我们想要的
bool IsSameWindow(HWND topWnd, HWND shareWnd) {
std::string topWndName = GetProcessName(GetProcessId(topWnd));
std::string shareWndName = GetProcessName(GetProcessId(shareWnd));
if (topWndName == shareWndName)
{
return true;
}
if (originProcNameList.size() != changeProcNameList.size()) {
return false;
}
UpperToLower(topWndName);
UpperToLower(shareWndName);
for (int i = 0; i < originProcNameList.size(); ++i) {
if (topWndName == originProcNameList[i] && shareWndName == changeProcNameList[i] || topWndName == changeProcNameList[i] && shareWndName == originProcNameList[i])
{
return true;
}
}
return false;
}
// orignWnd和targetWnd是最开始存下来的两个句柄
// targetWnd:表示要捕获的窗口句柄
// orignWnd:表示最开始选择的那个窗口句柄
void Capture()
{
std::vector<std::string>::iterator iter = std::find(SpecialProcNameList.begin(), SpecialProcNameList.end(), m_procName);
if (iter != SpecialProcNameList.end())
{
if (!IsWindow(targetWnd) && IsWindow(orignWnd))
{
targetWnd = orignWnd;
}
else if (IsWindow(targetWnd)) {
HWND hwnd = GetCurrentTopWindow();
if ((targetWnd== orignWnd&& CheckFullscreen(hwnd) ||
hwnd == orignWnd&& hwnd != targetWnd) &&
IsSameWindow(hwnd, targetWnd))
{
m_hTARGETWND = hwnd;
}
}
}
......
}
五、遗留问题
1、放映多个PPT时会导致画面错乱
2、外接显示器的情况下,放映PPT,除了轮询暂时没有想到更好的办法找到全屏的窗口
关于上面两个问题,如果有更好的想法,请多多指教
四、参考文章
4、Windows检测PPT,WPS幻灯片播放并获取窗口句柄