I've found this example, but I still don't understand it quite well because it's not commented, so I don't have a clue how to interact with the each thread and terminate all of them when I want to stop the process by closing the program or clicking on some "Stop" button. I don't want to do the creation of the external process or to call external file (DLL/EXE) or to use some complex classes/hacks, all I want is a simple multithreading which is almost ideal to the example that's mentioned above. Is that achievable i.e. is that project fixable or it has some flaws that can't be easily resolved?
My goal is to create asynchronous operations (timers) which will not freeze the UI - so the threading will be the solution.
Last edited by MikiSoft; Apr 5th, 2015 at 12:08 PM.
Simple Multithreading with VB6 would be considered an oxymoron
If you don't want to use an ActiveX EXE then throw away the word simple. Though not sure multithreading + VB6 can ever be termed simple. Any other method may require an external DLL and/or some serious hacks.
From the link you posted and the comments therein, doesn't that project create additional processes vs. threads?
If multithreading with VB6 was simple, you'd find 10s of thousands of examples posted all over the web. Wish it was easy.
Insomnia is just a byproduct of, "It can't be done"
Simple Multithreading with VB6 would be considered an oxymoron
If you don't want to use an ActiveX EXE then throw away the word simple. Though not sure multithreading + VB6 can ever be termed simple. Any other method may require an external DLL and/or some serious hacks.
From the link you posted and the comments therein, doesn't that project create additional processes vs. threads?
If multithreading with VB6 was simple, you'd find 10s of thousands of examples posted all over the web. Wish it was easy.
I know that it's not simple, but that man did it a lot simpler than all examples that I've found.
I didn't linked to the main post, I've linked to the comment in that topic where is attached another project which I'm talking about.
Last edited by MikiSoft; Apr 4th, 2015 at 03:48 PM.
Okay, here it is. Solid as rock with minimal code and hacking
BTW. I saw that this self-hosted ActiveX EXE approach was also presented by Olaf (Schmidt), but I didn't understood his code because it was created to calculate and draw fractals, so it was pretty complicated.
UPDATED #2:
Last edited by MikiSoft; Dec 8th, 2015 at 11:08 PM.
Hello MikiSoft. Specially for you I made this example. This is the usual project - StandartEXE. Take here the type library and read the description. Quite simply, instead of CreateThread are using vbCreateThread. Thread draws fractal. The main thread is used for the GUI.
Form:
Code:
Option Explicit
Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
Private Declare Function ColorHLSToRGB Lib "shlwapi.dll" (ByVal wHue As Integer, ByVal wLuminance As Integer, ByVal wSaturation As Integer) As Long
Dim hThread As Long
Dim di As Single
Dim dr As Single
Dim re As Single
Dim ie As Single
Private Sub Form_Load()
Dim i As Long
' Create palette
For i = 0 To 99
SimpleMultithreading.Palette(i) = ColorHLSToRGB(i * 2.42, 120, 240)
Next
' Set viewport
iLeft = -1
iRight = 1
iTop = -1
iBottom = 1
Randomize
' Set finish value
re = Rnd * 2 - 1
ie = Rnd * 2 - 1
dr = (re - Real) / 40
di = (ie - Imaginary) / 40
Process = True
' Call function in a new thread
hThread = vbCreateThread(0, 0, AddressOf DrawJulia, ObjPtr(picDisp), 0, 0)
End Sub
Private Sub Form_Unload(Cancel As Integer)
Process = False
' Wait until thread ends
WaitForSingleObject hThread, -1
End Sub
Private Sub tmrFPS_Timer()
Me.Caption = Format(FPS, "\F\P\S 0")
FPS = 0
End Sub
Private Sub tmrInterpolation_Timer()
' Interpolation
Real = Real + dr
Imaginary = Imaginary + di
If Abs(Real - re) < 0.0001 Then re = Rnd * 2 - 1: dr = (re - Real) / 40
If Abs(Imaginary - ie) < 0.0001 Then ie = Rnd * 2 - 1: di = (ie - Imaginary) / 40
End Sub
Module:
Code:
Option Explicit
Public Type RGBQUAD
rgbBlue As Byte
rgbGreen As Byte
rgbRed As Byte
rgbReserved As Byte
End Type
Public Type BITMAPINFOHEADER
biSize As Long
biWidth As Long
biHeight As Long
biPlanes As Integer
biBitCount As Integer
biCompression As Long
biSizeImage As Long
biXPelsPerMeter As Long
biYPelsPerMeter As Long
biClrUsed As Long
biClrImportant As Long
End Type
Public Type BITMAPINFO
bmiHeader As BITMAPINFOHEADER
bmiColors As RGBQUAD
End Type
Private Declare Function SetDIBitsToDevice Lib "gdi32" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long, ByVal dx As Long, ByVal dy As Long, ByVal SrcX As Long, ByVal SrcY As Long, ByVal Scan As Long, ByVal NumScans As Long, Bits As Any, BitsInfo As BITMAPINFO, ByVal wUsage As Long) As Long
Private Declare Function GetCurrentThreadId Lib "kernel32" () As Long
Public iLeft As Double
Public iTop As Double
Public iRight As Double
Public iBottom As Double
Public Real As Double
Public Imaginary As Double
Public Process As Boolean
Public Palette(99) As Long
Public FPS As Long
' // Function draw fractal on the picturebox
Public Function DrawJulia(ByVal pic As PictureBox) As Long
Dim x As Double, y As Double, Sx As Double, Sy As Double
Dim pt As Long, Bits() As Long, bi As BITMAPINFO
Dim lx As Long, ly As Long
ReDim Bits(pic.ScaleWidth * pic.ScaleHeight - 1)
With bi.bmiHeader
.biBitCount = 32
.biHeight = -pic.ScaleHeight
.biWidth = pic.ScaleWidth
.biPlanes = 1
.biSize = Len(bi.bmiHeader)
.biSizeImage = pic.ScaleWidth * pic.ScaleHeight * 4
End With
Do While Process
Sx = (iRight - iLeft) / (pic.ScaleWidth - 1)
Sy = (iRight - iLeft) / (pic.ScaleHeight - 1)
x = iLeft: y = iTop
Process = Not Not Process
pt = 0
For ly = 0 To pic.ScaleHeight - 1: For lx = 0 To pic.ScaleWidth - 1
x = x + Sx
Bits(pt) = Palette(Julia(x, y))
pt = pt + 1
Next: y = y + Sy: x = iLeft: Next
SetDIBitsToDevice pic.hdc, 0, 0, pic.ScaleWidth, ly, 0, 0, 0, ly, Bits(0), bi, 0
pic.CurrentX = 0
pic.CurrentY = 0
pic.Print Format(GetCurrentThreadId(), "T\hrea\d #")
FPS = FPS + 1
Loop
End Function
' // Function return value Julia set
Private Function Julia(x As Double, y As Double) As Single
Dim Zr As Double, Zi As Double
Dim Cr As Double, Ci As Double
Dim tZr As Double
Dim count As Long
Dim r As Single
count = 0
Zr = x: Zi = y
Cr = Real: Ci = Imaginary
Do While count < 99 And r < 10
tZr = Zr
Zr = Zr * Zr - Zi * Zi
Zi = tZr * Zi + Zi * tZr
Zr = Zr + Cr
Zi = Zi + Ci
r = Sqr(Zr * Zr + Zi * Zi)
count = count + 1
Loop
Julia = count
End Function
Re: [RESOLVED] The most easiest way of multithreading
Thanks for the effort, but I've already solved my problem with simple approach above. I want to ask why ActiveX EXE is different than Standard EXE as it does not require manual registration process (unlike DLL or OCX)?
Last edited by MikiSoft; Apr 5th, 2015 at 03:57 PM.
Re: [RESOLVED] The most easiest way of multithreading
MikiSoft, you edited your first post to include a goal. Timers should never freeze up the UI. I am assuming you are not talking about the timers, but rather the code that is called/executed once your timer event is received? If that is not the case, maybe we should be asking why your timers are freezing up your UI?
Insomnia is just a byproduct of, "It can't be done"
Re: [RESOLVED] The most easiest way of multithreading
@The trick: I see now. That can be a problem, but in almost every project I include ActiveX components so Administrator rights will be definitely needed.
@LaVople: I'm creating an array of timers to do some operation, so on slower computers they can freeze the UI and slow down each other.
Okay, here it is. Solid as rock with minimal code and hacking
BTW. I saw that this self-hosted ActiveX EXE approach was also presented by Olaf, but I didn't understood his code because it was created to calculate and draw fractals, so it was pretty complicated.
UPDATED:
I don't understand what is multi-threading about this. The threads only run one at a time.
Anything I post is an example only and is not intended to be the only solution, the total solution nor the final solution to your request nor do I claim that it is. If you find it useful then it is entirely up to you to make whatever changes necessary you feel are adequate for your purposes.
Hello MikiSoft. Specially for you I made this example. This is the usual project - StandartEXE. Take here the type library and read the description. Quite simply, instead of CreateThread are using vbCreateThread. Thread draws fractal. The main thread is used for the GUI.
Form:
Code:
Option Explicit
Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
Private Declare Function ColorHLSToRGB Lib "shlwapi.dll" (ByVal wHue As Integer, ByVal wLuminance As Integer, ByVal wSaturation As Integer) As Long
Dim hThread As Long
Dim di As Single
Dim dr As Single
Dim re As Single
Dim ie As Single
Private Sub Form_Load()
Dim i As Long
' Create palette
For i = 0 To 99
SimpleMultithreading.Palette(i) = ColorHLSToRGB(i * 2.42, 120, 240)
Next
' Set viewport
iLeft = -1
iRight = 1
iTop = -1
iBottom = 1
Randomize
' Set finish value
re = Rnd * 2 - 1
ie = Rnd * 2 - 1
dr = (re - Real) / 40
di = (ie - Imaginary) / 40
Process = True
' Call function in a new thread
hThread = vbCreateThread(0, 0, AddressOf DrawJulia, ObjPtr(picDisp), 0, 0)
End Sub
Private Sub Form_Unload(Cancel As Integer)
Process = False
' Wait until thread ends
WaitForSingleObject hThread, -1
End Sub
Private Sub tmrFPS_Timer()
Me.Caption = Format(FPS, "\F\P\S 0")
FPS = 0
End Sub
Private Sub tmrInterpolation_Timer()
' Interpolation
Real = Real + dr
Imaginary = Imaginary + di
If Abs(Real - re) < 0.0001 Then re = Rnd * 2 - 1: dr = (re - Real) / 40
If Abs(Imaginary - ie) < 0.0001 Then ie = Rnd * 2 - 1: di = (ie - Imaginary) / 40
End Sub
Module:
Code:
Option Explicit
Public Type RGBQUAD
rgbBlue As Byte
rgbGreen As Byte
rgbRed As Byte
rgbReserved As Byte
End Type
Public Type BITMAPINFOHEADER
biSize As Long
biWidth As Long
biHeight As Long
biPlanes As Integer
biBitCount As Integer
biCompression As Long
biSizeImage As Long
biXPelsPerMeter As Long
biYPelsPerMeter As Long
biClrUsed As Long
biClrImportant As Long
End Type
Public Type BITMAPINFO
bmiHeader As BITMAPINFOHEADER
bmiColors As RGBQUAD
End Type
Private Declare Function SetDIBitsToDevice Lib "gdi32" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long, ByVal dx As Long, ByVal dy As Long, ByVal SrcX As Long, ByVal SrcY As Long, ByVal Scan As Long, ByVal NumScans As Long, Bits As Any, BitsInfo As BITMAPINFO, ByVal wUsage As Long) As Long
Private Declare Function GetCurrentThreadId Lib "kernel32" () As Long
Public iLeft As Double
Public iTop As Double
Public iRight As Double
Public iBottom As Double
Public Real As Double
Public Imaginary As Double
Public Process As Boolean
Public Palette(99) As Long
Public FPS As Long
' // Function draw fractal on the picturebox
Public Function DrawJulia(ByVal pic As PictureBox) As Long
Dim x As Double, y As Double, Sx As Double, Sy As Double
Dim pt As Long, Bits() As Long, bi As BITMAPINFO
Dim lx As Long, ly As Long
ReDim Bits(pic.ScaleWidth * pic.ScaleHeight - 1)
With bi.bmiHeader
.biBitCount = 32
.biHeight = -pic.ScaleHeight
.biWidth = pic.ScaleWidth
.biPlanes = 1
.biSize = Len(bi.bmiHeader)
.biSizeImage = pic.ScaleWidth * pic.ScaleHeight * 4
End With
Do While Process
Sx = (iRight - iLeft) / (pic.ScaleWidth - 1)
Sy = (iRight - iLeft) / (pic.ScaleHeight - 1)
x = iLeft: y = iTop
Process = Not Not Process
pt = 0
For ly = 0 To pic.ScaleHeight - 1: For lx = 0 To pic.ScaleWidth - 1
x = x + Sx
Bits(pt) = Palette(Julia(x, y))
pt = pt + 1
Next: y = y + Sy: x = iLeft: Next
SetDIBitsToDevice pic.hdc, 0, 0, pic.ScaleWidth, ly, 0, 0, 0, ly, Bits(0), bi, 0
pic.CurrentX = 0
pic.CurrentY = 0
pic.Print Format(GetCurrentThreadId(), "T\hrea\d #")
FPS = FPS + 1
Loop
End Function
' // Function return value Julia set
Private Function Julia(x As Double, y As Double) As Single
Dim Zr As Double, Zi As Double
Dim Cr As Double, Ci As Double
Dim tZr As Double
Dim count As Long
Dim r As Single
count = 0
Zr = x: Zi = y
Cr = Real: Ci = Imaginary
Do While count < 99 And r < 10
tZr = Zr
Zr = Zr * Zr - Zi * Zi
Zi = tZr * Zi + Zi * tZr
Zr = Zr + Cr
Zi = Zi + Ci
r = Sqr(Zr * Zr + Zi * Zi)
count = count + 1
Loop
Julia = count
End Function
Anything I post is an example only and is not intended to be the only solution, the total solution nor the final solution to your request nor do I claim that it is. If you find it useful then it is entirely up to you to make whatever changes necessary you feel are adequate for your purposes.
You must compile it and run, otherwise in the IDE it won't work.
OK, very nice.
Anything I post is an example only and is not intended to be the only solution, the total solution nor the final solution to your request nor do I claim that it is. If you find it useful then it is entirely up to you to make whatever changes necessary you feel are adequate for your purposes.
Can't find project or library
tlsIndex = TlsAlloc()
Originally Posted by The trick
Take here the type library and read the description.
See type library in the reference thread.
What sense in this multi-threading? (I mean the ActiveX EXE) DoEvents in a loop many times slow. I understand This is needed to marshalling. Interactions are synchronous, then what meaning in that? If you need to get some sort of response from another thread I should wait.
@MikiSoft: You don't need reg-free COM activation for your ActiveX EXE if you know beforehand the relative path to this executable once the whole solution is deployed. Then you can just manually start it w/ CreateProcess/ShellExecute and it will automagically register its public class factories w/ CoRegisterClassObject in ROT.
Unfortunately VB6 out-of-processes servers try to autoregister some marshaling support on start-up and this fails with bizarre errors when the first user to start it is not an admin. So the answer is both yes and no, it cannot be done.
Here is another strategy that is very hard to be done with VB6.
Re: Can ActiveX EXE be Reg-Free or run without Administrator rights?
Thanks wqweto for the answer! Has anyone from here tried to accomplish that strategy or something similar, in order to avoid VB6 servers to do the registration process? It really bugs me, even more now when you said that, if I understood correctly, Administrator rights aren't really needed to do the proper registration of the out-of-process COM object.
Last edited by MikiSoft; Apr 7th, 2015 at 12:31 PM.
Unfortunately VB6 out-of-processes servers try to autoregister some marshaling support on start-up and this fails with bizarre errors when the first user to start it is not an admin. So the answer is both yes and no, it cannot be done.
I swear to God, ActiveX could be a real pain that threatens to drive one insane.
C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter
There's just no reason to use garbage like InputBox. - jmcilhinney
The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber
Re: Can ActiveX EXE be Reg-Free or run without Administrator rights?
Originally Posted by MikiSoft
... if I understood correctly, Administrator rights aren't really needed to do the proper registration of the out-of-process COM object.
No, you "understood" exactly incorrectly.
An ActiveX EXE is an out of process COM server, and a VB6 ActiveX EXE merely follows the ActiveX and DCOM specifications, which require self-registration. By their nature the keys must be registered globally, so elevated permission is required. In this context the "first run" can perform the required registration but it must be an elevated run.
If you really "need multithreading" you might consider whether or not you can use a worker process instead of a worker thread. Of course you'll need to choose an IPC mechanism and deal with synchronization.
But even then you can't write your "worker" code based on busy loops and blocking calls. If you do, the worker has no way to be responsive to something like a shutdown request from the main program.
If you take yourself out of that box (busy/blocking code) you may suddenly realize you never needed a worker thread in the first place.
Re: Can ActiveX EXE be Reg-Free or run without Administrator rights?
Originally Posted by dilettante
An ActiveX EXE is an out of process COM server, ...
The above line is of course right - but I catched it up, to maybe shift the focus again,
on what we have here (with regards to, what triggered the OPs question).
It's InProcess-Threading we talk about (by means of an ActiveX-Exe-Host, which
of course could also act as an OutOfProcess-Server - but in case of the OP is
never used in that mode).
The InProcess-STAs in this AxExe-Mode are created by (Process-internal) calls to
CreateObject("MyProject.cMyInternalPublicClass"), which (when properly registered)
will instantiate the ClassObject (according to the ProgID we used in CreateObject)
on a new STA which runs in the same Process as the CreateObject-Caller.
When the Object in question would be instantiated instead by: New cMyInternalPublicClass
then the instance is not being created on a new STA, but on the calling thread instead
(usually the Main-Thread - aka "normal Class-instantiation/communication").
Just for completeness (although it was already mentioned in this discussion) - when
running such an InProcessThreading AxExe-Project in the IDE, the just mentioned
CreateObject(ThreadClassProgID)-calls will not cause the creation of a new STA -
instead the behaviour is comparable to using the normal instantiation per New
(which allows for easier debugging).
So, we have a quite easy and comfortable way to use InProcess-Threading in VB6 -
but (as this thread shows) - the only "catch" is the requirement, that the ActiveX-
Exe in question needs to be registered on the target-machine, to perform its
InProcess-Threading as designed.
@the OP
The reason for that is, that communication among Objects on different STAs has to be
marshalled - and the systems COM-marshaller cannot make use of the Interface-Infos
which are already contained in the compiled VB-Executable - only the VB6-AxExe knows
about them (to be used when "Objects talk among themselves" on the Main-Thread) -
for transparent cross-STA-communication among Objects these Interface Infos for the
Proxy/Stub-pairs have to be additionally available in the registry (because that's where
the marshaller-APIs will search for them).
There's no easy way around it (with regards to "Setup-free deployment") - other than
giving the information to the potential User (either in a ReadMe - or in a small Popup-
Info-Message which is shown on first start) - that the first run of the executable
has to be done in elevated mode (per "Run as Administrator").
On subsequent runs this won't be necessary anymore - but it's still a nuisance
(that's why I do my Thread-stuff not in Public ActiveX-Exe-Classes, but in Public
Dll-Classes, since that approach allows "regfree instantiation on STAs").
Re: Can ActiveX EXE be Reg-Free or run without Administrator rights?
Originally Posted by Schmidt
that's why I do my Thread-stuff not in Public ActiveX-Exe-Classes, but in Public
Dll-Classes, since that approach allows "regfree instantiation on STAs"
Can you give me some example of that approach which is like my project above, if one could be ever made to be simple like that? Also, thanks for the detailed explanation, I fully understand it now!
Last edited by MikiSoft; Apr 8th, 2015 at 06:48 AM.
Re: Can ActiveX EXE be Reg-Free or run without Administrator rights?
Originally Posted by MikiSoft
Can you give me some example of that approach which is like my project above, if one could be ever made to be simple like that?
The "simplicity" will depend a bit on, whether you want to show Forms from within your Threads -
or if these Threads are meant as "invisible workers" only...
Though since you opened yourself up to the idea, to use ActiveX-Dlls
(relinquishing the "everything in one Exe"-approach), you will now
have to consider your regfree shipping with an additional \Bin\-Folder
in your Zip (instead of a zipped "single Exe").
Basically you now have 3 options (3 convenience-levels, if you want) - though
each requires at least the ActiveX-Dll which contains your Thread-Class.
1) - Anatolis (The Trick's) Dll-threading approach without any additional helpers:
\MyStd.exe
\..\Bin\MyActiveXThread.dll
2) - DirectCOM.dll based (DirectCOM.dll adding ~20kB to your Zip-Archives size)
\MyStd.exe
\..\Bin\MyActiveXThread.dll
\..\Bin\DirectCOM.dll
3) - vbRichClient5 based (RC5-BaseDlls adding ~2.3MB to your Zip-Archives size)
\MyStd.exe
\..\Bin\MyActiveXThread.dll
\..\Bin\DirectCOM.dll
\..\Bin\vbRichClient5.dll
\..\Bin\vb_cairo_sqlite.dll
I've ordered them already according to their comfort-level with regards to
the threading-implementation - though each of the above is able to cover
regfree deployment as a "portable App".
Before I write an example, I'd like to know which of the above modes you'd
prefer (e.g. if you can live with the RC5-dependency, since that would require
the least efforts on my end).
Re: Can ActiveX EXE be Reg-Free or run without Administrator rights?
I would choose the first approach since it's the most comfortable way, and if forms can't be included then they are not needed (the reason I have included them into my project is for better understanding and for showing how it works live). Thanks in advance!
Last edited by MikiSoft; Apr 8th, 2015 at 07:35 AM.
Re: Can ActiveX EXE be Reg-Free or run without Administrator rights?
Originally Posted by MikiSoft
I would choose the first approach since it's the most comfortable way,...
Ehm, to be more clear about that - when I mentioned that I ordered them according to their
comfort-level, I meant "increased comfort (coding-wise), the further you go down the list".
Re: Can ActiveX EXE be Reg-Free or run without Administrator rights?
The first method with ActiveX DLL seems to be easier but I don't understand how to implement that into my project, it's a sensitive job since it's working directly with CLSIDs and COM API calls. But it will definitely be the best solution for this problem (if it really doesn't need to be run with Administrator privileges).
Last edited by MikiSoft; Apr 8th, 2015 at 09:42 AM.
C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter
There's just no reason to use garbage like InputBox. - jmcilhinney
The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber
Re: Can ActiveX EXE be Reg-Free or run without Administrator rights?
Sorry for my bad English.
Originally Posted by MikiSoft
The first method with ActiveX DLL seems to be easier but I don't understand how to implement that into my project, it's a sensitive job since it's working directly with CLSIDs and COM API calls.
You can use the CreateObject. I wrote there.
Originally Posted by MikiSoft
Umm no? Why on the Earth would be a second method easier, since it works with native DLL which can't be made using VB6 without some hacking around...
I created the opportunity multithreading in a standard EXE. This is almost a complete analog CreateThread and _beginthread. Why not use it?
Re: Can ActiveX EXE be Reg-Free or run without Administrator rights?
Originally Posted by The trick
You can use the CreateObject. I wrote there.
Can you make a minimalist example using that command? I have now attached my project, splitted into Standard EXE and ActiveX DLL, so you can work with that.
Last edited by MikiSoft; Apr 10th, 2015 at 04:18 AM.
Re: Can ActiveX EXE be Reg-Free or run without Administrator rights?
Hello. As I promised (slightly delayed). The first example - using multi-threading ActiveX DLL.
Create a new project ActiveX DLL. Create a new class Thread.cls.
Code:
Public Event ThreadFinished()
Public Property Get ThreadID() As Long
ThreadID = App.ThreadID
End Property
Public Function GetValue() As Long
GetValue = frmThread.GetValue()
End Function
Sub ShowFormWithCalculation(Key As String)
frmThread.Caption = Key
frmThread.Show vbModal
RaiseEvent ThreadFinished
End Sub
The class has an event ThreadFinished, which is generated when the calculation is finished. GetValue method gets the current value of the counter. Method ShowFormWithCalculation starts calculation. Form code frmThread.frm
Code:
Dim abort As Boolean
Dim value As Long
' // Get calculated value
Public Property Get GetValue() As Long
GetValue = value
End Property
' // Long-long process
Public Sub RunCycle()
Do Until abort Or value = 150000
lblNum.Caption = Format(value, "000000")
lblNum.Refresh
value = value + 1
If (value And &HFF) = 0 Then DoEvents
Loop
Unload Me
End Sub
Private Sub cmdRun_Click()
RunCycle
End Sub
Private Sub Form_Unload(Cancel As Integer)
abort = True
End Sub
Ok. You're asked to do, like the above code. It would be easier not to use the form.
Consider a client application. Standart module modMain.bas:
Option Explicit
Dim col As Collection
' // Asynchronous event from another threads
Public Function NewEvent(obj As prjSMT.Thread)
Dim n As Long
MsgBox "Calculation finished " & obj.ThreadID
' Remove from listbox
For n = 0 To lstThreads.ListCount - 1
If lstThreads.List(n) = obj.ThreadID Then lstThreads.RemoveItem (n): Exit For
Next
End Function
Private Sub cmdCreateThread_Click()
Dim param As ThreadData
Dim iid As UUID
Dim hThread As Long
Dim obj As IUnknown
Dim handler As clsEventHandler
Dim tid As Long
' Warning! This key should pass as copy. This example is incorrect.
param.key = InputBox("Input window name")
' Create event
param.hEvent = CreateEvent(ByVal 0&, 1, 0, 0)
' Create new thread
hThread = CreateThread(ByVal 0&, 0, AddressOf ThreadProc, ByVal VarPtr(param), 0, tid)
If hThread = 0 Then
MsgBox "Error creating thread", vbCritical
Exit Sub
End If
CloseHandle hThread
' Wait object initialization
WaitForSingleObject param.hEvent, INFINITE
' Remove event
CloseHandle param.hEvent
' If success
If param.IStream Then
IIDFromString StrPtr(IID_IUnknown), iid
' Get marshalled interface
CoGetInterfaceAndReleaseStream param.IStream, iid, obj
' Add to threads collections
Set handler = New clsEventHandler
Set handler.Producer = obj
Set handler.receiver = Me
col.Add handler, CStr(tid)
' Add to listbox
lstThreads.AddItem CStr(tid)
Else
MsgBox "Error creating object", vbCritical
End
End If
End Sub
' // Get current value from selected thread
Private Sub cmdGet_Click()
Dim obj As prjSMT.Thread
Dim key As String
If lstThreads.ListIndex < 0 Then Exit Sub
Set obj = col(lstThreads.List(lstThreads.ListIndex)).Producer
MsgBox obj.GetValue()
End Sub
Private Sub Form_Load()
' Patching runtime
RemoveLastDllError
Set col = New Collection
End Sub
And the helper class - clsEventHandler.cls, which allows you to handle the event object Thread:
Code:
' // Class for receiving and processing event from a prjSMT.Thread object
Option Explicit
Public receiver As frmMain
Dim WithEvents obj As prjSMT.Thread
Public Property Set Producer(newObj As prjSMT.Thread)
Set obj = newObj
End Property
Public Property Get Producer() As prjSMT.Thread
Set Producer = obj
End Property
Private Sub obj_ThreadFinished()
receiver.NewEvent obj
End Sub
Compile the both code as Native-code. Later, I'll do an example using only one project StandartEXE, ie no external dependencies. www.youtube.com/watch?v=8TbdSiZRxm8
Last edited by The trick; Apr 14th, 2015 at 02:10 PM.
C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter
There's just no reason to use garbage like InputBox. - jmcilhinney
The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber
Re: Can ActiveX EXE be Reg-Free or run without Administrator rights?
Use module modMultiThreading.bas as is. Just insert it into your project and do not think about how it works, take it as a given. Also, do not forget to add a reference type library.
Using the function vbCreateThread can create threads where the runtime is already initialized (what used to be impossible). Thread function:
Code:
Public Type ThreadData
key As String
evt As Long
frm As frmThread
End Type
Public Sub ThreadProc(dat As ThreadData)
Dim frm As frmThread
' Create new instance of frmThread.frm
Set frm = New frmThread
' Set output parameter
Set dat.frm = frm
' Set caption
frm.Caption = dat.key
' Success
SetEvent dat.evt
' Show form
frm.Show vbModal
End Sub
Called by:
Code:
Private Sub cmdCreateThread_Click()
Dim param As ThreadData
Dim hThread As Long
Dim handler As clsEventHandler
Dim tid As Long
' Warning! This key should pass as copy. This example is incorrect.
param.key = InputBox("Input window name")
' Create event
param.evt = CreateEvent(ByVal 0&, 1, 0, 0)
' Create new thread
hThread = vbCreateThread(ByVal 0&, 0, AddressOf ThreadProc, ByVal VarPtr(param), 0, tid)
If hThread = 0 Then
MsgBox "Error creating thread", vbCritical
Exit Sub
End If
CloseHandle hThread
' Wait object initialization
WaitForSingleObject param.evt, -1
' Remove event
CloseHandle param.evt
End Sub
Bear in mind that in the example there is no synchronization. This can cause problems. Therefore, before using multithreading need to become familiar with it. To us falls into the hands of this wonderful tool as multithreading, but also on us great responsibility (such as in C ++). To many shared objects must be organized to synchronize access. You can use mutexes, critical sections, semaphores, etc. Also in this example, no marshaling, so each object lives in the thread in which it was created. Thus, events are generated in the other thread (as opposed to the 1st example). You can verify this by looking App.ThreadID value inside the event handler NewEvent.