Jump to content

CUI simulation when compiled as GUI


Recommended Posts

Here's the thing. I would like to have option with compiled script (GUI EXE) to be able to communicate with console if that script (exe) is run with command prompt or by another process (except explorer.exe). So, if parent is explorer.exe GUI mode is on and if parent is anything else CUI mode is forced.

This is what I have:

#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Outfile=Test.exe
#AutoIt3Wrapper_UseUpx=n
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****
#NoTrayIcon

Opt("MustDeclareVars", 1)

Global $sParent = _GetParent()

If @Compiled Then
    Switch $sParent
        Case "explorer.exe"
            Run("Test.exe")
        Case "cmd.exe"
            _ExecuteCommandLineAttachConsole($CmdLineRaw)
        Case Else
            _ExecuteCommandLine($CmdLineRaw)
    EndSwitch
Else
    Switch $sParent
        Case "cmd.exe"
            _ExecuteCommandLine($CmdLineRaw)
        Case Else
            Run("Test.exe", "", @SW_SHOW, 1) ; $STDIN_CHILD
    EndSwitch
EndIf





Func _GetParent()

    Local $sParent

    Local $aCall = DllCall("kernel32.dll", "int", "GetCurrentProcess")

    If @error Or Not $aCall[0] Then
        Return SetError(1, 0, "")
    EndIf

    Local $hProcess = $aCall[0]

    Local $tPROCESS_BASIC_INFORMATION = DllStructCreate("dword ExitStatus;" & _
            "ptr PebBaseAddress;" & _
            "dword AffinityMask;" & _
            "dword BasePriority;" & _
            "dword UniqueProcessId;" & _
            "dword InheritedFromUniqueProcessId")

    Local $aCall = DllCall("ntdll", "int", "NtQueryInformationProcess", _
            "hwnd", $hProcess, _
            "dword", 0, _
            "ptr", DllStructGetPtr($tPROCESS_BASIC_INFORMATION), _
            "dword", DllStructGetSize($tPROCESS_BASIC_INFORMATION), _
            "dword*", 0)

    If @error Then
        Return SetError(2, 0, "")
    EndIf

    Local $iParentPID = DllStructGetData($tPROCESS_BASIC_INFORMATION, "InheritedFromUniqueProcessId")

    $aCall = DllCall("kernel32.dll", "hwnd", "OpenProcess", _
            "dword", 1040, _ ; PROCESS_QUERY_INFORMATION|PROCESS_VM_READ
            "int", 0, _
            "dword", $iParentPID)

    If @error Or Not $aCall[0] Then
        Return SetError(3, 0, "")
    EndIf

    Local $hParentHandle = $aCall[0]

    $aCall = DllCall("psapi.dll", "dword", "GetModuleBaseNameW", _
            "hwnd", $hParentHandle, _
            "ptr", 0, _
            "wstr", "", _
            "dword", 65536)

    If @error Or Not $aCall[0] Then
        DllCall("kernel32.dll", "int", "CloseHandle", "hwnd", $hParentHandle)
        Return SetError(4, 0, "")
    EndIf

    $sParent = $aCall[3]

    DllCall("kernel32.dll", "int", "CloseHandle", "hwnd", $hParentHandle)

    Return SetError(0, 0, $sParent)

EndFunc   ;==>_GetParent


Func _ExecuteCommandLine($sCommandLine)

    If $sCommandLine Then
        _ExecuteCommandLineAttachConsole($sCommandLine)
        Return SetError(@error, 1, "")
    EndIf

    Local $aCall = DllCall("kernel32.dll", "int", "AllocConsole")


    If @error Or Not $aCall[0] Then
        Return SetError(1, 0, "")
    EndIf

    Local $hConIn = FileOpen("CONIN$", 4) ; reading handle ; _WinAPI_GetStdHandle(0)

    Local $hConOut = FileOpen("CONOUT$", 1) ; _WinAPI_GetStdHandle(1)
    FileWrite($hConOut, "Resources Viewer And Compiler >")
    FileClose($hConOut)

    Local $sPrintString

    While 1

        Sleep(100)

        Switch BinaryToString(FileRead($hConIn, 512))

            Case "?" & @CRLF
                $sPrintString = @CRLF & "... help string goes here ..." & @CRLF & @CRLF
                ConsoleWrite($sPrintString) ; write to the STDOUT stream
                $sPrintString &= "Resources Viewer And Compiler >"
                $hConOut = FileOpen("CONOUT$", 1) ; writing handle
                FileWrite($hConOut, $sPrintString) ; write to a console
                FileClose($hConOut) ; flush

            Case "gui mode" & @CRLF
                $sPrintString = "Switching to GUI mode..." & @CRLF
                ConsoleWrite($sPrintString)
                $hConOut = FileOpen("CONOUT$", 1)
                FileWrite($hConOut, $sPrintString)
                FileClose($hConOut)
                Sleep(300)
                DllCall("kernel32.dll", "int", "FreeConsole")
                Exit

            Case "exit" & @CRLF, "quit" & @CRLF
                Exit

            Case Else
                $sPrintString = @CRLF & "Invalid input. Type ? for help." & @CRLF & @CRLF
                ConsoleWrite($sPrintString)
                $sPrintString &= "Resources Viewer And Compiler >"
                $hConOut = FileOpen("CONOUT$", 1)
                FileWrite($hConOut, $sPrintString)
                FileClose($hConOut)

        EndSwitch

    WEnd

    FileClose($hConIn)

    Return

EndFunc   ;==>_ExecuteCommandLine


Func _ExecuteCommandLineAttachConsole($sCommandLine)

    Local $aCall = DllCall("kernel32.dll", "int", "AttachConsole", _
            "dword", -1) ; ATTACH_PARENT_PROCESS

    If @error Or Not $aCall[0] Then
        Return SetError(1, 0, "")
    EndIf
    Local $sPrintString

    Switch $sCommandLine ; let's say this is ok

        Case True
            $sPrintString = @CRLF & "Something was typed." & @CRLF & @CRLF
        Case Else
            $sPrintString = @CRLF & "Nothing typed." & @CRLF & @CRLF

    EndSwitch

    ConsoleWrite($sPrintString)
    Local $hConOut = FileOpen("CONOUT$", 1)
    FileWrite($hConOut, $sPrintString)
    FileClose($hConOut)
    ;Send("{ENTER}"); <- this is complete shit

EndFunc   ;==>_ExecuteCommandLineAttachConsole

Half of the script works as I expect it to. The problem is with _ExecuteCommandLineAttachConsole() function.

If you compile that script without changing AutoIt3Wrapper_GUI directives you and run exe from command prompt you will see what I mean. I need proper way of doing Send("{ENTER}") and before that I need not to lose console when I hit ENTER to run it (this sentence sounds dumb even to me, but I cannot explain it any better, so please just run it as I said).

You don't even need to compile the script, you can run it from command prompt to see the issues.

Any help is appreciated, of course.

Link to comment
Share on other sites

  • 1 month later...
  • 3 weeks later...

I compiled the script even though you said I don't need to, then I ran it from the command prompt like this

..>test dir_

then it says

something was typed

What do you want to happen after that? I assume that you want the parameter passed to test.exe to be written on th ecommand line then Enter pressed. What is wrong with

Send($sCommandLine & "{ENTER}")

?

Or do I not get it?

Serial port communications UDF Includes functions for binary transmission and reception.printing UDF Useful for graphs, forms, labels, reports etc.Add User Call Tips to SciTE for functions in UDFs not included with AutoIt and for your own scripts.Functions with parameters in OnEvent mode and for Hot Keys One function replaces GuiSetOnEvent, GuiCtrlSetOnEvent and HotKeySet.UDF IsConnected2 for notification of status of connected state of many urls or IPs, without slowing the script.
Link to comment
Share on other sites

I compiled the script even though you said I don't need to, then I ran it from the command prompt like this

..>test dir_

then it says

something was typed

What do you want to happen after that? I assume that you want the parameter passed to test.exe to be written on th ecommand line then Enter pressed. What is wrong with

Send($sCommandLine & "{ENTER}")

?

Or do I not get it?

Script works well if you run it (compiled) by double-click. It acts like it was compiled as CUI (I call that interactive mode - like telnet.exe for example).

But if it's run thru command prompt it's not ok. It look like this with me (copy/paste):

C:\Documents and Settings\trancexx>"C:\Documents and Settings\trancexx\Desktop\T
est.exe"

C:\Documents and Settings\trancexx>
Nothing typed

And it should be (like with normal CUI):

C:\Documents and Settings\trancexx>"C:\Documents and Settings\trancexx\Desktop\T
est.exe"

Nothing typed.

C:\Documents and Settings\trancexx>

I drop out of warp.

edit:

... I forgot to say that I'm not all that eager to find the solution to this because I really don't have much use for it now like I did back then. Maybe just as a curiosity now. I would have bumped this thread otherwise.

My puzzleness was about three bumps by french girl.

Edited by trancexx
Link to comment
Share on other sites

I think the problem is that 'cmd.exe' is a command interpreter, and interprets the @CRLF as an input command (pressing the enter key), which displays the current directory. I *think* when compiling to CUI that the child process has its own screen buffer, so this doesn't happen.

Maybe after attaching to the console, try

CreateConsoleScreenBuffer

SetConsoleActiveScreenBuffer

GetStdHandle to get STD_OUTPUT_HANDLE

write stuff with WriteConsoleA

CloseHandle on the new screen buffer

FreeConsole to detach

...untested, might also be crap :/

Edited by wraithdu
Link to comment
Share on other sites

Ok, this mostly works, except it covers whatever you have there already, then the old buffer shows again when you close the handle. Close enough?

Func _ExecuteCommandLineAttachConsole($sCommandLine)

    Local $aCall = DllCall("kernel32.dll", "int", "AttachConsole", "dword", -1)

    If @error Or Not $aCall[0] Then
        Return SetError(1, 0, "")
    EndIf
    Local $sPrintString

    Switch $sCommandLine ; let's say this is ok

        Case True
            $sPrintString = @CRLF & "Something was typed." & @CRLF & @CRLF
        Case Else
            $sPrintString = @CRLF & "Nothing typed." & @CRLF & @CRLF

    EndSwitch

    $aCall = DllCall("kernel32.dll", "ptr", "CreateConsoleScreenBuffer", "dword", BitOR(0x40000000, 0x80000000), "dword", 0, "ptr", 0, "dword", 1, "ptr", 0)
    Local $hBuff = $aCall[0]
;~  MsgBox(0, "Create", "hBuff:  " & $hBuff)
    $aCall = DllCall("kernel32.dll", "int", "SetConsoleActiveScreenBuffer", "ptr", $hBuff)
;~  MsgBox(0, "Active", "Set active:  " & $aCall[0])
    $aCall = DllCall("kernel32.dll", "int", "WriteConsoleA", "ptr", $hBuff, "str", $sPrintString, "dword", StringLen($sPrintString), "dword*", 0, "ptr", 0)
;~  MsgBox(0, "Write", "Chars written:  " & $aCall[4])
    Sleep(5000)
    DllCall("kernel32.dll", "int", "CloseHandle", "ptr", $hBuff)
    DllCall("kernel32.dll", "int", "FreeConsole")
EndFunc   ;==>_ExecuteCommandLineAttachConsole
Link to comment
Share on other sites

Very nice work. Thank u for sharing :D

simulation rachat credit

Last Seen: 5th May 2009 - 05:40 PM

Local Time: Jun 3 2009, 07:24 PM

very interesting! thank you for sharing

simulation rachat de credit

Last Seen: 11th May 2009 - 07:59 AM

Local Time: Jun 3 2009, 07:24 PM

interesting work. thanks for sharing :D

simulation assurance vie

Last Seen: 30th May 2009 - 09:24 AM

Local Time: Jun 3 2009, 07:24 PM

... and, all have just "one post" and, All are from the "same time zone" as yours.

... Hmmmm??? Maybe you got some girl after you!!!

8)

NEWHeader1.png

Link to comment
Share on other sites

I did some googling about this general idea, and no one on the net has come up with a good workaround to integrate a GUI app (/SUBSYSTEM:WINDOWS) into the console when launched by cmd.exe. As you can see my workaround provides you with a screen buffer to display data, but it seems very difficult (impossible?) to *read* data from cmd's STD_INPUT. I tried and failed. Even managed to BSOD Win7 once in the process :/

Options I saw:

1) create a console app and FreeConsole() or hide the window if it's not needed (get HWND with GetConsoleWindow())

2) create a GUI app and use AllocConsole() to create a console window if needed

3) create two versions of the app, on GUI and one console, and launch / relaunch appropriately

Edited by wraithdu
Link to comment
Share on other sites

I did some googling about this general idea, and no one on the net has come up with a good workaround to integrate a GUI app (/SUBSYSTEM:WINDOWS) into the console when launched by cmd.exe. As you can see my workaround provides you with a screen buffer to display data, but it seems very difficult (impossible?) to *read* data from cmd's STD_INPUT. I tried and failed. Even managed to BSOD Win7 once in the process :/

Options I saw:

1) create a console app and FreeConsole() or hide the window if it's not needed (get HWND with GetConsoleWindow())

2) create a GUI app and use AllocConsole() to create a console window if needed

3) create two versions of the app, on GUI and one console, and launch / relaunch appropriately

Now you are teasing me to take this again and work on it.

@Valuater, no doubt about that :D

Link to comment
Share on other sites

  • 1 year later...

I did some googling about this general idea, and no one on the net has come up with a good workaround to integrate a GUI app (/SUBSYSTEM:WINDOWS) into the console when launched by cmd.exe. As you can see my workaround provides you with a screen buffer to display data, but it seems very difficult (impossible?) to *read* data from cmd's STD_INPUT. I tried and failed. Even managed to BSOD Win7 once in the process :/

Options I saw:

1) create a console app and FreeConsole() or hide the window if it's not needed (get HWND with GetConsoleWindow())

2) create a GUI app and use AllocConsole() to create a console window if needed

3) create two versions of the app, on GUI and one console, and launch / relaunch appropriately

Sounds similiar to what I wanted: http://www.autoitscript.com/forum/index.php?showtopic=118552

Now you are teasing me to take this again and work on it.

@Valuater, no doubt about that ;)

trancexx, don't suppose you got anywhere with this did you?

To be honest, all I want is to display text back to the console window at the moment, so that if the exe is run from a commandline ConsoleWrite will output to the commandline. As per that thread, thanks to UEZ, got some logic to work out if it's been run from a console window or not, if not then default to using MsgBox instead of course.

Although, I can see that being able to send back into it, would be useful, a full CUI while still being a GUI too, would save on having to make two seperate executables.

Link to comment
Share on other sites

trancexx, don't suppose you got anywhere with this did you?

I remember that I have forgotten the answer to that question.

Lost interest back there since I've managed to approach the problem differently.

Now that I think of general idea I can't not think about thread context manipulation. (Not this thread of course)

Link to comment
Share on other sites

I remember that I have forgotten the answer to that question.

Lost interest back there since I've managed to approach the problem differently.

Now that I think of general idea I can't not think about thread context manipulation. (Not this thread of course)

Errr..... Ok... ???

Out of interest, what was your different approach, did it work out?

Link to comment
Share on other sites

Errr..... Ok... ???

Out of interest, what was your different approach, did it work out?

Yes it did.

Conclusion of intensive thinking (yeah right!) was that it's not CUI I want. It's non-GUI. So that if explorer.exe runs my app GUI creation is forced. And is some other program runs it, it would skip GUI creation and proceed with command line interpretation an execution through function called _ExecuteCommandLine().

ConsoleWrite() and ConsoleWriteError() are used for communication (one way, obviously). Had this as part of the comments/code:

;Suggestion for parents:
    #   Local $hResourcesExe = Run("Resources.exe -add -compile MyRes.dll -res SomeAnimated.gif -type GIF -name 1 -lang 0", "", @SW_HIDE, 6); $STDERR_CHILD + $STDOUT_CHILD
    #   Local $sLine, $sLineError
    #
    #   While 1
    #       $sLineError = StderrRead($hResourcesExe)
    #       If @error Then ExitLoop
    #       If $sLineError Then
    #           ConsoleWrite($sLineError)
    #       EndIf
    #       Sleep(100)
    #   WEnd
    #
    #   While 1
    #       $sLine = StdoutRead($hResourcesExe)
    #       If @error Then ExitLoop
    #       If $sLine Then
    #           ConsoleWrite($sLine)
    #       EndIf
    #       Sleep(100)
    #   WEnd
    ; End suggestion
Link to comment
Share on other sites

Yes it did.

Conclusion of intensive thinking (yeah right!) was that it's not CUI I want. It's non-GUI. So that if explorer.exe runs my app GUI creation is forced. And is some other program runs it, it would skip GUI creation and proceed with command line interpretation an execution through function called _ExecuteCommandLine().

ConsoleWrite() and ConsoleWriteError() are used for communication (one way, obviously). Had this as part of the comments/code:

Indeed, I didn't realise how full blown CUI is in autoit, at the moment I definately don't need that, just Consolewrite as you said, basically for outputting results to console window if launched from it with commandline switches but if double clicked run a GUI, pretty sure many exes are capable of this.

Unfortunately I don't see how your code snippet helps with that, what is resources.exe ?

PS. I found compiling as a CUI gives the functionality but when running by a double-click/explorer or not run from within a command window then you get an extra command window behind the GUI, which you can hide or free, but don't want it to appear at all really. Plus I get the feeling I should be compiling as a GUI as it's not really a CUI in the strict sense.

Edited by AJStevens
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...