2

I want to run a command in PowerShell and use the response in AutoHotkey. I have found a lot of information on how to run a PowerShell script, but none saying how I can use the response from it in AutoHotkey.

I have tried this:

MsgBox % ComObjCreate("WScript.Shell").Exec("powershell.exe -ExecutionPolicy Bypass -WindowStyle Hidden -noProfile -nologo dir").StdOut.ReadAll()

But this still flashes a window for a very brief time. I loop this command for every 25ms, so letting a window blink that often is not a valid solution.

Edit:

Ended up with this as the simplest solution:

cmd = powershell.exe -command "(Get-Process -Id " %pid% ").Threads[1].WaitReason"
shell := setup()
    Loop {
        string := shell.exec(cmd).stdout.readall()
        ...}


setup() {
    detecthiddenwindows on
    run %comspec% /k ,, hide useerrorlevel, pid
    winwait ahk_pid %pid%,, 10
    DllCall("AttachConsole", "uint", pid)
    con := DllCall("CreateFile"
        , "str", "CONOUT$", "uint", 0xC0000000, "uint", 7, "uint", 0, "uint", 3, "uint", 0, "uint", 0)

    oshell := comobjcreate("wscript.shell")

    return oshell
}
JonnyLee
  • 85
  • 7

1 Answers1

3

Note: Using AHK (AutoHotkey) with an external PowerShell process is ill-suited to a task that must run every 25ms, as you've discovered yourself - there's too much processing overhead.
If getting a directory listing is all that is needed, you can do that with built-in AHK features, using the Loop command for files - see this answer.

The solution below generally demonstrates how to run a console program:

  • hidden (no flashing windows)
  • synchronously (wait for it exit)
  • with its output captured

from AHK.


You can't use ComObjCreate("WScript.Shell").Exec() to run a console application hidden.

Conversely, while you can use RunWait to run hidden, you cannot use it to capture (console) output.

The workaround is to:

  • Use RunWait.

  • Add an output redirection to a (temporary) file to your console-program invocation.

  • Read that file's content with FileRead afterwards (and delete the temp. file).

; Get a temporary file path
tempFile := A_Temp "\" DllCall("GetCurrentProcessId") ".txt"                           ; "

; Run the console program hidden, redirecting its output to
; the temp. file (with a program other than powershell.exe or cmd.exe,
; prepend %ComSpec% /c; use 2> to redirect error output), and wait for it to exit.
RunWait, powershell.exe -ExecutionPolicy Bypass -WindowStyle Hidden -noProfile dir > %tempFile%,, Hide

; Read the temp file into a variable and then delete it.
FileRead, content, %tempFile%
FileDelete, %tempFile%

; Display the result.
MsgBox % content
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • It does work, but doing it 40 times a second puts 50% CPU load :/ I guess I have to look for another language to do this with. – JonnyLee Nov 18 '18 at 15:21
  • Thanks for the answer. It was not all that was needed (The actual command is in my edited post), and for this there seems to be no native approach. I also added an edit that shows a solution for this that doesn't require files. I have accepted your answer as it showed me that I need to use another tool for this task. – JonnyLee Nov 18 '18 at 16:33
  • Thanks, @JonnyLee - that's a pretty sophisticated solution and I encourage you to post it as a self-answer to a _new_ question focused on the performance aspect (a persistent, hidden console window that can be used for repeated console-program invocations, if I understand correctly) so that it's more easily discoverable. – mklement0 Nov 18 '18 at 18:29