Как заставить SendKeys действовать синхронно в IBM Host Access Library

Я использую IBM Host Access Class Library for COM Automation как способ связи с IBM AS400 (он же iSeries, IBM i, зеленый экран, 5250) через эмулятор терминала. Я заметил, что когда вы вводите инструкцию «SendKeys», управление возвращается вашему приложению до того, как эмулятор IBM завершит выполнение команды. Это может привести к проблемам со временем, потому что вы можете отправить еще одну инструкцию «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

будет работать правильно при пошаговом выполнении кода в отладчике, но будет давать сбой при нормальной работе, потому что вторая инструкция была отправлена ​​слишком рано.

Один из способов работать с этим — поставить таймер или цикл после каждой команды, чтобы замедлить вызывающую программу. Я считаю это менее чем идеальным, потому что продолжительность времени не всегда предсказуема, вы часто будете ждать дольше, чем необходимо, чтобы приспособиться к случайной икоте. Это замедляет время выполнения всего процесса.

Другой способ обойти это — подождать, пока на экране не появится тестируемое состояние в результате отправленной вами команды. Иногда это будет работать, но некоторые команды не вызывают изменения экрана для проверки, и если вы хотите абстрагировать вызов команды в класс или подпрограмму, вам нужно будет указать, какое состояние экрана следует отслеживать.

Что я хотел бы найти, так это один из методов «Ждать», который будет работать в общем случае. Такие параметры, как класс autECLScreenDesc, похоже, должны быть адаптированы к очень конкретным условиям.

Класс autECLPS (также известный как AutPS) имеет несколько методов ожидания (Wait, WaitForCursor, WaitWhileCursor, WaitForString, WaitWhileString, WaitForStringInRect, WaitWhileStringInRect, WaitForAttrib, WaitWhileAttrib, WaitForScreen, WaitWhileScreen), но они также, похоже, ожидают для конкретных условий и не работают для общего случая. Общий случай важен для меня, потому что я на самом деле пытаюсь написать подпрограмму обновления поля общего назначения, которую можно вызывать из многих мест внутри и за пределами моей .dll.

Этот пример написан на VB.NET, но я ожидаю такого же поведения от C#, C++, VB6, Java; действительно все, что использует IBM Personal Communications для Windows, библиотека классов доступа к хосту версии 6.0.


person Mike    schedule 23.10.2015    source источник
comment
Терминал с зеленым экраном для компьютеров IBM среднего класса (включая AS/400 и его преемников) — это не 3270, а скорее 5250. 3270 использовался для мейнфреймов IBM (начиная с System/370, согласно Википедии).   -  person John Y    schedule 11.11.2015
comment
@JohnY Вы правы. Я перепутал названия терминалов, потому что у этой компании есть как старая система IBM на основе CICS, использующая 3270 (для которой я написал несколько лет назад экранные оболочки), так и AS400 (которую мы недавно приобрели в результате слияния). Я обновлю этот вопрос, чтобы сказать 5250.   -  person Mike    schedule 11.11.2015


Ответы (1)


Класс «Информационная область оператора», по-видимому, обеспечивает решение этой проблемы.

Мой общий случай, кажется, работает правильно с этой реализацией:

 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

Я благодарю пользователя по имени "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
person Mike    schedule 23.10.2015