Використання функцій очікування в Visual Basic, Basic, Програмування, статті

Досить часто у програмістів, пишучих свої програми на Visual Basic, виникає потреба в використанні функцій Windows 32 API, що затримують виконання програми до настання певної події. Залишимо поки в осторонь питання, коли і як виникає така потреба – це тема для окремої статті. Також не будемо зупинятися на описі параметрів і повертаються значень обговорюваних функцій: бажаючий завжди може почерпнути ці відомості з
MSDN

Ось список цих функцій:






Sleep SleepEx
WaitForSingleObject WaitForSingleObjectEx
WaitForMultipleObjects WaitForMultipleObjectsEx
MsgWaitForMultipleObjects MsgWaitForMultipleObjectsEx
  SignalObjectAndWait

Ми будемо розглядати тільки функції з першого стовпця таблиці. Решта функцій застосовуються щодо рідко, і, в кінцевому рахунку, проблема їх використання в програмах на Visual Basic вирішується аналогічно тому, що буде викладено нижче.

Отже, з якими ж проблемами може зіткнутися програміст, використовуючи вищенаведені функції API?

На щастя, проблема всього одна, але вона досить серйозна. Справа в тому, що програми, написані на Visual Basic, за невеликим винятком, виконуються цілком в одному потоці операційної системи, а це означає, що, коли виповнюється одна з функцій очікування, “Життя” програми повністю завмирає: перестає оновлюватися візуальний інтерфейс, перестають натискатися кнопки на формі і т.д. Може статися гірше: деякі компоненти ОС взаємодіють з користувацькими програмами в синхронному режимі, і іноді це призводить до “підвисання” оболонки ОС на тривалий час.

Кожна з перерахованих вище функцій очікування має серед своїх параметрів інтервал часу очікування, після закінчення якого виконання програми триває. В принципі, якщо цей інтервал не перевищує 1 – 2 секунд, функції очікування можна використовувати без проблем, але якщо період очікування значно більше або взагалі нескінченний, потрібне застосування іншого підходу.

На щастя, серед функцій очікування є функція MsgWaitForMultipleObjects , Здатна “прокидатися”, коли в черзі потоку з’являється нове повідомлення. А це саме те, що потрібно! Згадаймо, що система називається Windows, а значить, вона складається з вікон, і вікна взаємодіють між собою шляхом посилки один одному повідомлення. Так Отож, не вдаючись у подробиці, відзначимо, що будь-яка подія, на яке має відреагувати вікно, представляє програму, будь то необхідність його перемальовування після того, як з нього прибрали вікно іншої програми, або необхідність реакції на натискання екранної кнопки, призводить до появи в черзі потоку, обслуговуючого дане вікно, нового повідомлення. Програма на Visual Basic обробить це повідомлення тільки при настанні одного з двох подій:

Узагальнюючи сказане вище, можна сформулювати принципи застосування функцій очікування API в програмах на
Visual Basic:

  • При невеликих інтервалах функції очікування можна застосовувати без обмежень
  • Якщо інтервал великий або невідомий, слід застосовувати тільки функцію MsgWaitForMultipleObjects
  • При появі в черзі потоку нового повідомлення потрібно викликати функцію DoEvents.

Тепер саме час проілюструвати сказане прикладом. Приводиться нижче функцію MsgWaitObj пропонується використовувати як неблокуючим еквівалента функцій Sleep,
WaitForSingleObject
і WaitForMultipleObjects.

Option Explicit '******************************************** '* (C) 1999 Сергій Мерзлікін * '******************************************** Private Const STATUS_TIMEOUT = &H102& Private Const INFINITE = -1 & 'Нескінченний інтервал Private Const QS_KEY = &H1& Private Const QS_MOUSEMOVE = &H2& Private Const QS_MOUSEBUTTON = &H4& Private Const QS_POSTMESSAGE = &H8& Private Const QS_TIMER = &H10& Private Const QS_PAINT = &H20& Private Const QS_SENDMESSAGE = &H40& Private Const QS_HOTKEY = &H80& Private Const QS_ALLINPUT = (QS_SENDMESSAGE Or QS_PAINT _ Or QS_TIMER Or QS_POSTMESSAGE Or QS_MOUSEBUTTON _ Or QS_MOUSEMOVE Or QS_HOTKEY Or QS_KEY) Private Declare Function MsgWaitForMultipleObjects Lib "user32" _ (ByVal nCount As Long, pHandles As Long, _ ByVal fWaitAll As Long, ByVal dwMilliseconds _ As Long, ByVal dwWakeMask As Long) As Long Private Declare Function GetTickCount Lib "kernel32" () As Long 'Функція MsgWaitObj служить для заміни функцій Sleep, ' WaitForSingleObject, WaitForMultipleObjects. 'На відміну від перерахованих, дана функція 'Не блокує обробку повідомлень потоку. 'Виклик замість Sleep: ' MsgWaitObj dwMilliseconds 'Виклик замість WaitForSingleObject: ' retval = MsgWaitObj(dwMilliseconds, hObj, 1) 'Виклик замість WaitForMultipleObjects: ' retval = MsgWaitObj(dwMilliseconds, hObj(0), n), 'Де n - кількість об'єктів, 'HObj () - масив їх описувачів. Public Function MsgWaitObj(Interval As Long, _ Optional hObj As Long = 0, _ Optional nObj As Long = 0) As Long Dim T As Long, T1 As Long If Interval <> INFINITE Then T = GetTickCount() On Error Resume Next T = T + Interval 'Запобігання переповнення If Err <> 0 Then T = _ ((T + &H80000000) + Interval) + &H80000000 On Error GoTo 0 'В змінної T - абсолютна час закінчення інтервалу Else T1 = INFINITE End If Do If Interval <> INFINITE Then T1 = GetTickCount() On Error Resume Next T1 = T - T1 'Запобігання переповнення If Err <> 0 Then T1 = _ ((T - &H80000000) - (T1 + &H80000000)) On Error GoTo 0 'В змінної T1 - решта інтервалу If T1 < 0 Then ' Інтервал минув, поки                                                ' виконувалася DoEvents                                              MsgWaitObj = STATUS_TIMEOUT                                                     Exit Function                                                 End If                                           End If                             ' Чекаємо події, закінчення інтервалу або                              ' появи повідомлення в черзі потоку                                         MsgWaitObj =                           MsgWaitForMultipleObjects (nObj, _                                                 hObj, 0, T1, QS_ALLINPUT)                            ' Даємо можливість повідомленням обробитися                                           DoEvents                                      If MsgWaitObj <> nObj Then Exit Function 'Було повідомлення в черзі потоку - продовжуємо чекати Loop End Function

Кілька коментарів до вищенаведеним кодом:

  1. Навіщо знадобилося запобігання переповнення? Справа в те, що функція GetTickCount, Повертаючи кількість мілісекунд, минули з моменту завантаження системи, повертає їх у вигляді беззнакового подвійного слова (DWord). Максимальна значення DWord&HFFFFFFFF. Найближчим еквівалентом такого типу в Бейсіку є Long, Але Long завжди зі знаком, і його максимальне значення для позитивних чисел – &H7FFFFFFF. Якщо значення, повертається функцією
    GetTickCount, Знаходиться близько до цього рубежу, може відбутися арифметична помилка переповнення в наступному рядку програми.
    Ви скажете, що такого ніколи не трапиться, оскільки комп’ютери так довго (якщо число &H7FFFFFFF мілісекунд перевести в звичний масштаб часу, то вийде трохи менше 25 діб) без перезавантаження не працюють? Я з вами не згоден. Надійна програма повинна враховувати і таку можливість.
    Коли ж комп’ютер працює вже так довго, що кількість мілісекунд НЕ поміщається навіть в DWord,GetTickCount починає рахунок з нуля. Правда, з точки зору арифметики Visual Basic, ніякої помилки не відбувається: просто за -1 слід 0.
  1. Win32API.txt говорить:
    Const INFINITE = &HFFFF

    В принципі це вірно, якщо не враховувати того, що таке визначення може збити з пантелику навіть досвідченого програміста. Коли ця константа з’являється у вигляді параметра типу Long функції API, можна подумати, що функції передається число 65535, Але це не так. Коли тип числової константи не описаний, вважається, що її тип – Integer, Якщо відповідне число поміщається в область допустимих значень цього типу. Але для Integer &HFFFF = -1, і саме це число, перетворене в тип Long, Передається функції API. Тому під уникнути непорозумінь раджу це визначення писати так:

    Const INFINITE = -1&

    або так:

    Const INFINITE =
    &HFFFFFFFF

Див також Microsoft
Knowledge Base Q231298.

Ось, власне, і все. Наведений вище код можна скопіювати прямо з браузера.

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*