已知的 InputBox 函数问题

0

InputBox 函数会占用检查按键函数,导致即使关闭对话框,使用 GetKeyState 函数时在 kernel32.dll 等出现 0x8001010E 错误,提示“应用程序调用一个已为另一线程整理的接口。”

说明,我用的是WinMain,不在控制台,不能用_getch

#define MAX_LOADSTRING 100
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPTSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
	initgraph(640, 480);
	TCHAR str[MAX_LOADSTRING];
	InputBox(str, MAX_LOADSTRING, _T("请输入"), _T("请输入"));
	if (GetKeyState(VK_ESCAPE) < 0) { ... }
}
ava
悲剧天下

2020-3-20

0

希望你可以补充个详细源码来说明问题。

补充 1:

你后来补充的代码,仍然是无法编译的。我根据你的代码做了额外补充,完整的可编译代码如下:

// 测试环境 1:Win10(1909),VS2019,EasyX_20200315(beta)
// 测试环境 2:WinXP(sp3),VS2010,EasyX_20200315(beta)
//
#include <graphics.h>
#define MAX_LOADSTRING 100

int APIENTRY _tWinMain(	_In_ HINSTANCE hInstance,
						_In_opt_ HINSTANCE hPrevInstance,
						_In_ LPTSTR lpCmdLine,
						_In_ int nCmdShow)
{
	initgraph(640, 480);
	TCHAR str[MAX_LOADSTRING];
	InputBox(str, MAX_LOADSTRING, _T("请输入"), _T("请输入"));

	if (GetKeyState(VK_ESCAPE) < 0)
	{
		Sleep(1000);
	}

	for (int x = 0; x < 640; x = (x + 10) % 640)
	{
		cleardevice();
		circle(x, 100, 20);
		Sleep(100);
	}
}

如代码中注释,我分别在 Win10 和 WinXP 两个系统下做了测试。同时,Win 10 下分别以 x86 和 x64 做了编译。

执行程序后在 InputBox 里面输入字符串“test”,点击确定。之后并未发现任何错误,Output 窗口也没有任何异常。

你看,为什么简单的问题,我们俩却看到了不同的结果?或许是因为你装了 360,或许是因为我的 Win10 升级到了最新版,或许是你电脑中毒了。。。

每个电脑的环境都有差异。很多时候,不能完整的描述环境,就无法定位问题所在。

PS:你在别人电脑上能否复现这个问题呢?

补充 2:

在我的“补充 1“的代码里面的 Sleep(1000) 加断点,不会命中。因为 InputBox 是阻塞调用,且默认不允许”取消“,所以按 ESC 是无效的。在 InputBox 窗口关闭后的瞬间按下 ESC 才会被 if 断点命中,显然我的反应能力拼不过计算机。

如果你需要 ESC 关闭 InputBox 窗口,请参考 InputBox 的帮助:https://docs.easyx.cn/inputbox。但请注意:关闭窗口的 ESC 按键,不会发送至绘图窗口,这是两码事。

另外,判断按键应该用异步方法 GetAsyncKeyState。

如果你希望按下 ESC 使 if 为真,代码可以这么修改:

// 编译环境:Win10(1909),VS2019,EasyX_20200315(beta)
// 
#include <graphics.h>
#define MAX_LOADSTRING 100

int APIENTRY _tWinMain(	_In_ HINSTANCE hInstance,
						_In_opt_ HINSTANCE hPrevInstance,
						_In_ LPTSTR lpCmdLine,
						_In_ int nCmdShow)
{
	initgraph(640, 480);
	TCHAR str[MAX_LOADSTRING];
	InputBox(str, MAX_LOADSTRING, _T("请输入"), _T("请输入"));

	Sleep(2000);

	if (GetAsyncKeyState(VK_ESCAPE) & 0x8000)
	{
		Sleep(1000);
	}

	for (int x = 0; x < 640; x = (x + 10) % 640)
	{
		cleardevice();
		circle(x, 100, 20);
		Sleep(100);
	}
}

PS:建议你将评论里面写的关键信息,补充到自己的提问里面。

ava
慢羊羊

2020-3-20

我不明白,这么简单的陈述,这么简单的问题,怎么还需要代码?不过我已经贴上了 - 悲剧天下 2020-3-21
@悲剧天下 因为我无法复现你的问题。所以不仅需要代码,还需要你对自己环境的详细描述。 - 慢羊羊 2020-3-21
我想问,如果你在Sleep(1000);那行加一个断点,该断点是否会命中? <audio style="display: none;" controls="controls"></audio> - 悲剧天下 2020-3-21
另外我再补充下,调试时按ESC输出窗口显示的是 onecore\com\combase\dcomrem\channelb.cxx(6385)\combase.dll!76B8076E: (caller: 75025BB9) ReturnHr(1) tid(4734) 8001010E 应用程序调用一个已为另一线程整理的接口。 0x75B43DB2 (KernelBase.dll)处(位于 Project.exe 中)引发的异常: 0x8001010E: 应用程序调用一个已为另一线程整理的接口。。  - 悲剧天下 2020-3-21
或者你把if里面执行的语句换为return试试  - 悲剧天下 2020-3-21
另外我遵从你的建议在另一台计算机试过了,目标计算机系统为 Windows 7 sp1,运行版本为Release,按下ESC仍然没有反应  - 悲剧天下 2020-3-21
我再多补充补充吧,具体来讲,我之所以认定问题出在InputBox函数,是因为将该函数注释掉程序可以响应ESC,而取消注释则程序不响应ESC。另外我这里说的问题不是在于程序不能顺利执行,而是在于GetKeyState函数无法调用。 - 悲剧天下 2020-3-21
另外,我贴的代码之所以在你那里通不过,是因为我的if块里只写了三个点,而没有写具体内容,将三个点替换为具体内容则程序可以编译通过  - 悲剧天下 2020-3-21
@悲剧天下 相关回复已在主贴中补充。 - 慢羊羊 2020-3-21
谢谢,那么说到底还是判断按键按下的方法错了。 但是,能不能请你讲讲,为什么不用InputBox时可以用刚才那种方法判断按键按下呢? 另外,我的判断方式之所以用GetKeyState<0,是因为我看到Windows API 里面检测鼠标按下的宏函数是这么写的。 - 悲剧天下 2020-3-21
@悲剧天下 具体的,你需要参考 MSDN 对 GetKeyState 和 GetAsyncKeyState 的解释。 - 慢羊羊 2020-3-21
@慢羊羊 我看Microsoft Docs 里好像是说 GetKeyState 获取逻辑按键状态,GetAsyncKeyState 获取物理按键状态,好像更推荐用前者。 另外我觉得对于禁止输入框退出,你们完全可以通过回调函数里响应 WM_KEYDOWN 的 VK_ESCAPE 或者是 WM_COMMAND 里的 IDCANCLE 来实现,没有必要采用这种有后遗症的阻塞按键的方式吧?  - 悲剧天下 2020-3-22
@悲剧天下 1. 你可以在 bing 里面搜索下 GetKeyState 与 GetAsyncKeyState 的区别。2. 阻塞方式暂时没有发现后遗症。如果你希望 InputBox 可以接受 ESC 按键关闭,直接通过参数控制即可。</span> - 慢羊羊 2020-3-22
@慢羊羊 我没有想InputBox是不是能接受ESC键,我一直考虑的是我的主窗口能不能接受ESC键,为什么你总是在说InputBox怎么接受ESC呢? 我提出的问题始终是 GetKeyState(VK_ESCAPE) 阻塞的问题,所谓“暂时没有发现后遗症”,难道你不觉得在主窗口无法使用 GetKeyState(VK_ESCAPE) 是 InputBox 函数的后遗症吗?我只是不理解,为什么要采用这种阻塞的方式呢?在回调函数中处理 WM_KEYDOWN 或 WM_COMMAND 不好吗?  - 悲剧天下 2020-3-22