Author Topic: Windows how to send Ctrl-C instead of Ctrl-Break for "stop build" alternative  (Read 11368 times)

rowbearto

  • Senior Community Member
  • Posts: 2335
  • Hero Points: 132
OK, with ntpinit as the leader, when you call GenerateConsoleCtrlEvent() you can only provide either the pid of ntpinit (as it is process leader) or 0. Using the pid of cmd would not work. What pid were you sending in GenerateConsoleCtrlEvent() ?

Clark

  • SlickEdit Team Member
  • Senior Community Member
  • *
  • Posts: 6864
  • Hero Points: 528
The pid for ntpinit

Clark

  • SlickEdit Team Member
  • Senior Community Member
  • *
  • Posts: 6864
  • Hero Points: 528
The hybrid solution isn't perfect for Java. The only other solution is just to always call TerminateProcess on all the child processes. Then there is no output.

You still get the following output:

2019-11-29 21:53:19
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.231-b11 mixed mode):

"Service Thread" #10 daemon prio=9 os_prio=0 tid=0x0000000019e2c800 nid=0xb40 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread3" #9 daemon prio=9 os_prio=2 tid=0x0000000019d9f800 nid=0x6bc waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread2" #8 daemon prio=9 os_prio=2 tid=0x0000000019d93800 nid=0xa30 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #7 daemon prio=9 os_prio=2 tid=0x0000000019d8d800 nid=0x2778 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #6 daemon prio=9 os_prio=2 tid=0x0000000019d87000 nid=0x246c waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x0000000019d85800 nid=0x1f4c runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x0000000019d84000 nid=0xf1c waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x0000000002e8d800 nid=0x29dc in Object.wait() [0x000000001a24f000]
   java.lang.Thread.State: WAITING (on object monitor)
   at java.lang.Object.wait(Native Method)
   - waiting on <0x00000000d5a08ed8> (a java.lang.ref.ReferenceQueue$Lock)
   at java.lang.ref.ReferenceQueue.remove(Unknown Source)
   - locked <0x00000000d5a08ed8> (a java.lang.ref.ReferenceQueue$Lock)
   at java.lang.ref.ReferenceQueue.remove(Unknown Source)
   at java.lang.ref.Finalizer$FinalizerThread.run(Unknown Source)

"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x0000000002e8c000 nid=0x2364 in Object.wait() [0x0000000019d4e000]
   java.lang.Thread.State: WAITING (on object monitor)
   at java.lang.Object.wait(Native Method)
   - waiting on <0x00000000d5a06c00> (a java.lang.ref.Reference$Lock)
   at java.lang.Object.wait(Unknown Source)
   at java.lang.ref.Reference.tryHandlePending(Unknown Source)
   - locked <0x00000000d5a06c00> (a java.lang.ref.Reference$Lock)
   at java.lang.ref.Reference$ReferenceHandler.run(Unknown Source)

"main" #1 prio=5 os_prio=0 tid=0x0000000002d96000 nid=0x20e8 waiting on condition [0x0000000002d8f000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
   at java.lang.Thread.sleep(Native Method)
   at javamain1.main(javamain1.java:7)

"VM Thread" os_prio=2 tid=0x0000000017e78800 nid=0x2634 runnable

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000002dac000 nid=0x28b8 runnable

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x0000000002dad800 nid=0x1070 runnable

"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x0000000002daf000 nid=0x1d9c runnable

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x0000000002db1800 nid=0x2054 runnable

"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x0000000002db2800 nid=0x270c runnable

"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x0000000002db3800 nid=0x1778 runnable

"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x0000000002db7000 nid=0x1440 runnable

"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x0000000002db8000 nid=0xa68 runnable

"VM Periodic Task Thread" os_prio=2 tid=0x0000000019e48000 nid=0x2c4 waiting on condition

JNI global references: 4

Heap
 PSYoungGen      total 38400K, used 1996K [0x00000000d5a00000, 0x00000000d8480000, 0x0000000100000000)
  eden space 33280K, 6% used [0x00000000d5a00000,0x00000000d5bf33b8,0x00000000d7a80000)
  from space 5120K, 0% used [0x00000000d7f80000,0x00000000d7f80000,0x00000000d8480000)
  to   space 5120K, 0% used [0x00000000d7a80000,0x00000000d7a80000,0x00000000d7f80000)
 ParOldGen       total 87552K, used 0K [0x0000000080e00000, 0x0000000086380000, 0x00000000d5a00000)
  object space 87552K, 0% used [0x0000000080e00000,0x0000000080e00000,0x0000000086380000)
 Metaspace       used 2464K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 273K, capacity 386K, committed 512K, reserved 1048576K

rowbearto

  • Senior Community Member
  • Posts: 2335
  • Hero Points: 132
I thought of some other things to try, just for curiosity (and maybe final product).

What if ntpinit/cmd is started in its own window? Would Ctrl-C work then? Visual C++ debugger does run programs in a dedicated console window, maybe this is why? While I would much rather run in the process buffer and not in a separate window, I wonder if this is the true reason why Visual C++ uses dedicated window? If this does work, I wonder if you can also view the input/output of the cmd in the process buffer as well - then maybe that dedicated window could be "hidden"?

Also, just to make sure - when you made ntpinit as process leader you also took away process leader for cmd? Did you also try 0 as pid in the GenerateConsoleCtrlEvent()? Did you remember to call SetConsoleCtrlHandler(null, FALSE) in ntpinit?

The hybrid solution that you did is certainly better than what we have now and I would gladly enjoy it in the next point release of SE. But it is not ideal for 2 reasons:

1) It does not do a "graceful shutdown". My java programs handle Ctrl-C and do cleanup, this cleanup will not be there. I would also not be able to debug the cleanup code in the SE debugger.
2) The output of the thread dump is not ideal, but I still think it is better than just terminating with no output.

rowbearto

  • Senior Community Member
  • Posts: 2335
  • Hero Points: 132
FYI: I was able to send a SIGINT to my java program running in the process buffer and have it gracefully exit by doing the following:

1) Open process buffer
2) Start cygwin: c:\cygwin\cygwin
3) Start bash under a pty (required for winpty): script -eqfc "bash -i" /dev/null
4) Run my java code under winpty: "winpty java ...."

Then when I sent a Kernel32.CTRL_C_EVENT from an external program that does AttachConsole(pid) and then GenerateConsoleCtrlEvent(Kernel32.CTRL_C_EVENT, 0);, it did gracefully exit!

winpty: https://github.com/rprichard/winpty

I was using winpty previously when launching my java code from a cygwin prompt outside of SlickEdit in order for Ctrl-C to work.

Notice in the README of winpty:

Quote
The software works by starting the winpty-agent.exe process with a new, hidden console window, which bridges between the console API and terminal input/output escape codes. It polls the hidden console's screen buffer for changes and generates a corresponding stream of output.

Perhapse SlickEdit could do something similar as winpty, but instead of bridging cygwin's pty, it can bridge SlickEdit?
« Last Edit: November 30, 2019, 03:54:32 PM by rowbearto »

Clark

  • SlickEdit Team Member
  • Senior Community Member
  • *
  • Posts: 6864
  • Hero Points: 528
I was wondering if I could create a hidden console window. I’ll have to look into that.

Clark

  • SlickEdit Team Member
  • Senior Community Member
  • *
  • Posts: 6864
  • Hero Points: 528
What are you running in SlickEdit's process buffer?

Are you running winpty in the process buffer?

I guess I'm pretty confused now.
« Last Edit: November 30, 2019, 06:30:42 PM by Clark »

rowbearto

  • Senior Community Member
  • Posts: 2335
  • Hero Points: 132
Yes I am running winpty in the process buffer.

I do steps 2-4 in the process buffer. Step 4 is where I run winpty, and yes I run it inside the process buffer, but that is inside of a cygwin bash shell, and the cygwin bash shell is running in the process buffer.

And I can send Ctrl-C (from external program) via GenerateConsoleCtrlEvent(Kernel32.CTRL_C_EVENT, 0) and my java code does gracefully shut down - it sees the Ctrl-C (not Ctrl-Break) and does all my cleanup, and log prints I have in the cleanup code, before exiting.

winpty is not available from the standard cygwin setup, I had to download the .exe and .dll from github and place it into c:\cygwin\bin
« Last Edit: November 30, 2019, 06:37:20 PM by rowbearto »

Clark

  • SlickEdit Team Member
  • Senior Community Member
  • *
  • Posts: 6864
  • Hero Points: 528
Are you sending the Ctrl-C event to winpty or java?

What does the java program run with winpty do?

Are you running another external java program to send the Ctrl+C event besides the one thats the argument to winpty?
« Last Edit: November 30, 2019, 07:07:44 PM by Clark »

rowbearto

  • Senior Community Member
  • Posts: 2335
  • Hero Points: 132
Quote
Are you running another external java program to send the Ctrl+C event besides the one thats the argument to winpty?

Yes I am.

I am working on cleaning it up so you can run it yourself.

Quote
What does the java program run with winpty do?

It basically runs "sleep 10000", and if Ctrl-C is received, it will do "cleanup" and print a few things.

I'm also working on providing this to you.

Quote
Are you sending the Ctrl-C event to winpty or java?

I send it to the console that is attached to the java program's pid.

It does the below:

Code: [Select]
// Get the console of the pid running java
Kernel32.INSTANCE.AttachConsole(pid);

// Send CTRL_C event to pid 0, which corresponds to the console that this
// program is running in. Since I used AttachConsole previously, this is the same
// console that "java" is running in.
Kernel32.INSTANCE.GenerateConsoleCtrlEvent(Kernel32.CTRL_C_EVENT, 0);

I based it on: https://stackoverflow.com/questions/813086/can-i-send-a-ctrl-c-sigint-to-an-application-on-windows/42839731#42839731
« Last Edit: November 30, 2019, 07:40:28 PM by rowbearto »

Clark

  • SlickEdit Team Member
  • Senior Community Member
  • *
  • Posts: 6864
  • Hero Points: 528
I just made some progress with something. Didn't need all the complexity you've mentioned recently. Went back to some docs you posted earlier and tried something. Got Ctrl+C event to work in the process buffer but not exactly with the process tree I want. Now I need to figure a better process tree.

Clark

  • SlickEdit Team Member
  • Senior Community Member
  • *
  • Posts: 6864
  • Hero Points: 528
I've narrowed down the problems for sure. It's looking like there were two issues

Issue #1 (simple to solve)
ntpinit needed to turn off the ENABLE_PROCESS_INPUT console mode. Actually it turns it off, starts cmd.exe, and then turns it back on.

Issue#2 (the harder problem)
There seems to be a problem with SetConsoleCtrlHandler. It's looking like CMD.EXE is messing up what the child process inherits. ntpinit.exe does SetConsoleCtrlHandler(NULL,0).  This doesn't help.

I modified my "cmtime" app which runs other applications to do a SetConsoleCtrlHandler(NULL,0) BEFORE running the child process. When I do this, I can send a Ctrl+C event in the process. (i.e start process buffer (new version), run "cmtime waitforkeypress", send Ctrl+C event, Zapola!!)

This unfortunately isn't a great solution because it requires another app.

rowbearto

  • Senior Community Member
  • Posts: 2335
  • Hero Points: 132
That is indeed some progress. Yes having another app that runs between cmd.exe and the desired child process is not ideal, but it is good to know that can work.

I suppose if ntpinit was made like winpty then could get around this, but that introduces much more complexity.

rowbearto

  • Senior Community Member
  • Posts: 2335
  • Hero Points: 132
FYI: I redid my previous winpty experiment, however instead of running my java program under winpty, I ran "winpty cmd" and got an cmd prompt (all in the process buffer). Then at the new cmd prompt I ran my java program that does a sleep. Then outside of SE with my "win-kill" java program, I send the Ctrl-C to the console of my Java program and it did indeed interrupt the java code. So it seems that running cmd under winpty any programs run under cmd are interruptible.

Clark

  • SlickEdit Team Member
  • Senior Community Member
  • *
  • Posts: 6864
  • Hero Points: 528
Are you sure the winkill program is sending a Ctrl+C event?