-
[ADVANCED] Multi-threaded VB6
Yes, it does exist. VB6 supports native multi-threading (proof)
I am trying to communicate between threads, but if thread A is busy and thread B tries to update thread A, thread B gets locked up until thread A finishes. I've tried using timers, custom events, nothing allows me to change memory on another thread without yielding (DoEvents).
Does anyone know how to do this?
The only thing I can think of is making a third thread that relays messages between thread A and B and doesn't do any processing to lock up thread A or B. However there should be some way to serialize/queue requests to other threads and continue. VB is doing this automatically when threads are busy.
Thanks,
PsuFan
-
Re: [ADVANCED] Multi-threaded VB6
LOL... and you call that "native" support?
EDIT: btw: I'm surprised that you didn't think of the obvious solution to try: Global variables.....
-
Re: [ADVANCED] Multi-threaded VB6
Well I guess it could be a little more native. I wish you could use global variables, however global variables are NOT common between threads. VB calls Sub Main again so that you can reload global variables, they do not use the same memory locations. They do this for performance due to cross thread marshaling.
-
Re: [ADVANCED] Multi-threaded VB6
OK. That article describes also, that you can pass the MainApp-Object (for the MainThread) as a Reference to any new ChildThread.
Why not just use a Public Property of MainApp?
MainApp has a Public Property called "CrossThread As Long" (Or whatever you have to pass between Threads)
ThreadA gets created/started with a reference to MainApp
ThreadB gets created/started with a reference to MainApp
ThreadA continously reads the Property of MainApp ("If MainApp.CrossThread=1 Then DoSomething")
ThreadB finishes first and wants to inform ThreadA.
Code:
'In ThreadB
MainApp.CrossThread=2
Of course untested, since i've never done anything like that in VB6
-
Re: [ADVANCED] Multi-threaded VB6
Found something
Quote:
*Note* Objects in different apartments can only communicate with each other if a client passes them references to each other. In this case, cross-thread marshaling is used to provide synchronization. Cross-thread marshaling is almost as slow as cross-process marshaling.
https://docs.microsoft.com/en-us/pre...361(v%3dvs.60)
There you have it.
Since every Thread runs in its own Apartment, you have to provide a Reference to each other.
I'd go the way to pass the reference to ThreadB (If ThreadB has to notify ThreadA) to your ThreadA, with ThreadA continously reading that Property of ThreadB.
It's kind of the same as with Parent- and child-forms: ThreadA would be the Parent, ThreadB would be the Child. ThreadB provides the Information, with ThreadA collecting the Information and then acting accordingly.
-
Re: [ADVANCED] Multi-threaded VB6
Quote:
Originally Posted by
PsuFan
I am trying to communicate between threads, but if thread A is busy and thread B tries to update thread A, thread B gets locked up until thread A finishes. I've tried using timers, custom events, nothing allows me to change memory on another thread without yielding (DoEvents).
What do you mean by "update thread A"? Notify it? Which one is the main thread? Also, is this AxEXE used by another Standard EXE, or does it work alone as a multi-threaded standalone that is showing a GUI?
I am not sure if you are using WaitForSingleObject(), but it's very common to use INFINITE, which could cause a deadlock; while 0 just checks the event and returns immediately.
-
Re: [ADVANCED] Multi-threaded VB6
Quote:
Originally Posted by
qvb6
Which one is the main thread?
Exactly what i was thinking.
From Pascal i'm used to having a MainThread which checks a Sync-Event in an infinite loop, and the ChildThreads can use that mechanism to pass on whatever information they want to pass to the MainThread, which itself can notify whichever ChildThread needs to be notified.
-
Re: [ADVANCED] Multi-threaded VB6
The link that the OP posted shows a method of finding the main thread, but I was asking whether Thread A or B is the main thread. I too haven't experimented much with multithreading, especially communication between threads.
-
Re: [ADVANCED] Multi-threaded VB6
As far as i understood the Link of the OP: It shows searching for the MainApp, for it to be invoked only once (because of the Form it uses).
But following the MS-Logic: Even the MainThread would be in its own Apartment? (<-- That's a Question!)
-
Re: [ADVANCED] Multi-threaded VB6
On the first run, the form caption is changed to include the Process ID, and so other threads find this form by using the API function FindWindow(), and so would know they are not the main thread, and act accordingly.
-
Re: [ADVANCED] Multi-threaded VB6
If you use alternative forms of threading then global variables are visible to all threads however this requires you to manage all your synchronization. When you say "communicate" what are you trying to pass around?
-
Re: [ADVANCED] Multi-threaded VB6
Just find a solid free-threaded object to implement a non-blocking queue. Free-threaded means both (or serveral) threads can call functions w/o any COM marshalling. There will be blocking (it's inevitable) but it will be internally implemented by the object, not by COM marshalling proxies.
Here is a tip: ADODB.Recordset is such a free-threaded coclass.
cheers,
</wqw>
-
Re: [ADVANCED] Multi-threaded VB6
@ Zvoni, I have referenced the objects. Problem is as soon as one accesses the other, it deadlocks until the thread accessed is finished. Yes, the main thread has its own apartment, and the FindWindow code is so that two forms don't get loaded. VB actually calls sub main twice when multi-threading, and only once in IDE/non-multi...
@ qvb6, update memory, call procedure, anything to update the thread from the other. Thread A is the main thread, though it doesn't seem to make a difference if I purposely lock thread A and try to update memory with B or B and update it with A. EXE is not used by another EXE. I am NOT using WaitForSingleObject(), never heard of it.
@ vbwins / wqweto, why is it not subject to marshaling? But yes, that is what I am looking for. Can I do that without storing in a database? I need a non-blocking queue to pass events. The main thread has my GUI and the second has a web server that could be busy downloading pictures/video. The web server has to pass limited data to the GUI to change screens / info. True the GUI shouldn't be that busy that it deadlocks the web server while its trying to update, it just seems like the best way to write it is with a non-blocking queue. MS documentation says that raisevent should be non-blocking if picked up by another thread, but it doesn't seem to work for me.
-
Re: [ADVANCED] Multi-threaded VB6
Please, notice to this module. There is AsynchDispMethodCall function which uses callback mechanism to notify the main thread about the call-completion.
The main thread posts the special message to child thread and returns immediately. When the child thread has processed the call it sends notify to the main thread.
You can create a thread with this asynchronous call dispatcher which delivers the messages to thread and returns immediately.
-
Re: [ADVANCED] Multi-threaded VB6
Run-time error '453':
Can't find DLL entry point IStream_Reset in Shlwapi
-
Re: [ADVANCED] Multi-threaded VB6
Quote:
Can't find DLL entry point IStream_Reset in Shlwapi
What's the OS?
-
Re: [ADVANCED] Multi-threaded VB6
Quote:
Originally Posted by
the trick
what's the os?
xp sp3
-
Re: [ADVANCED] Multi-threaded VB6
Quote:
Originally Posted by
the trick
what's the os?
xp sp3
-
Re: [ADVANCED] Multi-threaded VB6
Quote:
Originally Posted by
PsuFan
@ vbwins / wqweto, why is it not subject to marshaling? But yes, that is what I am looking for. Can I do that without storing in a database? I need a non-blocking queue to pass events. The main thread has my GUI and the second has a web server that could be busy downloading pictures/video. The web server has to pass limited data to the GUI to change screens / info. True the GUI shouldn't be that busy that it deadlocks the web server while its trying to update, it just seems like the best way to write it is with a non-blocking queue. MS documentation says that raisevent should be non-blocking if picked up by another thread, but it doesn't seem to work for me.
Free-threaded means methods can be called from arbitrary appartment simultaneously. ADO coclasses are marked Both threaded AFAIK which is the same for your use-case.
There are disconnected client-side ADODB.Recordsets that are something like an array of UDTs w/ named fields. These are completely disconnected from any DB.
In your GUI thread just create a client-side recordset w/ Set m_rsQueue = New ADODB.Recordset : m_rsQueue.Fields.Append ... : m_rsQueue.Open and pass it along w/ a post-back hWnd to your worker thread (somehow). Your worker thread just push data to queue w/ rs.AddNew ... and PostMessage hWndNotify, WM_CHAR, ... to notify GUI there are pending notifications.
In your GUI thread on your txtNotify_KeyPressed check m_rsQueue content and remove records as these are being processed.
cheers,
</wqw>
p.s. When you see a reply with multiple "just"s, this must be a red flag for you that probably the implementation is not going to be "just" that simple :-))
-
Re: [ADVANCED] Multi-threaded VB6
It's also perfectly possible in (VB6-)STA-threading, to "break out of TLS restrictions and COM-marshalling" -
then working against a shared Memory-area (by passing the pointer to an allocation, which is managed by the main-thread).
The easiest way to read-from/write-to such a shared Mem-area is via an UDT-definition,
which in turn is accessed via a SafeArray.
The Main-Thread would allocate (and later destroy) this UDT-SafeArray and work against it "normally" (non-virtual) -
whereas the secondary threads could each span their own virtual safe-array across this pre-allocated area via the usual safearray-pointer-mapping-techniques.
Note, as soon as you use such an approach, the usual rules with ensuring concurrent access to that mem-area will apply
(as in other, freethreaded languages).
Therefore - a separated "Data-Holder-only-ClassInstance" (which will not block "long", because it only does InMemory-saving/reading)
is usually easier to implement (and share) within the confines of VB6-STAs.
Olaf
-
Re: [ADVANCED] Multi-threaded VB6
Quote:
Originally Posted by
BD48
xp sp3
Try to replace
Code:
Private Declare Function IStream_Reset Lib "Shlwapi" ( _
ByVal pstm As Any) As Long
to
Code:
Private Declare Function IStream_Reset Lib "Shlwapi" Alias "#213" ( _
ByVal pstm As Any) As Long
-
Re: [ADVANCED] Multi-threaded VB6
Quote:
Originally Posted by
The trick
Try to replace
Code:
Private Declare Function IStream_Reset Lib "Shlwapi" ( _
ByVal pstm As Any) As Long
to
Code:
Private Declare Function IStream_Reset Lib "Shlwapi" Alias "#213" ( _
ByVal pstm As Any) As Long
Works Perfectly, Thank you.
-
Re: [ADVANCED] Multi-threaded VB6
How do you use safe array in VB6? I do already want to pass mainly UDTs. Otherwise I will look into the recordset solution.
Edit: I was considering a data only class but it seemed overkill and wont it need its own thread?
-
Re: [ADVANCED] Multi-threaded VB6
Quote:
Originally Posted by
PsuFan
How do you use safe array in VB6? I do already want to pass mainly UDTs. Otherwise I will look into the recordset solution.
I usually handle this with a few helpers in a *.bas-Module... (e.g. named modSharedArrays.bas)
I'd recommend, to also define the UDTs Publically within that *.bas (those which are shared via the Array) -
and then reference that very same Code-Module also in your Thread-Dll-project (so that changes on the UDT-Members are reflected there).
Here an example, what this *.bas might contain...
Code:
Option Explicit
Declare Function ArrPtr Lib "msvbvm60" Alias "VarPtr" (P() As Any) As Long
Declare Sub BindArray Lib "kernel32" Alias "RtlMoveMemory" (PArr() As Any, ByVal pSrc&, Optional ByVal CB& = 4)
Declare Sub ReleaseArray Lib "kernel32" Alias "RtlMoveMemory" (PArr() As Any, Optional pSrc& = 0, Optional ByVal CB& = 4)
Public Type tMyUDT
SomeLong As Long
SomeString As String
End Type
Now, in your Main-Thread (here simulated by a Form):
Code:
Option Explicit
Private SharedArrMain() As tMyUDT
Private Sub Form_Load()
'redim and allocate this only once - before you start "sharing it in thread-classes"...
ReDim SharedArrMain(0 To 1)
'...then leave the above allocation alone, for the lifetime of the threads
End Sub
Private Sub Form_Click()
Dim oThread As New cThread
oThread.InitSharedMemArea ArrPtr(SharedArrMain) 'pass the ArrPtr of the main-thread-allocation
oThread.Test 'this method writes into the shared-memory using the class-internal (virtual) Array
With SharedArrMain(0) 'we are back - and check the main-allocation to see what we got from the virtual-array-test above
Caption = .SomeLong & " " & .SomeString
End With
End Sub
Now, a Thread-Object (here simulated via a little Class, named cThread) could contain:
Code:
Option Explicit
Private SharedArr() As tMyUDT
Public Sub InitSharedMemArea(pSharedArr As Long)
If pSharedArr Then BindArray SharedArr, pSharedArr
End Sub
Public Sub Test()
Debug.Print "Ubound-of-SharedArr: "; UBound(SharedArr)
With SharedArr(0)
.SomeLong = .SomeLong + 1
.SomeString = .SomeString & Chr$(64 + .SomeLong)
End With
End Sub
Private Sub Class_Terminate() 'cleanup of the virtual ArrBinding
ReleaseArray SharedArr
End Sub
Note, that I left out the wrapping of Member-access via CriticalSections...
Whether you'll need those - depends on the type of UDT-members - but also on your concrete threading-scenario
(e.g. in larger RingBuffer-scenarios, or when you assign each thread its own Shared-Array-Index, you could get away without them..)
Quote:
Originally Posted by
PsuFan
Edit: I was considering a data only class but it seemed overkill and wont it need its own thread?
Such a "shared-data-class" would run on it's own thread - that's right (it needs to, to be reachable from "everywhere" in a non-blocking-manner) -
but it would only occupy just that one thread (and then acting as a singleton).
Note, that the example I've posted for the shared-array-stuff will work only, when the VB6-threading-approach runs entirely InProcess.
If you use AX-Exe-Threading in "Multi-Process-Mode" - then:
- the approach would need to be changed to FileMapping-allocations
- or you could use the just described Data-Only-Singleton-ClassInstance (which was instantiated on its own thread).
HTH
Olaf
-
Re: [ADVANCED] Multi-threaded VB6
I was running Ax-EXE threading. How do I know if I am multi-process-mode?
Why does the data module need its own thread? If the two threads have access to the same memory locations, cant they just use them without blocking/deadlocking?
Thanks for the help
-
1 Attachment(s)
Re: [ADVANCED] Multi-threaded VB6
Quote:
Originally Posted by
PsuFan
I was running Ax-EXE threading. How do I know if I am multi-process-mode?
If you create AX-Exe-Class-instances via the New Keyword (e.g. from a Std-Exe) -
then those (Thread-)Class-Instances run "Out-Of-Process" (of your Std-Exe).
The approach you linked to in your first post - is InProcess-AxExe-Threading,
because the Demo is running "from inside an AX-Exe in GUI-Mode" +
it starts new Ax-Exe-Class-Instances internally via CreateObject(AxExe.ProgId)
The Demo is a bit "clumsy" though - especially the FindWindow-fiddling in Sub Main
is completely unnecessary, because a simple test like below is enough:
Code:
Sub Main()
If App.TaskVisible Then fMain.Show 'follow-up Sub Main-entries will have App.TaskVisible = False
End Sub
Quote:
Originally Posted by
PsuFan
Why does the data module need its own thread?
First let's make it more clear, what "DataModule" we're talking about.
The little Demo in my prior posting is not "Shared-DataClass-based"
(but relies on SafeArray-Ptr-Passing from a normal VB-Array-allocation which was generated in the Main-Thread).
So this is the faster shared-data-approach, which avoids COM-marshalling (since it is not "AxExe-DataClass-based") -
but you will need to protect your Shared Array-Data "by hand" (via Critical Sections) - so the speed-advantage comes at a cost.
The other approach which was mentioned is the easier one - because concurrent access is "fully managed" under the covers.
This is the "AxExe-DataClass-based" approach, where COM handles the access for you.
And the Singleton-like DataClass-instance should neither be instantiated on the Main-Thread (the GUI-thread) -
nor should it be instantiated on one of the Worker-threads - because any of these threads might "enter a longer processing-routine"
(and whilst being "in that routine" the DataClass-instance on such a thread would not be reachable from other threads).
Note that I wrote instantiated on in the last paragraph - which is different from "passed into" or also "used within" (a thread).
Ok - long story short - the Singleton-AxExe-DataClass should be instantiated on its own thread (via CreateObject),
to guarantee a thread which will never "enter any longer processing-routines" (because this Class does not contain any processing-routines).
Quote:
Originally Posted by
PsuFan
If the two threads have access to the same memory locations, cant they just use them without blocking/deadlocking?
As said - in case of:
- the SafeArray-Ptr-Passing-approach, there is no blocking (other than your own CriticalSection-handling)
- in case of the Singleton-AxExe-DataClass-instance, there will also be no blocking (when it runs on its own STA)
Here is an example for the latter (simpler) approach in an "InProcess-AxExe-Project":
Attachment 168325
HTH
Olaf
-
Re: [ADVANCED] Multi-threaded VB6
Thanks for all the help. Sorry for all the questions, some of this stuff is barely English to me lol. So you're recommending the threaded data class? How (well) does a three threaded app run on a dual or single threaded CPU? I may introduce a fourth thread for syncing online media as well... I didn't get to checkout your example but I will. I thought the Ms example was crazy too, I already eliminated the hidden form but didn't know about app.taskvisible! Are you sure it works multi-thread? I read in my link above that App is not shared between threads thus app.threadid gives unique numbers. I will have to test this.
-
Re: [ADVANCED] Multi-threaded VB6
Interesting, I didn't think to use sub main to activate form instead of class... How many threads does this use, 4?
Code:
Private Sub Form_Activate() 'use this instead of Form_Load, in case you want to start threads at Form-Startup-time
Set SharedData = CreateObject("AxExeInProcess.cSharedData") 'the SharedData-Singleton needs its own thread, to work non-blocking
Set Worker1 = CreateObject("AxExeInProcess.cWorker").Init("Worker1", SharedData)
Set Worker2 = CreateObject("AxExeInProcess.cWorker").Init("Worker2", SharedData)
End Sub
your app.taskvisible works perfectly!
-
Re: [ADVANCED] Multi-threaded VB6
Quote:
Originally Posted by
PsuFan
your app.taskvisible works perfectly!
Unfortunately you cannot drop AxExe's in users Documents folder i.e. make it portable (and manifests won't help) -- it still needs COM registration with a setup etc. If you don't register it and start it directly, it will automagically self-register successfully if you are an admin. If you are not an admin and the COM registration is not present registry it will bomb with obscure error from VB6 run-time that cannot be trapped.
Clearly the AxExe's self-registration feature was conceived in Win9x era.
cheers,
</wqw>
-
Re: [ADVANCED] Multi-threaded VB6
I was wondering why it didn't start on my target computer... Thanks
-
Re: [ADVANCED] Multi-threaded VB6
so local support need some hex fix in code, but because that code is not created by me, so i can not place it here.
you can see it here, but it is Chinese url, and also the code. maybe quit difficult for u to understand
-
Re: [ADVANCED] Multi-threaded VB6
Quote:
Originally Posted by
wqweto
Unfortunately you cannot drop AxExe's in users Documents folder i.e. make it portable (and manifests won't help) -- it still needs COM registration with a setup etc. If you don't register it and start it directly, it will automagically self-register successfully if you are an admin. If you are not an admin and the COM registration is not present registry it will bomb with obscure error from VB6 run-time that cannot be trapped.
Clearly the AxExe's self-registration feature was conceived in Win9x era.
cheers,
</wqw>
Per COM rules, HKEY_CURRENT_USER(HKCU) is checked first for registration before checking HKLM(This is documented here, and also in MSDN October 2001, search for title "Classes and Servers"), so it's possible to make an ActiveX project "portable" without manifest, unless Microsoft broke things.
Also, I know that AxDLL's don't auto-register, but OCX do auto register, and one could create the necessary HKCU entries in Sub Main() before loading a form containing the EXE, or instantiating the AxDLL.
As for AxEXE, I am not sure, but I think that it doesn't auto-register, you have to use the command line option /regserver, or create the necessary HKCU entries in Sub Main(). So, I think only OCX files do auto-registration.
-
Re: [ADVANCED] Multi-threaded VB6
Quote:
Originally Posted by
qvb6
As for AxEXE, I am not sure, but I think that it doesn't auto-register, you have to use the command line option /regserver, or create the necessary HKCU entries in Sub Main(). So, I think only OCX files do auto-registration.
An AxEXE does auto-register w/o any option. The difference is that when you pass the option it does *not* start the start-up form/sub main.
HKCU entries of course work so a COM client can instantiate our out-of-proc coslasses/servers from out AxEXE but still the VB6 run-time insist on registering in HKLM. If it doesn't find the entries in HKLM it does not check HKCU and skip writing in HKLM. It's attempt on startup strighforward fails under non-priviledged users.
cheers,
</wqw>
-
Re: [ADVANCED] Multi-threaded VB6
Anyone know why when I RaiseEvents to other threads, it still happens synchronous?
https://docs.microsoft.com/en-us/pre...370(v%3dvs.60)
-
Re: [ADVANCED] Multi-threaded VB6
Only events? Did you manage to call methods/properties asynchronously? That would be a first :-))
Usually for async when you call a method on a worker thread it starts a fire-once timer and returns immediately, then the processing begins on the timer tick and a notification is posted when complete (various options for this).
cheers,
</wqw>
-
Re: [ADVANCED] Multi-threaded VB6
I don't think that will work non-blocking either though. If I purposely block the main thread, and try and call a function from a worker thread to the main thread that does nothing, it blocks the worker thread. With events or timers...
I have been building with Schmid's method but the documentation says you should be able to do it with events which would be so much easier/better.
-
Re: [ADVANCED] Multi-threaded VB6
PostMessage does not block if main thread is busy. Another option is main thread to poll on one or several hEvents which get signaled from worker threads. The latter is more scalable but the first one is easier to implement.
-
Re: [ADVANCED] Multi-threaded VB6
Cool, can I send custom messages? How do you receive them on main thread? That would eliminate timers checking for updates but would likely need to use your recordset solution or the extra thread shared memory to pass lots of data like UDTs.
-
Re: [ADVANCED] Multi-threaded VB6
Not sure if VB internally uses SendMessage, but from SendMessage Remarks section: "If the specified window was created by a different thread, the system switches to that thread and calls the appropriate window procedure. Messages sent between threads are processed only when the receiving thread executes message retrieval code. The sending thread is blocked until the receiving thread processes the message. However, the sending thread will process incoming nonqueued messages while waiting for its message to be processed. To prevent this, use SendMessageTimeout with SMTO_BLOCK set. For more information on nonqueued messages, see Nonqueued Messages."
PostMessage doesn't switch threads.
-
Re: [ADVANCED] Multi-threaded VB6
Quote:
Originally Posted by
PsuFan
Cool, can I send custom messages? How do you receive them on main thread? That would eliminate timers checking for updates but would likely need to use your recordset solution or the extra thread shared memory to pass lots of data like UDTs.
Just create a window (HWND_MESSAGE) and process the messages in its window proc.
Brief review of concept:
Code:
' // Class CYourClass
Dim tClass As WNDCLASSEX
' // Register class
tClass.cbSize = Len(tClass)
tClass.hInstance = App.hInstance
tClass.lpfnwndproc = FAR_PROC(AddressOf AsyncWndProc)
tClass.lpszClassName = StrPtr(YOUR_WINDOW_CLASS_NAME)
tClass.cbWndExtra2 = 4 ' // Reference to class instance
RegisterClassEx tClass
. . .
' // Create window
m_hWndAsync = CreateWindowEx(0, StrPtr(YOUR_WINDOW_CLASS_NAME), 0, 0, 0, 0, 0, 0, _
HWND_MESSAGE, 0, App.hInstance, ByVal 0&)
' // Set reference to class instance
SetWindowLong m_hWndAsync, 0, ObjPtr(Me)
. . .
' // Your thread func
' // Post to main thread and return immediatelly
PostMessage m_hWndAsync, WM_ID_ASYNCH_METHOD, ..., ...
. . .
' // Window proc of async window in main thread
Public Function AsyncWndProc( _
ByVal hwnd As Long, _
ByVal uMsg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) As Long
Dim cObj As CYourClass
' // Get object from window bytes
vbaObjSetAddref cObj, ByVal GetWindowLong(hwnd, 0)
Select Case uMsg
Case WM_ID_ASYNCH_METHOD
cObj.Method wParam, ....
Case Else
AsyncWndProc = DefWindowProc(hwnd, uMsg, wParam, lParam)
End Select
End Function
Alternatively you could use APC queue but main thread should be in the alertable state.
-
Re: [ADVANCED] Multi-threaded VB6
@qvb6: SendMessage *has* to switch threads as its synchronous by definition. PostMessage just appends to hWnd's message queue and returns (no thread switching). Subsequently the OS takes care this message queue to get "emptied" on the "correct" thread by the "correct" message pump -- remember that each thread has to have a separate msg pump, right?
cheers,
</wqw>
-
Re: [ADVANCED] Multi-threaded VB6
Looks promising, do you have the dim for WM_ID_ASYNCH_METHOD, cant find it anywhere...
-
Re: [ADVANCED] Multi-threaded VB6
What do we think about passing the main thread form hWnd to the worker thread and using SetTimer for async notification? LOL
It appears to be entering on the main ThreadID
PostMessage looks better if I can figure out how to receive the event.
-
Re: [ADVANCED] Multi-threaded VB6
Quote:
Originally Posted by
PsuFan
PostMessage looks better if I can figure out how to receive the event.
DoEvents.
-
Re: [ADVANCED] Multi-threaded VB6
Looks like you have to sub-class form in order to receive Send/PostMessage events. Should work well in combination with shared memory thread / recordset / shared memory address.
Code:
Private Sub Form_Load()
lngOldWindowProc = SetWindowLong(Me.hwnd, GWL_WNDPROC, AddressOf NewWindowProc)
End Sub
Private Sub Form_Unload(Cancel As Integer)
SetWindowLong Me.hwnd, GWL_WNDPROC, lngOldWindowProc
End Sub
' IN MODULE
Public Function NewWindowProc(ByVal hwnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Select Case Msg
Case USR_MESSAGE1
'Debug.Print wParam
NewWindowProc = 0
Case Else
NewWindowProc = CallWindowProc(lngOldWindowProc, hwnd, Msg, wParam, lParam)
End Select
End Function
-
Re: [ADVANCED] Multi-threaded VB6
I'm going to try and make a single post to this thread. I've been watching it, and I've also gone down the road of multi-threading-for-VB6 a couple of times in the past. I've come up with what I thought were good approaches to multi-threading, but I've never found anything that I'd feel good enough about to put into production.
Also, along the way, I learned that there's the hierarchy of process, thread, & fiber. I suppose you could even work more into that hierarchy. You could certainly put core above process.
However, because of all the marshaling issues, I decided that if/when I need something like this, I'm just going to write an entirely new VB6 application (a new process). This accomplishes a few things. First, it puts me back into the possibility of being portable. Second, it brings me back to ground on which I feel extremely solid. Third, I can still do inter-process memory copying similar to marshaled-shared memory (but not exactly because memory is copied and not shared).
If I don't want a user to execute this second process, I can just put some command line flag on it. And, I'll just do all my inter-process communications with SendMessageTimeout and WM_COPYDATA, and a subclassed window in the receiving process. I've got it all setup, and there's no problem going both directions. If someone wants, I'll post my subclassing and send message code.
Y'all Take Care,
Elroy
-
Re: [ADVANCED] Multi-threaded VB6
Elroy,
That's exactly what I WAS doing. I had a GUI ActiveX application and a Service application that did backend work. It relays information via Referenced COM. But it became a pain to debug because I have to compile the GUI so that the service can interact via COM. Once I found out that you can do multi-thread in VB6, I figured eliminating the other process would tidy up a lot. There's a lot of send/receive code between processes for each function requiring data.
-
1 Attachment(s)
Re: [ADVANCED] Multi-threaded VB6
***, EVEN POSTMESSAGE IS BLOCKING MY WORKER THREAD.... :mad:
What the heck am I doing wrong, all the documentation says these things run async, I can never get anything to run async between threads.
-
Re: [ADVANCED] Multi-threaded VB6
Quote:
Originally Posted by
PsuFan
***, EVEN POSTMESSAGE IS BLOCKING MY WORKER THREAD.... :mad:
What the heck am I doing wrong, ...
You've only relabled a SendMessageA to PostMessage in your Declare:
Declare Function PostMessage Lib "user32" Alias "SendMessageA"
Also - in case you'll correct this later - take note that you should use PostMessage
only in a low frequency from your Threads - because otherwise (e.g. when used as Progress-Callback in a high-frequented inner-loop),
you'll risk MessageQueue-Flooding at the receiving end (the hWnd of the "other thread" which was perhaps busy for a second or two).
I guess I'll never understand, why Timer-based Polling-approaches (as in the example I've posted),
are always regarded that "unfavourably" in the community.
Timerbased Polling is one of the few approaches, which allow you a true (and rock-stable)
decoupling between "independent tasks" (when detecting "changed state" with nearly unmeasurable system-resource-usage).
Olaf
-
Re: [ADVANCED] Multi-threaded VB6
Quote:
Originally Posted by
PsuFan
Looks promising, do you have the dim for WM_ID_ASYNCH_METHOD, cant find it anywhere...
It's your own message like WM_USER + ...
You can see how AsynchDispMethodCall works. Also there is InternetStatusCallback example where you can find the following methods:
Code:
Private Sub PutHandleAsync( _
ByVal hwnd As Long, _
ByVal hUrl As Long)
PostMessage hwnd, WM_PUTHANDLE, hUrl, ByVal 0&
End Sub
Private Sub PutLogAsync( _
ByVal hwnd As Long, _
ByRef sText As String)
PostMessage hwnd, WM_PUTLOG, SysAllocString(ByVal StrPtr(sText)), ByVal 0&
End Sub
Private Sub OnStatusCompleteAsynch( _
ByVal hwnd As Long, _
ByVal lStatus As Long, _
ByVal lError As Long)
PostMessage hwnd, WM_ONCOMPLETE, lStatus, ByVal lError
End Sub
' // Window proc of async window in main thread
Public Function AsyncWndProc( _
ByVal hwnd As Long, _
ByVal uMsg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) As Long
Dim cObj As CAsynchDownloader
' // Get object from window bytes
vbaObjSetAddref cObj, ByVal GetWindowLong(hwnd, 0)
Select Case uMsg
Case WM_PUTLOG ' // Show log entry
Dim bstrText As String
' // Extract string
GetMem4 wParam, ByVal VarPtr(bstrText)
cObj.PutLog bstrText
Case WM_PUTHANDLE ' // Put request handle
cObj.RequestHandle = wParam
Case WM_ONCOMPLETE ' // INTERNET_STATUS_REQUEST_COMPLETE event
cObj.OnStatusComplete wParam, lParam
Case Else
AsyncWndProc = DefWindowProc(hwnd, uMsg, wParam, lParam)
End Select
End Function
-
Re: [ADVANCED] Multi-threaded VB6
Quote:
Originally Posted by
Schmidt
I guess I'll never understand, why Timer-based Polling-approaches (as in the example I've posted),
are always regarded that "unfavourably" in the community.
I have no objection against Timers, but PostMessage is almost instantaneous compared to Timers, and does the same job, but a Timer is easier to use, while PostMessage requires subclassing.
-
Re: [ADVANCED] Multi-threaded VB6
Quote:
Originally Posted by
PsuFan
***, EVEN POSTMESSAGE IS BLOCKING MY WORKER THREAD.... :mad:
What the heck am I doing wrong, all the documentation says these things run async, I can never get anything to run async between threads.
Besides what others suggested, I don't see "Option Explicit" in any of the source files. Also, if you want a custom message, use one in the range of WM_APP, not WM_USER(VB Runtime and the Common Controls use some of WM_USER messages), or better yet, a custom message ID returned by RegisterWindowMessage.
-
Re: [ADVANCED] Multi-threaded VB6
The timer message has the lowest priority than other messages.
Quote:
while PostMessage requires subclassing.
Not necessary.
Quote:
Originally Posted by
qvb6
Also, if you want a custom message, use one in the range of WM_APP, not WM_USER(VB Runtime and the Common Controls use some of WM_USER messages), or better yet, a custom message ID returned by RegisterWindowMessage.
It isn't required for your own private windows.
-
Re: [ADVANCED] Multi-threaded VB6
In other words what The trick means is that you can post WM_KEYDOWN and receive Form_KeyDown event for instance -- no custom msg registration, no subclassing. There are other such pairs possible too.
cheers,
</wqw>
-
Re: [ADVANCED] Multi-threaded VB6
Quote:
Originally Posted by
The trick
Not necessary.
I know that one could post a message like WM_LBUTTONDOWN so simulate a click to a hidden control, so no subclassing is needed.
Quote:
Originally Posted by
The trick
It isn't required for your own private windows.
Not sure what you mean, but for windows created from scratch, then WM_USER is okay to use, but anything else could use WM_USER messages, and worse, even if a control doesn't use WM_USER now, it could use one in future updates of the runtime, or Common Controls updates, while WM_APP is save to use in both cases.
-
Re: [ADVANCED] Multi-threaded VB6
Quote:
Originally Posted by
qvb6
I know that one could post a message like WM_LBUTTONDOWN so simulate a click to a hidden control, so no subclassing is needed.
Subclassing is a technique that allows an application to intercept and process messages sent or posted to a particular window before the window has a chance to process them. You can create your own message-only window and process the messages without subclassing. Alternatively, your thread can have no window at all but you can process the messages (PostThreadMessage).
Quote:
Originally Posted by
qvb6
Not sure what you mean, but for windows created from scratch, then WM_USER is okay to use, but anything else could use WM_USER messages, and worse, even if a control doesn't use WM_USER now, it could use one in future updates of the runtime, or Common Controls updates, while WM_APP is save to use in both cases.
Yes, i told about private window classes. Alternatively, you can use APC to call an arbitrary function in the context of the needed thread. The route thread can have only the loop with the SleepEx which switch the thread to the alertable state.
-
Re: [ADVANCED] Multi-threaded VB6
I appreciate everyone chiming in here, thanks guys.
@ Schmidt, I did realize I forgot to change the alias. Nice catch, I didn't think it was going to fix anything. But it did, finally async notifications...
I'm sure everyone will freak out about this statement but I like being event driven and it just seems cleaner to me. Plus I don't have to implement my own queue on top of a timer.
I need it only rarely, when a user requests GUI change via web server.
@ The trick, I looked for info on APC but couldn't find enough to implement.
--------
Using a hidden control may be even better. What are my options, are there any messages that don't take focus and allow me to send a int/long to identify the type of notification? I don't really want a fake form, prefer hidden control.
How safe is passing pointers? Can you handle errors if the memory is moved via change by other thread?
Edit: It doesn't look like WM_CHAR changes focus, may be able to use...
PostMessage Text1.hWnd, WM_CHAR, Asc("A"), 0&
-
Re: [ADVANCED] Multi-threaded VB6
Also for others trying, you cant use Schmidt's Form_Activate example with multiple forms, it executes multiple times. I couldn't get threading at startup, you will have to use one time timer to start threads if app has multiple forms (or store var to check if already loaded).
-
Re: [ADVANCED] Multi-threaded VB6
For multithreading, the ThreadHandler in RC5c that I use is great overall, but there are still the following problems. For example, thread Dll debugging is difficult, and sometimes it crashes for no reason to find out exactly where it is.
In addition, how to actively uninstall loaded threads? (th = regfree. ThreadObjectCreate (cThreadKey, ThreadLibPath, cThreadClass)) is set directly to set th = nothing?
TH. JobQueueCount ‘represents the task currently executed (including the number of functions or processes being executed?)
TH. CancelExecution ‘Is to cancel the task being performed? If multiple tasks are cancelled altogether?
TH. TimeoutSecondsToHardTerminate 'What does this do?
-
Re: [ADVANCED] Multi-threaded VB6
I ended up using shared memory class / PostMessage(Form.hWnd, WM_CHAR, Asc("A"), 0&) / Private Sub Form_KeyPress(KeyAscii As Integer) for Async notifications. Seems to be pretty clean and doesn't have any issues. I cant even fake keypresses to the form via real user input. This specific app won't have a local user anyway.
Thanks for all the help guys!