Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Console application with ahk


  • Please log in to reply
9 replies to this topic
ahklerner
  • Members
  • 1386 posts
  • Last active: Oct 08 2014 10:29 AM
  • Joined: 26 Jun 2006
Credits:
Thanks to Lexikos for a simple demonstration.
Thanks to Huba for this post <!-- m -->http://www.autohotke...pic.php?t=18550<!-- m --> . I changed the names of the functions only to make them stdlib compatible.

;Init the console 
Console_Init()
Console_Write("Autohotkey Command Line Application Test`n")
Console_Write("----------------------------------------`n")
Console_Write("`n")
Console_Write("Welcome " . A_UserName . ". The Current Time is: " . A_Hour . ":" . A_Min . "`n")
Console_Write("What would you like to do today?`n")
ToDo := Console_GetUserInput()
Console_Write("Today you want to " . ToDo . ".`n")

Loop, 5
	{
	Console_Write("Exiting in " . 6 - A_Index . " seconds.`n")
	Sleep, 1000
	}
	
ExitApp

Console_Init(){
	if !(Process_GetModuleFileNameEx(Process_GetCurrentParentProcessID()) = COMSPEC)
		{
		;MsgBox % "Parent process is not '" . GetFileName(COMSPEC) . "'. The script will now be restarted.`nParent process name : " . Process_GetProcessName(Process_GetCurrentParentProcessID()) . "`nShould be: " . GetFileName(COMSPEC)
		If A_IsCompiled
			Run %comspec% /c ""%A_ScriptFullPath%"", , Hide
		else
			Run %comspec% /c ""%A_AhkPath%" "%A_ScriptFullPath%"", , Hide
		ExitApp
		}
	DllCall("AllocConsole")
	}

Console_Write(txt){
	FileAppend, %txt%, con
	}

Console_GetUserInput(){
	FileReadLine, l, con, 1
	return l
	}
	
GetFileName(pFile){
Loop %pFile%
	Return A_LoopFileName
	}

; ProcessInfo.ahk - Function library to retrieve various application process informations:
; - Script's own process identifier
; - Parent process ID of a process (the caller application)
; - Process name by process ID (filename without path)
; - Thread count by process ID (number of threads created by process)
; - Full filename by process ID (GetModuleFileNameEx() function)
;
; Tested with AutoHotkey 1.0.46.10
;
; Created by HuBa
; Contact: http://www.autohotkey.com/forum/profile.php?mode=viewprofile&u=4693
;
; Portions of the script are based upon the GetProcessList() function by wOxxOm
; (http://www.autohotkey.com/forum/viewtopic.php?p=65983#65983)

Process_GetCurrentProcessID(){
  Return DllCall("GetCurrentProcessId")  ; http://msdn2.microsoft.com/ms683180.aspx
}

Process_GetCurrentParentProcessID(){
  Return Process_GetParentProcessID(Process_GetCurrentProcessID())
}

Process_GetProcessName(ProcessID){
  Return Process_GetProcessInformation(ProcessID, "Str", 260, 36)  ; TCHAR szExeFile[MAX_PATH]
}

Process_GetParentProcessID(ProcessID){
  Return Process_GetProcessInformation(ProcessID, "UInt *", 4, 24)  ; DWORD th32ParentProcessID
}

Process_GetProcessThreadCount(ProcessID){
  Return Process_GetProcessInformation(ProcessID, "UInt *", 4, 20)  ; DWORD cntThreads
}

Process_GetProcessInformation(ProcessID, CallVariableType, VariableCapacity, DataOffset){
  hSnapshot := DLLCall("CreateToolhelp32Snapshot", "UInt", 2, "UInt", 0)  ; TH32CS_SNAPPROCESS = 2
  if (hSnapshot >= 0)
  {
    VarSetCapacity(PE32, 304, 0)  ; PROCESSENTRY32 structure -> http://msdn2.microsoft.com/ms684839.aspx
    DllCall("ntdll.dll\RtlFillMemoryUlong", "UInt", &PE32, "UInt", 4, "UInt", 304)  ; Set dwSize
    VarSetCapacity(th32ProcessID, 4, 0)
    if (DllCall("Process32First", "UInt", hSnapshot, "UInt", &PE32))  ; http://msdn2.microsoft.com/ms684834.aspx
      Loop
      {
        DllCall("RtlMoveMemory", "UInt *", th32ProcessID, "UInt", &PE32 + 8, "UInt", 4)  ; http://msdn2.microsoft.com/ms803004.aspx
        if (ProcessID = th32ProcessID)
        {
          VarSetCapacity(th32DataEntry, VariableCapacity, 0)
          DllCall("RtlMoveMemory", CallVariableType, th32DataEntry, "UInt", &PE32 + DataOffset, "UInt", VariableCapacity)
          DllCall("CloseHandle", "UInt", hSnapshot)  ; http://msdn2.microsoft.com/ms724211.aspx
          Return th32DataEntry  ; Process data found
        }
        if not DllCall("Process32Next", "UInt", hSnapshot, "UInt", &PE32)  ; http://msdn2.microsoft.com/ms684836.aspx
          Break
      }
    DllCall("CloseHandle", "UInt", hSnapshot)
  }
  Return  ; Cannot find process
}

Process_GetModuleFileNameEx(ProcessID)  ; modified version of shimanov's function
{
  if A_OSVersion in WIN_95, WIN_98, WIN_ME
    Return Process_GetProcessName(ProcessID)
  
  ; #define PROCESS_VM_READ           (0x0010)
  ; #define PROCESS_QUERY_INFORMATION (0x0400)
  hProcess := DllCall( "OpenProcess", "UInt", 0x10|0x400, "Int", False, "UInt", ProcessID)
  if (ErrorLevel or hProcess = 0)
    Return
  FileNameSize := 260
  VarSetCapacity(ModuleFileName, FileNameSize, 0)
  CallResult := DllCall("Psapi.dll\GetModuleFileNameExA", "UInt", hProcess, "UInt", 0, "Str", ModuleFileName, "UInt", FileNameSize)
  DllCall("CloseHandle", hProcess)
  Return ModuleFileName
}


Wouther
  • Members
  • 83 posts
  • Last active: Oct 02 2009 04:06 PM
  • Joined: 01 May 2007
Can you read my mind? :shock: This is exactly what I was looking for! Thank you very much! :D
Edit: If I run it from the command prompt, it opens a new window. Is it possible to write text to / get input from an already existing command prompt window, from which you called the script?

JBensimon
  • Members
  • 167 posts
  • Last active: Oct 18 2014 12:42 PM
  • Joined: 16 Nov 2004
Actually, it's fairly easy to turn a compiled AutoHotkey or AutoHotkey_L script into a console application, with the benefit that (a) when launched from a CMD windows, the prompt won't reappear until the program exists (the exit code will be available), and (B) text output to the console by the program (via FileAppend, %Text%, *) will appear immediately without the need to pipe to MORE or other similar workaround. As with all console apps, a CMD window will open automatically if the program is launched from Explorer.

How? You need to locate the first byte of the so-called "Subsystem" word in the compiled EXE's header and, with a Hex editor, change it from "02" (Windows GUI) to "03" (Console, aka Windows CUI). One reasonably easy way to get the absolute address of the "Subsystem" word is to use the utility TSFlag (http://dl.dropbox.co...ents/TSFlag.zip -- Syntax: TSFlag <filespec.exe>) on your compiled EXE and subtract 2 from the address displayed for its "DLL Characteristics" word (because the "Subsystem" word immediately precedes "DLL Characteristics" in an EXE header -- I might get around to updating TSFlag to explictly show the "Subsystem" entry and optionally change it -- TSFlag was designed for a different purpose: displaying and optionally changing the "Terminal Server Aware" flag).

Later,

Jacques.

AHKGuest
  • Guests
  • Last active:
  • Joined: --
I have tried changing it from "02" to "03" on a compiled AutoHotkey basic script but it says "EXE corrupted" with the CMD window. Why?

JBensimon
  • Members
  • 167 posts
  • Last active: Oct 18 2014 12:42 PM
  • Joined: 16 Nov 2004
Come to think of it, I really only tested with AutoHotkey_L scripts. For AutoHotkey, you can try either *not* compressing the .exe with upx.exe (then try the change again) *or* maybe you can make the change in AutoHotkeySC.bin *before* compiling. (I'll try some time this week-end and let you know).

Jacques.

Lexikos
  • Administrators
  • 9449 posts
  • Last active:
  • Joined: 17 Oct 2006
Modifying a AutoHotkey 1.0 compiled script invalidates its checksum, which prevents it from working. You must modify AutoHotkeySC.bin before compiling, instead.

JBensimon
  • Members
  • 167 posts
  • Last active: Oct 18 2014 12:42 PM
  • Joined: 16 Nov 2004
Thanks, Lexicos. I remembered something about that with AHK Classic.

This is also my first chance to thank you for picking up the project and for the great job you're doing with AHK_L, ... So Thank you! :-)

Jacques.

JBensimon
  • Members
  • 167 posts
  • Last active: Oct 18 2014 12:42 PM
  • Joined: 16 Nov 2004
I've run TSFlag.exe against AutoHotkeySC.bin (in the latest/last version of AutoHotkey "Classic") and conclude that the correct offset for the "Subsystem" word is 0x16C (and its value is 2). Create and save a copy of the file with a 3 at that location instead, then use it whenever you wish to compile a usable console app. [For AutoHotkey_L, the change can be made directly to the EXE after compilation, but remember that the offset could vary with the AHK_L version and "flavor"].

Jacques.

amnesiac
  • Members
  • 124 posts
  • Last active: May 01 2014 03:04 AM
  • Joined: 07 Nov 2010
The following lines can implement similar effect with Console_Init(). The only defect is that it can't reload the script with parameters.
if !InStr(StrGet(DllCall("GetCommandLine", "UPtr")), "/restart")
    reload
DllCall("AllocConsole")


Lexikos
  • Administrators
  • 9449 posts
  • Last active:
  • Joined: 17 Oct 2006

but remember that the offset could vary with the AHK_L version and "flavor"

It can be calculated by reading the file's PE header, as shown in this script written for AutoHotkey 1.0.

StrGet(DllCall("GetCommandLine", "UPtr"))

Well, that's one way two ways to prevent it from working on AutoHotkey 1.0. :?

DllCall("GetCommandLine", "Str")