如何使 SendKeys 在 IBM Host Access Library 中同步执行

How to make SendKeys act Synchronously in IBM Host Access Library

我使用 IBM Host Access Class Library for COM Automation 作为通过终端仿真器与 IBM AS400(又名 iSeries、IBM i、绿屏、5250)通信的方式。我注意到,当您发出 "SendKeys" 指令时,在 IBM 模拟器完成该命令之前控制 returns 到您的应用程序。这可能会导致计时问题,因为您可能会在系统准备好接受之前发送另一个 "SendKeys" 指令。

例如:

Imports AutPSTypeLibrary
Imports AutConnListTypeLibrary
Imports AutSessTypeLibrary

Sub Example
    Dim connections As New AutConnList
    connections.Refresh()
    If connections.Count < 1 Then Throw New InvalidOperationException("No AS400 screen can currently be found.")
    Dim connection As IAutConnInfo = DirectCast(connections(1), IAutConnInfo)

    _Session = New AutSess2
    _Session.SetConnectionByHandle(connection.Handle)
    Dim _Presentation As AutPS = DirectCast(_Session.autECLPS, AutPS)
    _Presentation.SendKeys("PM70[enter]", 22, 8)
    _Presentation.SendKeys("ND71221AD[enter]", 22, 20)

End Sub

在调试器中单步执行代码时会正常工作,但在运行正常时会失败,因为第二条指令发送得太早。

处理这个问题的一种方法是在每个命令之后放置一个计时器或循环来减慢调用程序的速度。我认为这不太理想,因为时间的长度并不总是可以预测的,您通常会等待比必要的时间更长的时间来适应偶尔的打嗝。这会减慢整个过程的 运行 时间。

解决此问题的另一种方法是等待屏幕上出现可测试条件作为您发送的命令的结果。这有时会起作用,但有些命令不会导致屏幕更改以进行测试,如果您希望将命令调用抽象为 class 或子例程,则必须传递要监视的屏幕条件。

我想找到的是 "Wait" 在一般情况下可以使用的方法之一。像 autECLScreenDesc class 这样的选项似乎必须根据非常具体的条件进行调整。

autECLPS(又名 AutPS)class 有许多 Wait 方法 但它们似乎也在等待特定条件,不适用于一般情况。一般情况对我来说很重要,因为我实际上正在尝试编写一个通用字段更新子例程,可以从我的 .dll 内外的许多地方调用它。

此示例是用 VB.NET 编写的,但我希望 C#、C++、VB6、Java 具有相同的行为;任何使用 IBM Personal Communications for Windows,版本 6.0 的东西 主机访问 Class 图书馆。

"Operator Information Area" class 似乎提供了解决这个问题的方法。

我的一般情况似乎与此实现一起正常工作:

 Friend Sub PutTextWithEnter(ByVal field As FieldDefinition, ByVal value As String)
    If IsNothing(field) Then Throw New ArgumentNullException("field")
    If IsNothing(value) Then Throw New ArgumentNullException("value")
    _Presentation.SendKeys(Mid(value.Trim, 1, field.Length).PadRight(field.Length) & "[enter]", field.Row, field.Column)
    WaitForEmulator(_Session.Handle)
End Sub

Private Sub WaitForEmulator(ByVal EmulatorHandle As Integer)
    Dim Oia As New AutOIATypeLibrary.AutOIA
    Oia.SetConnectionByHandle(EmulatorHandle)
    Oia.WaitForInputReady()
    Oia.WaitForAppAvailable()
End Sub

感谢 this message board 上一位名叫 "khieyzer" 的用户指出了我们这个干净且通用的解决方案。

编辑:

经过数周的调试并解决计时和资源释放问题后,此方法现在如下所示:

Private Sub WaitForEmulator(ByRef NeededReset As Boolean)
    Dim Oia As New AutOIA
    Oia.SetConnectionByHandle(_Presentation.Handle)

    Dim inhibit As InhibitReason = Oia.InputInhibited
    If inhibit = InhibitReason.pcOtherInhibit Then
        _Presentation.SendKeys("[reset]")
        NeededReset = True
        WaitForEmulator(NeededReset)
        Marshal.ReleaseComObject(Oia)
        Exit Sub
    End If

    If Not Oia.WaitForInputReady(6000) Then
        If Oia.InputInhibited = InhibitReason.pcOtherInhibit Then
            _Presentation.SendKeys("[reset]")
            NeededReset = True
            WaitForEmulator(NeededReset)
            Marshal.ReleaseComObject(Oia)
            Exit Sub
        Else
            Marshal.ReleaseComObject(Oia)
            Throw New InvalidOperationException("The system has stopped responding.")
        End If
    End If

    Oia.WaitForInputReady()
    Oia.WaitForAppAvailable()
    Marshal.ReleaseComObject(Oia)
End Sub