|
-
Apr 9th, 2024, 01:01 AM
#1
Thread Starter
PowerPoster
How to call a Procedure by Name.
I'm sure there's a way to do it and I even think I've done it in the past but I can't find it and an internet search gave me AddressOf and CallByName.
I use callbyname to recolor my forms at run-time per user preferences.
So this is what I'm trying to do.
I'm writing a thing that chooses a random thing to do. Let's say there's 20 choices of random things.
So I have this:
Sub Random00
End Sub
Sub Random01
End Sub
and so on.
To call it I do this:
Code:
Sub DoARandomThing
Dim nRnd as long
nRnd=int(rnd * 19)
Select Case nRnd
Case 0
Random00
Case 1
Random01
etc.
What I'd like to do is this:
Code:
Sub DoARandomThing
Dim nRnd as long
nRnd = int(rnd * 19)
CallSubroutineByName "Random" & format(nrnd, "00")
Last edited by cafeenman; Apr 9th, 2024 at 01:06 AM.
-
Apr 9th, 2024, 01:05 AM
#2
Re: How to call a Procedure by Name.
Only public methods of classes can be called by their name. All other names are lost after compilation (they become memory addresses).
-
Apr 9th, 2024, 01:08 AM
#3
Thread Starter
PowerPoster
Re: How to call a Procedure by Name.
OK, so I have a create a cRandomThings Class?
That will make this sticky because at least some of the random things are with controls so I'd have to pass those subs the controls.
I'm not sure it's worth it to just get out of doing a long select case thing.
-
Apr 9th, 2024, 02:55 AM
#4
Thread Starter
PowerPoster
Re: How to call a Procedure by Name.
Does anyone know how to make the actual call?
-
Apr 9th, 2024, 03:22 AM
#5
Re: How to call a Procedure by Name.
 Originally Posted by VanGoghGaming
Only public methods of classes can be called by their name. All other names are lost after compilation (they become memory addresses).
This sounds logical but VB6 leaves some internal VBProject structures in final binary which allow traversing modules/procedures and finding offset/address is current process. Another thing is that friend/private methods of classes do end up in runtime VTable albeit not used by codegen to make actual calls from callsites but the addresses in current process are present.
-
Apr 9th, 2024, 03:44 AM
#6
Re: How to call a Procedure by Name.
 Originally Posted by cafeenman
Does anyone know how to make the actual call?
If your 20 procedures are defined in the Form itself, then just make them Public there.
Then calling (also from within the Form):
CallByName Me, "Random" & Format(Int(Rnd * 20), "00"), vbMethod
...should do it (also note the 20 instead of your 19 above).
Olaf
-
Apr 9th, 2024, 03:53 AM
#7
Thread Starter
PowerPoster
Re: How to call a Procedure by Name.
Thanks. I do know how rnd works. (add 1 if you want 1-based). Otherwise zero-based.
so all my six-sided die rolls are actually 0 to 5 just so I'm not swapping back and forth all the time between number bases. Just make it all base 0.
E.g Int(Rnd * 6) returns 0 to 5.
Thanks again.
Edit: OK, I see why you specified the 20. The 19 in my OP which was wrong.
Last edited by cafeenman; Apr 9th, 2024 at 04:12 AM.
-
Apr 9th, 2024, 04:03 AM
#8
Re: How to call a Procedure by Name.
 Originally Posted by cafeenman
I do know how rnd works. (add 1 if you want 1-based).
Otherwise zero-based.
No - just play that through for a "zerobased" Coin-Toss... (a 50:50 distribution between result 0 or 1).
For that case - your proposed Int(Rnd * 1) will produce a 100:0 distribution
(always producing a 0 - and never the 1)...
For a zerobased coin-toss, the right Formula is Int(Rnd * 2)
Olaf
-
Apr 9th, 2024, 04:15 AM
#9
Re: How to call a Procedure by Name.
Also note that Int in Int(Rnd * 20) is important. Using CLng or leaving it without explicit conversion (i.e. simple Format(Rnd * 20, "00")) will result in bias in final distribution with both end values having half the probability of the rest of outcomes.
cheers,
</wqw>
-
Apr 9th, 2024, 08:44 AM
#10
Thread Starter
PowerPoster
Re: How to call a Procedure by Name.
 Originally Posted by Schmidt
No - just play that through for a "zerobased" Coin-Toss... (a 50:50 distribution between result 0 or 1).
For that case - your proposed Int(Rnd * 1) will produce a 100:0 distribution
(always producing a 0 - and never the 1)...
For a zerobased coin-toss, the right Formula is Int(Rnd * 2)
Olaf
PS: The code below is kind of cool. You just need a VScroll1 and HScroll1 with both of them having the same min and max. That part is important.
Mine are set to min=0 and max=25
I've got that thing all over this code.
Code:
Private Sub Anger()
Dim n As Long
Dim nrnd As Long
Dim nMid As Long
Dim nMax As Long
Dim nOffset As Long
' AddToText vbCrLf & vbTab & "You have Angered the Ghost!!" & vbCrLf
'Sleep 100
With HScroll1
nMid = .Max / 2
nMax = .Max / 3
For n = 0 To 49
nrnd = Int(Rnd * nMax)
.Value = nMid + nrnd
.Value = nMid - nrnd
Next n
End With
With HScroll1
nrnd = Int(Rnd * 2)
nOffset = .Max / 3
If nrnd = 0 Then
.Value = nMid + nOffset
Else
.Value = nMid - nOffset
End If
End With
End Sub
Private Sub HScroll1_Change()
With HScroll1
If Not .Enabled Then Exit Sub
VScroll1.Value = .Max - .Value
End With
'lblCount.Caption = Format(HScroll1.Value, PLAY_FORMAT)
'DoARandomThing
End Sub
Private Sub HScroll1_Scroll()
HScroll1_Change
End Sub
Private Sub VScroll1_Change()
With VScroll1
HScroll1.Value = .Max - .Value
End With
End Sub
Private Sub VScroll1_Scroll()
VScroll1_Change
End Sub
Last edited by cafeenman; Apr 9th, 2024 at 08:56 AM.
-
Apr 9th, 2024, 11:02 AM
#11
Re: How to call a Procedure by Name.
 Originally Posted by cafeenman
PS: The code below is kind of cool.
You just need a VScroll1 and HScroll1 with both of them having the same min and max. That part is important.
Not sure, what you want to express with the "Anger-part" of your snippet -
but it could have been written more efficiently this way:
Code:
Option Explicit
Private WithEvents H As VB.HScrollBar, WithEvents V As VB.VScrollBar
Private Sub Form_Load()
Set V = Controls.Add("VB.VScrollBar", "V"): V.Visible = True
Set H = Controls.Add("VB.HScrollBar", "H"): H.Visible = True
H = H.Max / 2 + H.Max / 3 * IIf(Int(Rnd * 2), 1, -1) ' Anger
End Sub
Private Sub H_Change()
V = H.Max - H
End Sub
Private Sub H_Scroll()
H_Change
End Sub
Private Sub V_Change()
H = V.Max - V
End Sub
Private Sub V_Scroll()
V_Change
End Sub
Olaf
-
Apr 10th, 2024, 01:48 AM
#12
Re: How to call a Procedure by Name.
 Originally Posted by wqweto
Another thing is that friend/private methods of classes do end up in runtime VTable albeit not used by codegen to make actual calls from callsites but the addresses in current process are present.
Only Friend methods, private ones is called using VTable.
-
Apr 10th, 2024, 03:12 AM
#13
Re: How to call a Procedure by Name.
 Originally Posted by The trick
Only Friend methods, private ones is called using VTable.
Weird! For private methods it does codegen VTable calls.
This class:
Code:
Option Explicit
Public Function Init() As Long
Test1
Test2
Test3
End Function
Public Function Test1() As Long
Test1 = 42
End Function
Private Function Test2() As Long
Test2 = 43
End Function
Friend Function Test3() As Long
Test3 = Test1 + Test2
End Function
... decompiles to this:
Code:
Public Function Init() As Long
00401DA9 8D 45 E4 lea eax,[unnamed_var1]
00401DAC 50 push eax
00401DAD 8B 45 08 mov eax,dword ptr [Me]
00401DB0 8B 00 mov eax,dword ptr [eax]
00401DB2 FF 75 08 push dword ptr [Me]
00401DB5 FF 50 20 call dword ptr [eax+20h]
00401DB8 89 45 E0 mov dword ptr [unnamed_var1],eax
00401DBB 83 7D E0 00 cmp dword ptr [unnamed_var1],0
00401DBF 7D 17 jge Class1::Init+6Fh (401DD8h)
00401DC1 6A 20 push 20h
00401DC3 68 10 1A 40 00 push offset ___vba@08C12B20 (401A10h)
00401DC8 FF 75 08 push dword ptr [Me]
00401DCB FF 75 E0 push dword ptr [unnamed_var1]
00401DCE E8 AF F3 FF FF call ___vbaHresultCheckObj (401182h)
00401DD3 89 45 D4 mov dword ptr [ebp-2Ch],eax
00401DD6 EB 04 jmp Class1::Init+73h (401DDCh)
00401DD8 83 65 D4 00 and dword ptr [ebp-2Ch],0
Test1()
00401DDC 8D 45 E4 lea eax,[unnamed_var1]
00401DDF 50 push eax
00401DE0 8B 45 08 mov eax,dword ptr [Me]
00401DE3 8B 00 mov eax,dword ptr [eax]
00401DE5 FF 75 08 push dword ptr [Me]
00401DE8 FF 50 24 call dword ptr [eax+24h]
Test2()
00401DEB 8D 45 E4 lea eax,[unnamed_var1]
00401DEE 50 push eax
00401DEF FF 75 08 push dword ptr [Me]
00401DF2 E8 B5 00 00 00 call Class1::Test3 (401EACh)
Test3()
00401DF7 8B 45 08 mov eax,dword ptr [Me]
00401DFA 8B 00 mov eax,dword ptr [eax]
00401DFC FF 75 08 push dword ptr [Me]
00401DFF FF 50 08 call dword ptr [eax+8]
00401E02 8B 45 0C mov eax,dword ptr [Init]
00401E05 8B 4D E8 mov ecx,dword ptr [Init]
00401E08 89 08 mov dword ptr [eax],ecx
00401E0A 8B 45 FC mov eax,dword ptr [ebp-4]
00401E0D 8B 4D EC mov ecx,dword ptr [ebp-14h]
00401E10 64 89 0D 00 00 00 00 mov dword ptr fs:[0],ecx
00401E17 5F pop edi
00401E18 5E pop esi
00401E19 5B pop ebx
00401E1A C9 leave
00401E1B C2 08 00 ret 8
End Function
So private Test2 method indeed uses call dword ptr [eax+24h] indirect call through VTable but lacks HResult check.
Friend Test3 method uses call Class1::Test3 (401EACh) direct call and again no HResult check which seems to be fastest approach.
Nothing comes to mind what could possible require VTable indirect calls for private methods.
cheers,
</wqw>
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|