Your code entered break mode too, but it hooked the button.
So I'm wondering why the code I wrote doesn't hook the button.
Option Explicit
Private oCmbInstance As InventoryUpdater_Class
Sub AddButton()
Set oCmbInstance = New InventoryUpdater_Class
oCmbInstance.MakeTheButton _
Sheet:=Sheets(1), ButtonName:="Glory", Caption:="Click me!!", _
Left:=30, Top:=30, Width:=100, Height:=50
End Sub
Option Explicit
Private WithEvents oCmb As CommandButton
Private Declare Function SetTimer Lib "user32" _
(ByVal hWnd As Long, ByVal nIDEvent As Long, _
ByVal uElapse As Long, ByRef lpTimerFunc As Byte) As Long
Private Declare Function KillTimer Lib "user32" _
(ByVal hWnd As Long, ByVal nIDEvent As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" _
Alias "RtlMoveMemory" (Destination As Any, Source As Any, _
ByVal Length As Long)
Private sButtonName As String
Private VBS_FILE As String
Private lTimerID As Long
Private mlngInterval As Long
Private bytData(12) As Byte
'=======================================================
'WARNING !!!
'THE TimerProc MUST ALWAYS BE THE FIRST PROCEDURE IN THE CLASS !!!! =
Public Sub TimerProc()
On Error Resume Next
If lTimerID <> 0 Then
Call KillTimer(0, lTimerID)
lTimerID = 0
End If
Set oCmb = ActiveSheet.OLEObjects(sButtonName).Object
Kill VBS_FILE
End Sub
'=======================================================
Private Sub oCmb_Click()
MsgBox "Hello " & "'" & oCmb.Name & "'"
End Sub
Public Sub MakeTheButton( _
Sheet As Worksheet, _
ButtonName As String, _
Optional Caption As String, _
Optional Left As Variant, _
Optional Top As Variant, _
Optional Width As Variant, _
Optional Height As Variant _
)
On Error Resume Next
'store the button name
sButtonName = ButtonName
'create a temp vbs file on the fly.
VBS_FILE = Environ("Temp") & "\AddButtons.vbs" '
Open VBS_FILE For Output As #1
Print #1, "set wb=Getobject(" & Chr(34) & _
ThisWorkbook.FullName & Chr(34) & ")"
Print #1, "On Error Resume Next"
'add the Button control from the vbs file to preserve the variables.
Print #1, "Set Cmb=Wb.Sheets(""" & Sheet.Name & """).OLEObjects.Add (""Forms.CommandButton.1"")"
Print #1, "Cmb.Name=" & Chr(34) & ButtonName & Chr(34) & ""
If Not IsMissing(Caption) Then Print #1, "Cmb.Object.Caption=" & Chr(34) & Caption & Chr(34)
If Not IsMissing(Left) Then Print #1, "Cmb.Left=" & Chr(32) & Str(Left) & Chr(32)
If Not IsMissing(Top) Then Print #1, "Cmb.Top=" & Chr(32) & Str(Top) & Chr(32)
If Not IsMissing(Width) Then Print #1, "Cmb.Width=" & Chr(32) & Str(Width) & Chr(32)
If Not IsMissing(Height) Then Print #1, "Cmb.Height=" & Chr(32) & Str(Height) & Chr(32)
Close #1
'execute the background vbs file.
Shell "WScript.exe " & VBS_FILE
'
If lTimerID = 0 Then
'hook the button click event.
lTimerID = SetTimer(0&, 0&, 1000, bytData(0))
End If
End Sub
Private Sub Class_Initialize()
Dim lClassPtr As Long
lTimerID = 0
CopyMemory lClassPtr, Me, 4 'lngX=ObjPtr(me)
bytData(0) = &H68
CopyMemory bytData(1), lClassPtr, 4 'push this (me) on stack
bytData(5) = &HE8
CopyMemory bytData(6), _
GetVTableAddress(Me, 7) - VarPtr(bytData(10)), 4 'call TimerProc
'value of 7 is used because TimerProc is actually the 8th function in
'the VTable
'after QueryInterface, AddRef, Release, GetTypeInfoCount, GetTypeInfo,
'GetIDsOfNames , Invoke
bytData(10) = &HC2
bytData(11) = &H10
bytData(12) = &H0 'ret 10 --- return from
'callback, removing 4 parameters from stack
End Sub
Private Function GetVTableAddress _
(ByVal objRef As Object, ByVal Offset As Long) As Long
Dim lClassPtr As Long
CopyMemory lClassPtr, ByVal ObjPtr(objRef), 4 'Get pointer to VTable
CopyMemory GetVTableAddress, ByVal lClassPtr + Offset * 4, 4 'Get function
'pointer
End Function
Option Explicit
Private oCmbInstance As InventoryUpdater_Class
Private oCol As New Collection
'Add four CommandButtons and hook each of them.
Sub AddButtons()
'button 1
Set oCmbInstance = New InventoryUpdater_Class
oCmbInstance.MakeTheButton _
Sheet:=ActiveSheet, ButtonName:="Glory", Caption:="Click me!!", _
Left:=30, Top:=30, Width:=100, Height:=40
oCol.Add oCmbInstance
'button 2
Set oCmbInstance = New InventoryUpdater_Class
oCmbInstance.MakeTheButton _
Sheet:=ActiveSheet, ButtonName:="Ruddles", Caption:="Click me!!", _
Left:=30, Top:=80, Width:=100, Height:=40
oCol.Add oCmbInstance
'button 3
Set oCmbInstance = New InventoryUpdater_Class
oCmbInstance.MakeTheButton _
Sheet:=ActiveSheet, ButtonName:="Jaafar", Caption:="Click me!!", _
Left:=160, Top:=30, Width:=100, Height:=40
oCol.Add oCmbInstance
'button 4
Set oCmbInstance = New InventoryUpdater_Class
oCmbInstance.MakeTheButton _
Sheet:=ActiveSheet, ButtonName:="Sektor", Caption:="Click me!!", _
Left:=160, Top:=80, Width:=100, Height:=40
oCol.Add oCmbInstance
End Sub
Option Explicit
Private WithEvents oCmb As CommandButton
Private Declare Function SetTimer Lib "user32" _
(ByVal hWnd As Long, ByVal nIDEvent As Long, _
ByVal uElapse As Long, ByRef lpTimerFunc As Byte) As Long
Private Declare Function KillTimer Lib "user32" _
(ByVal hWnd As Long, ByVal nIDEvent As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" _
Alias "RtlMoveMemory" (Destination As Any, Source As Any, _
ByVal Length As Long)
Private Declare Function LockWindowUpdate Lib "user32" _
(ByVal hwndLock As Long) As Long
Private sButtonName As String
Private VBS_FILE As String
Private lTimerID As Long
Private mlngInterval As Long
Private bytData(12) As Byte
'=======================================================
'WARNING !!!
'THE TimerProc MUST ALWAYS BE THE FIRST PROCEDURE IN THE CLASS !!!! =
Public Sub TimerProc()
On Error Resume Next
If lTimerID <> 0 Then
Call KillTimer(0, lTimerID)
lTimerID = 0
End If
Set oCmb = ActiveSheet.OLEObjects(sButtonName).Object
Kill VBS_FILE
LockWindowUpdate 0
End Sub
'=======================================================
Private Sub oCmb_Click()
MsgBox "Hello " & "'" & oCmb.Name & "'"
End Sub
Public Sub MakeTheButton( _
Sheet As Worksheet, _
ButtonName As String, _
Optional Caption As String, _
Optional Left As Variant, _
Optional Top As Variant, _
Optional Width As Variant, _
Optional Height As Variant _
)
On Error Resume Next
'store the button name
sButtonName = ButtonName
'create a temp vbs file on the fly.
VBS_FILE = Environ("Temp") & "\" & ButtonName & ".vbs" '
Open VBS_FILE For Output As #1
Print #1, "set wb=Getobject(" & Chr(34) & _
ThisWorkbook.FullName & Chr(34) & ")"
Print #1, "On Error Resume Next"
'add the Button control from the vbs file to preserve the variables.
Print #1, "Set Cmb=Wb.Sheets(""" & Sheet.Name & """).OLEObjects.Add (""Forms.CommandButton.1"")"
Print #1, "Cmb.Name=" & Chr(34) & ButtonName & Chr(34) & ""
If Not IsMissing(Caption) Then Print #1, "Cmb.Object.Caption=" & Chr(34) & Caption & Chr(34)
If Not IsMissing(Left) Then Print #1, "Cmb.Left=" & Chr(32) & Str(Left) & Chr(32)
If Not IsMissing(Top) Then Print #1, "Cmb.Top=" & Chr(32) & Str(Top) & Chr(32)
If Not IsMissing(Width) Then Print #1, "Cmb.Width=" & Chr(32) & Str(Width) & Chr(32)
If Not IsMissing(Height) Then Print #1, "Cmb.Height=" & Chr(32) & Str(Height) & Chr(32)
Close #1
LockWindowUpdate Application.hWnd
'execute the background vbs file.
Shell "WScript.exe " & VBS_FILE
If lTimerID = 0 Then
'hook the button click event.
lTimerID = SetTimer(0&, 0&, 2000, bytData(0))
End If
End Sub
Private Sub Class_Initialize()
Dim lClassPtr As Long
lTimerID = 0
CopyMemory lClassPtr, Me, 4 'lngX=ObjPtr(me)
bytData(0) = &H68
CopyMemory bytData(1), lClassPtr, 4 'push this (me) on stack
bytData(5) = &HE8
CopyMemory bytData(6), _
GetVTableAddress(Me, 7) - VarPtr(bytData(10)), 4 'call TimerProc
'value of 7 is used because TimerProc is actually the 8th function in
'the VTable
'after QueryInterface, AddRef, Release, GetTypeInfoCount, GetTypeInfo,
'GetIDsOfNames , Invoke
bytData(10) = &HC2
bytData(11) = &H10
bytData(12) = &H0 'ret 10 --- return from
'callback, removing 4 parameters from stack
End Sub
Private Function GetVTableAddress _
(ByVal objRef As Object, ByVal Offset As Long) As Long
Dim lClassPtr As Long
CopyMemory lClassPtr, ByVal ObjPtr(objRef), 4 'Get pointer to VTable
CopyMemory GetVTableAddress, ByVal lClassPtr + Offset * 4, 4 'Get function
'pointer
End Function