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

Программирование на Visual Basic, главный форум. Обсуждение тем программирования на VB 1—6.
Даже если вы плохо разбираетесь в VB и программировании вообще — тут вам помогут. В разумных пределах, конечно.
Правила форума
Темы, в которых будет сначала написано «что нужно сделать», а затем просьба «помогите», будут закрыты.
Читайте требования к создаваемым темам.
Antonariy
Повелитель Internet Explorer
Повелитель Internet Explorer
Аватара пользователя
 
Сообщения: 4824
Зарегистрирован: 28.04.2005 (Чт) 14:33
Откуда: Мимо проходил

Сообщение Antonariy » 20.03.2008 (Чт) 23:10

Почему пинговать надо через таймер в классе, и можно ли обойтись без событий(поне могу понять что к чему), почему нельзя вызвать метод класса (в моем случае ping).
Потому что вызывающий код будет ожидать завершения метода (в твоем случае ping), то есть подвесит программу. С чем мы боремся. После включения таймера метод завершится, вернув управление основной программе, а когда таймер сработает, пинговать и висеть будет другая программа, а после отвисания проинформирует основную программу о том, что пинг закончился, посредством события. Без него никак.

Твоя Private Sub stping() должна выглядеть примерно так:
Код: Выделить всё
Dim WithEvents lPing as PingExe.PingClass
Dim i

Private Sub stping()
    i = 1
    IP_ADR = GrdTable.TextMatrix(i, 1)
    If Len(IP_ADR) <> 0 Then lPing.Ping IP_ADR
end Sub

Private Sub lPing_PingComplete(Success as Boolean)
    If Success then
        Avaible = "ONLINE"
    Else
        Avaible = "OFFLINE"
    End If
    do
        i = i + 1
    Loop Until Len(GrdTable.TextMatrix(i, 1))<>0 Or i=15
    IP_ADR = GrdTable.TextMatrix(i, 1)
    If Len(IP_ADR) <> 0 Then lPing.Ping IP_ADR
End Sub
Лучший способ понять что-то самому — объяснить это другому.

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

Сообщение 2vv » 21.03.2008 (Пт) 0:46

Antonariy Спасибо. С алгоритмом вроде разобрался.
Вот код модуля, переделанный:
Код: Выделить всё
Option Explicit
Dim WithEvents Timer As Timer
Dim Form As Form1
Dim IpAdr As String
Dim objPing
Dim objStatus
Public Event PingComplete(Success As Boolean)
Public Sub Ping(IP_ADR As String)
    IpAdr = IP_ADR
    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()
    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
            RaiseEvent PingComplete(True)
            Else
            RaiseEvent PingComplete(False)
            End If
        Next
    Timer.Enabled = False
End Sub


А с главной формой проблема. Сейчас я могу получить ответ на один запрос. Раньше я в цикле обрабатывал все ячейки , а как мне теперь определить на какой i-й запрос пришел ответ. Может можно событию привязать еще одно свойство, номер запроса. И уже по нему определять какой ячейке выставлять статус.

И еще вопросик. Я не понял зачем нужно вот эта чать в вашем коде :

Код: Выделить всё
   do
    i = i + 1
    Loop Until Len(GrdTable.TextMatrix(i, 1))<>0 Or i=15
    IP_ADR = GrdTable.TextMatrix(i, 1)
    If Len(IP_ADR) <> 0 Then lPing.Ping IP_ADR


тут что идет опрос всех ячеек. я понял так , вы посылаете первый запрос , получаете событие, которое производит опрос остальных ячеек....?

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

Сообщение alibek » 21.03.2008 (Пт) 8:36

Твой текущий код не предназначен для асинхронного пингования нескольких адресов. Подозреваю, что глюки из-за таймера, он включается, затем еще раз включается, затем отключается (по PingComplete) и дальше не работает.
У тебя два варианта.
Ты можешь пинговать очередной адрес после того, как получишь PingComplete (в процедуре lPing_PingComplete), причем я бы рекомендовал развязать его запуск с самой процедурой, то есть создать еще один таймер, включать его в процедуре, а непосредственно метод Ping вызывать уже в таймере.
Либо более правильный вариант, в метод Ping передается массив адресов и класс PingClass сам обрабатывает все элементы массива.
Lasciate ogni speranza, voi ch'entrate.

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

Сообщение Хакер » 21.03.2008 (Пт) 9:39

Форма - это тоже класс.

У классов (и у форм) нет public переменных. Они есть у объектов этих классов.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

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

Сообщение alibek » 21.03.2008 (Пт) 10:00

Не объектов, а экземпляров, если уж на то пошло.
Lasciate ogni speranza, voi ch'entrate.

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

Сообщение Хакер » 21.03.2008 (Пт) 13:09

Итак и так.
Объект класса CSome, Экземпляр класса CSome.
Поэтому DllGetClassObject и CoCreateInstance.
—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 » 21.03.2008 (Пт) 16:46

alibek
Работать с массивом хорошая мысль. Правда в перспективе будет опрашиваться около 200-т компов, а тут я уже с трудом себе представляю как такое организовать.
Для меня проще опрашивать по очереди каждый комп и получать ответ.
Вот что я думаю. По другому организовать события. Главная форма будет отрабатывать метод ping в модуле. Этот метод будет делать только oдно - запускать таймер. Если таймер запустился то сработает событие , означающее что модуль готов принимать запросы. В таймере организовать цикл ожидания запроса, при появлении запроса произойдет обработка полученного адреса. А в главной форме , в процедуре события организовать опрос всех ячеек.
Что скажете , будет- ли такой алгоритм работать ?

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

Сообщение alibek » 21.03.2008 (Пт) 17:34

Да, просто в главной форме метод Ping вызывай по завершению PingComplete.
Также можно при вызове метода Ping увеличивать счетчик на 1 и добавлять указанный адрес в очередь. А в таймере, в котором выполняется собственно пингование для текущего элемента очереди, вызывать событие PingComplete, уменьшать счетчик на 1 и удалять текущий элемент очереди. А если счетчик стал равен 0, то и останавливать таймер. Это чуть сложнее в реализации PingClass, но зато не надо будет морочить голову с таймером в главной форме, и не надо будет мучаться с массивами.
Кстати, если уж этот способ для тебя приобрел не академический, а практический смысл, то советую избавиться от формы и таймера на ней, а использовать таймер через API. Для него необязательно окно, хотя можно и окно создавать для приема сообщений.
Lasciate ogni speranza, voi ch'entrate.

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

Сообщение 2vv » 21.03.2008 (Пт) 20:30

В том то и дело, что бы получить PingComplete , мне в начале надо выполнить Ping. А Ping я могу выполнить только после завершения PingComplete. Какой-то замкнутый круг. Пока я так и не могу сообразить как это все завязать....

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

Сообщение 2vv » 22.03.2008 (Сб) 21:33

Переделал код. По идее должен работать , но не рабтает.
Код:
в главной форме:
Код: Выделить всё
Private Sub CmdEnableDisable_Click()
lPing.StartT
End Sub
Private Sub lPing_PingComplete(Success As String)
    Select Case Success
      Case "init"
       GoTo 1:
      Case "offl"
       Avaible = "OFFLINE"
       GrdTable.TextMatrix(i, 3) = Avaible
       GoTo 1:
      Case "onl"
        Avaible = "ONLINE"
        GrdTable.TextMatrix(i, 3) = Avaible
        GoTo 1:
    End Select
1:
  if i >= 16 then goto 2:   
   i = i + 1
       IP_ADR = CStr(GrdTable.TextMatrix(i, 1))
       If Len(IP_ADR) <> 0 Then
        lPing.Ping IP_ADR
        Else: GoTo 1:
       End If
  end if
2:
End Sub

в модуле класса:
Код: Выделить всё
Option Explicit
Dim WithEvents Timer As Timer
Dim Form As Form1
Dim IpAdr As String
Dim objPing
Dim objStatus
Public Event PingComplete(Success As String)
Public Sub StartT()
RaiseEvent PingComplete("init")
End Sub
Public Sub Ping(IP_ADR As String)
    IpAdr = IP_ADR
    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()
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
            RaiseEvent PingComplete("offl")
            Else
            RaiseEvent PingComplete("onl")
            End If
         Next
End Sub


Должно работать так; после нажатия кнопки выполняется метод lPing.StartT , вызывается событие PingComplete("init") . После чего, в главной форме отрабатывает процедура события PingComplete , которая и запускает пинг адресов с перебором. Метод Ping запускает таймер, и задает значение переменной IpAdr. Стартует таймер , отрабатывается эта переменная , вызывается событие PingComplete("offl\onl"), таймер останавливается. В ответ на событие отрабатывает процедура в главной форме , счетчик увеличивается на 1 ....вобщем так далее и дет перебор. Так вот это почему-то не работает. Отрабатывается первый адрес, а последующие не отрабатываются , и когда список заканчивается выдается событие посл запроса. Не пойму что не так.

Спасибо.

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

Сообщение 2vv » 23.03.2008 (Вс) 16:02

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

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

Сообщение Хакер » 23.03.2008 (Вс) 16:16

2vv
Отложи свою затею. Прочитай 100 раз строчку, в которой говорится, что форма является подвидом класса. Затем найди информацию о том, что такое класс и объектно-ориентированное программирование. После этого ещё несколько раз прочитай о том, что такое члены класса, приватные, френды и публичные, общие (их нет в ВБ), о зонах видимости различных членов классов.

После этого, примени полученный знания к формам.

Тогда ты поймёшь, что вопрос:
Это реально, имеет-ли модуль доступ к переменным из главной формы?

бессмысленен и бредов. И самое главное, тебе станет очевиден предполагаемый ответ на этот вопрос.

После всего этого, продолжи работу над своим проектом.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

burik
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 514
Зарегистрирован: 03.11.2005 (Чт) 22:04
Откуда: Беларусь, Рогачев

Сообщение burik » 24.03.2008 (Пн) 14:59

2vv, тебе надо работать по следующей схеме (не буду писать полный код):
Код: Выделить всё

Dim currentItem As Integer
private sub Command1_Click()
    currentItem = 1
    lPing.Ping CStr(GrdTable.TextMatrix(currentItem, 1))
end sub

private sub lPing_PingComplete(Success As Boolean)
    if currentItem < (UBound(GrdTable.TextMatrix) - LBound(GrdTable.TextMatrix) + 1) then
        currentItem = currentItem + 1
        lPing.Ping CStr(GrdTable.TextMatrix(currentItem, 1))
    end if
end sub

Модуль класса содержит метод Ping, который запускае таймер, а в событии таймера происходит пинг и генерируется событие PingComplete и возвращаются результаты пинга.
Между слухов, сказок, мифов,
просто лжи, легенд сомнений
мы враждуем жарче скифов
за несходство заблуждений
Игорь Губерман

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

Сообщение 2vv » 25.03.2008 (Вт) 23:11

burik Алгоритм у меня такой как вы описали. А вот пример перебора ячеек, что вы написали, наверное использую. Спасибо.
Продолжаю понемногу осваивать Visual Basic. Работать с формой из модуля так или иначе , но можно! Поняв это я решил оставить идею с ActiveX , по вышеописанным причинам, и вернуться к вопорсу многопоточности. Как организовывается, работает, и вобще суть запуска фонового потока в VB еще не совсем понял. Использовал пример многопоточности. Который описывал выше. Снова уперся в недостаток знаний. Но это уже надо быть профессионалом что бы разобраться. Суть в чем. В фоновом потоке можно обращаться и вносить изменения в главную форму. В моем случае это таблица. На первый взглад то что и нужно вся работа будет делаться в фоновом потоке. Писал простые примерчики работы с формой в потоке, что бы разобрыться. Работает. Но! Я как-то писал что у меня не получилось пинговать через WMI . Так теперь у меня подозрение что вобще нельзя вызывать API . Так как контрол который я использую тоже отказался работать в потоке. Сам контрол использует icmp.dll для работы.
Можете объяснить почему так ?

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

Сообщение Хакер » 25.03.2008 (Вт) 23:17

Да блин, автор, во-первых,
Отложи свою затею. Прочитай 100 раз строчку, в которой говорится, что форма является подвидом класса. Затем найди информацию о том, что такое класс и объектно-ориентированное программирование. После этого ещё несколько раз прочитай о том, что такое члены класса, приватные, френды и публичные, общие (их нет в ВБ), о зонах видимости различных членов классов.

После этого, примени полученный знания к формам.
, во-вторых, чтобы написать стабильное многопоточное приложение на VB надо очень тонко знать VB6. У тебя не получится сейчас.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

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

Сообщение Antonariy » 26.03.2008 (Ср) 10:58

Угу. К тому же этот код -
Код: Выделить всё
    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
            RaiseEvent PingComplete(True)
            Else
            RaiseEvent PingComplete(False)
            End If
        Next
- не работает.
Лучший способ понять что-то самому — объяснить это другому.

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

Сообщение 2vv » 26.03.2008 (Ср) 11:46

Antonariy
Где не работает ?
У меня работает , если вызывать как метод из модуля. А если Через таймер, то обрабатывает только первый запрос, второй уже не хочет отрабатывать. Подозреваю что что-то с таймером.
А если использовать второй поток, то вобще не работает, причем не только этот код , а и другие , в которых используется API.

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

Сообщение Antonariy » 26.03.2008 (Ср) 11:51

Просто не пингует.
ExecQuery("select * from Win32_PingStatus where address = '127.0.0.1'") ничего не возвращает.
Лучший способ понять что-то самому — объяснить это другому.

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

Сообщение alibek » 26.03.2008 (Ср) 12:10

Antonariy, насколько я понял, фактический пинг выполняется при objStatus.StatusCode.
Lasciate ogni speranza, voi ch'entrate.

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

Сообщение Antonariy » 26.03.2008 (Ср) 12:34

alibek
Внутрь For код никогда не попадет потому что objPing.Count = 0

2vv
В аттаче полностью рабочий код.
Почти полностью. У меня почему-то не работает функция, определяющая ip по названию.
Вложения
PingExe2.rar
(12.37 Кб) Скачиваний: 176
Лучший способ понять что-то самому — объяснить это другому.

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

Сообщение 2vv » 26.03.2008 (Ср) 17:04

Antonariy
Сори. Но к сожалению у меня не заработал ваш код. Прикрепил файл со скриншотом, где вылетает ошибка.
Вложения
error.JPG
Скриншот с ошибкой.
error.JPG (144.93 Кб) Просмотров: 8447

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

Сообщение 2vv » 26.03.2008 (Ср) 17:41

Еще вот что нашел. Вот тут небольшая статья про многопоточность http://www.vbrussian.com/Article.asp?ID=42 . Но это по сути небольшое дополнение к статье Daniel Appleman , ссылку на которую я приводил выше. Так вот , в этой статье меня заинтересовали комментарии людей. А именно :
"Данный пример как и все другие не рабочий....
Это объясняется тем что при попытки ызове любй VB функции он вылетает...
Возможно поток неимеет доступ к классу VBA...
Что ещЈ поразило меня так это то что в потоке даже нельзя вызвать Api!!!"
Я столкнулся с тем же . И ответ на свой вопрос как бы получил.
И еще коммент.
"Ну в общем прочитав все вышесказанное и посмотрев пример я понял:
VB6 и МНОГОПОТОЧНОСТЬ понятия несовместимые. И если у некоторых получается, то это скорее исключение из общего правила.

ps:У меня (наверно как и у большенства) данный пример работал ,а вот правильно применить на практике не удалось! Может у вас получится лучше...."
Аналогично. Вобщем то что я хотел сделать в потоке невозможно.

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

Сообщение alibek » 26.03.2008 (Ср) 17:47

2vv, а ты читаешь, что тебе пишут?
Способ, описанный Antonariy -- это легальный способ использовать многопоточность в VB6.
То, что ты читал про несовместимость VB и многопоточности, не относится к данному случаю. Это применимо, когда ты попытаешься сделать свою программу многопоточной.

А не работает потому, что библиотеку надо регистрировать и подключать к VBA.
Lasciate ogni speranza, voi ch'entrate.

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

Сообщение 2vv » 27.03.2008 (Чт) 12:21

alibekЯ про способ от Antonariy и не говорю ничего. Предыдущий пост касался только соображений о фоновом потоке(многопоточности).
И еще я выше писал , что способ Antonariy все же не заработал. Подробнее писал выше.

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

Сообщение alibek » 27.03.2008 (Чт) 12:37

alibek писал(а):А не работает потому, что библиотеку надо регистрировать и подключать к VBA.

У тебя она если и зарегистрирована, то скорее всего старая.
Lasciate ogni speranza, voi ch'entrate.

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

Сообщение Antonariy » 27.03.2008 (Чт) 13:23

Ага, у события изменилось количество параметров.
Лучший способ понять что-то самому — объяснить это другому.

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

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

alibek Если вы про ActiveX библиотеку (pingexe.exe) , то я её конечно заново регистрировал, ту что в архиве pingexe2.

Antonariy
Ага, у события изменилось количество параметров.


Что вы хотели сказать?

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

Сообщение Antonariy » 27.03.2008 (Чт) 16:54

Я хотел сказать, что когда у публичных членов библиотеки меняются параметры, она становится несовместимой с предыдущей версией. Нужно перекомпилировать PingExe.exe.
Лучший способ понять что-то самому — объяснить это другому.

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

Сообщение 2vv » 27.03.2008 (Чт) 18:53

Antonariy
Понял. Так и сделал. Теперь работает. Попробую "прикрутить" к своему проекту. Спасибо.

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

Сообщение 2vv » 30.03.2008 (Вс) 14:06

Всем БОльшое спасибо за помощь. Собрал все до кучи, теперь работает как хотелось. Если кому будет интересно прикрепил архив со всем что получилось.
В перспективе компов будет не 15 а где-то 150. Хотелось бы еще сделать поиск по имени компа(не по ip, ip будут фиксированные). В связи с тем что компов будет очень много, думаю как сделать нагляднее и удобнее форму. Две мысли , либо это будет MDI-форма с десятком маленьких форм ,или это будет большая таблица.
Вложения
good.rar
Все что получилось.
(24.05 Кб) Скачиваний: 187

Пред.

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

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

Сейчас этот форум просматривают: SemrushBot, Yandex-бот и гости: 50

    TopList  
cron