随着Win11的普及,越来越多的人都能发现获取控制台窗口不能再使用以下两种传统方法了:
HWND hwnd = GetConsoleWindow();
HWND hwnd = FindWindowA("ConsoleWindowClass",NULL);
那是因为win11换了新的终端窗口,叫做WindowsTerminal,高端大气,字体看得也舒服。
(↑如果你想换回旧版控制台主机,可以右键标题栏“Settings”进入设置,按上图操作)
不过这个窗口是真的很难抓,
第一, 不推荐使用GetForegroundWindow,风险太大,容易抓成别的窗口,准确率低;
第二, GetConsoleWindow已经无法获取该窗口;
第三, FindWindow(“ConsoleWindowClass”,NULL)也不能抓新的终端;
第四, 抓类名,用第三方工具获得新终端类名,我这里是CASCADIA_HOSTING_WINDOW_CLASS 。用FindWindow,我这里失败了。。
第五, 我去问ChatGPT(3.5),它的回答是:
在 Windows 11 中,Windows Terminal 可能使用了不同的机制来管理其窗口句柄,可能会导致传统的方法无法准确地获取到正确的窗口句柄。
尝试使用 FindWindow 可能不会成功是因为该函数是基于窗口类名或窗口标题来查找窗口的,而 Windows Terminal 可能没有传统的类名或标题。
另一个选择是尝试使用 Windows 11 的新 API,例如 Windows.UI.Xaml 应用程序的 API。你可以考虑使用 Universal Windows Platform (UWP) 或 Windows Runtime (WinRT) API,来与 Windows Terminal 进行交互。
以下是可能用于获取 Windows Terminal 窗口句柄的一种方法,但请注意,这只是一个示例,并不保证可以直接适用于你的情况,而且需要你对 UWP 或 WinRT 有一定的了解:
#include <windows.h>
#include <winrt/Windows.UI.Composition.h>
#include <winrt/Windows.UI.Xaml.Hosting.h>
using namespace winrt::Windows::UI::Composition;
using namespace winrt::Windows::UI::Xaml::Hosting;
HWND GetWindowsTerminalWindowHandle() {
HWND terminalWindowHandle = nullptr;
try {
// 初始化Windows Runtime
winrt::init_apartment();
// 获取Windows Terminal的主机对象
auto terminalHost = DesktopWindowXamlSource();
// 获取Windows Terminal的根Visual对象
auto terminalVisual = ElementCompositionPreview::GetElementVisual(terminalHost);
// 获取Windows Terminal的窗口句柄
terminalHost.Handle(terminalWindowHandle);
} catch (winrt::hresult_error const& ex) {
// 处理异常
// 可以在这里输出错误信息,调试问题
}
return terminalWindowHandle;
}
int main() {
HWND terminalWindow = GetWindowsTerminalWindowHandle();
if (terminalWindow != nullptr) {
// 在这里使用获取到的窗口句柄进行你需要的操作
} else {
// 获取窗口句柄失败,处理错误
}
return 0;
}
请注意,这段代码假设你已经配置好了相应的开发环境,并且能够正确地使用 WinRT 和 UWP API。在使用这些 API 时,你需要适当的访问权限和合适的配置。
记住,由于 Windows Terminal 可能采用了新的技术和 API,因此获取其窗口句柄可能需要更多针对性的方法,并且需要根据具体情况进行调整。
有没有那俩头文件、能不能用是一回事,不过这明摆的是C++(11)啊,我要求是C语言。
于是我的方案是:
第一:FindWindow只抓标题。 没试过,但应该可以,不过标题一定要一字不差。
第二:枚举窗口,如果有窗口对应的PID对应的进程名叫WindowsTerminal.exe且标题是正确的标题,则为正确的窗口句柄。 这个方案成功了!我把代码放一下,该代码的作用是自动判断是否用的是新终端,如果是则抓,如果不是则抓旧版控制台:
HWND hwnd = NULL;
bool ExistProcess(LPCSTR lpName) //判断是否存在指定进程
{ //******警告!区分大小写!!!!******//
//*****警告!必须加扩展名!!!!*****//
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hSnapshot)
{
return false;
}
PROCESSENTRY32 pe = { sizeof(pe) };
BOOL fOk;
for (fOk = Process32First(hSnapshot, &pe); fOk; fOk = Process32Next(hSnapshot, &pe))
{
if (! stricmp(pe.szExeFile, lpName))
{
CloseHandle(hSnapshot);
return true;
}
}
return false;
}
bool TerminalCheck(DWORD dwPid, HWND _hwnd)
{
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hSnapshot)
{
return false;
}
PROCESSENTRY32 pe = { sizeof(pe) };
BOOL fOk;
for (fOk = Process32First(hSnapshot, &pe); fOk; fOk = Process32Next(hSnapshot, &pe))
{
if (! stricmp(pe.szExeFile, "WindowsTerminal.exe") //进程名
&& pe.th32ProcessID == dwPid)
{ //stricmp不区分大小写的字符串比较
CloseHandle(hSnapshot);
{
char title[MAX_PATH];
GetWindowText(_hwnd, title, MAX_PATH);
if(strcmp(title, _pgmptr) && strcmp(title, "学生管理系统 " CURRENT_VERSION))
//_pgmptr是本体路径(用于还没来得及改标题的情况) 后面那个则是具体你设的标题,自定义即可
return false;
return true;
}
}
}
return false;
}
BOOL CALLBACK EnumWindowsProc(HWND _hwnd, LPARAM lParam)
{ //回调函数
DWORD pid;
GetWindowThreadProcessId(_hwnd, &pid);
if(TerminalCheck(pid, _hwnd))
{
printf("Terminal Found! pid=%ld hwnd=%p\n", pid, _hwnd);
hwnd = _hwnd;
return FALSE; //返回false中断枚举
}
return TRUE;
}
int main(int argc, char* argv[])
{
if(ExistProcess("WindowsTerminal.exe"))
{ //win11电脑且使用新版终端
EnumWindows(EnumWindowsProc, 0);
}else{ //旧版控制台主机
hwnd = GetConsoleWindow();
}
//再不行就只能GetForegroundWindow了
if(!hwnd || hwnd == INVALID_HANDLE_VALUE)
{
hwnd = GetForegroundWindow();
printf("Failed! using GetForegroundWindow...");
}
//其他的一些事情...
return 0;
}
必要的头文件:stdio.h stdlib.h windows.h tlhelp32.h stdbool.h string.h
去试试看行不行,我刚试成功的代码