Jump to content

Brainstorming an idea for "threaded" MsgBox


GPinzone
 Share

Recommended Posts

I came up with this idea based on other users' posts. If you search this forum, you will find out it's possible to display a MsgBox for informational purposes without stopping your script if you run it as another program. For example, the function

Func _NoHaltMsgBox($code=0, $title = "",$text = "" ,$timeout = 0)
  Run (@AutoItExe & ' /AutoIt3ExecuteLine  "MsgBox(' & $code & ', '''& $title & ''', '''& $text &''',' & $timeout & ')"')
EndFunc

will display a MsgBox in a separate thread. The problem is that it's impossible to know what the value of the button click since it will terminate when the selection is made and make no effort to send the return value back to the main script.

I discovered today that AutoIt has functions that allow a script to read from the standard output (and standard error) of a running process. Therefore, by outputting the return value to the threaded process' console, it is possible to send the return value back to the main script!

#include <Constants.au3>

Local $mypid, $myStr

$mypid = _NoHaltMsgBox(50, "Hello", "This will return a value of 3, 4, or 5.")

; Do some other stuff...

$myStr = _GetNoHaltMsgBoxValue($mypid)

MsgBox(0, "Returned value...", $myStr)

Exit ; End MAIN

Func _NoHaltMsgBox($code=0, $title = "",$text = "" ,$timeout = 0)
  Local $PID

  $PID = Run (@AutoItExe & ' /AutoIt3ExecuteLine  "ConsoleWrite( MsgBox(' & $code & ', '''& $title & ''', '''& $text &''',' & $timeout & ')")', @SystemDir, "", $STDOUT_CHILD)

  Return $PID
EndFunc

Func _GetNoHaltMsgBoxValue($PID)
  Local $Output

  While (ProcessExists($PID))
    If @error Then ExitLoop
    Sleep(500)
    $Output = $Output & StdoutRead($PID) ; Appending ensures value won't get overwritten
  WEnd

  Return $Output
EndFunc

This seems to work well with one caveat: The processes in between the non-blocking message box and the function to read its return value CANNOT take longer than it takes the user to click the message box's button. If it does, the value is lost. To see an example of this, create another MsgBox under the "Do some other stuff..." section. If you close that message box first, it works. If you close the threaded message box first, the return value is blank because the thread has already been terminated.

I don't know if any of this is useful, since I'm not an expert. Perhaps someone with more experience with AutoIt can determine if there's any use before I pursue this further.

Gerard J. Pinzonegpinzone AT yahoo.com
Link to comment
Share on other sites

Actually, you can get the exit code after a process closes. A at one time by a certain rakishly good looking bird...

:graduated:

Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law
Link to comment
Share on other sites

Dumb question: Will _ProcessGetExitCode() be able to get the exit code if the process had already finished? You know in advance the good ping will finish before the bad one.

So, if:

; Wait for both to finish...
ProcessWaitClose($iRunPID1)
ProcessWaitClose($iRunPID2)

was

; Wait for both to finish...
ProcessWaitClose($iRunPID2)
ProcessWaitClose($iRunPID1)

would it still work? I can't test it right now. :graduated:

Gerard J. Pinzonegpinzone AT yahoo.com
Link to comment
Share on other sites

Note the exit codes in that script are not retrieved until ProcessWaitClose() is done on both. So both are retrieved after the process exits.

Bigger problem is that increased process security in Vista/Win7/2008 may keep you from getting that to work on those OS's. The demo was originally tested on XP, where it still works fine. I added #RequireAdmin, changed the ConsoleWrite() to ToolTip(), compiled it, and ran it with "Run As Administrator" on Vista Business and I still got 0 returned for both pings. Probably have to specify a higher permission than $PROCESS_QUERY_INFORMATION to get that value in the later versions.

:graduated:

Edited by PsaltyDS
Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law
Link to comment
Share on other sites

Very nice stuff! I made the following changes to my original post based on PsaltyDS' code if anyone is interested:

Local $myPID, $myStr, $hRun

$myPID = _NoHaltMsgBox(50, "Hello", "This will return a value of 3, 4, or 5.")
$hRun  = _ProcessGetHandle($myPID)

; Do some other stuff...
MsgBox(0, "", "Wasting time...")

$myStr = _GetNoHaltMsgBoxValue($myPID, $hRun)

MsgBox(0, "Returned value...", $myStr)

Exit ; End MAIN

Func _NoHaltMsgBox($code=0, $title = "",$text = "" ,$timeout = 0)
  Local $iPID
  $iPID = Run (@AutoItExe & ' /AutoIt3ExecuteLine  "Exit(MsgBox(' & $code & ', '''& $title & ''', '''& $text &''',' & $timeout & ')")')

  Return $iPID
EndFunc

Func _GetNoHaltMsgBoxValue($iPID, $hRun)
  Local $iExit

  ; Wait for both to finish...
  ProcessWaitClose($iPID)

  $iExit = _ProcessGetExitCode($hRun)
  _ProcessCloseHandle($hRun)

  Return $iExit
EndFunc

Func _ProcessGetHandle($iPID)
    Local Const $PROCESS_QUERY_INFORMATION = 0x0400
    Local $avRET = DllCall("kernel32.dll", "ptr", "OpenProcess", "int", $PROCESS_QUERY_INFORMATION, "int", 0, "int", $iPID)
    If @error Then
        Return SetError(1, 0, 0)
    Else
        Return $avRET[0]
    EndIf
EndFunc   ;==>_ProcessGetHandle

; Close process handle
Func _ProcessCloseHandle($hProc)
    Local $avRET = DllCall("kernel32.dll", "int", "CloseHandle", "ptr", $hProc)
    If @error Then
        Return SetError(1, 0, 0)
    Else
        Return 1
    EndIf
EndFunc   ;==>_ProcessCloseHandle

; Get process exit code from handle
Func _ProcessGetExitCode($hProc)
    Local $t_ExitCode = DllStructCreate("int")
    Local $avRET = DllCall("kernel32.dll", "int", "GetExitCodeProcess", "ptr", $hProc, "ptr", DllStructGetPtr($t_ExitCode))
    If @error Then
        Return SetError(1, 0, 0)
    Else
        Return DllStructGetData($t_ExitCode, 1)
    EndIf
EndFunc   ;==>_ProcessGetExitCode

Now the spawned MsgBox can be closed before the main thread completes or after. Of course, it may not work on Vista or Windows 7 due to the security issues, but it works perfectly on XP.

Some notes: If you don't get the handle before you throw up another MsgBox ($hRun = _ProcessGetHandle($myPID)), the exit code of the "threaded" MsgBox will be lost. I'd add it to the _NoHaltMsgBox function, but I don't know how to send both the handle and pid back. Do I need the PID once the handle is created? What I mean is maybe the PID could be determined from the handle. If so, I could just return the handle and just pass that around.

Gerard J. Pinzonegpinzone AT yahoo.com
Link to comment
Share on other sites

#NoTrayIcon

#Include <WindowsConstants.au3>

Opt('TrayAutoPause', 0)

If ($CmdLine[0] = 6) And ($CmdLine[1] = '-msgbox') Then
    $ID = MsgBox($CmdLine[3], $CmdLine[4], $CmdLine[5], $CmdLine[6])
    $tData = DllStructCreate('ulong_ptr;dword;ptr')
    DllStructSetData($tData, 1, BitOR(BitShift(@AutoItPID, -16), $ID))
    DllStructSetData($tData, 2, 0)
    DllStructSetData($tData, 3, 0)
    DllCall('user32.dll', 'lresult', 'SendMessage', 'hwnd', $CmdLine[2], 'uint', $WM_COPYDATA, 'ptr', 0, 'ptr', DllStructGetPtr($tData))
    Exit
EndIf

Opt('TrayIconHide', 0)

$hReceiver = GUICreate('')
GUIRegisterMsg($WM_COPYDATA, 'WM_COPYDATA')

ConsoleWrite('#1 = ' & _NoHaltMsgBox(3 + 32, '#1', 'Simple text.') & @CR)
ConsoleWrite('#2 = ' & _NoHaltMsgBox(3 + 32, '#2', 'Simple text.') & @CR)
ConsoleWrite('#3 = ' & _NoHaltMsgBox(3 + 32, '#3', 'Simple text.') & @CR)
ConsoleWrite('------------------------------' & @CR)

While 1
    Sleep(1000)
WEnd

Func _NoHaltMsgBox($iFlags, $sTitle, $sText, $iTimeOut = 0)
    If @compiled Then
        Return Run(@ScriptFullPath & ' -msgbox')
    Else
        Return Run(@AutoItExe & ' "' & @ScriptFullPath & '" -msgbox ' & $hReceiver & ' ' & $iFlags & ' "' & $sTitle & '" "' & $sText & '" ' & $iTimeOut)
    EndIf
EndFunc   ;==>_NoHaltMsgBox

Func WM_COPYDATA($hWnd, $iMsg, $wParam, $lParam)
    Switch $hWnd
        Case $hReceiver

            Local $tData = DllStructCreate('ulong_ptr;dword;ptr', $lParam)

            ConsoleWrite('PID = ' & BitShift(DllStructGetData($tData, 1), 16) & '   ID = ' & BitAND(DllStructGetData($tData, 1), 0xFFFF) & @CR)

            Return 1
    EndSwitch
    Return 0
EndFunc   ;==>WM_COPYDATA

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...