Forum > Windows

how to close or control an attached console from GUI

(1/2) > >>

robert rozee:
hi,
    attached is an example of a GUI program that does almost exactly what i want, but not quite   :)   hopefully someone will be able to suggest how to get over the final hurdle.

three buttons on a form:

1. click on the first button, and a console is attached,
2. click on the second button, and a text message is written to the console via writeln(stderr, 'message'),
3. click on the third button, and the console is freed - the GUI application carries on working.

at the console:
4. press control-C, nothing happens. up until here, everything is going as planned.
5. click on the console's 'X' (close) button, and... without any intervention, the console and GUI both close. this is NOT what i am after. i'd be happy for either (a) just the console to close, or (b) nothing at all to happen.

for the moment, i've "thrown a spanner in the works" so to speak. a conveniently places showmessage() placed in the CTRL_CLOSE_EVENT handler code disrupts closing the GUI side of things, causing windows (XP) to pop up a "Windows cannot end this program" message after a few seconds, which i can then click 'Cancel' on and everything carries on happily. but this is rather messy.


any suggestions on how to 'fix' things so that the console either can not close itself at all, or is safely rendered incapable of closing the GUI?


--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---function ConsoleHandler( dwCtrlType: DWORD ): BOOL; stdcall;begin  case dwCtrlType of CTRL_CLOSE_EVENT:begin            // console window closed                                        FreeConsole;                                        showmessage('here');                                        IsConsole:=false;                                        result:=true                                      end;                         CTRL_C_EVENT:result:=true     // Avoid terminating with Ctrl+C                  else                result:=false    // all others unhandled  end  { of case }end;  procedure TForm1.Button1Click(Sender: TObject);begin  AllocConsole;      // in Windows unit  IsConsole:=True;   // in System unit  SysInitStdIO;      // in System unit  SetConsoleCtrlHandler({nil,} @ConsoleHandler, true)end;  procedure TForm1.Button2Click(Sender: TObject);begin  writeln(stderr, 'hello world')end;  procedure TForm1.Button3Click(Sender: TObject);begin  FreeConsole;  IsConsole:=falseend;

ASerge:
From HandlerRoutine callback function:

--- Quote ---The CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, and CTRL_SHUTDOWN_EVENT signals give the process an opportunity to clean up before termination. A HandlerRoutine can perform any necessary cleanup, then take one of the following actions:

* Call the ExitProcess function to terminate the process.
* Return FALSE. If none of the registered handler functions returns TRUE, the default handler terminates the process.
* Return TRUE. In this case, no other handler functions are called and the system terminates the process.
--- End quote ---
I.e. always "terminates the process".

robert rozee:
managed to get things to a 'workable' stage, see attached zip file containing example project.

shifting the code that detaches the console out of the CTRL_CLOSE_EVENT handler fixed the need for a spanner-in-the-works to keep the GUI alive. the handler now just sets a flag (detach:=true), and this is picked up later by a timer event that carries out the FreeConsole, etc:


--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---procedure TForm1.Timer1Timer(Sender: TObject);begin  if detach and IsConsole then  begin    detach:=false;    SetConsoleCtrlHandler(@ConsoleHandler, false);    IsConsole:=false;    FreeConsole                                        // takes approx 990ms  endend;        
the "Windows cannot end this program" query still pops up, but just hitting 'Cancel' closes this and the GUI carries on. tried hooking the console's message handler, but this proved evasive due to the console window not being owned by the GUI program.

found the following code to fix the StdIO setup, so that writeln('message') works - ie, no need to write to StdErr:


--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---  if not IsConsole then  if AllocConsole<>BOOL(0) then                        // in Windows unit  begin    SetConsoleTitle('GFXterm - Diagnostics Output');    StdInputHandle:=0;    StdOutputHandle:=0;    StdErrorHandle:=0;    IsConsole:=True;                                   // in System unit    SysInitStdIO;                                      // in System unit    SetConsoleCtrlHandler(@ConsoleHandler, true)       // hook for ctrl-C, break, and close  end
the solution is now 'good enough' for my needs. hopefully the attached code may be useful to others!


cheers,
rob   :-)

BobDog:

Your compiled program executable is 23 Mbytes??
Here is a windows simulation:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} --- program consolestuff; usescrt; function msgbox(p:pointer;msg:ansistring;title:pchar;i1:int32):int32; external 'user32.dll' name 'MessageBoxA';function getconsole():boolean;external 'kernel32.dll' name 'AllocConsole';function hideconsole():boolean;external 'kernel32.dll' name 'FreeConsole';function gethandle(): int64;external 'kernel32.dll' name 'GetConsoleWindow';function GetSystemMenu(p:int64;b:boolean):pointer;external 'user32.dll' name 'GetSystemMenu';function EnableMenuItem(p:pointer;i1:integer;i2:integer):boolean;external 'user32.dll' name 'EnableMenuItem'; varmsg:int32;h,counter:int64;flag:boolean=true;pt:pointer; beginrepeatmsg:=msgbox(nil,'Yes = console'+#13#10+'No = no console'+#13#10+'Cancel = end the program','hello',3 );if (msg=2) then flag:=false;if (msg=7) then hideconsole;if (msg=6) thenbegingetconsole;counter:=counter+1;pt:=GetSystemMenu(gethandle(),false);EnableMenuItem(pt,61536,1);writeln('hello again ',counter,'  times');end;until flag=false;end. 

robert rozee:
taking a slightly different tack, i've been experimenting with the GUI detecting if launched from a console and then simply taking over that console solely for itself. this was prompted by the discovery that even if the GUI's standard I/O was sent back to the console that launched it, the console still ALSO had a 'live' command processor attached to it   >:(   and so would respond to any keyboard input in a 'muddled' way.

the below is the results of experiments thus far, something that you just drop at the end of your main form:


--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---var ProcessList:array [1..64] of DWORD;        AHandle:THandle;           I, N:integer; begin  if AttachConsole(ATTACH_PARENT_PROCESS) then  begin    StdInputHandle:=0;    StdOutputHandle:=0;    StdErrorHandle:=0;    IsConsole:=True;                                   // in System unit    SysInitStdIO;                                      // in System unit    SetConsoleCtrlHandler(@ConsoleHandler, true);      // hook for ctrl-C, break, and close     writeln;    writeln(StringOfChar('-', 72));    N:=GetConsoleProcessList(@ProcessList, 64);     if N<=64 then    begin       writeln('process', #9, 'handle', #9);       for I:=1 to N do if ProcessList[I]=GetProcessID then writeln(ProcessList[I], #9#9, '(self)') else      begin        AHandle:=OpenProcess(PROCESS_TERMINATE, false, ProcessList[I]);        // NOT us        if (AHandle<>0) and TerminateProcess(AHandle,255) then writeln(ProcessList[I], #9, AHandle, #9, 'closed')                                                          else writeln(ProcessList[I], #9, AHandle, #9)       end    end;     writeln(StringOfChar('-', 72));    writeln  endend. 
what this does is:

1. try to attach back to the launching console. if this fails (as happens if there is no launching console), then carry on as a "GUI only" application.

2a. if a console was found, set up a control handler (ctrl-C, break, etc) for it,
2b. find all OTHER processes attached to that console and kill them,
2c. carry on as a "composite GUI + console" application. if either GUI or console is closed, then the other also closes.

this gives the GUI 100% exclusive access to the console, and appears to function (for my particular application) as a fairly clean solution.


but in an ideal world, it would be nice to be able to suspend the other process(es) attached to the console instead of killing them, and when the GUI is ready to exit simply resume them. but that has proven elusive. we can simply do a "START /WAIT project1", but the GUI has no way to tell if the user has actually started it this way or not and so is less than entirely bullet-proof.

any suggestions on how to suspend a console-attached process instead of killing it?


cheers,
rob   :-)

Navigation

[0] Message Index

[#] Next page

Go to full version