Page 1 of 1

Launch Persistent Shell

Posted: Wed Sep 22, 2021 10:04 pm
by studawg66
I have an external script that I want to trigger on openStack, but the cmd (shell) window needs to stay open. If I run this executable outside of LC, the cmd window stays open because of my input command, so it says "Press enter to exit" and awaits that key stroke.
But whenever I do a "get shell(myScript)" in LC it launches the command line utility but then immediately closes it. Is there a way to keep that cmd window open, awaiting the input request?

Re: Launch Persistent Shell

Posted: Fri Sep 24, 2021 10:06 am
by Bernard
Why can't I reply to this. My reply (with sample code) always fails to be accepted by the forum software.

Re: Launch Persistent Shell

Posted: Fri Sep 24, 2021 10:09 am
by Bernard
studawg66 wrote:
Wed Sep 22, 2021 10:04 pm
I have an external script that I want to trigger on openStack, but the cmd (shell) window needs to stay open. If I run this executable outside of LC, the cmd window stays open because of my input command, so it says "Press enter to exit" and awaits that key stroke.
But whenever I do a "get shell(myScript)" in LC it launches the command line utility but then immediately closes it. Is there a way to keep that cmd window open, awaiting the input request?

Code: Select all

get shell("start x /K dir "); put the long time
(replace x with cmd dot exe above)

will open a terminal window and list the files in that directory (the defaultFolder). The terminal window will have focus.

I'm not clear if you want to stop execution of your code whilst you are waiting for the user to interact with your terminal window. In the above case LC will continue processing (as evidenced by the long time appearing in the message box.

Re: Launch Persistent Shell

Posted: Fri Sep 24, 2021 10:10 am
by Bernard
it looks like the characters "cmd dot exe" are banned by the forum software.

Re: Launch Persistent Shell

Posted: Fri Sep 24, 2021 5:48 pm
by studawg66
Thanks, Bernard!
I was able to make a slight modification to your suggestion and it worked like a charm!
I added the "min" flag to minimize the cmd window as soon as it launches, so that gets it out of the way and lets it do its thing.

Code: Select all

get shell("start /min x /k C:/temp/myprogram dot exe")
where x is cmd dot exe

Re: Launch Persistent Shell

Posted: Fri Sep 24, 2021 9:32 pm
by studawg66
I thought this next step would be easy but I'm struggling. How would I close out this cmd window on closing the LC application?
I tried several things in the "closeStack" routine, like:

Code: Select all

put shell("exit")

Code: Select all

put shell("cmd-dot-exe exit")
...and some other iterations of that. But I can't seem to find a way to make it happen. Is this possible? Is there a way to pass more information to that open cmd instance after the initial "start" command has been run? Like an enter key, or an exit command?

Re: Launch Persistent Shell

Posted: Fri Sep 24, 2021 9:40 pm
by FourthWorld
In the Linux shell you can append a command with & to return control to the parent process.

I don't know the shell syntax for that on Windows, but maybe easier to just use the "open process" command with the "for neither" option.

Re: Launch Persistent Shell

Posted: Fri Sep 24, 2021 10:37 pm
by mtalluto
I have used: [start /b] to manage running extra processes. This method does not run the app in another window. It feels more like a deamon.

https://ss64.com/nt/start.html

Re: Launch Persistent Shell

Posted: Sat Sep 25, 2021 12:03 pm
by Bernard
studawg66 wrote:
Fri Sep 24, 2021 9:32 pm
I thought this next step would be easy but I'm struggling. How would I close out this cmd window on closing the LC application?
I tried several things in the "closeStack" routine, like:

Code: Select all

put shell("exit")

Code: Select all

put shell("cmd-dot-exe exit")
...and some other iterations of that. But I can't seem to find a way to make it happen. Is this possible? Is there a way to pass more information to that open cmd instance after the initial "start" command has been run? Like an enter key, or an exit command?
I'm not aware of anyway to use Windows taskkill with the "title" given to a cmd-dot-exe instance. You would need the process ID to target a specific window (you could perhaps just get a list of all cmd-dot-exe PIDs and kill them all with Windows

Code: Select all

tasklist|find "cmd-dot-exe"
, but that would be a very unfriendly thing to do to users of your application.

Perhaps what you need is "open process" rather than shell() -- see the LC Dictionary. Once another exe is opened via open process you can write to it/read from it then call "close process" when you have finished.

Shell() is the simple/limited way to do things. Open process is more powerful but also can be more complicated.

Re: Launch Persistent Shell

Posted: Sat Sep 25, 2021 2:04 pm
by Bill

Code: Select all

-- a file to pipe output to
   put "%userprofile%/Documents/myCommand.txt" into tFile 
   
   -- set up your command window title
   put quote & "MyCommandWindow" & quote into myTitle
   
   --build your command in a string  
   put "start <Your_Program.exe> & title" && myTitle && "& tasklist /v /fo csv | findstr /i" && myTitle &&">>" && tFile  into tCom
   get shell(tCom)
Then item 2 of the file is the PID with quotes.
You probably want to set that file path up entirely with Livecode so you can access it for read directly.

Re: Launch Persistent Shell

Posted: Mon Sep 27, 2021 8:00 pm
by studawg66
Thanks for these responses! I have tried a couple of the suggestions but it is not accomplishing what I'm looking for. I think it would help if I provided more detail on what I'm doing.
I built an external executable that disables the Windows screensaver by wrapping the following python code in an exe:

Code: Select all

import ctypes
ctypes.windll.kernel32.SetThreadExecutionState(0x80000002)
input('{Screensaver has been DISABLED. Press Enter or close this window to enable it.}')
The input line was required to keep the thread open, because if the script just runs and closes the thread (returns to the prompt or closes the window), that ThreadExecutionState returns to its default value and the screensaver is enabled again.
So I have the following in Livecode in the openStack routine to run this script:

Code: Select all

get shell("start /min cmd-dot-exe /k C:/temp/SleepDisable-dot-exe")
This runs that script, keeps it active, and immediately minimizes it to get it out of the way of my Livecode GUI.

It works, but I would like to be able to clear things out when I close Livecode. Right now the only way is to close Livecode then go close that cmd window manually. Not a huge deal but if there was some way to interact with that open cmd window or to kill it, that would clean things up for the end user and not have screensavers permanently disabled by 17 open cmd windows :D

Ideally, I would love for Livecode to directly access that "kernel32.dll" SetThreadExecutionState function without need of this external cmd window, but I've asked that elsewhere on these forums and received no response. So if you have any ideas there, I'm all ears!
I hope this makes more sense.

Re: Launch Persistent Shell

Posted: Wed Sep 29, 2021 12:48 pm
by Bernard
Assuming you STARTed the program and set the title of that process to (say) _MyTitle_ you should be able to kill off the hidden process when you close your own app using

Code: Select all

put "_MyTitle_" into tTitle
put quote into q
shell(  merge( "taskkill /FI [[q]]WINDOWTITLE eq [[tTitle]][[q]]" )  )
I just tried killing a process this way and it worked. Let us know if it doesn't work for you.

You may have to use this version:

taskkill /F /FI "WINDOWTITLE eq _MyTitle_"

where /F means "force closure".

Re: Launch Persistent Shell

Posted: Wed Sep 29, 2021 8:07 pm
by studawg66
Ah, yes. Naming the task! This worked perfectly!

To summarize, I disable the screensaver by calling my "SleepDisable.exe" program and NAME it "SleepDisable" for future, using the "/min" to minimize the window immediately, and the "/k" parameter to keep the window open, which keeps my SleepDisable function running:

Code: Select all

get shell("start /min " & quote & "SleepDisable" & quote & " cmd-dot-exe /k " & quote & myFilePath & quote)
Then, on a closeStack request, I run the following, which looks for open processes called "SleepDisable" and kills them:

Code: Select all

get shell("taskkill /FI " & quote & "WINDOWTITLE eq SleepDisable*" & quote)
Works like a charm! Thanks again for getting me on the right track.