7

I'm trying to read Windows CMD's stdout with AutoHotkey. For example, I'd like to have the output of the setconsole command inside AHK stored in a variable. I already achieved it a while ago, which makes me all the more perplex why it's not working now.
In the AHK forums, there's a rather old thread about CMDret, a DLL based functionality to do exactly what I want. The first problem was to find a working download for it, since all the links in the post were dead. Google gave me another site, hosting v3.1.2. Altough there seems to be a newer one (v3.2.1 respectively 4d Beta), I checked it out and tested a simple example:

msgbox % CMDret(COMSPEC " /C set")
CMDret(CMD) 
{ 
  VarSetCapacity(StrOut, 10000) 
  RetVal := DllCall("cmdret.dll\RunReturn", "str", CMD, "str", StrOut)
  Return, %StrOut%
}

Unfortunately, the MsgBox contained nothing. I then checked out RetVal which had a value of 0; and the attached readme says:

If the function fails, the return value is zero.

Further down, it says:

Note: only 32 bit console applications will currently work with the this dll version of CMDret (v3.1.2 or lower). Calls that require command.com will likely not produce any output and may crash. To avoid this I have included a file named "cmdstub.exe" with the download (in the Win9x folder). This file should be used when calling 16 bit console applications to enable returning output.

In conclusion, I am not sure what the problem is. My machine is running on 64 bit. But is the corresponding clause in the readme supposed to solely exclude 16 bit systems or does it rather only include 32 bit?
If the computing architecture is probably not the problem, then what could be?

What I am looking for is either one of the following:

  1. Can I fix the problem and keep using v3.1.2?
  2. Has anyone a working source (or even a local copy) of a newer version I could check out?
  3. Is there another approach [library, .ahk code, etc.] I could use for my purpose? (preferably similar, because CMDret seems very straightforward)
MCL
  • 3,985
  • 3
  • 27
  • 39
  • Hey! ;) do you still require an answer?, I was just browsing through and I found this question, It can be done entirely in ahk, I will post an answer when I can... I'm currently traveling... – Joe DF Aug 23 '14 at 15:32

6 Answers6

4

New recommended 2 ways of doing as of Nov 2019 - https://www.autohotkey.com/docs/FAQ.htm#output:

How can the output of a command line operation be retrieved?

Testing shows that due to file caching, a temporary file can be very fast for relatively small outputs. In fact, if the file is deleted immediately after use, it often does not actually get written to disk. For example:

RunWait %ComSpec% /c dir > C:\My Temp File.txt
FileRead, VarToContainContents, C:\My Temp File.txt
FileDelete, C:\My Temp File.txt

To avoid using a temporary file (especially if the output is large), consider using the Shell.Exec() method as shown in the examples for the Run command.

Example for the Run command - https://www.autohotkey.com/docs/commands/Run.htm#StdOut:

MsgBox % RunWaitOne("dir " A_ScriptDir)


RunWaitOne(command) {
    shell := ComObjCreate("WScript.Shell")
    exec := shell.Exec(ComSpec " /C " command)
    return exec.StdOut.ReadAll()
}

Note: the latter method (shell.Exec) will cause quick display of a cmd window. You can reduce the duration of its appearance by putting these lines at the top of your script, which will also cause the flickering to happen only once the first time you call the cmd command. From https://autohotkey.com/board/topic/92032-how-to-hide-a-wscript-comspec-window/:

;Following 2 lines : the cmd window will flash only once and quickly
DllCall("AllocConsole")
WinHide % "ahk_id " DllCall("GetConsoleWindow", "ptr")
amynbe
  • 501
  • 7
  • 12
3

If you don't need a live output, you could use the cmd box itself to save a text file of itself and then you could have autohotkey detect when the console's PID finished (using the returned result of runwait and read the outputted file into memory.

So you could do this in your run command (just a regular cmd parameter):

ipconfig > myoutput.txt
exit

Now you have a text file with the ipconfig output in it.

OR you could do the same thing, but instead of outputting to a text file, you could output to the clipboard, like this:

ipconfig | clip

Then, since the output is on the clipboard, you can easily grab it into autohotkey.

bgmCoder
  • 6,205
  • 8
  • 58
  • 105
  • I see a security problem here, too. Since I'm handling sensitive data, I don't want my passwords to show up in a file or in the clipboard. Also, I need the ability to capture a stream in real time. I think using these methods, it would be rather complicated. – MCL Jun 28 '13 at 15:34
  • Well, of course, you would have to deal with that in the script. If it is sensitive, then you can't use the clipboard because something else may be tracking it. However, if you output to a text file, and load the file into memory, then you can erase the text file and delete it. I do it in some of my own applications and it works like a charm. It only takes three or four lines of autothotkey code to do it. – bgmCoder Jun 28 '13 at 18:23
  • Files can also be accessed by any other process. Of course, the file doesn't exist very long and maybe I'm a bit paranoid, but there's still a chance that I don't want to take. Especially since AHK only deletes the files, which doesn't make the data unreadable. – MCL Jun 29 '13 at 01:38
  • Hmmm... if you erase the contents before you delete it (using `fileopen`). – bgmCoder Jun 29 '13 at 02:14
  • This is brilliant! I'm using this command to save my WAN IP address into a variable: `nslookup myip.opendns.com resolver1.opendns.com | find /i "address" | find /v "208.67.222.222" | clip` – Shayan Jun 14 '19 at 14:27
1

How about this script, StdoutToVar ?
It has support for 64bit consoles. http://www.autohotkey.com/board/topic/15455-stdouttovar/page-7

bgmCoder
  • 6,205
  • 8
  • 58
  • 105
  • I also found this but haven't tested it https://github.com/camerb/AHKs/blob/master/thirdParty/StdoutToVar.ahk – Shayan Jun 14 '19 at 14:26
1

This has been bugging me for some time now - and finally this works !

The only prerequisite for this is MS sqlcmd.exe, a database called AHK_Dev and of course AHK_DBA to read the value when you wish to make use of it.

PS. make sure you replace {yourDir} and {yourServer} with you own values!

USE AHK_DEV
CREATE TABLE [dbo].[AHK_DOS]([dos_out] [varchar](max) NULL) ON [PRIMARY];
insert into ahk_dos select 'empty'

Create the follow script ... call it dos_out.bat

@echo off
if "%1" == "" ( 
    set v_cmd=""
) else (
    set v_cmd=%1
)
set v_cmd=%v_cmd:~1,-1%
SETLOCAL ENABLEDELAYEDEXPANSION
if "!v_cmd!" == "" (
    set v_cmd="echo ... %COMPUTERNAME% %USERNAME% %DATE% %TIME%"
    set v_cmd=!v_cmd:~1,-1!
)
set v_data=""
FOR /F "usebackq delims=¬" %%i in (`!v_cmd!`) do (
    set v_data="!v_data:~1,-1!%%i~"
)
set q_cmd="set nocount on;update ahk_dos set dos_out=N'!v_data:~1,-1!'"
"{yourDir}\Microsoft SQL Server\90\Tools\Binn\sqlcmd.exe" -S {yourServer} -E -d ahk_dev -Q !q_cmd! -W 
set q_cmd="set nocount on;select len(dos_out) as out_len, dos_out from ahk_dos"
"{yourDir}\Microsoft SQL Server\90\Tools\Binn\sqlcmd.exe" -S {yourServer} -E -d ahk_dev -Q !q_cmd! -W -w 8000
pause

you can run it from AHK using...

dosCmd2db(c) {
runwait, {yourDir\}dos_out.bat "%c%", , , dospid
msgbox %dospid% closed
}
dosCmd2db("")
dosCmd2db("echo This is a test")
dosCmd2db("dir")

As the same field is being updated each time, you would clearly need to do something between each one to make this example useful!

Try it, and let me know how you get on

Regards, Geoff

geoff
  • 11
  • 1
  • Ugh! To my understanding, this will create and write/read from a local database? That sounds awfully extensive. Also, one of my requirements is that no data is accessible to other processes, since I want to pass passwords as parameters. I don't want them to show up in a database. – MCL Feb 21 '14 at 17:27
1

Just an update to @amynbe answer.

MsgBox % RunWaitOne("dir " A_ScriptDir)


RunWaitOne(command) {
    shell := ComObjCreate("WScript.Shell")
    exec := shell.Exec(ComSpec " /C " command)
    return exec.StdOut.ReadAll() }

Note: the latter method (shell.Exec) will cause quick display of a cmd window. You can reduce > the duration of its appearance by putting these lines at the top of > your script, which will also cause the flickering to happen only once > the first time you call the cmd command.

You can just do this below to hide cmd and avoid flashing.

MsgBox % RunWaitOne("dir " A_ScriptDir)


RunWaitOne(command) {
  DetectHiddenWindows On
  Run %ComSpec%,, Hide, pid
  WinWait ahk_pid %pid%
  DllCall("AttachConsole", "UInt", pid)

  shell := ComObjCreate("WScript.Shell")
  exec := shell.Exec(ComSpec " /C " command)

  DllCall( "FreeConsole" )
    return exec.StdOut.ReadAll()
}
0

I found a script only solution that works for AutoHotKey L 64bit at: http://www.autohotkey.com/board/topic/67687-ahkahk-lusing-wsh-to-interact-with-command-line-progs/

After playing with it a bit I was able to capthre the entire output of a 40k text file that I listed using the DOS Type command. There is a demo that shows how you can interact with time command, which is nice if you need limited two way interaction with a dos command or batch script.

  • I found that one, too in the meantime. Unfortunately, it always produces a window and I want to avoid that, especially since I'm calling utilities with sensitive data like passwords. – MCL Jun 21 '13 at 14:55