News:

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

Using TerminateProcess to close a .bat file.

Started by pcMike, September 03, 2006, 05:17:19 AM

Previous topic - Next topic

pcMike

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


sinsi

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...
Light travels faster than sound, that's why some people seem bright until you hear them.

pcMike

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

sinsi

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...
Light travels faster than sound, that's why some people seem bright until you hear them.

pcMike

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

sinsi

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.
Light travels faster than sound, that's why some people seem bright until you hear them.

pcMike

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

pcMike

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

PBrennick

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
The GeneSys Project is available from:
The Repository or My crappy website

sinsi

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...
Light travels faster than sound, that's why some people seem bright until you hear them.

Mark Jones

Mike, how about sending the DOS window a CTRL-C scancode? Then maybe ENTER if needed. :thumbu
"To deny our impulses... foolish; to revel in them, chaos." MCJ 2003.08

pcMike

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