Hi Guys
I´m having some problems with understanding threads. I´m trying to grab values from inside a thread while the main function is still running t show them on other parts of an app (Like in edit controls for example). The problem is that the main functin inside the thread have loops and the only way i found to show the data was t insert SetDlgItemInt, SendMessage inside the main loop so it could pass the values to the controls.
But, this is not the wanted functionality. For example, let´s say that the main loop inside the thread i want to put on a dll as a external function that will mainly store the values to the user grab them and put on his own controls . How to do that ????
I´m posting here a small example that maybe used to what i need.
The main part of the code is as:
...Else_If D@uMsg = &WM_COMMAND
..If D@wParam = IDC_LAUNCH
call Generate ; <---- The starting thread function
..Else_If D@wParam = IDC_STOP
call 'KERNEL32.TerminateThread' D$SecondThread, &NULL
...Else_If D@uMsg = &WM_CLOSE
[ThreadID: D$ 0]
[SecondThread: D$ 0]
Proc Generate:
Uses ebx, esi, edi
call 'KERNEL32.TerminateThread' D$SecondThread, &NULL
call 'KERNEL32.CreateThread' &NULL, &NULL, ThreadProc, &NULL, &NULL, ThreadID
mov D$SecondThread eax
EndP
The Main thread functionrelies here
[Fixed_Value: D$ 0]
Proc ThreadProc:
Arguments @lParam
Local @Red, @Green, @iCounter
Uses ebx, esi, edi
mov D$Fixed_Value 700
mov eax D$Fixed_Value
and eax 0FFFF | movzx ecx ax | shl ecx 16
call 'USER32.SendMessageA' D$hProgress, &PBM_SETRANGE, 0, ecx
call PBEngine D$Fixed_Value, D$Speed
xor eax eax
EndP
The functionPBEngine containms internally a loop and the messages to be send to the control as:
Proc PBEngine:
Arguments @FixedVal, @PBSpeed
Local @Red, @Green, @iCounter
mov D@iCounter 0
mov eax D@FixedVal
.While D@iCounter <= eax
mov D@Red 255
mov D@Green 0
mov eax D@FixedVal
cdq
sub eax edx
sar eax 1
If D@iCounter < eax
mov D@Red 255
mov ecx D@iCounter
imul ecx ecx 255
mov eax D@FixedVal
cdq
sub eax edx
mov esi eax
sar esi 1
mov eax ecx
cdq
idiv esi
mov D@Green eax
End_If
mov eax D@FixedVal
cdq
sub eax edx
sar eax 1
.If D@iCounter >= eax
mov D@Green 255
mov eax D@iCounter
imul eax eax 255
cdq
idiv D@FixedVal
mov ecx 255
sub ecx eax
shl ecx 1
mov D@Red ecx
mov eax D@Red
sub eax 01
mov D@Red eax
If D@Red <s 0
mov D@Red 0
End_If
.End_If
call 'USER32.SetDlgItemInt' D$hDlg, IDC_EDIT_RED, D@Red, &FALSE
call 'USER32.SetDlgItemInt' D$hDlg, IDC_EDIT_GREEN, D@Green, &FALSE
movzx eax B@Red
movzx ecx B@Green
shl ecx 8
or eax ecx
call 'USER32.SendMessageA' D$hProgress, &PBM_SETBARCOLOR, 0, eax
call 'USER32.SendMessageA' D$hProgress, &PBM_SETPOS, D@iCounter, 0
call 'USER32.SetDlgItemInt' D$hDlg, IDC_VALUE, D@iCounter, &FALSE
call 'KERNEL32.Sleep' D@PBSpeed
inc D@iCounter
mov eax D@FixedVal
.End_While
EndP
The problem is...how to get rid of
call 'USER32.SetDlgItemInt' D$hDlg, IDC_EDIT_RED, D@Red, &FALSE
call 'USER32.SetDlgItemInt' D$hDlg, IDC_EDIT_GREEN, D@Green, &FALSE
movzx eax B@Red
movzx ecx B@Green
shl ecx 8
or eax ecx
call 'USER32.SendMessageA' D$hProgress, &PBM_SETBARCOLOR, 0, eax
call 'USER32.SendMessageA' D$hProgress, &PBM_SETPOS, D@iCounter, 0
call 'USER32.SetDlgItemInt' D$hDlg, IDC_VALUE, D@iCounter, &FALSE
And make those routines be outside the main thread so the user can simply grab the values of RED, GReen and icounter (for example) and place them on his own controls on a thread function built by him self ? So, the main function here is PBEngine, but i wanted to make it without any window handle, just exporting the variables during running, so the user can grab them and use a thread to do whatever he wants (place in his controls etc)
I mean..what if PBEngne were a dll (for example) so i could allow the user to do this:
Proc ThreadProc:
Arguments @lParam
Local @Red, @Green, @iCounter
Uses ebx, esi, edi
mov D$Fixed_Value 700
mov eax D$Fixed_Value
and eax 0FFFF | movzx ecx ax | shl ecx 16
call 'USER32.SendMessageA' D$hProgress, &PBM_SETRANGE, 0, ecx
call 'PBDLL.PBEngine' D$Fixed_Value, D$Speed <--- Assume now that PBEngine is a function inside a dll.
call MyUserRoutine
xor eax eax
EndP
Proc MyUserRoutine:
call 'USER32.SetDlgItemInt' D$hDlg, IDC_EDIT_RED, D@Red, &FALSE
call 'USER32.SetDlgItemInt' D$hDlg, IDC_EDIT_GREEN, D@Green, &FALSE
movzx eax B@Red
movzx ecx B@Green
shl ecx 8
or eax ecx
call 'USER32.SendMessageA' D$hProgress, &PBM_SETBARCOLOR, 0, eax
call 'USER32.SendMessageA' D$hProgress, &PBM_SETPOS, D@iCounter, 0
call 'USER32.SetDlgItemInt' D$hDlg, IDC_VALUE, D@iCounter, &FALSE
EndP
see ? can i export the values of Red, Green and iCounter to external variables from where the user cn collect and use in his own controls???
I´m asking this because i´m trying to implement it on my CodeTune project http://masm32.com/board/index.php?topic=4925.0. So you guys can also use the dll functions of the benchmark algos and use the collected data how you want. Ie. placing the values on your own windows controls, or making them be shown on a console handle etc.
Ok, i guess i found a way, but it will require another argument of the main function inside the thread. It can be used as a callback to retrieve the data allowing the user to insert it on the controls he want, the way he wants. To retrieve the data it is necessary call another function (that can be a export) inside the callback. (I named on this example as GetData")
So, inside ThreadProc i added an argument on the function PBEngine called "MyPointer" resulting in:
Proc ThreadProc:
Arguments @lParam
Local @Red, @Green, @iCounter
Uses ebx, esi, edi
mov D$Fixed_Value 700
mov eax D$Fixed_Value
and eax 0FFFF | movzx ecx ax | shl ecx 16
call 'USER32.SendMessageA' D$hProgress, &PBM_SETRANGE, 0, ecx
call PBEngine D$Fixed_Value, D$Speed, MyPointer
xor eax eax
EndP
PBEngne now have a call to this pointer like:
[ColorData:
ColorData.Red: D$ 0
ColorData.Green: D$ 0
ColorData.Counter: D$ 0]
Proc PBEngine:
Arguments @FixedVal, @PBSpeed, @pointer
Local @Red, @Green, @iCounter
mov D@iCounter 0
mov eax D@FixedVal
.While D@iCounter <= eax
mov D@Red 255
mov D@Green 0
mov eax D@FixedVal
cdq
sub eax edx
sar eax 1
If D@iCounter < eax
mov D@Red 255
mov ecx D@iCounter
imul ecx ecx 255
mov eax D@FixedVal
cdq
sub eax edx
mov esi eax
sar esi 1
mov eax ecx
cdq
idiv esi
mov D@Green eax
End_If
mov eax D@FixedVal
cdq
sub eax edx
sar eax 1
.If D@iCounter >= eax
mov D@Green 255
mov eax D@iCounter
imul eax eax 255
cdq
idiv D@FixedVal
mov ecx 255
sub ecx eax
shl ecx 1
mov D@Red ecx
mov eax D@Red
sub eax 01
mov D@Red eax
If D@Red <s 0
mov D@Red 0
End_If
.End_If
move D$ColorData.Red D@Red
move D$ColorData.Green D@Green
move D$ColorData.Counter D@iCounter
call 'KERNEL32.Sleep' D@PBSpeed
inc D@iCounter
mov eax D@FixedVal
call PlaceHolder D@pointer
.End_While
EndP
Proc PlaceHolder:
Arguments @Pointer
pushad
call D@Pointer
popad
EndP
; This is the fucntion that actually collects the data and can be used as an export inside the callback.
Proc Getdata:
Arguments @pStruct
mov edi D@pStruct
move D$edi D$ColorData.Red
move D$edi+4 D$ColorData.Green
move D$edi+8 D$ColorData.Counter
EndP
The resultant code to the user grab the data and use it as he wish is simply call the function "GetData" inside his callback Like:
[UserStruct:
UserStruct.Red: D$ 0
UserStruct.Green: D$ 0
UserStruct.Counter: D$ 0]
Proc MyPointer:
call GetData UserStruct
call 'USER32.SetDlgItemInt' D$hDlg, IDC_EDIT_RED, D$UserStruct.Red, &FALSE
call 'USER32.SetDlgItemInt' D$hDlg, IDC_EDIT_GREEN, D$UserStruct.Green, &FALSE
movzx eax B$UserStruct.Red
movzx ecx B$UserStruct.Green
shl ecx 8
or eax ecx
call 'USER32.SendMessageA' D$hProgress, &PBM_SETBARCOLOR, 0, eax
call 'USER32.SendMessageA' D$hProgress, &PBM_SETPOS, D$UserStruct.Counter, 0
call 'USER32.SetDlgItemInt' D$hDlg, IDC_VALUE, D$UserStruct.Counter, &FALSE
EndP
So..at the end, if the main PBEngine and Getdata are exported functions inside a dll for example. MyDLL.dll. then to the user point of view all he will need to code are 2 functions. The thread proc as he wants...and the callback as showend here:
Proc ThreadProc:
Arguments @lParam
Local @Red, @Green, @iCounter
Uses ebx, esi, edi
mov D$Fixed_Value 700
mov eax D$Fixed_Value
and eax 0FFFF | movzx ecx ax | shl ecx 16
call 'USER32.SendMessageA' D$hProgress, &PBM_SETRANGE, 0, ecx
call 'Mydll.PBEngine' D$Fixed_Value, D$Speed, MyPointer
xor eax eax
EndP
callback MyPointer
[UserStruct:
UserStruct.Red: D$ 0
UserStruct.Green: D$ 0
UserStruct.Counter: D$ 0]
Proc MyPointer:
call 'Mydll.GetData' UserStruct
call 'USER32.SetDlgItemInt' D$hDlg, IDC_EDIT_RED, D$UserStruct.Red, &FALSE
call 'USER32.SetDlgItemInt' D$hDlg, IDC_EDIT_GREEN, D$UserStruct.Green, &FALSE
movzx eax B$UserStruct.Red
movzx ecx B$UserStruct.Green
shl ecx 8
or eax ecx
call 'USER32.SendMessageA' D$hProgress, &PBM_SETBARCOLOR, 0, eax
call 'USER32.SendMessageA' D$hProgress, &PBM_SETPOS, D$UserStruct.Counter, 0
call 'USER32.SetDlgItemInt' D$hDlg, IDC_VALUE, D$UserStruct.Counter, &FALSE
EndP
I´m not sure if this is the proper way to grab the necessary information inside the main loop, but..it seems to work. Attached the example fixed.
If someone knows another method to do it, please let me know before i start implementing on the CodeTune project. :)
That makes sense. I would have also suggested a callback routine. Other functions like URLDownloadToFile makes use of a callback function, so that user can display progress bars or whatever they want.
I would suggest the callback function have some sort of code to indicate when processing has finished, or if error occured.
So you could still do something like
Invoke PBEngine D$Fixed_Value, D$Speed, Addr PBECallback
PBECallback could be defined as:
PBECallback PROTO :DWORD, :DWORD, :DWORD, :DWORD
PBECallBack PROC dwRed:DWORD, dwGreen:DWORD, dwCounter:DWORD, dwStatusCode:DWORD
Then you can allow user to respond to the dwStatusCode if it is starting to process, still in middle of processing, has finished processing or an error occured.
dwStatusCode == 1 for starting/initializing
dwStatusCode == 2 for processing, so dwRed, dwGreen and dwCounter will have the relevant info for user.
dwStatusCode == 3 for finished processing, so user can display some info to state that, clean up stuff or whatever
dwStatusCode == -1 for an error occurred, or < 0 for various error states if applicable: out of memory, invalid values, or whatever
Indeed a statuscode is needed to avoid hangs.
Using PBECallBack with more parameters would be nice. It would avoid the usage of GetData function, i suppose. :) Very well thought :t
It seems i´m at the right path now. I´ll start implementing them on CodeTune
Many tks
you probably want some sort of "semaphore" to signal one thread that another has produced a new result
https://msdn.microsoft.com/en-us/library/windows/desktop/ms686360%28v=vs.85%29.aspx (https://msdn.microsoft.com/en-us/library/windows/desktop/ms686360%28v=vs.85%29.aspx)
if you have a possibility of multiple results, you could do something like this...
create a circular buffer, with head and tail pointers
use a semaphore to access the pointers
i sometimes use something like XCHG EAX,dwFlags to create a semaphore
XCHG reg,mem creates an implied bus lock
Tks dave, i´m not fully understood the functionality of the threads yet.
I suceeded to make the benchmark algo i´m building works inside or from outside a thread, but, for testing it, i inserted a simple thread as
; ------- inside WM_COMMAND
call 'KERNEL32.TerminateThread' D$SecondThread, &NULL
call 'KERNEL32.CreateThread' &NULL, &NULL, MyThread, D@hwnd, &FALSE, ThreahID
mov D$SecondThread eax
; when exiting the app (WM_CLOSE/WM_DESTROY or a Button Control )
call 'KERNEL32.TerminateThread' D$SecondThread, &NULL
call 'user32.EndDialog' D@hWnd, 0
Not sure if this is the proper way to create it, but it seems to be working enough to do the necessary tests of the main function. Since i´m tryong to make the function be part of a dll, i mainly need to make it work as an normal impotted function, so others can use it how they want (from inside a thread or not).
Personally, i prefer to test the thread usage, since i would like to know exactly how to make those kind of function not hang the main application. (I mean, i can use other function while the others are still running on the thread)
Quote from: guga on December 29, 2015, 05:50:39 AM
Tks dave, i´m not fully understood the functionality of the threads yet.
Take this as pseudocode:
- you launch a thread e.g. in the WM_CREATE handler
- your app continues to work normally
- when the thread thinks it's ready, let it send a message, e.g. WM_COPYDATA
- which is received normally within your WndProc, e.g. as WM_COPYDATA
The point is that this happens asynchronously: You may study the menu entries, or resizing the window, but when the thread ends after about five seconds, the MsgBox pops up whatever you do in that moment.
GuiMenu equ @File, &Open, &Save, -, E&xit, @Edit, Undo, Copy, Paste
include \masm32\MasmBasic\Res\MbGui.asm
.if 0
MyThread:
Delay 5000 ; simulate five seconds thread activity
mov ecx, stack[4] ; get thread parameter
SendData (http://www.webalice.it/jj2006/MasmBasicQuickReference.htm#Mb1081) "ThreadSaysHello", Cat$("Hi folks, tomorrow will be the "+fDate$(1)+Str$(", and\nyou passed the value %i to this thread", ecx))
retn 4
.endif
invoke CreateThread, 0, 0, MyThread, 123456789, 0, 0
Event Message
.if uMsg==WM_COPYDATA
MsgBox 0, CopyData$, "Message from MyThread:", MB_OK
invoke SendMessage, hWnd, WM_CLOSE, 0, 0
.endif
GuiEnd
Tks Jochen :)
i like your way to explain. A couple of thing i didn´t understood.
1) what is the purpose of saving the stack onto ecx ? The lparam is used for what ?
2) How to end the thread ? I mean, sometimes i read people saying that it is needed to use waitforsingleobject, other times to use TerminateThread and others to never use TerminateThread. How to properly close it ?
This example is using a timer. I presume it can be done with everything else,such as calling the thread through a command button (using WM_COMMAND) etc.....But, how to get the values inside the thread ?
I mean, using a callbackworks fine, but, let´s say i dont´want to use a callback function. Example, what is being used inside the thread is a export function from a dll, and inside this export function it have variables that i want to use. The variable changes because it is on a loop. How to grab them ?
Example of a pseudo code
MyExportedFunction:
Do
Variable1 ----<> the functionoes not return this values. It is used internally
Repeat
My Thread:
call MyexportedFunction
I want to grab the value of "variable1" and place it here after the call to it is being showend while the value of variable1 is updating internally by the loop
Is there a way to do it ? or i would need another export function that collects this value so i can use it ?
Example:
MyexportedFunction2
mov eax Variable1
My Thread:
call MyexportedFunction
call MyexportedFunction2
does eax will contains variable1 that is being changed accordingly to the internal loop inside MyexportedFunction ? Or, the function MyexportedFunction2 needs to be called outside the thread ???
I'm not sure I understand the goal or the problem here, but why not just put the variables you want to access in global memory, and then code the accessing instructions with a LOCK prefix, so you have "atomic" access to the variables?
Quote from: guga on December 29, 2015, 09:06:28 AM
1) what is the purpose of saving the stack onto ecx ? The lparam is used for what ?
CreateThread allows passing one dword on the stack, illustrated in my example with 123456789. This could be a pointer to a global STRUCT, for example, or to the start of an array.
Quote2) How to end the thread ?
The
retn 4 ends the thread. With a proper proc/endp, the
ret would end it. The important part is to signal the end to the main process: "Boss, I've finished my work, results are in global struct abc. Bye".
I have used the WM_COPYDATA message, but any WM_USER message would be OK, too.
Note that there are many ways to end a thread (I've highlighted the best method):
Quote from: MSDNHow Threads are Terminated (https://msdn.microsoft.com/en-us/library/windows/desktop/ms686724%28v=vs.85%29.aspx)
A thread executes until one of the following events occurs:
The thread calls the ExitThread function.
Any thread of the process calls the ExitProcess function.
The thread function returns.
Any thread calls the TerminateThread function with a handle to the thread.
Any thread calls the TerminateProcess function with a handle to the process.
QuoteThis example is using a timer.
More precisely, a delay that simulates activity. In a real world example, your thread might do a lot of number crunching, and after ten minutes it sends a message to the boss "numbers ready for use".
@Michael: Why would you need atomic access, if you can simply send a "ready" message to the main process?
It think its due to the how the variables are to be used
So variable1 is to be used internally - internal to the dll / library, and in case other threads (now or in future ) are accessing the values in the variables - the atomic status.
QuoteIt is used internally
So variable1 can be declared in the .data section of the .dll, it will be global, but only the functions in the dll / library will be using it.
And using the callback you can tell the boss the work has been done, with a statuscode, or some other mechanism.