lowe517, you can't call the methods of an object between threads without marshaling. You can pass the socket handle between threads so you can create an auxiliary object which lives in the thread. See the example.
Printable View
lowe517, you can't call the methods of an object between threads without marshaling. You can pass the socket handle between threads so you can create an auxiliary object which lives in the thread. See the example.
Dear The trick,
I find that I can do the some work using VBCreatethread, as well as using a private object as your example showed,all I need to do is that I dim an object(cTCPClient) in the threadFunc and after its method calling I free it. the object exists only in the threadfunc, everything seems all right, but is there anything potentially wrong ?
So I'm trying to follow how this all works but it's quite difficult, so sorry if this is a stupid question, but will threads created in this manner terminate normally, when the Thread Proc completes? As in, I can duplicate the handle and use WaitOnSingleObject, and expect it terminate as expected when the thread proc exits?
Hi Trick,
I am using the version below. When I create more than one thread AND i run my projects in WinDbg I get a crash inside
Attachment 186145Code:Private Function ThreadProc( _
ByVal pParameter As Long) As Long
It only seems to happen inside WinDbg when I have created two threads. It does not seem to happen outside WinDbg but this may be a timing issue. I have included a screen shot of the stack.
Perhaps you have some idea what is happening?
EDIT - I take it back. It does happen outside WinDbg
EDIT EDIT - It does happen outside WinDbg but not reliably. Sometimes its fine.
' //
' // modMultiThreading2.bas - The module provides support for multi-threading.
' // Version 2.1
' // © Krivous Anatoly Anatolevich (The trick), 2015-2019
' // No TLB, No additional DLLs, No Asm-thunks (in compiled form)
' // Private object creation based on NameBasedObjectFactory by firehacker
It may be my fault. I was calling this in the startup for each thread right before vbCreateThread. If I only call it once it seems fine.
Code:Public Function Initialize() As Boolean
10 On Error GoTo errorHandler
Dim bIsInIDE As Boolean
20 Debug.Assert MakeTrue(bIsInIDE)
40 If Not bIsInIDE Then
50 If Not tLockMarshal.bIsInitialized Then
60 writeAgentLog "INFORMATION - Initialized Marshal lock"
70 InitializeCriticalSection tLockMarshal.tWinApiSection
80 tLockMarshal.bIsInitialized = True
90 End If
100 If Not tLockHeap.bIsInitialized Then
110 writeAgentLog "INFORMATION - Initialized heap lock"
120 InitializeCriticalSection tLockHeap.tWinApiSection
130 tLockHeap.bIsInitialized = True
140 End If
150 hModule = App.hInstance
160 pVBHeader = GetVBHeader()
170 If pVBHeader = 0 Then GoTo CleanUp
180 ModifyVBHeader AddressOf FakeMain
190 hHeadersHeap = HeapCreate(0, 0, 65536)
200 If hHeadersHeap = 0 Then GoTo CleanUp
210 End If
220 lTlsSlot = TlsAlloc()
230 If lTlsSlot = TLS_OUT_OF_INDEXES Then GoTo CleanUp
' // Main thread already initialized
240 TlsSetValue lTlsSlot, ByVal 1&
250 Initialize = True
CleanUp:
300 If Not Initialize Then
'writeAgentLog "Calling uninitialize"
310 Uninitialize
320 End If
330 Exit Function
End Function
You should call Initialize once. Attach your project with error.
Unfortunately I can't attach the project. Calling initialize once solved the problem. Thanks Andrew
Thanks a lot for the multithreading files.
However, I have a problem with it:
It works only when compiling to system code
When compiling to p-code the exe it seems hangs when the thread is started and the crashses (the window just closes).
I have a large project and when compiling it to systerm code I get a linker error:
........Form....frm(-22483) : fatal error C1001: INTERNAL COMPILER ERROR
(compiler file 'E:\8783\vc98\p2\src\P2\main.c', line 494)
Please choose the Technical Support command on the Visual C++
Help menu, or open the Technical Support help file for more information
I have no idea how to solve that.
Any chance the multithreading will work with p-code?
thanks, Chris
There is no way to implement in-proc multi-threading with p-code compiled modules either using this submission or any other one because the p-code interpreter is not thread safe.
In your case if you *have* to use in-proc multi-threading (this or other impl) there is no other way for this than to fix the internal compiler error by figuring out what is causing it in your VB6 project.
It is either targeting p-code and forget about in-proc multi-threading, fix internal compiler error and use in-proc multi-threading or try using ActiveX EXEs i.e. out-of-proc multi-threading w/ p-code.
cheers,
</wqw>
Thanks for the quick reply.
It seems I have to find out what line is causing the linker error.
Or does this work with an active x dll in p code?
Is there a thread about difference between p-code and native code?
E.g.
For i = 0 to ubound(List())
Next
hangs in the for loop when compiling to native code and List() is empty, but with compiled to p-code it works fine.
When On Error Resume Next is used
Other such known differences?
Why do you think this is a known difference? Is there a list of known differences you are looking at which has this one listed there?
Better ask in the main forum if someone (who cares about these differences) knows anything might chime in because this thread is about multi-threading.
cheers,
</wqw>
No, I do not have a list. I just wanted to know if there is such a list (and gave an example) before starting a new thread.
Why would you want compiled to p-Code in the first place? what's the benifits over compiling to native code (as p-code is a lot slower as native)?
P-Code:
- Smaller files
- im my experience not really much slower, maybe some %
- faster compiling
- I had problems with native code some years ago (cant remember details) and with p-code it was working fine.
And now I have that damn compiler error, still after 2 hours changing code etc.
Maybe the compiler has a problem in a form with more than ~49000 lines.
Smaller files?? P-code always makes substantially larger files for me, usually at least 2-3x the size.
P-Code file are usually factor 2-2.5 smaller than file in native code.
I now found this thread here:
https://www.vbforums.com/showthread....us-Native-Code
with a project with multithreading working with p-code. Well, not really, crashes when thread is finished.
Checked more samples and it's all over the map... pcode is ranging from half the size, to very close (most common), to 4 bigger
I think it's safe to say that, when we compile "code-only" Projects (like e.g. Ax-Dlls),
PCode produces "roughly half the size" compared to NativeCode.
In your 1:1 cases there's probably "a lot of Bitmaps and Icons" embedded
(coming from visual-resource-parts as *.frx or *.ctx,
making the PCode-savings insignificant compared to the resource-volume)...
And your case where PCode is 4 times larger might be related to Typelib-embedding?
Never investigated that, but PCode might embed a referenced *.tlb in its entirety -
whereas native-compiles "embed only the needed parts of a typelib"?
Olaf
I now got one project working.
Thanks again for this great piece of code.
My exe is now 16 MB (native code) instead of 6 MB (p code).
My other project with the "fatal error C1001: INTERNAL COMPILER ERROR": I moved some routines to another bas file. Now it compiles. But the exe just quits after some seconds. I need to investigate further.
(It seems for the line number a signed 16bit integer, as the line number of my error message was Form....frm(-22483)), though the line number was -22483 + 65536 = 43053.)
The linker can remove unused code from executables (native-code) whenas P-code saves everything. So if you use an universal standard module with bunch of functions and use few ones the native code can have smaller size.
I now have the problem that my exe sometimes terminates when a thread ends.
Any idea how to solve that?
You have to *manually* release global state i.e. private and public variable of Object and String type in a .bas module has to be cleared (set to Nothing or vbNullString) in ThreadProc before calling CoUninitialize and subsequenctly returning execution to OS.
This is practically impossible with static "local" variables in procedures so in-proc mutli-threading in VB6 is doomed unless threaded lightly by the app. developers themselves (take care of every wrinkle introduced).
Another thing which might help if you are using Ax DLL for the in-proc is to enable Retained in Memory in project settings though I wouldn't hold my breath.
cheers,
</wqw>
Thanks for the answer.
But, can you please clarify this.
1. clear private strings and objects, ok. Why only in bas modukes, but not in Forms?
2. public strings:
Do i have to clear them only when they are set/modified in a thread? Or when they are only accessed by reading them, too?
From the event log, thats all I have, There is no crash message.
- <Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
- <System>
<Provider Name="Application Error" Guid="{a0e9b465-b939-57d7-b27d-95d8e925ff57}" />
<EventID>1000</EventID>
<Version>0</Version>
<Level>2</Level>
<Task>100</Task>
<Opcode>0</Opcode>
<Keywords>0x8000000000000000</Keywords>
<TimeCreated SystemTime="2023-03-27T15:34:56.7881203Z" />
<EventRecordID>41944</EventRecordID>
<Correlation />
<Execution ProcessID="24792" ThreadID="10068" />
<Channel>Application</Channel>
<Computer>NewTowerWork</Computer>
<Security UserID="S-1-5-21-245563044-3042615410-272610636-1001" />
</System>
- <EventData>
<Data Name="AppName">My.exe</Data>
<Data Name="AppVersion">5.3.0.652</Data>
<Data Name="AppTimeStamp">6421b786</Data>
<Data Name="ModuleName">MSVBVM60.DLL</Data>
<Data Name="ModuleVersion">6.0.98.48</Data>
<Data Name="ModuleTimeStamp">5ea8e7bc</Data>
<Data Name="ExceptionCode">c0000005</Data>
<Data Name="FaultingOffset">00020f36</Data>
<Data Name="ProcessId">0x5a4c</Data>
<Data Name="ProcessCreationTime">0x1d960c1a8c52a49</Data>
<Data Name="AppPath">P:\Projects\6Vb6\My.exe</Data>
<Data Name="ModulePath">C:\WINDOWS\SYSTEM32\MSVBVM60.DLL</Data>
<Data Name="IntegratorReportId">964bd0eb-4527-45b5-a0a8-1e5c71d86a74</Data>
<Data Name="PackageFullName" />
<Data Name="PackageRelativeAppId" />
</EventData>
</Event>
I suppose (according to your log) you call Uninitialize when you have worked threads. So you should either wait until the threads end or don't call Uninitialize at all.
When I need to transfer data in strings from a thread to the main thread, i see these options:
1. a public String variable in a form
2. a textfield control in a form
3. a string with fixed length
4. a public string, which I prealloc in the main thread e.g. with mystring = String$(1000, 0) and in the the thread I copy a local string to that string using the StrPtr
Is this correct?
I now have another issue/question.
The created threads are working fine multithreadwise.
But not the main thread and the created threads. Only one of the is active, either the main thread or the created threads.
e.g.
MyThread1 and MyThread2 are not started after calling vbCreateThread, they are called when the procedure Test is finished and the main thread gets idle. Of course, I could add some DoEvents, but then still, either the main thread OR the created threads are executed.Code:Sub Test()
Dim i As Long
vbCreateThread 0, 0, AddressOf MyThread1, 0, 0, 0
vbCreateThread 0, 0, AddressOf MyThread2, 0, 0, 0
for i = 0 to 10
sleep 100
next
End Sub
Why is that so?
Is there a way to have full multithreading here, too?
Thank you, Chris
Make a small demo. Probably the child threads wait for main thread being idle like marshaling things etc.
You have a bug with marshaling. You can't just access to an object from an arbitrary thread. You should use marshaling always to avoid the bugs in future. The behavior of your code is explained by the fact that you call Form1.Text2.Text = "Thread 1: " & CStr(GetCurrentThreadId()) & vbNewLine and Form1.Text3.Text = "Thread 2: " & CStr(GetCurrentThreadId()) & vbNewLine which just call SetWindowText that calls SendMessage WM_SETTEXT. SendMessage waits until the window process the message in main thread.
Hello Trick,
I wanted to use atlsimpleobjforvb simpleobject from Atlsimpleobjdeno atl project using CreateActiveXObjectInNewThread2 of modMultiThreading2.bas as follows by adding atl dll project thru references:
Code:Dim x As ATLSIMPLEOBJDENOLib.atlsimpleobjforvb
Private Sub Command1_Click()
Dim cObj As Object
Dim asyncid As Long
Set cObj = CreateActiveXObjectInNewThread2("ATLSIMPLEOBJDENOLib.atlsimpleobjforvb", , asyncid) 'fails with Invalid procedure call or argument error
cObj.method1
End Sub
Private Sub Form_Load()
modMultiThreading.Initialize
End Sub
Private Sub Form_Unload(Cancel As Integer)
modMultiThreading.Uninitialize
End Sub
when I add atl project thru references to vb st exe projectI run the project I get msgbox with the following error.
---------------------------
Project1
---------------------------
Run-time error '5':
Invalid procedure call or argument
---------------------------
OK
---------------------------
Both Atl dll and vb6 std exe projects attached
How to create atl simple object in the vb client and call its method method1 without error.
Thanks