Вопрос про doevents. Как побороть подвисание формы...?

Программирование на Visual Basic, главный форум. Обсуждение тем программирования на VB 1—6.
Даже если вы плохо разбираетесь в VB и программировании вообще — тут вам помогут. В разумных пределах, конечно.
Правила форума
Темы, в которых будет сначала написано «что нужно сделать», а затем просьба «помогите», будут закрыты.
Читайте требования к создаваемым темам.
2vv
Новичок
Новичок
 
Сообщения: 36
Зарегистрирован: 14.03.2008 (Пт) 17:36

Вопрос про doevents. Как побороть подвисание формы...?

Сообщение 2vv » 17.03.2008 (Пн) 19:37

Пользовался поиском, но так и не смог самостоятельно разобраться с проблемкой.
В коде есть функция , она выполняется определенное время, так вот пока она не отработает форма "замораживается"(перестает отвечать). Пытался разобраться с функцией DoEvents. Насколько я понял она передает управление системе принудительно , и если в очереди есть события то система их отрабатывает. Эта функция помогает если в коде есть затяжной цикл, а как быть в моем случае?
Читал что можно организовать цикл ожидания , но так и не понял как это сделать.
Пока проблему решаю таймером. Вызываю через каждые 200мс DoEvents,
немного помогает но мне кажется это неправильно.
Подскажите как решают такую проблему.
Спасибо.

ANDLL
Великий гастроном
Великий гастроном
Аватара пользователя
 
Сообщения: 3450
Зарегистрирован: 29.06.2003 (Вс) 18:55

Сообщение ANDLL » 17.03.2008 (Пн) 20:05

немного помогает но мне кажется это неправильно.
Что, правда помогает? :shock:
Насколько я понял она передает управление системе принудительно
Не, она делает нечто совершенно другое. Просто обрабатывает накопленные клики, клавиши и прочее.
Система сама себе передает управление, когда ей захочется.

Вообще решение проблемы - просто понаставить в узких местах функции DoEvents.
Гастрономия - наука о пище, о ее приготовлении, употреблении, переварении и испражнении.
Блог

Wildwhiteash
Начинающий
Начинающий
 
Сообщения: 21
Зарегистрирован: 21.06.2007 (Чт) 17:02
Откуда: Украина, Николаев

Сообщение Wildwhiteash » 17.03.2008 (Пн) 22:34

покажу кусок кода с камментами
Код: Выделить всё
'******************************
'этот кусок для понимания не принципиален
'время его выполнения где то от 0,05сек до 3 сек
Private Sub mnuWriteToDisk_Click()
ReDim BuferS(0)
Call WriteModule(SelectedItemMenuMod) ' SelectedItemMenuMod - переменная с номером эл-та списка
'пишет модуль служебной информации на блины винта
ReDim BuferS(0)
End Sub
'******************************

'******************************
'а вот эта фиговина пишет туду все отмеченные
'если не делать DoEvents
'то пока всё не отработает, то формы приложения висят
'и ессно по кнопке СТОП ничего не происходит
Private Sub mnuWriteAllToDisk_Click()

Dim XX As Byte
Dim NextMod As Label

ProgressBarMod.Min = 0
ProgressBarMod.Max = 100

bStopMOD = False
For XX = 1 To (lw1.ListItems.Count)
lw1.ListItems.Item(XX).Selected = True
SelectedItemMenuMod = XX
If lw1.ListItems.Item(XX).Checked = False Then GoTo NextMod


ReDim BuferS(0)
Call WriteModule(SelectedItemMenuMod)
ReDim BuferS(0)

DoEvents ' ВОТ ОНО!!!!!!!!!!!!!!!!!!
'перед проверкой кнопки СТОП
'можно тыкать несколько штук в процедурине
'там где хотелось бы что то развесить (антоним "завесить")

If bStopMOD = True Then
ProgressBarMod.Value = 0
ProgressBarMod.DrawProgressBar
Exit Sub
End If

NextMod: ProgressBarMod.Value = Int((XX * 100) \ (lw1.ListItems.Count))
ProgressBarMod.DrawProgressBar
lw1.Refresh

Next XX
ProgressBarMod.Value = 0
ProgressBarMod.DrawProgressBar
End Sub
'******************************

2vv
Новичок
Новичок
 
Сообщения: 36
Зарегистрирован: 14.03.2008 (Пт) 17:36

Сообщение 2vv » 17.03.2008 (Пн) 23:18

Wildwhiteash В приведенном примере Doevents стоит после процедуры. Это я к тому , что до DoEvents еще очередь должна дойти, а дойдет она после того как выполниться процедура "Call WriteModule(SelectedItemMenuMod)" .
Привожу код , который я использую:
Код: Выделить всё
strComputer = "."
Set objWMIService = GetObject(_
"winmgmts:\\" & strComputer & "\root\cimv2")
Set colPings = objWMIService.ExecQuery _
("Select * From Win32_PingStatus where Address = '192.168.1.1'")

For Each objStatus in colPings
If IsNull(objStatus.StatusCode) _
or objStatus.StatusCode<>0 Then
WScript.Echo "Computer did not respond."
Else
Wscript.Echo "Computer responded."
End If
Next


Так вот, у меня , пока не отработает IsNull(objStatus.StatusCode) программа дальше не выполняется, а ждать приходится несколько секунд, на которые программка "вешается". Добавление DoEvents строкой выше или ниже проблему не решает, потому как ждемс пока отработает вышеприведенная функция.
Если я не прав, поправьте.

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 18.03.2008 (Вт) 9:28

Если у тебя нет доступа к длительной процедуре, то ты ничего не сделаешь.
Перед вызовом делай MousePointer в виде песочных часов, Me.Refresh, после отработки процедуры возвращай MousePointer. Лучше ничего не сделаешь.
Lasciate ogni speranza, voi ch'entrate.

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 18.03.2008 (Вт) 9:29

Но в твоем конкретном случае от тормозов можно избавиться, если пинговать нормально, а не через WMI. Поищи по строчке "icmp", где-то уже выкладывали пример.
Lasciate ogni speranza, voi ch'entrate.

2vv
Новичок
Новичок
 
Сообщения: 36
Зарегистрирован: 14.03.2008 (Пт) 17:36

Сообщение 2vv » 18.03.2008 (Вт) 12:36

alibek Я находил примеры пинга где используется библиотека icmp.dll . Изначально пробовал их. Хоть в модуле ,хоть не в модуле , работают они одинаково, точно таке же тормоза. Остановился на вышеприведенном примере из-за простоты, чтобы код был поменьше. Да и мне кажется что при обращении через WMI тоже задействуется icmp.dll .
Повторюсь, сейчас поставил таймер и использую его для вызова DoEvents
, все равно форма "подвисает" , но ненадолго. Результат устраивает, но это как-то неправильно.
Проблема бы решилась если бы функцию можно было бы запускать в фоновом потоке. Но прошелся по форуму и понял что для VB6 это крайне сложно, ни одного конкретного примера не нашел.

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 18.03.2008 (Вт) 16:02

С многопоточным действительно будет сложновато.
Но ты можешь просто при отправке запроса делать небольшое время таймаута (в локальной сети это допустимо).
Lasciate ogni speranza, voi ch'entrate.

2vv
Новичок
Новичок
 
Сообщения: 36
Зарегистрирован: 14.03.2008 (Пт) 17:36

Сообщение 2vv » 18.03.2008 (Вт) 19:24

Нашел статейку про многопоточность. Хоть мне из неё непонятно и половины но там есть пример. Попробую им воспользоваться.
Адрес статьи: http://www.codenet.ru/progr/vbasic/threads.php

2vv
Новичок
Новичок
 
Сообщения: 36
Зарегистрирован: 14.03.2008 (Пт) 17:36

Сообщение 2vv » 18.03.2008 (Вт) 19:26

А вобще сложившаяся ситуация разочаровывает меня в возможностях VB6. На первый взгляд простая проблема, а решения нет. Вроде в VB.NET
есть возможность запускать поток в фоне.

Viper
Артефакт VBStreets
Артефакт VBStreets
Аватара пользователя
 
Сообщения: 4394
Зарегистрирован: 12.04.2005 (Вт) 17:50
Откуда: Н.Новгород

Сообщение Viper » 19.03.2008 (Ср) 7:56

2vv, решить проблему средствами VB6 в принципе можно. А так да, VB.NET позволяет работать с несколькими потоками.
Весь мир матрица, а мы в нем потоки байтов!

Antonariy
Повелитель Internet Explorer
Повелитель Internet Explorer
Аватара пользователя
 
Сообщения: 4824
Зарегистрирован: 28.04.2005 (Чт) 14:33
Откуда: Мимо проходил

Сообщение Antonariy » 19.03.2008 (Ср) 10:26

Можно попинговать из отдельного ActiveX EXE.
Лучший способ понять что-то самому — объяснить это другому.

2vv
Новичок
Новичок
 
Сообщения: 36
Зарегистрирован: 14.03.2008 (Пт) 17:36

Сообщение 2vv » 19.03.2008 (Ср) 11:39

Antonariy Не могли бы пояснить как это сделать.

Что еще пробовал:
Посмотрел пример многопоточности с вышеприведенной ссылки. Попробовал свой код запустить в другом потоке - неудачно. Похоже что в фоновом потоке нельзя обращаться к WMI.

Решил поискать готовый контрол для работы с icmp . Оказалось что есть и такие. Например здесь OstroSoft.com. С этим контролом еще проще чем с WMI. Но все-таки форма "подвисает" когда пингуешь выключенные компы, но в целом стало значительно лучше. Остановлюсь на этом варианте. Хотя на досуге попробую организовать пингование в фоновом потоке.

Antonariy
Повелитель Internet Explorer
Повелитель Internet Explorer
Аватара пользователя
 
Сообщения: 4824
Зарегистрирован: 28.04.2005 (Чт) 14:33
Откуда: Мимо проходил

Сообщение Antonariy » 19.03.2008 (Ср) 14:00

2vv писал(а):Antonariy Не могли бы пояснить как это сделать.
New Project - ActiveX Exe
В классе прописываешь событие например PingComplete, публичную процедуру Ping, в которой включается таймер, который пингует твоим любимым способом, выполняет RaiseEvent PingComplete и отключается.
В основной проге объявляешь
Dim WithEvents mPing as PingExe.PingClass
Для пинга делаешь mPing.Ping "ya.ru" и ждешь без каких-либо подвисаний mPing_PingComplete.
Лучший способ понять что-то самому — объяснить это другому.

2vv
Новичок
Новичок
 
Сообщения: 36
Зарегистрирован: 14.03.2008 (Пт) 17:36

Сообщение 2vv » 19.03.2008 (Ср) 17:50

Antonariy
Честно говоря, я очень слаб в VB. Попробую объяснить что я понял.
Мне надо создать модуль класса. В нем я создаю Public Sub PING , в ней пишу код который пингует, результатом этого кода (пинга) будет событие
PingComplete. Тоесть PingComplete это переменная , которая зависит от исхода пинга. Верно ?
Может ли модуль взаимодействовать с формой?. Потому как у меня в цикле идет опрос нескольких адресов, котрые ввожу в форме.

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 19.03.2008 (Ср) 17:59

Основное тут не в этом.
Основное -- в первом предложении, тип проекта - Active EXE.
Это единственный легальный способ сделать в VB многопоточную программу.

Ты пишешь объект, у которого будет метод Ping и событие PingComplete. Из своей основной программы ты создаешь этот объект, вызываешь метод Ping, а тот вызовет событие PingComplete по завершению работы. При этом этот метод будет работать в отдельном потоке и не будет приостанавливать твою программу.
Lasciate ogni speranza, voi ch'entrate.

Antonariy
Повелитель Internet Explorer
Повелитель Internet Explorer
Аватара пользователя
 
Сообщения: 4824
Зарегистрирован: 28.04.2005 (Чт) 14:33
Откуда: Мимо проходил

Сообщение Antonariy » 19.03.2008 (Ср) 19:10

и не будет приостанавливать твою программу
если сам пинг выполняется не в методе. Это важно.

В общем, вот заготовка.
Вложения
PingExe.rar
(5.83 Кб) Скачиваний: 172
Лучший способ понять что-то самому — объяснить это другому.

2vv
Новичок
Новичок
 
Сообщения: 36
Зарегистрирован: 14.03.2008 (Пт) 17:36

Сообщение 2vv » 19.03.2008 (Ср) 19:41

Спасибо за помощь. Попробую разобраться.

2vv
Новичок
Новичок
 
Сообщения: 36
Зарегистрирован: 14.03.2008 (Пт) 17:36

Сообщение 2vv » 19.03.2008 (Ср) 19:57

Antonariy
Сорри. Как пользоваться вашим примером.
Создаю ActiveX EXE. Добавляю туда две формы и модуль класса, запускаю - ошибка. Или так неправильно ?

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Сообщение Хакер » 19.03.2008 (Ср) 20:48

2vv
Нафига туда две формы. Твоя программа поделится на два проекта - на обычный EXE (с формами и прочей дрянью), и ActiveX EXE - с классом пинговщиком.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

2vv
Новичок
Новичок
 
Сообщения: 36
Зарегистрирован: 14.03.2008 (Пт) 17:36

Сообщение 2vv » 19.03.2008 (Ср) 22:04

Хакер
Я догадываюсь что программа поделиться. Но к сожалению я пока даже не могу воспользоваться вышеприведенным примером. В архиве лежит два проекта, в одном есть классмодуль и форма с таймером , в другом просто форма с кнопкой пуск, есть еще exe-шник(не запускается). На первый взляд все понятно, но как связать их вместе ?

2vv
Новичок
Новичок
 
Сообщения: 36
Зарегистрирован: 14.03.2008 (Пт) 17:36

Сообщение 2vv » 19.03.2008 (Ср) 22:30

Вроде бы разобрался. С N-ой попытки :) . Алгоритм работы ясен. Попробую в классе прописать пинговальщик через WMI, если не получится тогда через контрол .

2vv
Новичок
Новичок
 
Сообщения: 36
Зарегистрирован: 14.03.2008 (Пт) 17:36

Сообщение 2vv » 20.03.2008 (Чт) 1:30

Собрал по примеру. Но не могу разобраться с ошибкой. Что означает "Параметр не факультативный" ? Относительно примера ругается на lPing_PingComplete . Это событие , в зависимости от того как отработает метод пинг , ему присваивается значение. Потом в основной форме относительно значения этого события заполняются поля (мол удаленный комп. вкл. или выкл. ) ...

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Сообщение Хакер » 20.03.2008 (Чт) 2:21

2vv
"Параметр не факультативный" означает в первую очередь, что надо избавиться от руссифицированного VB6.

Во-втторую, что параметр, который не Optional, почему-то тобой не задан.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

2vv
Новичок
Новичок
 
Сообщения: 36
Зарегистрирован: 14.03.2008 (Пт) 17:36

Сообщение 2vv » 20.03.2008 (Чт) 14:48

На работе стоит английская версия. Так ней даже класс не может открыть. Пишет "Automation error" на строке:
Set lPing = New PingExe.PingClass

Дома такой ошибки нет. Ссылку на класс в проекте прописал.
Может в VB какие-то настройки есть хитрые ?

2vv
Новичок
Новичок
 
Сообщения: 36
Зарегистрирован: 14.03.2008 (Пт) 17:36

Сообщение 2vv » 20.03.2008 (Чт) 15:30

Проблема которая описана постом выше решена. Проект был на рабочем столе. Английский VB6 не умеет работать с русскими папками.

Возвращаясь к теме, как задать параметр который не Optional.
Я объявляю класс:
Dim WithEvents lPing as PingExe.PingClass

в классе есть событие PingComplete, которое получает значение по прохождении процедуры Ping (это метод класса). Так вот mPing_PingComplete - "Argument no optional" . Я не понимаю в чем загвоздка.

Antonariy
Повелитель Internet Explorer
Повелитель Internet Explorer
Аватара пользователя
 
Сообщения: 4824
Зарегистрирован: 28.04.2005 (Чт) 14:33
Откуда: Мимо проходил

Сообщение Antonariy » 20.03.2008 (Чт) 15:39

Английский VB6 не умеет работать с русскими папками.
Умеет. Проблема была не в этом.

На строчке mPing_PingComplete не может возникать такая ошибка. Она может возникнуть если событие описано с параметрами, а генеришь его без параметров, типа этого:
Код: Выделить всё
Public Event PingComplete(Success as Boolean, Timeout as Single)

RaiseEvent PingComplete(True)
Лучший способ понять что-то самому — объяснить это другому.

2vv
Новичок
Новичок
 
Сообщения: 36
Зарегистрирован: 14.03.2008 (Пт) 17:36

Сообщение 2vv » 20.03.2008 (Чт) 15:45

Что бы было проще, покажу что написал
Это процедура главной формы опрашивает ячейки таблицы и присваивает статус:
Код: Выделить всё
Private Sub stping()
Dim i
  For i = 1 To 15 Step 1
    IP_ADR = CStr(GrdTable.TextMatrix(i, 1))
  If Len(IP_ADR) <> 0 Then
   lPing.Ping IP_ADR
     If lPing_PingComplete <> 0 Then
            Avaible = "OFFLINE"
            GrdTable.Row = i
            GrdTable.Col = 3
            GrdTable.CellBackColor = vbRed
            Else
            Avaible = "ONLINE"
            GrdTable.Row = i
            GrdTable.Col = 3
            GrdTable.CellBackColor = vbGreen
            End If
      GrdTable.TextMatrix(i, 3) = Avaible
    'WinsockInit
    'GrdTable.TextMatrix(i, 2) = HostByAddress(IP_ADR)
Else
End If
Next
End Sub


Это модуль класса:
Код: Выделить всё
Option Explicit
Dim WithEvents Timer As Timer
Dim Form As Form1
Dim IpAdr As String
Dim objPing
Dim objStatus
Dim Ret As Long
Public Event PingComplete(Status As Long)

Public Sub Ping(IP_ADR As String)
    IpAdr = IP_ADR
    Set objPing = GetObject("winmgmts:{impersonationLevel=impersonate}").ExecQuery("select * from Win32_PingStatus where address = '" & IpAdr & "'")
        For Each objStatus In objPing
            If IsNull(objStatus.StatusCode) Or objStatus.StatusCode <> 0 Then
            Ret = 1
            Else
            Ret = 0
            End If
        Next
    Timer.Enabled = True
End Sub
Private Sub Class_Initialize()
    Set Form = New Form1
    Load Form
    Set Timer = Form.Timer1
End Sub
Private Sub Class_Terminate()
    Set Timer = Nothing
    Unload Form
    Set Form = Nothing
End Sub
Private Sub Timer_Timer()
    Timer.Enabled = False
    RaiseEvent PingComplete(Ret)
End Sub


Вобщем lPing_PingComplete ничего не отдает. Правда я еще не до конца разобрался как работать с событиями.

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 20.03.2008 (Чт) 15:54

2vv, ты написал ерунду, не разобравшись, что тебе посоветовали.
lPing_PingComplete -- это не переменная.
Твой код должен быть таким: lPing.Ping IP_ADR
Все.
Остальное пишется в событии lPing_PingComplete.

Кроме того, то как ты сделал, не поможет тебе избавиться от тормозов.
Непосредственно пингование должно быть в процедуре таймера, а не в методе Ping. В методе Ping ты задаешь внутренние переменные и запускаешь таймер.
Lasciate ogni speranza, voi ch'entrate.

2vv
Новичок
Новичок
 
Сообщения: 36
Зарегистрирован: 14.03.2008 (Пт) 17:36

Сообщение 2vv » 20.03.2008 (Чт) 22:02

Понимаю что код неправильный, поэтому и выложил, чтоб быстрее разобраться.
То что lPing_PingComplete это не переменная мне понятно. Это событие. Вобщем теперь я даже не представляю алгоритма, не хватает опыта и знаний.
Почему пинговать надо через таймер в классе, и можно ли обойтись без событий(поне могу понять что к чему), почему нельзя вызвать метод класса (в моем случае ping). Это первое . Второе - как же мне организовать взаимосвязь между главной формой и классом. Ведь адреса удаленных компов прописаны в таблице главной формы, и туда же надо выводить статус. Будет ли public переменая из формы доступна в классе , и наоборот будет ли public переменная доступна форме.?

След.

Вернуться в Visual Basic 1–6

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 40

    TopList