3

我的问题是关于非常古老的技术。我的任务是自动化在 Windows 98 中以 Windows 模式运行的旧 DOS 软件(光谱)。我做了两个不同的解决方案,但它们都不适用于 DOS 应用程序

  1. 第一个解决方案

    • 激活 DOS 应用程序
    • 通过 SendInput 函数发送输入,如下所示:
    void MossbauerLab::Sm2201::SaveManager::AutoSaveManager::sendKeysViaInput(const std::vector<DWORD>& keys, int keyPause)
    {
        std::vector<DWORD>::const_iterator it;
        INPUT keyBoardInput;
        keyBoardInput.type = INPUT_KEYBOARD;
        keyBoardInput.ki.wScan = 0;
        keyBoardInput.ki.time = 0;
        keyBoardInput.ki.dwExtraInfo = 0;

        for(it = keys.begin(); it != keys.end(); it++)
        {
            keyBoardInput.ki.wVk = (*it);
            keyBoardInput.ki.dwFlags = 0;   // key down
            SendInput(1, &keyBoardInput, sizeof(INPUT));
            Sleep(keyPause);
            keyBoardInput.ki.dwFlags = 2;   // key up
            SendInput(1, &keyBoardInput, sizeof(INPUT));
            Sleep(keyPause);
        }
    }
  1. 通过 i8042 键盘控制器生成按键:使用 D2 命令写入键盘缓冲区命令,如下所示(KEYBOARD_CMD_REG - 0x64,KEYBOARD_DATA_REG - 0x60):
    void MossbauerLab::Sm2201::SaveManager::AutoSaveManager::sendKeysViaKeyboardController(const std::vector<BYTE>& scanCodes, int keyPause)
    {
        std::vector<BYTE>::const_iterator it;
        for(it = scanCodes.begin(); it != scanCodes.end(); it++)
        {
            // wait untill buffer is empty
            int status = 0;
            int result = 0;
            do
            {
                status = _inp(0x64);
                // std::cout <<"Keyboard status: "<< status << std::endl;
                Sleep(10);
            }
            while (status & 1);

            // send scan code for key down
            _outp(KEYBOARD_CMD_REG, 0xD2);
            _outp(KEYBOARD_DATA_REG, (*it));
            result = _inp(KEYBOARD_DATA_REG);
            std::cout <<"Keyboard command result for KEY DOWN: "<< result << std::endl;
            // send scan code for key up
            BYTE keyUpCode = (*it) | 128;
            Sleep(keyPause);
            _outp(KEYBOARD_CMD_REG, 0xD2);
            _outp(KEYBOARD_DATA_REG, keyUpCode);
            result = _inp(KEYBOARD_DATA_REG);
            std::cout <<"Keyboard command result for KEY UP: "<< result << std::endl;
        }
    }

用标准记事本窗口(notepad.exe)测试了这两种解决方案,它们都工作正常,但我无法让它与 DOS 应用程序一起工作。

我在其中生成键盘输入(和整个项目)的代码:https ://github.com/MossbauerLab/Sm2201Autosave/blob/master/MossbauerLab.Sm2201.ExtSaveUtility/src/saveManager/autoSaveManager.cpp

你能帮我解决这个问题吗?

4

3 回答 3

1

首先我要感谢所有对这篇文章感兴趣的人,你们都给了我一些有用的信息,我终于做出了我想要的。

关于它是如何实现的:我已经创建了 VXD 驱动程序(它是我的专有驱动程序,我不能在这里发布它的代码,因为我计划将它用于商业项目)。但此评论的主要目标是——是的,可以模拟 KEYBOARD INPUT 用于在 Windows 98 中以窗口模式运行的 DOS 应用程序。我可以发布的部分解决方案在 github 上:https ://github.com/MossbauerLab/Sm2201Autosave/blob/master/MossbauerLab.Sm2201.ExtSaveUtility/src/saveManager/autoSaveManager.cpp

这里我使用 vXd 访问器类:(https://github.com/MossbauerLab/Sm2201Autosave/blob/master/MossbauerLab.Sm2201.ExtSaveUtility/src/utils/windows/vxdAccessor.cpp

主要思想 - 使用 W32_DEVICEIOCONTROL 驱动程序消息处理程序和 DeviceIoControl 函数与驱动程序交互,并使用 CTL(在我的驱动程序中定义)传递具有 1 个字节对齐的结构代码:我没有使用 CTL_CODE 宏。这个 Github 项目几乎准备好了~95%(目前我只需要手动选择窗口来发送,通过鼠标点击激活它并检查是否有可用的内存)。

于 2020-03-02T09:04:27.230 回答
0

16 年前我遇到了同样的问题,我的(不太优雅但可能更容易实现)的解决方案是将要插入的文本放入剪贴板,获取前景窗口 hWnd,然后使用 SendMessage() 发送它 WM_SYSCOMMAND以 0xE001 作为数据。IIRC 这触发了窗口上下文菜单中的粘贴命令(我使用 Spy++ 找到了)。

我希望有人觉得这很有用。:)

于 2021-02-11T16:42:33.483 回答
-1

Windows 98 中的 MS-DOS 应用程序在虚拟实模式下运行,而不是在保护模式下,这就是为什么使用保护模式驱动程序或系统调用无法与它们通信的原因。您需要使用 BIOS / DOS 中断与键盘交互,Windows 驱动程序将无法为您执行此操作。

因此,这样做的唯一方法是使用另一个 DOS 应用程序模拟按键,但我不确定 BIOS 键盘缓冲区是否是虚拟化的,但如果没有,那么您应该能够通过启动 Virtual Real 来将其关闭来自您的应用程序的模式进程(必须是一个单独的 exe,但您应该能够摆脱无窗口运行它)并将按键作为命令行参数传递以将它们发送到 BIOS。

这是一个非常有趣的问题,希望对您有所帮助并且您设法解决它。

于 2020-02-10T15:58:45.807 回答