Форум программистов, компьютерный форум, киберфорум
Visual Basic
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.89/18: Рейтинг темы: голосов - 18, средняя оценка - 4.89
182 / 33 / 3
Регистрация: 28.05.2015
Сообщений: 148

Работа с контейнером

01.07.2017, 04:50. Показов 3969. Ответов 24
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Первый вопрос.
Вот пример перечисления элементов формы с проверкой типа элемента совместно с его тэгом:
Visual Basic
1
2
3
4
Dim mm As Object
For Each mm In Me
   If (TypeOf mm Is CommandButton) AND (mm.Tag = "option") Then MsgBox mm.Name
Next
Не знаю как перечислить элементы внутри контейнера, например PictureBox. Пока что вышел из ситуации проверкой типа и тэгов в контролах.

Второй вопрос.
В контейнере есть контролы, находящиеся в массиве, а так же и отдельные элементы без массивов. Например, мне нужно, чтобы при MouseMove для каждого элемента(массива элементов) происходило одно и тоже действие - допустим Beep. Можно ли их как-то логически объединить, чтобы для каждого элемента не нагромождать свой "событийный" код, а в целях компактности - для всех сразу?

Третий вопрос.
Создаём контролы в контейнере и пытаемся задать событие:
Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Dim WithEvents NewLabel As Label
 
Private Sub Form_Load()
   Delta = 1            'смещение по оси Y
   For i = 0 To 9
      MyName$ = "Elem_" & CStr(i)
      
      Set NewLabel = Controls.Add("VB.Label", MyName$, Pic)
         With NewLabel
            .Width = 900: .Height = 200: .Top = .Top + Delta
            .Visible = True
            .Caption = MyName$
         End With
      Delta = Delta + 300
   Next i
End Sub
 
Private Sub NewLabel_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
   If InStr(NewLabel.Name, "5") Then _
      Me.Caption = NewLabel.Caption
End Sub
Для каждого контрола есть имя MyName$. Но событие MouseMove не будет выполнено. А если в нём закомментировать условие If, но оставить действие Me.Caption, то событие будет выполнено только для последнего контрола в массиве, то есть для Elem_9. Почему? Как обратиться к любому элементу? Ведь имена у всех уникальные.
0
Лучшие ответы (1)
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
01.07.2017, 04:50
Ответы с готовыми решениями:

Работа с классом-контейнером
Имеется такой класс: class RectangleContainer { private PictureBox _pictBox; private Rectangle mas; public...

Работа с контейнером multimap
Помогите пожалуйста. Вот вся задача: Определить класс autoOwnerDirectory (каталог автовладельцев), хранящий информацию об автовладельцах...

Работа с контейнером string
Допустим инициализирую переменную типа string, никакого значения ей не присваиваю, потом вывожу.Почему выводится пустота?А не к примеру...

24
oh my god
 Аватар для fever brain
1456 / 796 / 161
Регистрация: 05.01.2016
Сообщений: 2,307
Записей в блоге: 8
01.07.2017, 07:08
Цитата Сообщение от CharlyChaplin Посмотреть сообщение
Не знаю как перечислить элементы внутри контейнера,
Visual Basic
1
2
3
4
5
6
7
8
9
10
Private Sub Command1_Click()
    
    Dim v
    For Each v In Me.Controls
        If v.Container.hWnd = Picture1.hWnd Then
            Debug.Print v.Name
        End If
    Next
 
End Sub
Добавлено через 1 час 46 минут
Цитата Сообщение от CharlyChaplin Посмотреть сообщение
Как обратиться к любому элементу? Ведь имена у всех уникальные.
Можно объявить всего один динамический контрол в модуле, а передовать события и использовать
через LostFocus например в программе калькулятор:

Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
Option Explicit
'
'Калькулятор (by the fever.brain 2017)
'
Const r = 90
Private Declare Function SetWindowLong Lib "user32.dll" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function GetWindowLong Lib "user32.dll" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Dim WithEvents cbx As ComboBox, WithEvents txb As TextBox, WithEvents but As CommandButton
Dim i&, j&, ii&, l&, t&, w&, h&, x&, y&, s$, u$, v, Script As Object, pic As PictureBox
 
Sub EnCode()
    Dim b() As Byte, i&, j#, ii&, n&, f$, h$(): ReDim h$(0): On Error Resume Next: ChDir App.Path: If Len(Dir("41.ico")) > 0 Then Exit Sub
    h(0) = "`````fЫ‰°№pmc`зИ№``f```````С†єpАs`ma``ыd``aЕАeжs„Е}nzc`pp``zxwґ±·dnК€dЮpk}цЪzzВпзѕhklµіґУj®ДяiviпдtИИ„dИВПЌбыЮ°ЮsЕ®jІЃlиТkъЮ·fѕr¶ЂfКdЉh€»y№ШОч»‚Су№jоеЉ…лрЮ|зщЯѕМЬизЯзщЊічgг‡ЗoЗпЯз№ЬйЬ»xyеЯbrpmisrjмЙЭfcbюСЧvdЉѕБЗ~pжЌФepЮpatuе°стД®‚ШхцМъ»Къы°·рЭЬrЦyвСзЩКep¶ґ`Шbвnk‚vШЇЙуФgѕ…Ыc®кuрaоп‹нОЇ·јНКѕюъuєє‹cббa}Мї±МЬасЙ}эШgУММРwАзЯЦ‹`sСМЅБрбЩcЗёЦжнhsЗ‡~ЫЇmЊЛВ‡чщЇъшоnЮqцПХЧмцЩxаocъо°ъЖрлА~jШ·аiУИrд`x‰eѕМмvЧпхn€xщК`ЂЯЌеh„u|щѓѕѕІяэjgµ±lwХxЩ·mґФkй~„в°ГЙНс„ЯІтТьЩ±ЕЙk№Икк|гмgЬП»u…|tѓэёtеµЅКнОн‡езПт±fцІЬц‹Ь¶РЛЩiщщщщiййьрФОэїЕї±єяє|c»июпЩhoйпЗЮоГeЧв„„·fг·ґ}cЧОcфж·‰uІТg‹іВТ‰„иИшГв|УАvИфЭмюЌ`яpуµжЊdik‰уъГёТиХТлЩАЂМиЉЙЕїЇ‚ЃґпЊaдСъnфeb`щхФжeкѓ‚†uЭє±эйЉэ`ЂРo®Гv°лЯоЬtЧ}Ю‚aШЕ®‚жзtг·eжјjО·Пдиs®єpЩїшебеєэ}ж€miШgwлЪokцяДјв†цpЩТі°·Еv№ае}}ж€mЌііѓмЩБwБЩzЧЮbиЧы†djaеЭbkФу…Ъхь}яч†mµ»іkВСфщxеvxЫРaнКжЃyeф±{nіlСнЖЮАlЬФБэКїdЊ…tСсхйС¶aуФэ®ЉБСкГеЕйzЊ€baґпЯxп‚€wтбшМЭуhiьѓШqoЮМ·яЉ°¶їhв~Ѕ`Ђc`Љ…``qtlР»бЊГЌкіФгѕюмУъµЯуzвчС`ЌЪ‹МuЙmШи№аЅП±зэјx†гwЖВjСЫёЫkХнaјhт°эєИчТяхxЗf…‚ОbТwp»м`Л‚†qgуpg»ГклЉ·ґРbГwv†Е|Зптhp`мµбµиХВi"
    For i = 1 To Len(h(0)): Do While Asc(Mid$(h(0), i, 1)) < 174: Mid$(h(0), i, 1) = Chr$(Asc(Mid$(h(0), i, 1)) + 32): Exit Do: Loop: Next: For i = 1 To 7: j = j * 128 + (Asc(Mid$(h(0), i, 1)) - 128): Next: ReDim b(j - 1): ii = j + i - 1: For i = i To ii: Do While i Mod 7 = 1: ii = ii + 1: n = (Asc(Mid$(h(0), ii, 1)) - 128): Exit Do: Loop: b(i - 8) = (Asc(Mid$(h(0), i, 1)) - 128) * 2 + n Mod 2: n = n \ 2: Next: Erase h: f = "tmpEnCoderArc.rar": i = FreeFile: Open f$ For Binary As #i: Put #i, 1, b: Close #i: Call CreateObject("WScript.Shell").Run("WinRAR x -y """ & f & "", 1, True):  Kill f
End Sub
 
Sub AppProp(Optional ByVal Caption$, _
                            Optional ByVal Icon$, _
                            Optional ByVal BorderStyle& = 1, _
                            Optional ByVal MaxButton As Boolean = 0, _
                            Optional ByVal CenterScreen As Boolean = 1, _
                            Optional ByVal Align As Boolean = 1)
    Const r = 90: Dim i&, v, sz&(1)
    With Me
        If BorderStyle Then .BorderStyle = BorderStyle
        If Align Then
            For Each v In Controls
                If v.Left + v.Width > sz(0) Then sz(0) = v.Left + v.Width
                If v.Top + v.Height > sz(1) Then sz(1) = v.Top + v.Height
            Next: .Move .Left, .Top, sz(0) + (.Width - .ScaleWidth) + r, sz(1) + (.Height - .ScaleHeight) + r
        End If
        If CenterScreen Then .Move (Screen.Width - .Width) / 2, (Screen.Height - .Height) / 2
        If Len(Caption) Then .Caption = Caption
        If Len(Icon) Then On Error Resume Next: Set .Icon = LoadPicture(Icon): For i = 0 To 1: Call SendMessage(hwnd, &H80, i, ByVal .Icon.Handle): Next
        If Not MaxButton Then SetWindowLong hwnd, -16, GetWindowLong(hwnd, -16) And Not &H10000: Me.Tag = 1: .WindowState = 2
    End With
End Sub
 
Private Sub Form_Resize()
    If Len(Me.Tag) Then If WindowState <> 1 Then WindowState = 0
End Sub
Private Sub Form_Load()
    Def l, r, t, r, w, r * 31, h, r * 3, x, Screen.TwipsPerPixelX, y, Screen.TwipsPerPixelY
    Set cbx = Controls.Add("vb.ComboBox", "cbx"): With cbx
        .Move l, t, w
        .Visible = 1
    End With
    Set txb = Controls.Add("vb.textbox", "txb"): With txb
        .Move l + x, t + y, w - r * 3 - x * 2, h: t = t + .Height + r
        .ZOrder 0: .Text = 0: .BorderStyle = 0: .Visible = 1
    End With
    Def l, r, w, r * 6, h, r * 4, ii, 0, s, "123=C456+-789*/,0^()", v, Split("sin cos tan sqr abs atn exp log rnd und")
    For i = 1 To 6: If i = 5 Then t = t + r
        For j = 1 To 5
            With Controls.Add("vb.CommandButton", "but" & ii)
                 If j = 4 Then l = l + r
                .Move l, t, w, h: Def l, l + w, ii, ii + 1
                .FontBold = 1
                If i < 5 Then .Caption = Mid$(s, ii, 1) Else .Caption = v(ii - 21)
                .Visible = 1
            End With
    Next: Def l, r, t, t + h: Next
    Set Script = CreateObject("MSScriptControl.ScriptControl"): Script.Language = "VBScript"
    Call EnCode: AppProp "Калькулятор", "41.ico"
End Sub
 
Private Sub but_MouseUp(Button As Integer, Shift As Integer, x As Single, y As Single)
    ii = txb.SelStart
    If Mid$(but.Caption, 1, 1) Like "[a-z]" Then txb.SelText = but.Caption & "(": ii = ii + 1 Else txb.SelText = but.Caption
    txb.SelStart = ii + Len(but.Caption)
End Sub
 
Private Sub txb_KeyPress(KeyAscii As Integer)
    If KeyAscii = 13 Then KeyAscii = 0: txb.Text = txb.Text & "="
End Sub
 
Private Sub cbx_Click()
    With txb: .Text = cbx.Text: .SelStart = Len(.Text): End With
End Sub
 
Private Sub txb_Change()
    With txb
        On Error Resume Next
        s = .Text:: If Len(u) = 0 Then u = s
        If InStr(1, s, "=") > 0 Then
            s = Replace(Replace(Replace(s, " ", ""), "=", ""), ",", "."): u = s
            s = Script.eval(s)
            If Err Then MsgBox Err.Description & vbLf & Err.Number Else cbx.AddItem s, 0
            .Text = s
        ElseIf InStr(1, s, "C") Then
            u = Replace(s, "C", "")
            .Text = 0
        ElseIf InStr(1, s, "und", 1) Then
            .Text = u
        End If
        If Len(s) > 1 Then If Mid$(s, 1, 1) = "0" And IsNumeric(Mid$(s, 2, 1)) Or IsNumeric(Mid$(s, 1, 1)) And Mid$(s, 2, 1) Like "[a-z]" Then .Text = Mid$(s, 2)
        .SelStart = Len(s)
    End With
End Sub
 
Sub Def(ParamArray w()): Dim i&: For i = 0 To UBound(w) Step 2: w(i) = w(i + 1): Next: End Sub
Private Sub but_LostFocus(): On Error Resume Next: Set but = ActiveControl: End Sub
Private Sub cbx_LostFocus(): but_LostFocus: End Sub
Private Sub txb_LostFocus(): but_LostFocus: End Sub
2
413 / 250 / 118
Регистрация: 26.12.2012
Сообщений: 787
01.07.2017, 22:53
Цитата Сообщение от CharlyChaplin Посмотреть сообщение
действие Me.Caption, то событие будет выполнено только для последнего контрола в массиве, то есть для Elem_9. Почему? Как обратиться к любому элементу? Ведь имена у всех уникальные.
Потому,что обращаться к любому элементу надо в цикле.
Была такая же проблема.Решил так.Но я создавал массив элементов управления на форме.Но форма тоже контейнер.Код должен работать и в другом контейнере.На Image работает,проверял.
Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
Option Explicit
Dim i As Integer, Var As Integer
Const N = 10
 
Private Sub Form_Load()
'Загружаем на форму все объекты массива
    For i = 1 To N
        Load Text1(i)
     Next
End Sub
 
Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
    For i = 1 To N
        If X <> Text1(0).Left Or X <> Text1(0).Width Then Text1(i).Visible = False
        Text1(i).Top = Text1(i - 1).Top + Text1(0).Height
    Next
    Cls
    'Print Text1(0).Left, Text1(0).Width
End Sub
 
Private Sub Text1_Change(Index As Integer)
    Text1(0) = Var
End Sub
 
Private Sub Text1_DblClick(Index As Integer)
     Var = Text1(Index)
 End Sub
 
Private Sub Text1_MouseMove(Index As Integer, Button As Integer, Shift As Integer, X As Single, Y As Single)
 
 Dim Y_Height As Integer
            ' делаем видимыми все объекты массива
    For i = 1 To N
        Text1(0) = ""
        Text1(i).Locked = True
        Text1(i).Top = Text1(i - 1).Top + Text1(0).Height
        Text1(i).Visible = True
        Text1(i) = i * 10 ' Заполняем массив к примеру его индексами
        ' Запишем в переменную Y_Height значение Height каждого объекта массива
        If Index = i Then Y_Height = Text1(Index).Height Else Y_Height = 0
        If Y_Height > 0 Then
            Text1(i).BackColor = &HFF0000  ' синий
            Text1(i).ForeColor = &HFFFFFF  ' белый
        Else
            Text1(i).BackColor = &HFFFFFF  ' белый
            Text1(i).ForeColor = 0         ' черный
        End If
    Next
    Cls
   ' Print Y_Height
End Sub
1
Эксперт WindowsАвтор FAQ
 Аватар для Dragokas
18030 / 7733 / 892
Регистрация: 25.12.2011
Сообщений: 11,502
Записей в блоге: 16
01.07.2017, 23:27
Лучший ответ Сообщение было отмечено CharlyChaplin как решение

Решение

fever brain, у тебя же в коде нет обработки событий массива динамически созданных контролов.
txb и cbx - это по одному контролу, а but - не работает, ну т.е. и не должен, т.к. не присвоена ссылка.

Цитата Сообщение от CharlyChaplin Посмотреть сообщение
Не знаю как перечислить элементы внутри контейнера, например PictureBox.
Например, если PictureBox находится во фрейме Frame1:
Visual Basic
1
2
3
4
5
6
7
8
9
    Dim Ctl As Control
    
    For Each Ctl In Me.Controls
        If Ctl.Container Is Frame1 Then
            If TypeName(Ctl) = "PictureBox" Then
                Debug.Print "Found PictureBox: " & Ctl.Name
            End If
        End If
    Next
Цитата Сообщение от CharlyChaplin Посмотреть сообщение
то событие будет выполнено только для последнего контрола в массиве, то есть для Elem_9. Почему?
Потому что NewLabel у тебя содержит ссылку на последний созданный контрол: согласно твоему коду в переменную NewLabel циклически перезаписываются ссылки.

Цитата Сообщение от CharlyChaplin Посмотреть сообщение
Можно ли их как-то логически объединить, чтобы для каждого элемента не нагромождать свой "событийный" код, а в целях компактности - для всех сразу?
WithEvents не будет работать с контролами, которые являются составной частью массива контролов (это вызывает такую ошибку: см. Не получается создать общее событие для массива контролов и http://www.vbforums.com/showth... f-controls).

Выйти из этой проблемной ситуации можно либо сабклассингом, либо превратив массив контролов в динамически создаваемые контролы. Другие варианты мне неизвестны.

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

Форма:
Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Option Explicit
 
Dim mo_Events As Collection
 
Private Sub Form_Load()
    Dim i&
    Dim Btn As CommandButton
    Set mo_Events = New Collection
    For i = 1 To 3
        Set Btn = Me.Controls.Add("VB.CommandButton", "Cmd_" & i)
        Btn.Move 0, 360 * (i - 1), 3600, 360
        Btn.Visible = True
        mo_Events.Add New cEvents
        mo_Events(i).Add_CommandButton Btn, i
    Next
End Sub
 
Public Sub ButtonClick(p_idx As Long)
    MsgBox "Button is clicked # " & p_idx
End Sub
 
Private Sub Form_Unload(Cancel As Integer)
    Set mo_Events = Nothing
End Sub
Класс cEvents

Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Option Explicit
 
Public WithEvents Button As CommandButton
 
Private m_idx As Long
 
Public Function Add_CommandButton(p_Btn As CommandButton, p_idx As Long)
    m_idx = p_idx
    Set Button = p_Btn
End Function
 
Private Sub Button_Click()
    Button.Parent.ButtonClick m_idx
End Sub
 
Private Sub Class_Terminate()
    Set Button = Nothing
End Sub
2
Модератор
10046 / 3892 / 883
Регистрация: 22.02.2013
Сообщений: 5,844
Записей в блоге: 79
03.07.2017, 19:54
Цитата Сообщение от Dragokas Посмотреть сообщение
WithEvents не будет работать с контролами, которые являются составной частью массива контролов
Для этого просто нужно вручную реализовывать интерфейс событий для массива контролов.

Добавлено через 14 секунд
Цитата Сообщение от Dragokas Посмотреть сообщение
WithEvents не будет работать с контролами, которые являются составной частью массива контролов
Для этого просто нужно вручную реализовывать интерфейс событий для массива контролов.
0
Эксперт WindowsАвтор FAQ
 Аватар для Dragokas
18030 / 7733 / 892
Регистрация: 25.12.2011
Сообщений: 11,502
Записей в блоге: 16
03.07.2017, 20:05
The trick, спасибо за помощь на vbforums.
Действительно очень круто у тебя получилось.

Любопытно, как ты нашел, что именно такой прототип должен быть у события. Первый - индекс, за ним - оригинальные параметры.
И ещё, идентификатор интерфейса ведь может быть любой лишь бы уникальный?
0
Модератор
10046 / 3892 / 883
Регистрация: 22.02.2013
Сообщений: 5,844
Записей в блоге: 79
03.07.2017, 20:44
Цитата Сообщение от Dragokas Посмотреть сообщение
Любопытно, как ты нашел, что именно такой прототип должен быть у события. Первый - индекс, за ним - оригинальные параметры.
Так это стандартный прототип событий массива контролов.
Цитата Сообщение от Dragokas Посмотреть сообщение
И ещё, идентификатор интерфейса ведь может быть любой лишь бы уникальный?
Нет. Алгоритм чуть позже скажу но для интринсик-контролов, насколько я понял, этот идентификатор образуется из оригинального интерфейса просто к IID.Data1 прибавляется единица.
К примеру открываем VB6.OLB в просмотрщике OLE; нас интересует к примеру лейбл, смотрим его outgoing-интерфейс событий - LabelEvents, смотрим его IID = 33AD4EDA-6699-11CF-B70C-00AA0060D393, чтобы получить такой интерфейс для массива контролов прибавляем 1 к IID.Data1, получается 33AD4EDB-6699-11CF-B70C-00AA0060D393.
1
Эксперт WindowsАвтор FAQ
 Аватар для Dragokas
18030 / 7733 / 892
Регистрация: 25.12.2011
Сообщений: 11,502
Записей в блоге: 16
03.07.2017, 21:41
Цитата Сообщение от The trick Посмотреть сообщение
Алгоритм чуть позже скажу
Может, как вариант:

подключить нужный массив, затем:

IConnectionPointContainer::EnumConnectio nPoints
+
IConnectionPoint::GetConnectionInterface

и оттуда извлечь GUID.

Добавлено через 3 минуты
только среда почему то ругается на EnumConnectionPoints, "метод не найден", хотя IntelliSense и отображает его.
0
Модератор
10046 / 3892 / 883
Регистрация: 22.02.2013
Сообщений: 5,844
Записей в блоге: 79
03.07.2017, 21:48
Dragokas, так не получится. Насколько я понял интерфейс реализует VB6, а не ActiveX контрол, а он не реализует метод EnumConnectionPoints. Нужно реверсить и смотреть каким образом формируется IID для юзерконтролов и сторонних контролов. Пока не могу, позже.
1
Эксперт WindowsАвтор FAQ
 Аватар для Dragokas
18030 / 7733 / 892
Регистрация: 25.12.2011
Сообщений: 11,502
Записей в блоге: 16
03.07.2017, 21:55
А... разобрался. Не правильно сделал вызов (точнее в olelib декларация отличается от MSDN). Но ты прав, там он говорит об ошибке, что "объект не поддерживает это действие".
0
Модератор
10046 / 3892 / 883
Регистрация: 22.02.2013
Сообщений: 5,844
Записей в блоге: 79
05.07.2017, 23:37
Сделал другой вариант, попроще с использованием WithEvents, но он не так гибок как в первом примере. В первом примере можно к примеру абсолютно все массивы контролов одного типа "пропустить" через общий обработчик.
Вложения
Тип файла: zip ArrayControls_TextBox_EventsHanding.zip (6.5 Кб, 42 просмотров)
1
Эксперт WindowsАвтор FAQ
 Аватар для Dragokas
18030 / 7733 / 892
Регистрация: 25.12.2011
Сообщений: 11,502
Записей в блоге: 16
06.07.2017, 14:40
Спасибо. Как раз то, о чём мы и говорили с LaVolpe, что WithEvents нужно объявить с правильным интерфейсом. Ты так и сделал, добавив сокласс.
0
Модератор
10046 / 3892 / 883
Регистрация: 22.02.2013
Сообщений: 5,844
Записей в блоге: 79
08.07.2017, 11:17
Про ActiveX контролы.

Как известно VB6 при подключении библиотеки ActiveX контролов через компоненты, создает OCA файл с именем как у оригинального EXE. При исследовании интерфейса массива контролов было выяснено что описание интерфейса находится в соответствующем OCA файле, VB6 просто добавляет интерфейсы для массивов контролов, а также еще какие-то интерфейсы, вероятно врапперы. Причем при каждом создании такой библиотеки (если она была удалена) все UUID'ы генерируются случайно.

Для массива контролов создается интерфейс с именем _\x01EventXXX, где XXX номер. Однако, если попытаться создать TLB с коклассом имплементирующим этот интерфейс в качестве дефолтного для событий, то ничего не выйдет. Конечно же, никакого сообщения об ошибке не будет т.к. IID интерфейса корректный, но события будут сдвинуты.

Дальнейшее исследование показало что VB6 по неизвестной пока причине сдвигает первый метод события на 0x30 байт. Просмотр vTable полученного интерфейса показал что там содержатся нули, т.е. методы не реализованы. Может быть это используется к примеру для DataBinding'а и т.п. Теперь для создания TLB нам нужно определить базовый интерфейс для обеспечения сдвига на 0x30 байт и сделать его ограниченным. В итоге код для моей версии OCA получился таким:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
[
  uuid(6de481a3-2360-4005-8fe4-50ade6a77bbb)
]
library ListView_ControlArray_Interface
{
   importlib("stdole2.tlb");
   importlib("C:\Program Files (x86)\Microsoft Visual Studio\VB98\VB6.OLB");
   importlib("msvbvm60.dll\3");
   importlib("comctl32.ocx");
 
   typedef float single;
 
[ odl,
  uuid(e11ea785-60c6-48c6-84b8-6f75f39e0083),
  hidden,
  nonextensible,
  restricted
]
interface _VB6_unk_event: IUnknown {
    [restricted] void unk_method0();
    [restricted] void unk_method1();
    [restricted] void unk_method2();
    [restricted] void unk_method3();
    [restricted] void unk_method4();
    [restricted] void unk_method5();
    [restricted] void unk_method6();
    [restricted] void unk_method7();
    [restricted] void unk_method8();
};
 
[
  odl,
  uuid(67D8CF6A-0F34-4D40-A650-D5D6F7F02DD3),
  version(3.0),
  helpstring("Event interface for List View Control"),
  helpcontext(0x000336af),
  hidden,
  nonextensible
]
interface _Event10 : _VB6_unk_event {
    [helpstring("Occurs when a user attempts to edit the label of the currently selected ListItem or Node object."), helpcontext(0x000335b7)]
    HRESULT _stdcall BeforeLabelEdit(
                    [in] short* Index, 
                    short* Cancel);
    [helpstring("Occurs after a user edits the label of the currently selected Node or ListItem object."), helpcontext(0x000335b8)]
    HRESULT _stdcall AfterLabelEdit(
                    [in] short* Index, 
                    short* Cancel, 
                    BSTR* NewString);
    [helpstring("Occurs when a ColumnHeader object in a ListView control is clicked."), helpcontext(0x000335b9)]
    HRESULT _stdcall ColumnClick(
                    [in] short* Index, 
                    ColumnHeader* ColumnHeader);
    [helpstring("Occurs when a ListItem object is clicked or selected"), helpcontext(0x000335ba)]
    HRESULT _stdcall ItemClick(
                    [in] short* Index, 
                    ListItem* Item);
    [helpstring("Occurs when the user presses a key while an object has the focus."), helpcontext(0x000335bb)]
    HRESULT _stdcall KeyDown(
                    [in] short* Index, 
                    short* KeyCode, 
                    [in] short* Shift);
    [helpstring("Occurs when the user releases a key while an object has the focus."), helpcontext(0x000335bc)]
    HRESULT _stdcall KeyUp(
                    [in] short* Index, 
                    short* KeyCode, 
                    [in] short* Shift);
    [helpstring("Occurs when the user presses and releases an ANSI key."), helpcontext(0x000335bd)]
    HRESULT _stdcall KeyPress(
                    [in] short* Index, 
                    short* KeyAscii);
    [helpstring("Occurs when the user presses the mouse button while an object has the focus."), helpcontext(0x000335be)]
    HRESULT _stdcall MouseDown(
                    [in] short* Index, 
                    [in] short* Button, 
                    [in] short* Shift, 
                    [in] single* x, 
                    [in] single* y);
    [helpstring("Occurs when the user moves the mouse."), helpcontext(0x000335bf)]
    HRESULT _stdcall MouseMove(
                    [in] short* Index, 
                    [in] short* Button, 
                    [in] short* Shift, 
                    [in] single* x, 
                    [in] single* y);
    [helpstring("Occurs when the user releases the mouse button while an object has the focus."), helpcontext(0x000335c0)]
    HRESULT _stdcall MouseUp(
                    [in] short* Index, 
                    [in] short* Button, 
                    [in] short* Shift, 
                    [in] single* x, 
                    [in] single* y);
    [helpstring("Occurs when the user presses and then releases a mouse button over an object."), helpcontext(0x000335c1)]
    HRESULT _stdcall Click([in] short* Index);
    [helpstring("Occurs when you press and release a mouse button and then press and release it again over an object."), helpcontext(0x000335c2)]
    HRESULT _stdcall DblClick([in] short* Index);
    [helpstring("OLEStartDrag event"), helpcontext(0x000336cf)]
    HRESULT _stdcall OLEStartDrag(
                    [in] short* Index, 
                    [in, out] DataObject** Data, 
                    [in, out] long* AllowedEffects);
    [helpstring("OLEGiveFeedback event"), helpcontext(0x000336d0)]
    HRESULT _stdcall OLEGiveFeedback(
                    [in] short* Index, 
                    [in, out] long* Effect, 
                    [in, out] VARIANT_BOOL* DefaultCursors);
    [helpstring("OLESetData event"), helpcontext(0x000336d1)]
    HRESULT _stdcall OLESetData(
                    [in] short* Index, 
                    [in, out] DataObject** Data, 
                    [in, out] short* DataFormat);
    [helpstring("OLECompleteDrag event"), helpcontext(0x000336d2)]
    HRESULT _stdcall OLECompleteDrag(
                    [in] short* Index, 
                    [in, out] long* Effect);
    [helpstring("OLEDragOver event"), helpcontext(0x000336d3)]
    HRESULT _stdcall OLEDragOver(
                    [in] short* Index, 
                    [in, out] DataObject** Data, 
                    [in, out] long* Effect, 
                    [in, out] short* Button, 
                    [in, out] short* Shift, 
                    [in, out] single* x, 
                    [in, out] single* y, 
                    [in, out] short* State);
    [helpstring("OLEDragDrop event"), helpcontext(0x000336d4)]
    HRESULT _stdcall OLEDragDrop(
                    [in] short* Index, 
                    [in, out] DataObject** Data, 
                    [in, out] long* Effect, 
                    [in, out] short* Button, 
                    [in, out] short* Shift, 
                    [in, out] single* x, 
                    [in, out] single* y);
    [helpstring("Occurs when an object receives the focus."), helpcontext(0x000dfb53)]
    HRESULT _stdcall GotFocus([in] short* Index);
    [helpstring("Occurs when an object loses the focus."), helpcontext(0x000dfb5e)]
    HRESULT _stdcall LostFocus([in] short* Index);
    [helpstring("Occurs when a drag-and-drop operation is completed."), helpcontext(0x000dfb50)]
    HRESULT _stdcall DragDrop(
                    [in] short* Index, 
                    [in, out] Control** Source, 
                    [in, out] single* x, 
                    [in, out] single* y);
    [helpstring("Occurs when a drag-and-drop operation is in progress."), helpcontext(0x000dfb51)]
    HRESULT _stdcall DragOver(
                    [in] short* Index, 
                    [in, out] Control** Source, 
                    [in, out] single* x, 
                    [in, out] single* y, 
                    [in, out] short* State);
    [helpstring("Occurs when a control loses focus to a control that causes validation."), helpcontext(0x000dfb87)]
    HRESULT _stdcall Validate(
                    [in] short* Index, 
                    [in, out] VARIANT_BOOL* Cancel);
};
 
 
[
  uuid(2FEE4F40-BE91-4395-934E-EBB8E78657E7),
  helpcontext(0x0003357e),
  noncreatable
]
coclass ListView_Array {
    [default] interface IDispatch;
    [default, source] interface _Event10;
};
Теперь можно объявлять переменную как WithEvents As ListView_Array и обрабатывать события от массива контролов ListView. Эту TLB можно использовать до тех пор пока не будет удален OCA файл. То что UUID'ы на каждом компьютере различны не будет влиять на работоспособность приложения, т.к. все IID "прошиваются" в EXE и таскать TLB с собой не нужно.

1
Модератор
10046 / 3892 / 883
Регистрация: 22.02.2013
Сообщений: 5,844
Записей в блоге: 79
08.07.2017, 11:28
Про общий интерфейс массива контролов.


Для массива контролов VB6 реализует общий недокументированный интерфейс. Небольшой реверс показал что этот интерфейс имеет IID = E93AD7C1-C347-11D1-A3E2-00A0C90AEA82. При дальнейшем реверсе я пришел к выводу что интерфейс имеет следующую структуру (уже в TLB):
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
[
   uuid(cc767765-0237-4f2b-a581-aa666644befb),
   helpstring("VB6 controls array type library"),
]
library CtlArray {
 
   importlib("stdole2.tlb");
   
   typedef float single;
 
[
  odl,
  uuid(E93AD7C1-C347-11D1-A3E2-00A0C90AEA82),
  helpstring("Controls array main interface")
]
interface IVB6ControlsArray : IDispatch {
    [helpstring("This method returns 0x800A01A9 error always in EXE file"), restricted]
    HRESULT _stdcall Unknown1();
    [helpstring("This method returns 0x800A01A9 error always in EXE file"), restricted]
    HRESULT _stdcall Unknown2(
                     long lP1);
    [helpstring("This method returns 0x800A01A9 error always in EXE file"), restricted]
    HRESULT _stdcall Unknown3(
                     long lP1);
    [helpstring("This method returns 0x800A01A9 error always in EXE file"), restricted]
    HRESULT _stdcall Unknown4(
                     long lP1);
    [helpstring("This method returns 0x800A01A9 error always in EXE file"), restricted]
    HRESULT _stdcall Unknown5(
                     long lP1);
    [helpstring("This method returns 0x800A01A9 error always in EXE file"), restricted]
    HRESULT _stdcall Unknown6(
                     long lP1);
    [helpstring("This method returns 0x800A01A9 error always in EXE file"), restricted]
    HRESULT _stdcall Unknown7(
                     long lP1);
    [helpstring("DONT CALL IT! STACK CORRUPTION!"), restricted]
    HRESULT _stdcall DontCall1();
    [helpstring("DONT CALL IT! STACK CORRUPTION!"), restricted]
    void _stdcall DontCall2();
    [helpstring("Get item"), propget, id(00000000)]
    HRESULT _stdcall Item(short wIndex, 
                     [retval, out] IDispatch** ppObject);
    [helpstring("LBound property"), propget]
    HRESULT _stdcall LBound([retval, out] short* pRet);
    [helpstring("UBound property"), propget]
    HRESULT _stdcall UBound([retval, out] short* pRet);
    [helpstring("Count property"), propget]
    HRESULT _stdcall Count([retval, out] short* pRet);
 
};
 
}
Первые 9 методов являются заглушками (в EXE) и возвращают ошибку 0x800A01A9, я пометил их атрибутом restricted. Далее идут методы стандартные методы для получения информации о массиве контролов. В этой TLB не описаны методы которые идут потом, т.к. они не работают корректно. Может быть это уже другой интерфейс (хотя обычно в конце интерфейса стоит нулевая заглушка), либо IVB6ControlsArray является базовым для какого-то другого интерфейса.

Я сделал небольшой пример использования этого интерфейса для получения информации минуя позднее связывание:
Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Option Explicit
 
Private Sub Form_Load()
 
    PrintControlsArray mnuArr
    PrintControlsArray txtArr
    PrintControlsArray imgArr
    PrintControlsArray uctArr
    PrintControlsArray rtbArr
    
End Sub
 
Private Sub PrintControlsArray( _
            ByVal cArr As IVB6ControlsArray)
    Dim cCtl    As Control
    
    Set cCtl = cArr(1)
    
    Debug.Print
    Debug.Print "Controls name: "; cCtl.Name
    Debug.Print "Count: "; cArr.Count()
    Debug.Print "LBound: "; cArr.LBound()
    Debug.Print "UBound: "; cArr.UBound()
    Debug.Print
    
End Sub
Вложения
Тип файла: zip ControlArrayInterface.zip (4.9 Кб, 15 просмотров)
0
182 / 33 / 3
Регистрация: 28.05.2015
Сообщений: 148
08.07.2017, 20:54  [ТС]
Может у себя в блоге добавишь запись? А то в тему не каждый заглядывает. Вдруг ещё кому полезным будет.

Не в тему будет сказано. Но мне почему-то подумалось про стамеску и бревно: что даже такой простой вещью из бревна можно сделать скульптуру, если есть мозги и руки. Мне преподаватель как-то сказал: `Не нужно сбрасывать со счетов устаревшие языки. Многие считают их неэффективными, маскируя тем самым неэффективность собственных мозгов и неумении применить "старый" инструмент.`
0
oh my god
 Аватар для fever brain
1456 / 796 / 161
Регистрация: 05.01.2016
Сообщений: 2,307
Записей в блоге: 8
09.07.2017, 19:49
Ребята конечно перестарались, отвечая на простенький вопрос, если честно из последних постов я сам с трудом понимаю о чем беседа, а вы то хоть поняли CharlyChaplin, ?
0
Эксперт WindowsАвтор FAQ
 Аватар для Dragokas
18030 / 7733 / 892
Регистрация: 25.12.2011
Сообщений: 11,502
Записей в блоге: 16
09.07.2017, 19:54
fever brain, так ты спрашивай, коль чего не понятно.

Если тебе кажется, что на этот простенький вопрос есть простенький ответ, не стесняйся, пиши своё решение.

Добавлено через 2 минуты
The trick сделал огромную работу, реализовав то, что с десяток лет не могли сделать даже на vbforums и для чего нет в нативной реализации от разработчиков VB6.
0
Модератор
10046 / 3892 / 883
Регистрация: 22.02.2013
Сообщений: 5,844
Записей в блоге: 79
09.07.2017, 20:35
fever brain, говорил тебе - учи COM. Сколько уже лет прошло? Один из подпунктов вопроса был - как сделать переменную WithEvents и обработать ей событие от массива контролов. Знаешь путь?
Просто так интересно совпало что на двух тематических форумах был задан интересный вопрос об обработке событий массива контролов.
0
182 / 33 / 3
Регистрация: 28.05.2015
Сообщений: 148
11.07.2017, 03:24  [ТС]
The trick, скинь материалы по COM плиз.
0
Модератор
10046 / 3892 / 883
Регистрация: 22.02.2013
Сообщений: 5,844
Записей в блоге: 79
11.07.2017, 11:53
Цитата Сообщение от CharlyChaplin Посмотреть сообщение
The trick, скинь материалы по COM плиз.
Деннинг А. - ActiveX (для профессионалов)
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
11.07.2017, 11:53
Помогаю со студенческими работами здесь

Работа с контейнером библиотеки STL
Помогите реализовать программу на с++ 2010 года , не понимаю , как это делать . Задание на картинке П.5.18.Правил Запрещено...

Работа с контейнером map, запрос на выборку
Здравствуйте дамы и господа. Возник вопрос std::map&lt;std::pair&lt;int, int&gt;, Cell&gt; myVek; myVek = Cell(20, 20, false, false); Не...

Работа с контейнером map: найти и вывести те тройки чисел, где последние числа равны
Есть программа, она с файла забирает значения. В файле набор цифр 1 2 45 2 3 60 1 2 60 Вывод я сделал, а вот вторая часть ставит в...

Проблемы с контейнером
имеются следующие типы: typedef int (*CLI_Function ) ( const std::vector &lt;const std::string&gt; arguments, void * data ); typedef std::map...

задача с контейнером
помогите с решение задачи с контейнером Создать контейнер &quot;мультимножество&quot; с элементами типа int. Контейнер пустой. Заполнить его...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
Access
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
Фото: Daniel Greenwood
kumehtar 13.11.2025
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru