News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Thread problem - How to intercept internal values ?

Started by guga, December 27, 2015, 04:05:14 AM

Previous topic - Next topic

guga

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.
Coding in Assembly requires a mix of:
80% of brain, passion, intuition, creativity
10% of programming skills
10% of alcoholic levels in your blood.

My Code Sites:
http://rosasm.freeforums.org
http://winasm.tripod.com

guga

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. :)
Coding in Assembly requires a mix of:
80% of brain, passion, intuition, creativity
10% of programming skills
10% of alcoholic levels in your blood.

My Code Sites:
http://rosasm.freeforums.org
http://winasm.tripod.com

fearless

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, :DWORDPBECallBack 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


guga

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
Coding in Assembly requires a mix of:
80% of brain, passion, intuition, creativity
10% of programming skills
10% of alcoholic levels in your blood.

My Code Sites:
http://rosasm.freeforums.org
http://winasm.tripod.com

dedndave

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

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

guga

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)
Coding in Assembly requires a mix of:
80% of brain, passion, intuition, creativity
10% of programming skills
10% of alcoholic levels in your blood.

My Code Sites:
http://rosasm.freeforums.org
http://winasm.tripod.com

jj2007

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 "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


guga

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 ???
Coding in Assembly requires a mix of:
80% of brain, passion, intuition, creativity
10% of programming skills
10% of alcoholic levels in your blood.

My Code Sites:
http://rosasm.freeforums.org
http://winasm.tripod.com

MichaelW

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?
Well Microsoft, here's another nice mess you've gotten us into.

jj2007

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

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?

fearless

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.