Jump to content

Best way to send commands to the cmd shell while hiding cmd?


Recommended Posts

Hello everyone! I'm working on a cmd script that executes within a GUI when a button is pressed and I'm having trouble with some people who run the program. My goal is to open cmd invisible (or hidden) and then have it execute certain commands that AutoIt sends it (maybe there's a better way than sendkeys method?). I can't use a .bat file, so that's not an option.

Opt("WinTitleMatchMode", 2)
$enter = 400

; Run cmd and wait for it to start
Run("cmd.exe", "", @SW_HIDE)
WinWaitNotActive("cmd.exe", "", 15)

ControlSend($hWnd, "", "", "taskkill /f /im adb.exe /t")
sleep($enter)
ControlSend($hWnd, "", "", "{ENTER}")
sleep($enter)
ControlSend($hWnd, "", "", "taskkill /f /im cmd.exe /t")
sleep($enter)
ControlSend($hWnd, "", "", "{ENTER}")
sleep(1000)

That's just an example of what I have. There are a lot more commands and other things not mentioned, but there's an idea of how I'm looking at it.

Help is greatly appreciated, thanks!

Link to comment
Share on other sites

Awesome, thanks!! So is this how I would successfully use comspec?

Opt("WinTitleMatchMode", 2)
$enter = 400
$hWnd = "cmd.exe"

; Run cmd and wait for it to start
RunWait(@COMSPEC, "", @SW_HIDE)

ControlSend($hWnd, "", "", "taskkill /f /im adb.exe /t")
sleep($enter)
ControlSend($hWnd, "", "", "{ENTER}")
sleep($enter)
ControlSend($hWnd, "", "", "taskkill /f /im cmd.exe /t")
sleep($enter)
ControlSend($hWnd, "", "", "{ENTER}")
sleep(1000)

For each ControlSend, should I change the value of $hWnd to @COMSPEC? Thanks!

Link to comment
Share on other sites

I read up more on RunWait() and it's not what I'm looking for exactly. What's going to happen is within the cmd shell (I need it to stay open) there is going to be another shell opened (adb shell) and I will have to run commands within that shell within the cmd shell. Unfortunately, .bat files and other single-command methods will not allow for commands to be typed inside a shell within cmd, so that's why I can't use them.

RunWait (AFAIK) uses the cmd shell for one command to be initiated, then closes it. I need that window to stay open (but hidden when final compiling with @SW_HIDE) so multiple commands must be sent ultimately via "simulated keystrokes" such as SendKeys or in my case, ControlSend.

So as I'm grateful for you teaching me about @COMSPEC instead of jumping hoops finding the cmd.exe PID and assigning it there, I have another question:

What alternative to ControlSend would be more reliable and type into a hidden command shell properly 100% of the time? Are there any that will wait until the command is finished working (similar to RunWait) that WILL NOT close the cmd shell after working?

Thanks a bunch, more help leads to happiness!! ;):)

Link to comment
Share on other sites

@ JoHanatCent - I'm reading up on _RunDOS now, thanks!! And what do you mean by I can have multi tasks run from .bat files?

@ Calistoga - Yeah, Android phones are the best!! ;):)

So how does _RunDOS work? I read the helpfile and all it says is "RunDOS is a command in a hidden command window." Does that mean I stack all these commands right after each other (about 25) and then after the last _RunDOS it will automatically close DOS? Or does this run a command in DOS, close it, then run another command in DOS, then close it, etc.?

Thanks a bunch for the help guys, I would have never known about _RunDOS!!! ;)

Ex. How would I run these commands?

_RunDOS("cd c:\somefolder")
_RunDOS(someprogram.exe")
Edited by RDKamikaze
Link to comment
Share on other sites

Android is awesome indeed :) Are we talking HTC or something else?

The _RunDOS() function will create and destroy a separate cmd.exe process for each task you request it to perform. If we look at the example I posted a little up, you could say it goes as follows:

  • cmd.exe is launched.
  • Command ("taskkill /f /im adb.exe /t") is processed by cmd.exe
  • cmd.exe closes after the execution of the command finishes.
Then the whole process repeats for the next command you use. I agree with you that this looks inefficient and maybe a little clumsy. The alternative though, is to do as you did in the first post:

  • Create an instance of cmd.exe
  • Send each command using ControlSend().
I'm quoting the AutoIt help file:

ControlSend is only unreliable for command prompts as that works differently to normal windows (seems to check physical states rather than accepting the keystroke messages). For normal windows ControlSend should be way more reliable than a normal Send - and yes it does send shift, ctrl, alt etc.

Taking this into consideration, I think i would prefer the _RunDOS() way of doing it. That is, as long as these functions are not running every minute or so if you see what I mean. It is not as efficient as we would wish, but it does the job.

The example you mentioned, "cd c:\somefolder". Here you would eventually use another approach. Instead of interacting with cmd.exe, you would in this case use the AutoIt function FileChangeDir("c:\somefolder"). Just remember to change back to where you were originally if that is of importance for the execution of your program.

However, even though I have been using AutoIt for some time now, there's still a lot I don't know about. I'm learning every day. So if someone else got a better idea I would love to learn about it.

;)

Edited by Calistoga
Link to comment
Share on other sites

Android is awesome indeed ;) Are we talking HTC or something else?

The _RunDOS() function will create and destroy a separate cmd.exe process for each task you request it to perform. If we look at the example I posted a little up, you could say it goes as follows:

  • cmd.exe is launched.
  • Command ("taskkill /f /im adb.exe /t") is processed by cmd.exe
  • cmd.exe closes after the execution of the command finishes.
Then the whole process repeats for the next command you use. I agree with you that this looks inefficient and maybe a little clumsy. The alternative though, is to do as you did in the first post:

  • Create an instance of cmd.exe
  • Send each command using ControlSend().
I'm quoting the AutoIt help file:

Taking this into consideration, I think i would prefer the _RunDOS() way of doing it. That is, as long as these functions are not running every minute or so if you see what I mean. It is not as efficient as we would wish, but it does the job.

The example you mentioned, "cd c:\somefolder". Here you would eventually use another approach. Instead of interacting with cmd.exe, you would in this case use the AutoIt function FileChangeDir("c:\somefolder"). Just remember to change back to where you were originally if that is of importance for the execution of your program.

However, even though I have been using AutoIt for some time now, there's still a lot I don't know about. I'm learning every day. So if someone else got a better idea I would love to learn about it.

;)

We're talking about the Droid 1 (A855). If you really want to know (because I don't know if I'm allowed to talk about it here) PM me or just google my username and you'll find out :)

The example I gave of the two different commands for cd was just an example of showing how two commands work together, not necessary needing that exact command. I also saw that message from the helpfile that you quoted, but I don't get it exactly; why would ControlSend be unreliable in cmd?

I have made the program I'm working on more reliable (I think) by removing "cmd.exe" as the value for $hWnd (see above) and changed it with @ComSpec and a few commands I was able to change to _RunDOS, but a majority is still ControlSend. So you don't think there's anything better than controlsend for entering multiple commands in one cmd window?

I have another question! I'm confused (have been since forever ago) about loops. I bet a loop would help remove a LOT of clutter and make my program flow much more efficiently, but I have no clue how to use them. I'm betting that I could make variables that are linked to each command I send with ControlSend (ex. $val1, $val2, $val3, etc.) and just have a loop increase the $val# as well as execute that command's stuff, then loop to the next one until it does all 30-some-odd commands. Any help with that idea would be great!!

Thanks again for your help guys, you are a REALLY nice forum!!! :shocked::)

Edited by RDKamikaze
Link to comment
Share on other sites

I used _RunDOS() as the example here, but I see what you mean. I'm not sure if is was something like this you asked for, I'm a little slow in 'getting' things today ;)

Don't mind me using semicolons after every line, it's a habit I got from C# and now I can't stop doing it :)

Include <Process.au3>

Local $a_commands[3];

$a_commands[0] = "msg * Command 1";
$a_commands[1] = "msg * Command 2";
$a_commands[2] = "msg * Command 3";

; This loop will iterate over every item in the array $a_commands.
; The currently evaluated item will always be $s_command.
For $s_command In $a_commands
    
    _RunDOS($s_command);
    
Next

; This loop works by incrementing $i by 1 every time the loop begins from the top and down.
; You can access the current element in the array by doing as shown below.
; E.g. $a_commands[1] will return the second element in this array.
For $i = 0 To UBound($a_commands)
    
    _RunDOS($a_commands[$i]);
    
Next

I agree with you on the cmd window part. It sounds kind of ridiculous to in reality open 25 independent instances of cmd.exe just because we got tasks to do. But I'm not aware of any "nice and clean" way of doing it without using ControlSend() etc. I would love to know about one though ;) I guess there is no really good reason for not using ControlSend() but I've personally always preferred to use API's etc. You know.

Edited by Calistoga
Link to comment
Share on other sites

Hold on, I just had a question answered (finally) in another forum from weeks ago ;) They taught me how to run commands in another shell in DOS by opening adb shell and calling a .txt file with the commands and I think it might work (can't test until later)!!

If this way works that the guys are saying, can't I just theoretically do what you said and Run("file.bat") it?

Another random question if you know: How could I get a .bat file to work within a temp directory? My goal is to have my program leave no trace on someone's computer by installing in a temp directory when being run.

Link to comment
Share on other sites

I did not propose using a bat file because I personally despise temporary files. That is just me though, and to be pragmatic, it would solve this problem more or less.

You could do something like this: (note that I have not tested this function, you might have to change something to satisfy the interpreter)

#include <Process.au3>

Func _CMD_RunScript()
    
    Local $s_batPath = @AppDataDir & \"adb.bat"; Where we want to extract the .bat
    
    If Not (FileInstall("adb.bat", $s_batPath, 1)) Then Return SetError(1, 0, False); Could not extract .bat
    
    _RunDOS($s_batPath); We'll let cmd.exe handle the .bat
    
    If (FileDelete($s_batPath)) Then Return SetError(0, 0, True); Everything OK
    
    Return SetError(2, 0, False); Unable to delete the .bat
    
EndFunc

;)

Link to comment
Share on other sites

I see where you're going with that! Install a bat then delete it after execution, smooooth ;) What I don't get is how do I install the .bat? Where do I put it so that AutoIt will see it and put it into the AppData?

So that gets me thinking into another thing:

Can I put multiple files into one executable? I have been compiling with WinRAR SFX for .exe and having it run the .exe converted from .au3 BUT is there a way to do this automagically all inside AutoIt? Also, a problem I have seen a couple people have with my way of doing things is they have Windows installed on the D: drive or L: drive (what the heck??) so C: won't work.

How could I make all this floatable and essentially "portable" so it could be used on any drive, anywhere?

Is there a way to get rip out hardware drivers from an installer (ex. Moto Drivers) and magically install just the drivers the user would need? I want to make this as light and painless as possible lol!

Edited by RDKamikaze
Link to comment
Share on other sites

$sWindrive = StringLeft(@WindowsDir, 3)

George

Question about decompiling code? Read the decompiling FAQ and don't bother posting the question in the forums.

Be sure to read and follow the forum rules. -AKA the AutoIt Reading and Comprehension Skills test.***

The PCRE (Regular Expression) ToolKit for AutoIT - (Updated Oct 20, 2011 ver:3.0.1.13) - Please update your current version before filing any bug reports. The installer now includes both 32 and 64 bit versions. No change in version number.

Visit my Blog .. currently not active but it will soon be resplendent with news and views. Also please remove any links you may have to my website. it is soon to be closed and replaced with something else.

"Old age and treachery will always overcome youth and skill!"

Link to comment
Share on other sites

Wow, that was fast!! Thanks for the replies guys, I'm really hoping to make this program really super awesome ;)

Side note: Do you know how to tell a .bat file the system drive? I'm not familiar with auto-detecting the drive letter on cmd, it would save me some googling if anyone knew :)

Link to comment
Share on other sites

Wow, that was fast!! Thanks for the replies guys, I'm really hoping to make this program really super awesome ;)

Side note: Do you know how to tell a .bat file the system drive? I'm not familiar with auto-detecting the drive letter on cmd, it would save me some googling if anyone knew :)

If you are going to use AutoIt to do this you can either buid the .bat file from AutoIt with all the info

OR

Pass the info to the .bat when you Run the .bat file from AutoIt.

$dir = "D:"
Run(@ScriptDir & "\try.bat "&$dir, "", @SW_Hide)

In the above remember to add %1 for example in the .bat file.

Edited by JoHanatCent
Link to comment
Share on other sites

Here's how I do It without messy Send(), ControlSend(), or external batch files.

; Set Constants
Const $STDIN_CHILD = 1
Const $STDOUT_CHILD = 2

; Set Buffer
Dim $buffer

; Start CMD
$cmd = Run("cmd.exe", @WindowsDir & "\system32\", @SW_HIDE, $STDIN_CHILD + $STDOUT_CHILD

; Send Commands
$command = "Echo Hello"
StdinWrite($cmd, $command & @LF)

; Read Outputed Data
While 1
    
    $cmdOut = StdoutRead($cmd)

    If $cmdOut <> $buffer Then
        $buffer = $cmdOut
        ConsoleWrite($buffer)
    EndIf
    Sleep(10)
Wend
Edited by Deltaforce229

[size="1"]Please stop confusing "how to" with "how do"[/size]

Link to comment
Share on other sites

Thanks! I kind of combined your efforts with an idea I got about an hour ago and placed the .bat inside the same folder as my .exe, therefor being able to do this:

Global $bat = @ScriptDir & "\log.bat"
RunWait($bat, @ScriptDir, @SW_HIDE)

This took me some trial and error but alas, I achieved my gold of running it in the same directory of the .au3 (converted to .exe) so I can essentially run the program from anywhere ;)

I'm going to compile now and the only trick left is figuring out which compiler to use. WinRAR I don't think will run a file after extracting in a temp directory, whereas 7-ZIP's SFX maker I think will. If you have input on that I would love it!!

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...