; ; ConsoleApps V2 ; ; AutoHotkey Version: 1.x ; Language: English ; Platform: Win9x/NT ; Author: Marcus Cortes ; ; Script Function: ; Provides a set of functions to redirect and capture ; the standard input and output of other programs. ; #NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases. ;;; ;;; Runs an application and retrieves its standard output. ;;; ;;; ;;; The executable file to run including any path information and arguments. ;;; ;;; ;;; The startup directory for the program. ;;; ;;; ;;; Specifies a variable to receive the ProcessID of the program. This parameter is optional. ;;; ;;; ;;; When the function returns, this parameter is set to the exit code of the program. ;;; ;;; ;;; Returns the StdOut of the program. ;;; ;;; ;;; The output of programs that are explicity redirected or "piped" in their command-lines will ;;; not be captured by this function (e.g. "cmd.exe /C echo Hello World > test.txt"). ;;; ;;; ;;; MsgBox, % ConsoleApp_RunWait("cmd.exe /c echo Hello World.") ;;; ConsoleApp_RunWait(CmdLine, WorkingDir="", byref ExitCode="") { global local ConsoleAppHandle, ConsoleAppStillRunning, StdOut, BytesAppended, FirstWin32Error ConsoleApps_Initialize() ; Create the process ConsoleAppHandle := ConsoleApp_Run(CmdLine, WorkingDir) ; In reality, this function will never return with a ConsoleAppHandle value of 0, but ; it is good to check for this possibility in case future versions do return a value of ; 0 for failure. if (ConsoleAppHandle == 0) CONSOLEAPPS_PRIVATE_throw(ERROR_GENERIC_ERROR, "Unable to run console application.") /* Continuously retrieve the process's output and write it to the edit control * in a loop as it arrives. */ Loop { ; Append the ConsoleApps standard output to StdOut. ConsoleAppStillRunning := ConsoleApp_GetStdOut(ConsoleAppHandle, StdOut, BytesAppended, ExitCode) ; If the ConsoleApp is no longer running, then stop checking for output. if (!ConsoleAppStillRunning) break ; Give up remainder of time-slice to give the child process a chance to generate more output ; before checking again. sleep 0 } ; This function must be called when the ConsoleAppHandle is no longer needed. ConsoleApp_CloseHandle(ConsoleAppHandle) return StdOut } ;;; ;;; Runs an application and redirects its standard input/output handles such that they can be accessed ;;; from the script using the ConsoleApp_GetStdOut() function. ;;; ;;; ;;; The executable file to run including any path information and arguments. ;;; ;;; ;;; The startup directory for the program. ;;; ;;; ;;; This parameter is reserved for future use and should be a null string ("") or should be omitted. ;;; ;;; ;;; Specifies a variable to receive the ProcessID of the program. This parameter is optional. ;;; ;;; ;;; Returns a proprietary handle to the ConsoleApp for use in calls to ConsoleApp_GetStdOut() and ;;; ConsoleApp_CloseHandle(). This handle cannot be specified in any Win32 API functions to identify ;;; a process. ;;; ;;; ;;; If this function completes successfully, the handle returned by this function must be closed ;;; when no longer needed using the ConsoleApp_CloseHandle() function. ;;; The output of programs that are explicity redirected or "piped" in their command-lines will ;;; not be captured in calls to ConsoleApp_GetStdOut (e.g. "cmd.exe /C echo Hello World > test.txt"). ;;; ;;; ;;; ConsoleAppHandle := ConsoleApp_Run("cmd.exe /c echo Hello World.") ;;; if (ConsoleAppHandle == 0) ;;; MsgBox, Unable to start cmd.exe ;;; else ;;; ConsoleApp_CloseHandle(ConsoleAppHandle) ;;; ConsoleApp_Run(CmdLine, WorkingDir="", Reserved="", byref PID="") { global local sa, pi, si, lpCurrentDirectory local hStdoutRead, hStdoutWrite ; local hStdinRead, hStdinWrite local FirstWin32Error local hHeap, lpCAPI static RedProcNextHandle := 1 ConsoleApps_Initialize() CONSOLEAPPS_PRIVATE_calloc(pi, 16, 0) ;PROCESS_INFORMATION CONSOLEAPPS_PRIVATE_calloc(si, 4*18, 0) ;STARTUP_INFO NumPut(4*18, si, 0, "uint") ;STARTUP_INFO {HANDLE cbSize} NumPut(0x100 | 0x1, si, 4*11, "uint") ;STARTUP_INFO {dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW} ;NumPut(0x0, si, 4*12, "ushort") ;STARTUP_INFO {wShowWindow = SW_HIDE} ;ALLOC HANDLES CONSOLEAPPS_PRIVATE_malloc(hStdoutRead, 4) CONSOLEAPPS_PRIVATE_malloc(hStdoutWrite, 4) CONSOLEAPPS_PRIVATE_malloc(hStdinRead, 4) CONSOLEAPPS_PRIVATE_malloc(hStdinWrite, 4) ;ALLOC AND INIT SECURITY_ATTRIBUTES STRUC CONSOLEAPPS_PRIVATE_calloc(sa, 12, 0) ;security attributes NumPut(12, sa, 0, "uint") NumPut(0, sa, 4, "uint") NumPut(true, sa, 8, "int") ;CREATE PIPE FOR STDOUT if (!DllCall("CreatePipe", "uint", &hStdoutRead, "uint", &hStdoutWrite, "uint", &sa, "uint", 0)) CONSOLEAPPS_PRIVATE_throw(CONSOLEAPPS_PRIVATE_ERROR_WIN32_ERROR, "Unable to create stdout pipe: ") hStdoutRead := NumGet(hStdoutRead, 0, "uint") hStdoutWrite := NumGet(hStdoutWrite, 0, "uint") if (!DllCall("SetHandleInformation", "uint", hStdoutRead, "uint", 1, "uint", 0, "int")) { FirstWin32Error := A_LastError DllCall("CloseHandle", "uint", hStdoutRead) ;HANDLE hThread DllCall("CloseHandle", "uint", hStdoutWrite) ;HANDLE hThread CONSOLEAPPS_PRIVATE_throw(CONSOLEAPPS_PRIVATE_ERROR_WIN32_ERROR, "Unable to set handle information: ", "", FirstWin32Error) } /* ;CREATE PIPE FOR STDIN if (!DllCall("CreatePipe", "uint", &hStdinRead, "uint", &hStdinWrite, "uint", &sa, "uint", 0)) { FirstWin32Error := A_LastError DllCall("CloseHandle", "uint", hStdoutRead) ;HANDLE hThread DllCall("CloseHandle", "uint", hStdoutWrite) ;HANDLE hThread CONSOLEAPPS_PRIVATE_throw(CONSOLEAPPS_PRIVATE_ERROR_WIN32_ERROR, "Unable to create stdin pipe: ", "", FirstWin32Error) } hStdinRead := NumGet(hStdinRead, 0, "uint") hStdinWrite := NumGet(hStdinWrite, 0, "uint") if (!DllCall("SetHandleInformation", "uint", hStdinWrite, "uint", 1, "uint", 0, "int")) { FirstWin32Error := A_LastError DllCall("CloseHandle", "uint", hStdoutRead) ;HANDLE hThread DllCall("CloseHandle", "uint", hStdoutWrite) ;HANDLE hThread DllCall("CloseHandle", "uint", hStdinRead) ;HANDLE hThread DllCall("CloseHandle", "uint", hStdinWrite) ;HANDLE hThread CONSOLEAPPS_PRIVATE_throw(CONSOLEAPPS_PRIVATE_ERROR_WIN32_ERROR, "Unable to set handle information: ", "", FirstWin32Error) } */ ;USE PIPE HANDLES FOR STDIO ; NumPut(hStdinRead, si, 4*14, "uint") ;STARTUP_INFO {HANDLE hStdInput} NumPut(hStdoutWrite, si, 4*15, "uint") ;STARTUP_INFO {HANDLE hStdOutput} NumPut(hStdoutWrite, si, 4*16, "uint") ;STARTUP_INFO {HANDLE hStdError} if (StrLen(WorkingDir) == 0) lpCurrentDirectory := 0 else lpCurrentDirectory := &WorkingDir if (!DllCall("CreateProcess", "uint", 0 , "uint", &CmdLine , "uint", 0 , "uint", 0 , "int", true , "uint", 0 , "uint", 0 , "uint", lpCurrentDirectory , "uint", &si , "uint", &pi)) { FirstWin32Error := A_LastError DllCall("CloseHandle", "uint", hStdoutRead) ;HANDLE hThread DllCall("CloseHandle", "uint", hStdoutWrite) ;HANDLE hThread ; DllCall("CloseHandle", "uint", hStdinRead) ;HANDLE hThread ; DllCall("CloseHandle", "uint", hStdinWrite) ;HANDLE hThread CONSOLEAPPS_PRIVATE_throw(CONSOLEAPPS_PRIVATE_ERROR_WIN32_ERROR, "Unable to run process: ", "", FirstWin32Error) } hProcess := NumGet(pi, 0, "uint") hThread := NumGet(pi, 4, "uint") PID := NumGet(pi, 8, "uint") ;DWORD dwProcessId DllCall("CloseHandle", "uint", hThread) ;HANDLE hThread ; struct ConsoleAppsProcessInfo ; { ; int hProcess; ; int hStdoutRead; ; int hStdoutWrite; ; //int hStdinRead; ; //int hStdinWrite; ; } hHeap := DllCall("GetProcessHeap", "uint") lpCAPI := DllCall("HeapAlloc", "uint", hHeap, "uint", 0, "uint", 0xC, "uint") NumPut(hProcess, lpCAPI + 0x0) NumPut(hStdoutRead, lpCAPI + 0x4) NumPut(hStdoutWrite, lpCAPI + 0x8) ; NumPut(hStdinRead, lpCAPI + 0xC) ; NumPut(hStdinWrite, lpCAPI + 0x10) return lpCAPI } ;;; ;;; Retrieves the standard output of a ConsoleApp that was run using ConsoleApp_Run(). ;;; ;;; ;;; A handle to a ConsoleApp that was created in a call to ConsoleApp_Run. ;;; ;;; ;;; A variable that is to be appended with the standard output of the ConsoleApp. The ;;; variable is appended with all of the standard output of the program since the last ;;; call to ConsoleApp_GetStdOut. ;;; ;;; ;;; A variable that is to receive the number of bytes that were appended to the Stdout ;;; parameter. ;;; ;;; ;;; Returns true if the ConsoleApp is still running. ;;; Returns false if the ConsoleApp is no longer running. ;;; ;;; ;;; The output of programs that are explicity redirected or "piped" in their command-lines will ;;; not be captured by this function (e.g. "cmd.exe /C echo Hello World > test.txt"). ;;; ;;; ;;; ; To run this example perform the following steps: ;;; ; 1. Create a folder named ConsoleApps ;;; ; 2. Copy "ConsoleApps.ahk" to the folder ;;; ; 3. Create 2 new files in the folder named "ConsoleApps_Test.ahk" and "ConsoleApps_TestFile.bat" ;;; ; 4. Paste the following text (without the comments) into "ConsoleApps_TestFile.bat": ;;; @ECHO OFF ;;; ECHO Processing %2 1 of 3 ;;; ping 127.0.0.1 -n 2 -w 1000 > nul ;;; ping 127.0.0.1 -n 1 -w 1000 > nul ;;; ECHO Processing %2 2 of 3 ;;; ping 127.0.0.1 -n 2 -w 1000 > nul ;;; ping 127.0.0.1 -n 1 -w 1000 > nul ;;; ECHO Processing %2 3 of 3 ;;; ping 127.0.0.1 -n 2 -w 1000 > nul ;;; ping 127.0.0.1 -n 1 -w 1000 > nul ;;; ECHO Processing Complete;;; ; ;;; ; 5. Paste the following text (without the comments) into "ConsoleApps_Test.ahk", and then run "ConsoleApps_Test.ahk" ;;; #NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases. ;;; #Include ConsoleApps.ahk ;;; ;;; ;Create GUI window with an edit control to print the output. ;;; Gui, Add, Edit, x15 y15 w460 h300 -Wrap ReadOnly hscroll vtxtStdout, ;;; Gui, Show, w490 h330, Standard Input/Output Redirection Example ;;; ;;; ; Create the process ;;; ConsoleAppHandle := ConsoleApp_Run("ConsoleApps_TestFile.bat -p nothing", A_ScriptDir) ;;; ; In reality, this function will never return with a ConsoleAppHandle value of 0, but ;;; ; it is good to check for this possibility in case future versions do return a value of ;;; ; 0 for failure. ;;; if (ConsoleAppHandle == 0) ;;; { ;;; MsgBox, Error running cmd.exe ;;; ExitApp ;;; } ;;; ;;; /* Continuously retrieve the process's output and write it to the edit control ;;; * in a loop as it arrives. ;;; */ ;;; Loop ;;; { ;;; ; Append the ConsoleApps standard output to StdOut. ;;; ConsoleAppStillRunning := ConsoleApp_GetStdOut(ConsoleAppHandle, StdOut, BytesAppended, ExitCode) ;;; ; If StdOut was appended with new text, write it to the GUI window. ;;; if (BytesAppended) ;;; GuiControl, , txtStdOut, %StdOut% ; write the output to the edit control. ;;; ; If the ConsoleApp is no longer running, then stop checking for output. ;;; if (!ConsoleAppStillRunning) ;;; break ;;; ; Give up remainder of time-slice to give the child process a chance to generate more output ;;; ; before checking again. ;;; sleep 0 ;;; } ;;; ; This function must be called when the ConsoleAppHandle is no longer needed. ;;; ConsoleApp_CloseHandle(ConsoleAppHandle) ;;; if (!ExitCode) ;;; MsgBox, The program has completed successfully. ;;; else ;;; MsgBox, The program has exitted with an error code of %ExitCode%. ;;; return ;;; ;;; ; The following label is receives control when the user closes the GUI window. ;;; GuiClose: ;;; ExitApp ;;; ConsoleApp_GetStdOut(ConsoleAppHandle, byref Stdout, byref BytesAppended = 0, byref ExitCode="") { global local hProcess, hStdoutRead, buf, ec, cb, FirstWin32Error ConsoleApps_Initialize() hProcess := NumGet(ConsoleAppHandle + 0x0) hStdoutRead := NumGet(ConsoleAppHandle + 0x4) ; hStdoutWrite := NumGet(ConsoleAppHandle + 0x8) ; hStdinRead := NumGet(ConsoleAppHandle + 0xC) ; hStdinWrite := NumGet(ConsoleAppHandle + 0x10) CONSOLEAPPS_PRIVATE_malloc(cb, 4) BytesAppended := 0 Loop { DllCall("PeekNamedPipe", "uint", hStdoutRead, "uint", 0, "uint", 0, "uint", 0, "uint", &cb, "uint", 0) if (!NumGet(cb, 0, "uint")) break if (!CONSOLEAPPS_PRIVATE_ReadFile(hStdoutRead, buf, cb, 1024)) { FirstWin32Error := A_LastError ConsoleApp_CloseHandle(ConsoleAppHandle) CONSOLEAPPS_PRIVATE_throw(CONSOLEAPPS_PRIVATE_ERROR_WIN32_ERROR, "Error reading process stdout: ", "", FirstWin32Error) } stdout := stdout . buf BytesAppended += NumGet(cb, 0, "uint") } CONSOLEAPPS_PRIVATE_malloc(ec, 4) if (!DllCall("GetExitCodeProcess", uint, hProcess, uint, &ec)) { FirstWin32Error := A_LastError ConsoleApp_CloseHandle(ConsoleAppHandle) CONSOLEAPPS_PRIVATE_throw(CONSOLEAPPS_PRIVATE_ERROR_WIN32_ERROR, "Error getting process exit code: ", "", FirstWin32Error) } ExitCode := NumGet(ec) ; If the exit code of the process is STILL_ACTIVE (0x103) then the process is still running if (ExitCode == 0x103) return true return false } ;;; ;;; Closes a ConsoleApp handle returned by ConsoleApp_Run() ;;; ;;; ;;; Handle to close. ;;; ;;; ;;; This function does not return a value. ;;; ;;; ;;; Every handle returned by a successful call to ConsoleApp_Run() must be closed ;;; with this function. ;;; ConsoleApp_CloseHandle(ConsoleAppHandle) { global local hProcess, hStdoutRead, hStdOutWrite ConsoleApps_Initialize() hProcess := NumGet(ConsoleAppHandle + 0x0) hStdoutRead := NumGet(ConsoleAppHandle + 0x4) hStdoutWrite := NumGet(ConsoleAppHandle + 0x8) ; hStdinRead := NumGet(ConsoleAppHandle + 0xC) ; hStdinWrite := NumGet(ConsoleAppHandle + 0x10) ; If any of the following functions fail, then the ConsoleAppProcessInfo structure must ; not be valid, or has already been closed. In this case we raise an error to alert the ; user of a possible mis-handling of the handle and prevent further corruption and memory ; leaks. if (!DllCall("CloseHandle", "uint", hProcess)) ;HANDLE hThread CONSOLEAPPS_PRIVATE_throw(ERROR_GENERIC_ERROR, "Error ConsoleAppHandle is corrupt or has already been closed.") if (!DllCall("CloseHandle", "uint", hStdoutRead)) ;HANDLE hThread CONSOLEAPPS_PRIVATE_throw(ERROR_GENERIC_ERROR, "Error ConsoleAppHandle is corrupt or has already been closed.") if (!DllCall("CloseHandle", "uint", hStdoutWrite)) ;HANDLE hThread CONSOLEAPPS_PRIVATE_throw(ERROR_GENERIC_ERROR, "Error ConsoleAppHandle is corrupt or has already been closed.") /* if (!DllCall("CloseHandle", "uint", hStdinRead)) ;HANDLE hThread CONSOLEAPPS_PRIVATE_throw(ERROR_GENERIC_ERROR, "Error ConsoleAppHandle is corrupt or has already been closed.") if (!DllCall("CloseHandle", "uint", hStdinWrite)) ;HANDLE hThread CONSOLEAPPS_PRIVATE_throw(ERROR_GENERIC_ERROR, "Error ConsoleAppHandle is corrupt or has already been closed.") */ hHeap := DllCall("GetProcessHeap", "uint") if (!DllCall("HeapFree", "uint", hHeap, "uint", 0, "uint", ConsoleAppHandle)) CONSOLEAPPS_PRIVATE_throw(CONSOLEAPPS_PRIVATE_ERROR_WIN32_ERROR, "Error deallocating heap. This is a serious error which can cause heap corruption. This program will now close. Specifically: ") } ; +----------------------+ ; | PRIVATE DECLARATIONS | ; +----------------------+ ConsoleApps_Initialize() { global if (ConsoleApps_Initialized) Return ConsoleApps_Initialized := True CONSOLEAPPS_PRIVATE_EXIT_FAILURE := 1 CONSOLEAPPS_PRIVATE_MAXIMUM_INTEGER := 0x7FFFFFFF ; 2147483647 CONSOLEAPPS_PRIVATE_MAXIMUM_UNSIGNED_INTEGER := 0xFFFFFFFF ; 4294967295 CONSOLEAPPS_PRIVATE_ERROR_NOT_ENOUGH_MEMORY := 0x8 CONSOLEAPPS_PRIVATE_ERROR_INVALID_PARAMETER := 0x57 CONSOLEAPPS_PRIVATE_ERROR_WIN32_ERROR := CONSOLEAPPS_PRIVATE_MAXIMUM_UNSIGNED_INTEGER + 1 CONSOLEAPPS_PRIVATE_STD_ERROR_CODES[%CONSOLEAPPS_PRIVATE_ERROR_NOT_ENOUGH_MEMORY%] := "Not enough storage is available to process this command." CONSOLEAPPS_PRIVATE_STD_ERROR_CODES[%CONSOLEAPPS_PRIVATE_ERROR_INVALID_PARAMETER%] := "Invalid parameter value." CONSOLEAPPS_PRIVATE_STD_ERROR_CODES[%CONSOLEAPPS_PRIVATE_ERROR_WIN32_ERROR%] := "Unspecified exception." } ;BufferSize is including the terminating null character. CONSOLEAPPS_PRIVATE_ReadFile(hFile, byref buf, byref BytesRead=0, BufferSize=4096) { if (BufferSize <= 0) return false CONSOLEAPPS_PRIVATE_malloc(Buf, BufferSize) CONSOLEAPPS_PRIVATE_malloc(BytesRead, 4) if (!DllCall("ReadFile", "uint", hFile, "uint", &buf, "uint", BufferSize-1, "uint", &BytesRead, "uint", 0)) return false BytesRead := NumGet(BytesRead, 0, "uint") NumPut(0, Buf, BytesRead, "uchar") ;terminate data read with a null VarSetCapacity(buf, -1) return true } CONSOLEAPPS_PRIVATE_abort() { global Critical, %CONSOLEAPPS_PRIVATE_MAXIMUM_INTEGER% CONSOLEAPPS_PRIVATE_STD_Exiting := True OnExit BlockInput, MouseMoveOff BlockInput, Off ExitApp, %CONSOLEAPPS_PRIVATE_EXIT_FAILURE% } CONSOLEAPPS_PRIVATE_calloc(byref Var, size, fillbyte=0) { global local cb if (cb := VarSetCapacity(Var, size, fillbyte) != size) CONSOLEAPPS_PRIVATE_throw(CONSOLEAPPS_PRIVATE_ERROR_NOT_ENOUGH_MEMORY) return cb } CONSOLEAPPS_PRIVATE_free(byref Var) { VarSetCapacity(Var, 0) } CONSOLEAPPS_PRIVATE_WIN32_MAKELANGID(p, s) { return (s << 10) | p } CONSOLEAPPS_PRIVATE_malloc(byref var, size) { global if (cb := VarSetCapacity(var, size) != size) CONSOLEAPPS_PRIVATE_throw(CONSOLEAPPS_PRIVATE_ERROR_NOT_ENOUGH_MEMORY) return cb } ;;; ;;; Returns the C string at the address in lpStr ;;; ;;; ;;; The address of a string in memory. lpStr is an AutoHotkey integer numeric. ;;; NOT a 4-byte dword value. ;;; CONSOLEAPPS_PRIVATE_PtrToStr(lpStr) { return DllCall("MSVCRT\memcpy", UINT, lpStr, UINT, 0, UINT, 0, "str") ; Doesn't copy data, only returns string } ; ParamName is only valid if ErrorCode is CONSOLEAPPS_PRIVATE_ERROR_INVALID_PARAMETER ; LastWin32Error is only necessary if ErrorCode is CONSOLEAPPS_PRIVATE_ERROR_WIN32_ERROR ; and the A_LastError built-in variable is no longer valid. CONSOLEAPPS_PRIVATE_throw(ErrorCode, ErrorMessage="", ParamName="", LastWin32Error="") { global local lpMsg Critical, %CONSOLEAPPS_PRIVATE_MAXIMUM_INTEGER% if (ErrorCode == CONSOLEAPPS_PRIVATE_ERROR_INVALID_PARAMETER) { if (StrLen(ErrorMessage) == 0) ErrorMessage := CONSOLEAPPS_PRIVATE_STD_ERROR_CODES[%ErrorCode%] if (StrLen(ParamName) != 0) ErrorMessage .= "\nParameter: " . ParamName } else if (ErrorCode == CONSOLEAPPS_PRIVATE_ERROR_WIN32_ERROR) { if (StrLen(ErrorMessage) == 0) ErrorMessage := CONSOLEAPPS_PRIVATE_STD_ERROR_CODES[%ErrorCode%] if (StrLen(LastWin32Error) == 0) LastWin32Error := A_LastError CONSOLEAPPS_PRIVATE_malloc(lpMsg, 4) /* DWORD TCharsCopied = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, * 0, * LastWin32Error, * MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), * (LPTSTR)&lpMsg, * 0, * NULL) */ if (t := DllCall("FormatMessageA" , "uint", 0x1300 , "uint", 0 , "uint", LastWin32Error , "uint", CONSOLEAPPS_PRIVATE_WIN32_MAKELANGID(0x0, 0x1) , "uint", &lpMsg , "uint", 0 , "uint", 0, "uint") == 0) { ErrorMessage .= CONSOLEAPPS_PRIVATE_STD_ERROR_CODES[%CONSOLEAPPS_PRIVATE_ERROR_WIN32_ERROR%] } else { ErrorMessage .= CONSOLEAPPS_PRIVATE_PtrToStr(NumGet(lpMsg)) DllCall("LocalFree", "uint", lpMsg) CONSOLEAPPS_PRIVATE_free(lpMsg) } } else { if (StrLen(ErrorMessage) == 0) ErrorMessage := CONSOLEAPPS_PRIVATE_STD_ERROR_CODES[%ErrorCode%] } MsgBox, 0x1010, %A_ScriptName%, %ErrorMessage% CONSOLEAPPS_PRIVATE_abort() }