Recent

Author Topic: [Experience Sharing] Win32 GUI program WriteLn  (Read 8427 times)

mercury

  • Full Member
  • ***
  • Posts: 154
[Experience Sharing] Win32 GUI program WriteLn
« on: August 23, 2015, 09:14:17 am »
The main goal is
if you start the program from the command line it is written to the terminal it was started from. Even when started from GUI (say, a Run dialog), the stdout can still be written to, but is discarded since there it is not redirected anywhere;

This problem troubled me almost one day.
After I googled it many times, read lots of posts.
and make the conclusion :
Code: [Select]
AFAIK there is no way to do this.

The reason:
In PE structure, there is an item "Subsystem" in "IMAGE_OPTIONAL_HEARDER".
Value "IMAGE_SUBSYSTEM_WINDOWS_CUI" Means Windows character-mode user interface (CUI) subsystem.
Value "IMAGE_SUBSYSTEM_WINDOWS_GUI" Means Windows graphical user interface (GUI) subsystem.
Look this two pages for details.
Code: [Select]
http://bbs.csdn.net/topics/360238669#post-371345748
https://msdn.microsoft.com/en-us/library/windows/desktop/ms680339%28v=vs.85%29.aspx
So you can't change it after program started.



Maybe AttachConsole() can create a console, but WriteLn still doesn’t work.
And project1.exe>out.txt doesn’t work either.
You must use WriteConsole() instead. And this is not stdout! It only output to the console handle.
Code: [Select]
function WriteConsole(hConsoleOutput: THandle; const lpBuffer: Pointer; nNumberOfCharsToWrite: DWORD; var lpNumberOfCharsWritten: DWORD; lpReserved: Pointer): BOOL;external 'kernel32' name 'WriteConsoleA';


i think the best way is:
Code: [Select]
make the program in console mode.
And hide the console window if started from GUI.
code like this
Code: [Select]
...
var
  H: THandle;
begin
  H := FindWindowEx(0, 0, nil, PChar(Utf8ToAnsi(Application.Params[0])));
  if H <> 0 then
    SetWindowPos(H, HWND_TOPMOST, 0, 0, 0, 0, $80);
...
end.
Anyway, there will be a splash window. ╮(╯_╰)╭
(In my computer it's about 100ms, so normal people may can't notice)

If you have any other idea, please tell me.


« Last Edit: August 23, 2015, 09:18:20 am by mercury »

Cyrax

  • Hero Member
  • *****
  • Posts: 836
Re: [Experience Sharing] Win32 GUI program WriteLn
« Reply #1 on: August 23, 2015, 04:01:41 pm »
You need to use AllocConsole (add Windows unit in to your unit clause) and reinitialize code related WriteLn/ReadLn handling from System unit. See code snipper on how to do this :
Code: [Select]
IsConsole := True; // in System unit
SysInitStdIO;      // in System unit

See this link for more info : http://stackoverflow.com/questions/20134421/can-a-windows-gui-program-written-in-lazarus-create-a-console-and-write-to-it-at
« Last Edit: August 23, 2015, 04:03:59 pm by Cyrax »

wp

  • Hero Member
  • *****
  • Posts: 11916
Re: [Experience Sharing] Win32 GUI program WriteLn
« Reply #2 on: August 23, 2015, 04:13:31 pm »
I do not completely understand what you want to achieve, but did you know that there is a checkbox "Win32 gui application" in "Project options" / "Compiler options" / "Config and Target"? If you uncheck it you get a console window in addition to the normal form window and you can WriteLn to it.

jc99

  • Hero Member
  • *****
  • Posts: 553
    • My private Site
Re: [Experience Sharing] Win32 GUI program WriteLn
« Reply #3 on: August 23, 2015, 11:11:11 pm »
I do not completely understand what you want to achieve, but did you know that there is a checkbox "Win32 gui application" in "Project options" / "Compiler options" / "Config and Target"? If you uncheck it you get a console window in addition to the normal form window and you can WriteLn to it.
I think the goal here is: Have a normal GUI-Program open it's own console for output at it's will, e.G: if you hit a debug-Checkbox or to use it as a console program if you pass -NoGUI as parameter ...
BTW: I think it's a very interesting concept.
OS: Win XP x64, Win 7, Win 7 x64, Win 10, Win 10 x64, Suse Linux 13.2
Laz: 1.4 - 1.8.4, 2.0
https://github.com/joecare99/public
'~|    /''
,_|oe \_,are
If you want to do something for the environment: Twitter: #reduceCO2 or
https://www.betterplace.me/klimawandel-stoppen-co-ueber-preis-reduzieren

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11452
  • FPC developer.
Re: [Experience Sharing] Win32 GUI program WriteLn
« Reply #4 on: August 24, 2015, 12:12:06 pm »
Or, if just for debugging,  one can also create a custom filedriver and assign the output of writeln to a memo (and stderr to a different memo or both to the same, but play with color etc)

I usually look at the unit source of fcl-base/src/streamio.pp how to remember writing a custom textfile driver.

Lazarus does something the same with its options to redirect debug console to file etc.

mercury

  • Full Member
  • ***
  • Posts: 154
Re: [Experience Sharing] Win32 GUI program WriteLn
« Reply #5 on: August 30, 2015, 04:37:49 pm »
You need to use AllocConsole (add Windows unit in to your unit clause) and reinitialize code related WriteLn/ReadLn handling from System unit. See code snipper on how to do this :
Code: [Select]
IsConsole := True; // in System unit
SysInitStdIO;      // in System unit

See this link for more info : http://stackoverflow.com/questions/20134421/can-a-windows-gui-program-written-in-lazarus-create-a-console-and-write-to-it-at
Anyway, this can't used by pipe.
My original goal is make a GUI program can used by other program through pipe.

Thaddy

  • Hero Member
  • *****
  • Posts: 14373
  • Sensorship about opinions does not belong here.
Re: [Experience Sharing] Win32 GUI program WriteLn
« Reply #6 on: August 30, 2015, 06:54:39 pm »
In that case you can simply copy out the pipes example code, which will do exactly what you want. In pipes.pp you will find CreatePipeStreams.
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

derek.john.evans

  • Guest
Re: [Experience Sharing] Win32 GUI program WriteLn
« Reply #7 on: August 30, 2015, 07:17:13 pm »
Ive been reading this thread with some interest. Hopefully im not missing the goal here.

Is the idea to have a GUI/Console app, with the option of running it without the console showing? How about bootstrapping?

I created a GUI/console app (project1). As a test, if you place Sleep(10000); in project1.lpr, you can see a console is displayed before any project code can be even executed, which means closing the window causes a flash of console.

So, in project1, If the Output file handle is not available, I assign it to a text file. Other streams would need to be redirected to files or controls using StreamIO.
Code: [Select]
  if TextRec(Output).Handle = 0 then begin
    AssignFile(Output, 'test.txt');
    Rewrite(Output);
  end;     

Then I created project2, (which is a GUI only app). In OnCreate I put:

Code: [Select]
  Process1.Executable := ProgramDirectory + 'project1.exe';
  Process1.Options := [poNoConsole];
  Process1.Execute;
  Halt;   

This executed project1 with no console ever showing, and halted project2 before it displayed any window. Project1 continued to run and wrote output to 'test.txt'.

Unsure if that is any help to anyone.
« Last Edit: August 30, 2015, 07:18:59 pm by derek.john.evans »

mercury

  • Full Member
  • ***
  • Posts: 154
Re: [Experience Sharing] Win32 GUI program WriteLn
« Reply #8 on: September 27, 2015, 03:00:32 pm »
@Geepster

Yeah, it’s good.
But I don’t like make 2 execute files. If that, just make a GUI version and a console version.
I just want make 1 execute file can both used in GUI or console (also used by other program by pipe). And no console window when used in GUI.

In that case you can simply copy out the pipes example code, which will do exactly what you want. In pipes.pp you will find CreatePipeStreams.
CreatePipeStreams ?
Would you give me an example?

« Last Edit: September 27, 2015, 03:03:24 pm by mercury »

sky_khan

  • Guest
Re: [Experience Sharing] Win32 GUI program WriteLn
« Reply #9 on: September 27, 2015, 04:35:16 pm »
you can have gui in console mode application (for windows)

Code: [Select]
program project1;
{$APPTYPE CONSOLE}
{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Interfaces, // this includes the LCL widgetset
  Forms,
  JwaWinCon,
  Unit1
  { you can add units after this };

{$R *.res}

var
  isGUI : boolean;
begin
  isGUI:= (ParamCount>=1) and (ParamStr(1)='/gui');
  if isGUI then
  begin
    FreeConsole;
    RequireDerivedFormResource := True;
    Application.Initialize;
    Application.CreateForm(TForm1, Form1);
    Application.Run;
  end
  else
  begin
    writeln('Hello World');
    readln;
  end;
end.

it will be a regular console application but it will close console immediately and show a gui if it is launched with /gui parameter.
not sure if it would work for you, though

mercury

  • Full Member
  • ***
  • Posts: 154
Re: [Experience Sharing] Win32 GUI program WriteLn
« Reply #10 on: September 30, 2015, 03:26:08 pm »
you can have gui in console mode application (for windows)

Code: [Select]
program project1;
{$APPTYPE CONSOLE}
{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Interfaces, // this includes the LCL widgetset
  Forms,
  JwaWinCon,
  Unit1
  { you can add units after this };

{$R *.res}

var
  isGUI : boolean;
begin
  isGUI:= (ParamCount>=1) and (ParamStr(1)='/gui');
  if isGUI then
  begin
    FreeConsole;
    RequireDerivedFormResource := True;
    Application.Initialize;
    Application.CreateForm(TForm1, Form1);
    Application.Run;
  end
  else
  begin
    writeln('Hello World');
    readln;
  end;
end.

it will be a regular console application but it will close console immediately and show a gui if it is launched with /gui parameter.
not sure if it would work for you, though

I think this is nothing different to my solution. Also has a splash console window.  :)

 

TinyPortal © 2005-2018