News:

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

How to tell a second process to shutdown

Started by pcMike, September 30, 2008, 03:38:17 AM

Previous topic - Next topic

pcMike

I have a console application, which I want to be able to be shut down from another application.

I thought this could be done using an event to signal the other application, but I discovered that if an event is used, it needs to be created by the originating application before the receiving application can monitor it. In my case the receiving application will be running first, and I want to be able to run a second application only to tell the first application to exit.

After searching the MASM32 forums I found some information about doing this using HWND_BROADCAST, but Im not clear if this can work from a console application or not.

Can anyone give me an example of the simplest way implement this?

Regards,  Mike

Edit: just to clarify, both applications are my own, and I simpley want to send a message telling the other app to shut itself down.

BlackVortex

Check out the TerminateProcess API. Or you could use a mutex for interprocess communication.

Mark Jones

Quote from: pcMike on September 30, 2008, 03:38:17 AM
...I want to be able to run a second application only to tell the first application to exit.

Hello, perhaps consider going the opposite way... how about the first application (to be closed) periodically looks for the second app to be running, and if it is, it terminates itself? Presto, no need for intra-process messaging.

Also, search for "ReadDirectoryChangesW" here for a low-CPU way to detect when a file is created.

"To deny our impulses... foolish; to revel in them, chaos." MCJ 2003.08

pcMike

Thanks for the replies guys...

Mark: Your idea sounds good, but how would I check if the second app is running? I thought about checking if a semaphore file exists, but figured the disk access should be avoided. The ReadDirectoryChangesW method sounds intresting, but I would still need to check for the specific filename afterwards I belive.

BlackVortex: I've never used a mutex before, but it sounds like it should be a simple solution. I've found the following mutex code example here:
http://www.masm32.com/board/index.php?topic=8120.0


; A message box will be displayed. When you close it,
; the app will be destroyed allowing another copy to begin its work

.386
.model flat,stdcall
option casemap:none

include \masm32\include\windows.inc

include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib

include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib

.data
mutexName db "Global\TEST"
hMutex dd 0

.code

start:

WaitForMutexToLeave:

    invoke CreateMutexA,0,0,ADDR mutexName     ; try to create the mutex
    test eax,eax                               ; No copies running yet?
    je BeginProc                               ; Start the process

    invoke OpenMutexA,\
      01F0001h,\                               ; (MUTEX_ALL_ACCES)
      TRUE,\                                   ; Do inherit
      ADDR mutexName                           ; Mutex String
    mov [hMutex],eax                           ; save the handle

    invoke WaitForSingleObject, eax, -1
    cmp eax,WAIT_ABANDONED                     ; Is the other process dead?
    je BeginProc                               ; If not, restart the wait

      push dword ptr [hMutex]
    call CloseHandle

    jmp WaitForMutexToLeave
BeginProc:
      push dword ptr [hMutex]
    call CloseHandle
    invoke MessageBox,0,0,0,0
    invoke ExitProcess,0
end start



I'm assuming that the first application (to be closed) should not create the mutex,  it should simply call OpenMutexA once, and then it should occasionally do a "WaitForSingleObject,hMutex,0" until it gets a result. 
Here is my modified code to wait for the mutex:

start:

    invoke OpenMutexA,\
      01F0001h,\                               ; (MUTEX_ALL_ACCES)
      TRUE,\                                   ; Do inherit
      ADDR mutexName                           ; Mutex String
    mov [hMutex],eax                           ; save the handle

waitloop:
    invoke Sleep,10
    invoke WaitForSingleObject, hMutex, 0
    cmp eax,WAIT_ABANDONED
    jne waitloop

    push dword ptr [hMutex]
    call CloseHandle
    invoke MessageBox,0,0,0,0
    invoke ExitProcess,0
end start


And then the second application simply does this to create the mutex:

  invoke CreateMutexA,0,0,ADDR mutexName     ; try to create the mutex


But this didn't work... Am I overlooking something?

Regards,  Mike

pcMike

I did some more searching, and it appears that a Mutex does not work like an Event....

So, I think it would have to work like this:


waitloop:
invoke CreateMutexA,0,0,ADDR mutexName     ; try to create the mutex
mov hMutex, eax                            ; save mutex handle
invoke GetLastError                        ; check error
cmp eax, ERROR_ALREADY_EXISTS              ; does mutex already exist?
jne alreadyExist                           ; exit if it does
invoke CloseHandle, hMutex                 ; destroy the created mutex
jmp waitloop                               ; keep looping until it exists
AlreadyExist:
invoke exitprocess,0


This method looks ugly, is there a better way?

Regards,  Mike

jdoe


pcMike,

I think Mark is right. Make your console application to monitor something that the second one will launch. Could be many things... a registry value 0 or 1, an ini file with a parameter in it, etc. This way you don't have to think about a solution that does not worth complexity.

:wink


P1

Quote from: BlackVortex on September 30, 2008, 03:52:42 AM
Check out the TerminateProcess API. Or you could use a mutex for interprocess communication.
I have learned to stay away from mutexes.  They are namespace specific in the newer OSes.

So that if you have a RDP session running on a server for instance.  The mutex in the console namespace is NOT seen by the RDP namespace session.

Save someone a bit of trouble shooting in mutexes.

I know you were not going that way, but for anyone thinking about that as a result.

Regards,  P1  :8)

MichaelW

Why be sophisticated when stupid will do the job :8)

target.asm:

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    include \masm32\include\masm32rt.inc
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    .data
      buff db 1000 dup(0)
    .code
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    invoke GetConsoleTitle, ADDR buff, 1000
    print ADDR buff,13,10,13,10

    inkey "Press any key to exit..."
    exit
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start


; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    include \masm32\include\masm32rt.inc

    MOUSEINPUT STRUCT
      d_x         DWORD ?
      d_y         DWORD ?
      mouseData   DWORD ?
      dwFlags     DWORD ?
      time        DWORD ?
      dwExtraInfo DWORD ?
    MOUSEINPUT ENDS

    KEYBDINPUT STRUCT
      wVk         WORD ?
      wScan       WORD ?
      dwFlags     DWORD ?
      time        DWORD ?
      dwExtraInfo DWORD ?
    KEYBDINPUT ENDS

    INPUTS STRUCT
      dwType      DWORD ?
      UNION
        mi        MOUSEINPUT <>
        ki        KEYBDINPUT <>
      ENDS
    INPUTS ENDS

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    .data
      hwnd    dd 0
      rc      RECT <>
      inputs  INPUTS <>
    .code
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««


; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    ;----------------------------------------------------
    ; This window name is specific to my system, and was
    ; simply copied from the target application console.
    ;----------------------------------------------------

    invoke FindWindow, chr$("ConsoleWindowClass"),
                       chr$("D:\masm32\My\CloseConsoleApp\target.exe")
    mov hwnd, eax
    .IF hwnd

      print uhex$(hwnd),13,10

      invoke GetWindowRect, hwnd, ADDR rc

      print ustr$(rc.right),13,10
      print ustr$(rc.top),13,10

      ;---------------------------------------------
      ; This is a quick and dirty approximation to
      ; put the mouse cursor over the close button.
      ;---------------------------------------------

      invoke GetSystemMetrics, SM_CXSIZE
      sub rc.right, eax
      invoke GetSystemMetrics, SM_CYSIZE
      shr eax, 1
      add rc.top, eax

      ;------------------------------------------------------------
      ; For MOUSEEVENTF_ABSOLUTE dx and dy must contain normalized
      ; absolute coordinates between 0 and 65,535.
      ;------------------------------------------------------------

      invoke GetSystemMetrics, SM_CXSCREEN
      mov ecx, eax
      mov eax, rc.right
      shl eax, 16
      xor edx, edx
      div ecx
      mov rc.right, eax

      invoke GetSystemMetrics, SM_CYSCREEN
      mov ecx, eax
      mov eax, rc.top
      shl eax, 16
      xor edx, edx
      div ecx
      mov rc.top, eax
   
      mov inputs.dwType, INPUT_MOUSE
      m2m inputs.mi.d_x, rc.right
      m2m inputs.mi.d_y, rc.top
      mov inputs.mi.mouseData, 0
      mov inputs.mi.dwFlags, \
        MOUSEEVENTF_MOVE or MOUSEEVENTF_ABSOLUTE or MOUSEEVENTF_VIRTUALDESK

      invoke SendInput, 1, ADDR inputs, SIZEOF INPUTS

      invoke SetForegroundWindow, hwnd

      ;------------------
      ; Delay for debug.
      ;------------------

      invoke Sleep, 2000

      mov inputs.mi.dwFlags, MOUSEEVENTF_LEFTDOWN
      invoke SendInput, 1, ADDR inputs, SIZEOF INPUTS
      mov inputs.mi.dwFlags, MOUSEEVENTF_LEFTUP
      invoke SendInput, 1, ADDR inputs, SIZEOF INPUTS

    .ENDIF
   
    inkey "Press any key to exit..."
    exit
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start

eschew obfuscation

jdoe


Michael,

What is the difference between simulate mouse input and the equally stupid use of TerminateProcess. Maybe less coding for TerminateProcess.

Both of these solutions are not clean if the process is doing something and it is forced to terminate.

::)


MichaelW

The method I am using is equivalent to the user clicking the close button, and for a console app the default handler function will call ExitProcess to terminate the process cleanly. If the process might be doing something that should not be interrupted, then it should install its own handler function.
eschew obfuscation

jdoe


Michael,

I looked at your code closely and it could be much simpler... and by the way your solution is, at second sight, a lot less stupid then TerminateProcess. The close button seems to do, as you said, at pretty good job.


:U




invoke FindWindow, chr$("ConsoleWindowClass"), chr$("D:\masm32\My\CloseConsoleApp\target.exe")
invoke PostMessage, eax, WM_SYSCOMMAND, SC_CLOSE, NULL





MichaelW

Thanks,

I started out looking for something like SC_CLOSE, but couldn't recall enough details to find it. It works, and it causes the Ctrl handler function to be called just as clicking the close button does.


; target.asm
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    include \masm32\include\masm32rt.inc
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    .data
      buff db 300 dup(0)
    .code
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
HandlerRoutine proc dwCtrlType:DWORD
    .IF dwCtrlType == CTRL_CLOSE_EVENT
      print chr$(13,10,13,10)
      print "CTRL_CLOSE_EVENT",13,10
      invoke Sleep, 2000
    .ENDIF
    xor eax, eax
    ret
HandlerRoutine endp
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    invoke GetConsoleTitle, ADDR buff, 300

    print ADDR buff,13,10,13,10

    invoke SetConsoleCtrlHandler, ADDR HandlerRoutine, TRUE

    inkey "Press any key to exit..."
    exit
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start


; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    include \masm32\include\masm32rt.inc
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    .data
    .code
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

    ;----------------------------------------------------
    ; This window name is specific to my system, and was
    ; simply copied from the target application console.
    ;----------------------------------------------------

    invoke FindWindow, chr$("ConsoleWindowClass"),
                       chr$("D:\masm32\My\CloseConsoleApp\target.exe")

    .IF eax

      invoke PostMessage, eax, WM_SYSCOMMAND, SC_CLOSE, NULL
    .ENDIF

    inkey "Press any key to exit..."
    exit
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start

eschew obfuscation

jj2007

You might also try

invoke SendMessage, eax, WM_CLOSE, 0, 0

MichaelW

Quote from: jj2007 on October 03, 2008, 12:50:17 AM
You might also try

invoke SendMessage, eax, WM_CLOSE, 0, 0


That was the first thing I tried, but it had no effect on a console app.
eschew obfuscation

jj2007

Quote from: MichaelW on October 03, 2008, 04:42:19 AM
Quote from: jj2007 on October 03, 2008, 12:50:17 AM
You might also try

invoke SendMessage, eax, WM_CLOSE, 0, 0


That was the first thing I tried, but it had no effect on a console app.


Works perfectly over here in old Europe :bdg
You have Windows 2000, right?