I'm trying to write a program that will spawn a batch file, and this batch file will run a DOS application.
The problem is I need to terminate the batch file (and the DOS application) afterwards.
It seems this should be possible using TerminateProcess, and I am able to close a batch file which is waiting at a "pause" command using this method... But if the batch file runs a DOS executable, or even a windows command such as "ping localhost -t", then using TerminateProcess has no effect.
Here is the code:
Quote
; This should be built as a console program
.686
.model flat, stdcall
option casemap :none ; case sensitive
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\masm32.inc
include \masm32\include\user32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\masm32.lib
includelib \masm32\lib\user32.lib
print MACRO Quoted_Text:VARARG
LOCAL Txt
.data
Txt db Quoted_Text,0
.code
invoke StdOut,ADDR Txt
ENDM
Main PROTO
.data
processtxt db "c:\test.bat",0
.data?
exitcode dd ?
.code
start:
invoke Main
invoke ExitProcess,0
Main proc
LOCAL startupinfo:STARTUPINFO
LOCAL processinfo:PROCESS_INFORMATION
;-------------------------
; Create a process
;-------------------------
invoke ClearScreen
print "Creating process...",10,13
mov startupinfo.cb,sizeof STARTUPINFO
invoke GetStartupInfo,addr startupinfo
invoke CreateProcess, NULL,
addr processtxt,
NULL,
NULL,
TRUE,
0,
NULL,
NULL,
addr startupinfo,
addr processinfo
.if eax==NULL
print "CreateProcess failed",10,13
jmp exit
.endif
invoke Sleep,10000 ; wait for 10 seconds
;---------------------------
; Terminate Process
;---------------------------
invoke TerminateProcess,processinfo.hProcess,offset exitcode
invoke GetExitCodeProcess,processinfo.hProcess,offset exitcode
cmp exitcode,STILL_ACTIVE
jne terminated
print "process failed to terminate!",0dh,0ah
jmp exit
terminated:
print "process terminated sucessfully",0dh,0ah
exit:
invoke Sleep,2000
ret
Main endp
end start
If test.bat contains only a "pause" command, then TerminateProcess works fine.
If test.bat contains only "ping localhost -t", then it says its sucessful, but its not.
If test.bat runs a DOS app, then it says its sucessful, but its not.
Does anyone know a solution?
Regards, Mike
Can't you just run the DOS app? or does the batch file do other things.
Have a look at WaitForSingleObject using your processinfo.hProcess handle, or make
your batch file create a file at the end and your program to detect that file...
In the future my application will need to be able to spawn any batch file that is passed on the command line, but I hardcoded test.bat here to keep it simple. While I do need to spawn a batch file, I see the identical problem if I spawn a DOS app directly without a batch file.
Normally these DOS applications will close themselfs after some time, but I need to be able to force the DOS application to close if something goes wrong.
I have tried creating a loop that checks the exitcode over and over, and does a Sleep,100 each time before looping again. The result is is that the exitcode does not equal STILL_ACTIVE immediatly after the TerminateProcess command, so it acts like it terminated sucessfully but it never does.
Regards, Mike
Maybe it's because a batch file needs CMD.EXE?
There was a topic on getting a file to self-delete (I think) and there were lots of win->bat ideas, have a search...
Again, I see the same problem when CreateProcess is given the name of the DOS file (.com or .exe) without a batch file.
I have done considerable searching on both the MASM32 board, and from Google but I have not been able to come up with any solution.
I located an MS knowledge base entry discussing closing 16-bit Windows apps, here:
http://support.microsoft.com/kb/182559/
The last paragraph mentions closing DOS applications:
None of the VDMDBG functions work with 16-bit DOS applications. To enumerate DOS VDMs, you need to use another method. First, you could use VDMEnumProcessWOW() to make a list of all Win16 VDMs, and then enumerate all instances of NTVDM.exe using some other scheme (such as PSAPI). Any NTVDM.exe from the full enumeration that was not in the Win16 list is a DOS VDM. You can create and terminate 16-bit DOS applications with CreateProcess() and TerminateProcess().
I don't follow how they expect this to be done, but the last line does confirm that I should be able to use CreateProcess and TerminateProcess, so it seems my code should work.
Regards, Mike
I think a 16-bit DOS process is a COM or EXE file, not a BAT file. If you used "CMD.EXE c:\test.bat" then
you could test for CMD.EXE (the batch interpreter) terminating, rather than the BAT file.
I tried spawning CMD.EXE (with and without /c) but both ways also failed to close either a specific DOS application (.com or .exe), or a batch file which runs a DOS app.
And as I mentioned, after posting the original msg I discovered it is not just a problem with batch files:
Quote
Again, I see the same problem when CreateProcess is given the name of the DOS file (.com or .exe) without a batch file.
Regards, Mike
After a lot more tinkering, I see why I was unable to spawn a specific DOS application (.com or .exe) - It was because my program was a Console application. I changed it back to a non-console, and now I can use TerminateProcess to close a specific DOS application.
So I am back to my original problem - It fails to terminate if I run my DOS app. from a batch file.
Here is an example: using a test.bat which only contains "c:\windows\system32\edit.com".
I did 3 tests, passing each of the following command lines to CreateProcess:
processtxt db "c:\test.bat",0
processtxt db "c:\windows\system32\cmd.exe /c c:\test.bat",0
processtxt db "c:\windows\system32\edit.com",0
The first two tests failed to Terminate, Yet the result said they were sucessful.
The third test did Terminate edit.com sucessfully, because a batch file was not used.
Is it impossible to close a program which has been run from a batch file?
Here is the non-console version of my code:
Quote
.386
.model flat, stdcall
option casemap :none ; case sensitive
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
szText MACRO Name, Text:VARARG
LOCAL lbl
jmp lbl
Name db Text,0
lbl:
ENDM
Main PROTO
.data
processtxt db "c:\windows\system32\edit.com",0
; processtxt db "c:\test.bat",0
; processtxt db "c:\winnt\system32\cmd.exe /c c:\test.bat",0
.data?
exitcode dd ?
.code
start:
invoke Main
invoke ExitProcess,0
Main Proc
LOCAL startupinfo:STARTUPINFO
LOCAL processinfo:PROCESS_INFORMATION
szText LmTitle,"Spawn Test"
;------------------
; Create a process
;------------------
mov startupinfo.cb,sizeof STARTUPINFO
invoke GetStartupInfo,addr startupinfo
invoke CreateProcess, NULL,
addr processtxt,
NULL,
NULL,
TRUE,
CREATE_NEW_CONSOLE AND CREATE_SEPARATE_WOW_VDM,
NULL,
NULL,
addr startupinfo,
addr processinfo
.if eax==NULL
szText CREATEFAILED,"Create Process Failed"
invoke MessageBox,NULL,addr CREATEFAILED,addr LmTitle,MB_OK
jmp exit
.endif
invoke Sleep,5000 ; Wait for 5 seconds
;------------------
; Terminate Process
;------------------
invoke TerminateProcess,processinfo.hProcess,offset exitcode
invoke Sleep,1000
invoke GetExitCodeProcess,processinfo.hProcess,offset exitcode
cmp exitcode,STILL_ACTIVE
jne terminated
szText TERMINATEFAILED,"Process Failed to Terminate"
invoke MessageBox,NULL,addr TERMINATEFAILED,addr LmTitle,MB_OK
jmp exit
terminated:
szText TERMINATED,"Process Terminated Sucessfully"
invoke MessageBox,NULL,addr TERMINATED,addr LmTitle,MB_OK
exit:
ret
Main ENDP
end start
Regards, Mike
pcMike,
After looking at your code, I notice that you are declaring your data globally but then you are using a local method in your invoke statements. This will cause EAX to behave in unexpected ways as it will be trashed. If you are going to declare your data globally (in .data) then in the invoke statements you need to use offset instead of addr. This will ensure that the value in EAX is preserved. If you wish to use addr then declare the data locally within the procedure.
Paul
PBrennick,
From what I've seen, my version of ML treats "ADDR global_x" the same as "OFFSET global_x" and pushes the 0040xxxx address,
and uses "lea eax,[ebp+?] / push eax" for "ADDR local_x".
Mind you, I always use ADDR for locals and OFFSET for globals, just to keep them straight...
Mike, how about sending the DOS window a CTRL-C scancode? Then maybe ENTER if needed. :thumbu
PBrennick and sinsi - Thanks for pointing out the ADDR/OFFSET diffferences, I was unaware of that.
Mark: Great idea on sending a CTRL-C... I checked how many of my DOS programs respond to Ctrl-C, and found only a few did, but some other DOS programs respond to Ctrl-Break. Still the majority dont respond to either one, so my program could try both, but if all else fails I'm back to needing TerminateProcess to work.
Could there be a Process Creation Flag that the CreateProcess needs to set in order for TerminateProcess to terminate what a batch file runs? I tried CREATE_SEPARATE_WOW_VDM and CREATE_SHARED_WOW_VDM without any luck.
Otherwise, is there a way I can determine what processes were started by a batch file?
Regards, Mike