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.
Check out the TerminateProcess API. Or you could use a mutex for interprocess communication.
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.
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
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
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
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)
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
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.
::)
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.
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
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
You might also try
invoke SendMessage, eax, WM_CLOSE, 0, 0
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.
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?
Yes, it works here too. I overwrote my initial test code so at this point I don't know exactly what I did.
Quote from: MichaelW on October 03, 2008, 06:41:15 AM
Yes, it works here too. I overwrote my initial test code so at this point I don't know exactly what I did.
Probably you had the wrong handle. Anyway, your solution is equally short & crispy.