News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Run synchronous process algo

Started by hutch--, May 10, 2005, 12:33:55 AM

Previous topic - Next topic

hutch--

A few people have showed interest in running synchronous processes but have had reservations about using a historical "shell" style technique. This version bypasses the wrapper WaitForSingleObject() and directly calls the extended version. It has user defined process priority and user defined timeout. I no longer have a method to test this on very old machines but its running OK on my Win2k box.

The only oddity is that when nesting calls to synchronous processes, the chain can be broken by closing one of the applications within the chain which is probably a design shortcoming in the operating system. It demonstrates that the operating system method is not properly synchronous which will break critical operations that depend on certain tasks being performed in sequence with the following task depending on the previous one.

Jibz has made a suggestion about error handling but with the range of return values from the extended version, I am not sure if there is a point to doing this. ANy suggestions are welcome. It is designed for win2k up but the early documentation shows it as having been available since win95 so it may run on old versions.


; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

align 4

run_synch_process proc lpfilename:DWORD,priority:DWORD,timeout:DWORD

    LOCAL st_info:STARTUPINFO
    LOCAL pr_info:PROCESS_INFORMATION

  ; ---------------------
  ; zero fill STARTUPINFO
  ; ---------------------
    mov ecx, 17                 ; 68 bytes SIZEOF STARTUPINFO
    lea edx, st_info
    xor eax, eax
  @@:
    mov [edx], eax
    add edx, 4
    sub ecx, 1
    jnz @B

    mov st_info.cb, 68          ; set the structure size member

    invoke CreateProcess,NULL,lpfilename,NULL,NULL,
                         NULL,NULL,NULL,NULL,
                         ADDR st_info,
                         ADDR pr_info

    test eax, eax               ; if CreateProcess fails
    jnz @F
    mov eax, -2                 ; set return value for CreateProcess fail
    ret

  @@:
    invoke SetPriorityClass,pr_info.hProcess,priority

    invoke WaitForSingleObjectEx,pr_info.hProcess,timeout,0
    push eax

    invoke CloseHandle, pr_info.hThread
    invoke CloseHandle, pr_info.hProcess

    pop eax                     ; return value from WaitForSingleObjectEx

    ret

comment * ---------------------------------
    WaitForSingleObjectEx return values

    WAIT_FAILED               equ -1
    WAIT_OBJECT_0             equ 0
    WAIT_ABANDONED            equ 00000080h
    WAIT_ABANDONED_0          equ 00000080h
    WAIT_TIMEOUT              equ 00000102h
    WAIT_IO_COMPLETION        equ 000000C0h
        --------------------------------- *

run_synch_process endp

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««



LATER : The timeout parameter must be set to INFINITE or the process ceases to be synchronous once the timeout has lapsed.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

Jibz

Uhm .. the point with the error handling was to have the procedure return the exit code from the program, and at the same time report any error.

shell proc lpFileSpec:DWORD,lpExitCode:DWORD

Returns zero on error, and non-zero on success. In case of success, the exit code from the process is stored at lpExitCode, unless it's NULL.

James Ladd

I was wondering where the process result would be ?

Jibz

Quote from: strikerI was wondering where the process result would be ?

In which case?

In my version, the exit code from the created process would be stored in the DWORD that the lpExitCode parameter points to (unless it's NULL).

hutch--

This works OK, it returns the external process exit code in ECX.


; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

align 4

run_synch_process proc lpfilename:DWORD,priority:DWORD,timeout:DWORD

    LOCAL xc    :DWORD
    LOCAL st_info:STARTUPINFO
    LOCAL pr_info:PROCESS_INFORMATION

  ; ---------------------
  ; zero fill STARTUPINFO
  ; ---------------------
    mov ecx, 17                 ; 68 bytes SIZEOF STARTUPINFO
    lea edx, st_info
    xor eax, eax
  @@:
    mov [edx], eax
    add edx, 4
    sub ecx, 1
    jnz @B

    mov st_info.cb, 68          ; set the structure size member

    invoke CreateProcess,NULL,lpfilename,NULL,NULL,
                         NULL,NULL,NULL,NULL,
                         ADDR st_info,
                         ADDR pr_info

    test eax, eax               ; if CreateProcess fails
    jnz @F
    mov eax, -2                 ; set return value for CreateProcess fail
    ret

  @@:
    invoke SetPriorityClass,pr_info.hProcess,priority

    invoke WaitForSingleObjectEx,pr_info.hProcess,timeout,0
    push eax

    invoke GetExitCodeProcess,pr_info.hProcess,ADDR xc

    invoke CloseHandle, pr_info.hThread
    invoke CloseHandle, pr_info.hProcess

    pop eax                     ; return value from WaitForSingleObjectEx
    mov ecx, xc                 ; return exit code in ECX

    ret

comment * ---------------------------------
    WaitForSingleObjectEx return values

    WAIT_FAILED               equ -1
    WAIT_OBJECT_0             equ 0
    WAIT_ABANDONED            equ 00000080h
    WAIT_ABANDONED_0          equ 00000080h
    WAIT_TIMEOUT              equ 00000102h
    WAIT_IO_COMPLETION        equ 000000C0h
        --------------------------------- *

run_synch_process endp

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

James Ladd


Jibz

Of course returning stuff in ECX makes it more or less useless from other languages, but I guess that's not of much concern here :bg.

In theory Microsoft could use -2 as an error code for something different in WaitForSingleObject(Ex) at some later point.

hutch--

The alternative seems to be to pass a structure and dump all of the results in that. This would solved the problem of what can be returned but personally the only one I see as useful most of the time is the reurn value n the process success or failure as very few programs actually use the exit value.

LATER : This is the version with a structure to both pass parameters and return values in. It still has return values in EAX and ECX but also fills the structure so it can be used by high level languages.



      RUN_SYNCH_PROCESS STRUCT
        priority dd ?       ; priority setting
        timeout  dd ?       ; timeout interval
        rvcreate dd ?       ; create process return value
        exitcode dd ?       ; run process exit code
        rvwait   dd ?       ; wait return value
      RUN_SYNCH_PROCESS ENDS

Set the priority and timeout in the structure BEFORE passing it to the procedure.


; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

align 4

run_synch_process proc lpfilename:DWORD,lpstruct:DWORD

    LOCAL priority  :DWORD
    LOCAL timeout   :DWORD
    LOCAL rvcreate  :DWORD       ; create process return value
    LOCAL exitcode  :DWORD       ; run process exit code
    LOCAL rvwait    :DWORD       ; wait return value
    LOCAL xc        :DWORD
    LOCAL st_info   :STARTUPINFO
    LOCAL pr_info   :PROCESS_INFORMATION

    mov eax, lpstruct
    mov ecx, (RUN_SYNCH_PROCESS PTR [eax]).priority
    mov priority, eax
    mov ecx, (RUN_SYNCH_PROCESS PTR [eax]).timeout
    mov timeout, eax

  ; ---------------------
  ; zero fill STARTUPINFO
  ; ---------------------
    mov ecx, 17                 ; 68 bytes SIZEOF STARTUPINFO
    lea edx, st_info
    xor eax, eax
  @@:
    mov [edx], eax
    add edx, 4
    sub ecx, 1
    jnz @B

    mov st_info.cb, 68          ; set the structure size member

    invoke CreateProcess,NULL,lpfilename,NULL,NULL,
                         NULL,NULL,NULL,NULL,
                         ADDR st_info,
                         ADDR pr_info
    mov rvcreate, eax

    test eax, eax               ; if CreateProcess fails
    jz quit

    invoke SetPriorityClass,pr_info.hProcess,priority

    invoke WaitForSingleObjectEx,pr_info.hProcess,timeout,0
    mov rvwait, eax

    invoke GetExitCodeProcess,pr_info.hProcess,ADDR exitcode

    invoke CloseHandle, pr_info.hThread
    invoke CloseHandle, pr_info.hProcess

   
    mov eax, lpstruct

    mov ecx, rvcreate
    mov (RUN_SYNCH_PROCESS PTR [eax]).rvcreate, ecx
    mov ecx, exitcode
    mov (RUN_SYNCH_PROCESS PTR [eax]).exitcode, ecx
    mov ecx, rvwait
    mov (RUN_SYNCH_PROCESS PTR [eax]).rvwait, ecx

    mov eax, rvwait
    mov ecx, exitcode

    ret

  quit:
    mov eax, lpstruct

    mov (RUN_SYNCH_PROCESS PTR [eax]).rvcreate, 0
    mov (RUN_SYNCH_PROCESS PTR [eax]).exitcode, 0
    mov (RUN_SYNCH_PROCESS PTR [eax]).rvwait, 0

    ret

run_synch_process endp

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

Jibz

Hey, no reason to be sarcastic :toothy.

The only thing I was after was to get a BOOL result saying if the whole process succeeded or failed, and in case of success, the DWORD exit code from the process if needed.

That was why I suggested the standard Win32API method of returning a BOOL, and passing a pointer to where to store the exit code if needed :U.

hutch--

 :bg

If no-one was looking I would probably pass the 3 values back in 3 registers so you had a variant of FASTCALL called FASTRET but passing a structure is a reasonably good way to do this type of task where a fair bit of data is going both ways. The dereferencing looks cluttery but its only a small amount of code and it hardly effects something that has so many API calls in it. Getting the return value from WaitForSingleObjectEx() tells you what happened to the child process and the exit code gives the value from the called app.

I wonder if there is a viable way to do an exception handler for the 2 calls to CloseHandle as that is where I had problems on the old box. It did not display an OS handled exception though, it used to lock the box up and it had to be rebooted so an exception handler may not do the task.

Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

James Ladd

hutch,

you do


    mov eax, lpstruct
    mov (RUN_SYNCH_PROCESS PTR [eax]).rvcreate


whereas I do


    mov eax, lpstruct
    mov [eax].RUN_SYNCH_PROCESS.rvcreate


Why do you do it the way that you do ?
Is there a difference (as I dont thing there is) ?

hutch--

James,

MASM support a number of syntaxes here and I doubt there is any difference at all. If in doubt, jut pop a small test piece and disassemble it and you will know for sure.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

James Ladd

To me the difference is just confusion.
I noticed that you specify PTR and I do not yet the result is the same.
Very strange.

I will write some test code to really see the difference.