win11 下 visual studio 运行 easyx 时无法使用控制台函数交互

2

正常情况下运行 easyx 时只会弹出 easyx 的图形窗口而不会弹出平时常出现的终端黑色窗口,但 win11 某次版本更新之后它会同时弹出这两个窗口,并且键盘交互只能在黑色的终端窗口中进行,没法对着 easyx 的图形窗口进行键盘交互,并且关闭黑色终端窗口的同时 easyx 的窗口也会随之关闭。

操作系统:Windows 11 家庭中文版,操作系统版本 22621.819

编译环境:Microsoft visual studio 2022 community,EasyX_20220901

以下代码是临时编写的,一个简单的提供键盘交互功能的 easyx 图形窗口的代码

#include <graphics.h>
#include <conio.h>
#include <stdlib.h>

int main()
{
	initgraph(640, 480);

	char c;
	do
	{
		c = _getch();
		switch (c)
		{
		case 'a':	// 画小圆
			circle(rand() % 640, rand() % 480, 10);
			break;
		case 'b':	// 画大圆
			circle(rand() % 640, rand() % 480, 30);
			break;
		case 'c':	// 退出
			break;
		}
	} while (c != 'c');

	_getch();
	closegraph();
	return 0;
}
ava
氟拉基米尔

2022-11-25

3

原因

因为 getch / getchar 是控制台函数,依赖控制台的形式。

新的 windows 升级后将 PowerShell 作为默认控制台,导致 easyx 无法获取到 cmd 的输入,从而导致这个问题。

方法一:恢复原来的 cmd 控制台

打开 win11 的系统设置 -> 隐私和安全性 -> 开发者选项,找到“终端”项,里面有三项:“让 Windows 决定(默认)、Windows 控制台主机、Windows 终端”,改为“Windows 控制台主机”即可。

方法二:替换 getch() 获取字符输入

可以将对应的获取按键语句修改为用 getmessage 实现,具体如下:

#include <graphics.h>
#include <conio.h>
#include <stdlib.h>

int main()
{
	initgraph(640, 480);

	ExMessage m;
	do
	{
		m = getmessage(EX_CHAR);
		switch (m.ch)
		{
		case 'a':	// 画小圆
			circle(rand() % 640, rand() % 480, 10);
			break;
		case 'b':	// 画大圆
			circle(rand() % 640, rand() % 480, 30);
			break;
		case 'c':	// 退出
			break;
		}
	} while (m.ch != 'c');

	getmessage(EX_CHAR);
	closegraph();
	return 0;
}

补充 1:不显示控制台窗口

以上这样做,可以解决输入的问题,但是控制台的黑窗口仍然在。如果想彻底去掉控制台的黑窗口,需要创建 Win32 Application 类型的应用,而不是控制台类型的应用。具体可以看:https://codebus.cn/yangw/use-easyx-in-win32-application

重新创建项目后,代码也要将 main 对应的修改为 WinMain 格式,就可以彻底去掉控制台的黑窗口。

补充 2:实现更流畅的按键输入

在使用 getch() 输入字符时,如果长按某个按键,会遇到先接收一个字符,然后停顿一下,然后再接收一串字符。这是因为 getch() 获取的是字符,平时字符输入的时候就是这样响应的,所以会导致这样的效果。使用 getmessage(EX_CHAR) 仍然是获取字符消息,所以和 getch 效果一样。

如果需要更流畅的按键输入,可以获取按键消息。

按键消息与字符消息不同,对于字符消息,'A' 和 'a' 是不同的。对于按键消息,得到的是虚拟键码,'A' 和 'a' 对应的按键是一样的,没有区别。

同时,getmessage 是阻塞式的消息获取函数,如果没有新的消息,会阻断程序的执行。为了使控制上显得更流畅,可以使用非阻塞式的消息获取函数 peekmessage,该函数在获取到消息后返回 true,否则返回 false。

以下是一个例子,可以实现 a s d w 或方向键控制一个圆的移动(支持多个按键同时按下实现斜向移动),按 ESC 键退出程序。完整代码如下:

(可以根据需要,修改为 Win32 Application)

#include <graphics.h>

int main()
{
	initgraph(640, 480);
	BeginBatchDraw();

	int x = 320, y = 240;
	bool w = false, s = false, a = false, d = false;	// 记录各按键的按下状态
	bool exit = false;
	
	ExMessage m;

	while(!exit)
	{
		// 获取控制
		while(peekmessage(&m, EX_KEY))
		{
			if (m.message == WM_KEYDOWN)
			{
				switch (m.vkcode)
				{
					case VK_UP:
					case 'W':		w = true;		break;
					case VK_DOWN:
					case 'S':		s = true;		break;
					case VK_LEFT:
					case 'A':		a = true;		break;
					case VK_RIGHT:
					case 'D':		d = true;		break;
					case VK_ESCAPE:	exit = true;	break;
				}
			}
			if (m.message == WM_KEYUP)
			{
				switch (m.vkcode)
				{
					case VK_UP:
					case 'W':		w = false;		break;
					case VK_DOWN:
					case 'S':		s = false;		break;
					case VK_LEFT:
					case 'A':		a = false;		break;
					case VK_RIGHT:
					case 'D':		d = false;		break;
				}
			}
		}

		// 计算数据
		if (w)	y--;
		if (s)	y++;
		if (a)	x--;
		if (d)	x++;

		// 绘制
		cleardevice();
		circle(x, y, 20);

		// 刷新屏幕并延时
		FlushBatchDraw();
		Sleep(10);

	} while (m.vkcode != VK_ESCAPE);

	EndBatchDraw();
	closegraph();
	return 0;
}

同时,也可以使用 Windows API 实现流畅的按键控制,请参考:https://codebus.cn/yangw/detect-combination-keys-and-advanced-key-handling

ava
慢羊羊

2022-11-25

太牛了,彻底解决了我的问题! -  the man of  2023-12-4
技术讨论社区