Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2011-12

How to poll for a byte in Input?

10 messages · 5 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

tcoq

Hello,
I'm trying to poll the input stream to see whether there is a new character available, without blocking my software. I've tried "Read" and "THandleStream.ReadByte" but with no success: when the input is empty, the program just waits.

Is there a way to ask whether the input stream is not empty, without waiting and without using threads/processes? And, in addition, is there an OS-independent way (linux, windows)?

something like:
var
  inputStream: THandleStream;
  aByte: Byte;
begin
  InputStream := THandleStream.Create(StdInputHandle);
  try
    while true do
    begin
      {$I-}
      aByte := InputStream.ReadByte;
      DoSomethingWithByte(aByte);
      result := true;
    end;
  except
    On E: EStreamError do result := false;
  end;
  //This code here is never executed
  ThereIsNothingInInputStream_DoDefault;
end;

(PS. In windows, I traced the "readByte" procedure down to a Windows API function: ReadFile in Kernel32. This seems to be a blocking function). Best regards,
Thierry

Reply by email

Michael Van Canneyt
to tcoq
tcoq wrote 28 lines
Hello, I'm trying to poll the input stream to see whether there is a new character available, without blocking my software. I've tried "Read" and "THandleStream.ReadByte" but with no success: when the input is empty, the program just waits. Is there a way to ask whether the input stream is not empty, without waiting and without using threads/processes? And, in addition, is there an OS-independent way (linux, windows)? something like: var inputStream: THandleStream; aByte: Byte; begin InputStream := THandleStream.Create(StdInputHandle); try while true do begin {$I-} aByte := InputStream.ReadByte; DoSomethingWithByte(aByte); result := true; end; except On E: EStreamError do result := false; end; //This code here is never executed ThereIsNothingInInputStream_DoDefault; end;

You must use fpSelect() or Poll on the handle on linux/unix. On windows, you'll need to WaitForObject() or so on the file handle.

Michael.

Reply by email

tcoq
to Michael Van Canneyt

Thanks Michael,
I'll try that!
Thierry
----- Mail Original -----
De: "Michael Van Canneyt"
À: "FPC-Pascal users discussions"
Envoyé: Vendredi 9 Décembre 2011 16h57:47 GMT +01:00 Amsterdam / Berlin / Berne / Rome / Stockholm / Vienne Objet: Re: [fpc-pascal] How to poll for a byte in Input? ...

You must use fpSelect() or Poll on the handle on linux/unix. On windows, you'll need to WaitForObject() or so on the file handle.

Michael.

Reply by email

Marco van de Voort
to Michael Van Canneyt: You must use fpSelect() or Poll on the handle on linux/unix. On windows, you'll need to…

In our previous episode, Michael Van Canneyt said:

Show quoted text 26 lines
> Is there a way to ask whether the input stream is not empty, without waiting and without using threads/processes? > And, in addition, is there an OS-independent way (linux, windows)? > > something like: > var > inputStream: THandleStream; > aByte: Byte; > begin > InputStream := THandleStream.Create(StdInputHandle); > try > while true do > begin > {$I-} > aByte := InputStream.ReadByte; > DoSomethingWithByte(aByte); > result := true; > end; > except > On E: EStreamError do result := false; > end; > //This code here is never executed > ThereIsNothingInInputStream_DoDefault; > end; You must use fpSelect() or Poll on the handle on linux/unix. On windows, you'll need to WaitForObject() or so on the file handle.

Be careful on streams that might have buffers though. Then you first need to check if there are still bytes in the buffer.

Reply by email

tcoq
to Michael Van Canneyt: You must use fpSelect() or Poll on the handle on linux/unix. On windows, you'll need to…

On windows, I did not find WaitForSingleObject, which is actually going to wait for the file. However I found ReadFileEx in the Windows API, which is an asynchronous read on the file. I'll try using that.

----- Mail Original -----
De: "Michael Van Canneyt"
À: "FPC-Pascal users discussions"
Envoyé: Vendredi 9 Décembre 2011 16h57:47 GMT +01:00 Amsterdam / Berlin / Berne / Rome / Stockholm / Vienne Objet: Re: [fpc-pascal] How to poll for a byte in Input?

...

You must use fpSelect() or Poll on the handle on linux/unix. On windows, you'll need to WaitForObject() or so on the file handle.

Michael.

Reply by email

Jorge Aldo G. de F. Junior
to tcoq

Thats an old problem on the way TStream was implemented.

I for one needed to know the result size of a uncompress stream (IE.: i have a compressed stream and want to know in advance how many bytes the uncompress method would yield).

The stream system is currently a mess, sometimes you can know in advance how many bytes you can read, sometimes you cant, depends on the class you are dealing with.

2011/12/9 tcoq :

Show quoted text 19 lines
On windows, I did not find WaitForSingleObject, which is actually going to wait for the file. However I found ReadFileEx in the Windows API, which is an asynchronous read on the file. I'll try using that. ----- Mail Original ----- De: "Michael Van Canneyt" À: "FPC-Pascal users discussions" Envoyé: Vendredi 9 Décembre 2011 16h57:47 GMT +01:00 Amsterdam / Berlin / Berne / Rome / Stockholm / Vienne Objet: Re: [fpc-pascal] How to poll for a byte in Input? >... You must use fpSelect() or Poll on the handle on linux/unix. On windows, you'll need to WaitForObject() or so on the file handle. Michael. _______________________________________________ fpc-pascal maillist  -   http://lists.freepascal.org/mailman/listinfo/fpc-pascal _______________________________________________ fpc-pascal maillist  -   http://lists.freepascal.org/mailman/listinfo/fpc-pascal

Reply by email

Michael Van Canneyt
to Jorge Aldo G. de F. Junior
Jorge Aldo G. de F. Junior wrote 5 lines
Thats an old problem on the way TStream was implemented. I for one needed to know the result size of a uncompress stream (IE.: i have a compressed stream and want to know in advance how many bytes the uncompress method would yield).

This information is not stored in the compressed data. To be able to give you this number, the compression stream would need to actually decompress the whole stream.

The stream system is currently a mess, sometimes you can know in advance how many bytes you can read, sometimes you cant, depends on the class you are dealing with.

There are simply streams which have no size.

In each case, I am working on the 'StreamCapabilities' property, which should be able to tell you what is available.

Michael.

Reply by email

Jorge Aldo G. de F. Junior
to Michael Van Canneyt

I think the main problem is that TStream tries to abstract two kinds of resources :

Character based resources and block based resources. The end result is that it is no good for neither of the two cases.

2011/12/9 Michael Van Canneyt :

Show quoted text 32 lines
On Fri, 9 Dec 2011, Jorge Aldo G. de F. Junior wrote: > Thats an old problem on the way TStream was implemented. > > I for one needed to know the result size of a uncompress stream (IE.: > i have a compressed stream and want to know in advance how many bytes > the uncompress method would yield). This information is not stored in the compressed data. To be able to give you this number, the compression stream would need to actually decompress the whole stream. > > The stream system is currently a mess, sometimes you can know in > advance how many bytes you can read, sometimes you cant, depends on > the class you are dealing with. There are simply streams which have no size. In each case, I am working on the 'StreamCapabilities' property, which should be able to tell you what is available. Michael. _______________________________________________ fpc-pascal maillist  -   http://lists.freepascal.org/mailman/listinfo/fpc-pascal

Reply by email

Michael Van Canneyt
to Jorge Aldo G. de F. Junior
Jorge Aldo G. de F. Junior wrote 5 lines
I think the main problem is that TStream tries to abstract two kinds of resources : Character based resources and block based resources. The end result is that it is no good for neither of the two cases.

You are not forced to use it :-)

I've been using it for many years, never had any problems...

Michael.

Reply by email

Chad Berchek
to tcoq: I'm trying to poll the input stream to see whether there is a new character available,…
tcoq wrote
Hello, I'm trying to poll the input stream to see whether there is a new character available, without blocking my software.

Maybe TInputPipeStream.NumBytesAvailable can help. You can find it in the documentation of the Pipes unit.

I tried an experiment to make sure it would work. I included the code for the experiment below. It works when stdin is from another process. It does not seem to work if stdin is from the console and I'm not sure why. I tried adding the Windows API call SetConsoleMode(StdInputHandle,
1) to disable line-input mode but it didn't seem to make any difference.

Regarding WaitForSingleObject, there are some comments on the MSDN documentation page regarding the use of this function for console input. In particular, one observation was that if the console is in line-input mode, WaitForSingleObject can return but ReadConsole will still block until a carriage return is entered. Perhaps the same is true for ReadFile. I have not tested this. See:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms687032%28v=vs.85%29.aspx

Code for the test: There are two programs. You can run WriteToPipe to see writing to the stdin stream of one process from another process. You can also test piped input using TestPipes by typing a command like:

type TestPipes.pas | TestPipes

However this does not seem to work:

TestPipes < TestPipes.pas

I don't know why exactly it doesn't appear to work with redirected input from a file or from the console. By the way, I'm using Windows XP Pro 64-bit. Here is the test code.

----- WriteToPipe.pas -----

program WriteToPipe;
{$MODE ObjFpc}

uses
SysUtils,
Process,

   Windows;
var
   AProc: TProcess;
   AString: string;
   ACount: Integer;
begin
   WriteLn('This will take about 20 seconds.');
   AProc := TProcess.Create(nil);
   try
     AProc.CommandLine := 'TestPipes.exe';
     AProc.Options := [poUsePipes];
     AProc.Execute;
     try
       for ACount := 0 to 5 do
       begin
         AString := IntToStr(ACount);
         AProc.Input.WriteBuffer(AString[1], Length(AString));
         Sleep (3000);
       end;
     finally
       AProc.Terminate(0);
     end;
   finally
     AProc.Free;
   end;
end.

----- End -----

----- TestPipes.pas -----

program TestPipes;
{$MODE ObjFpc}

uses
Classes,
Pipes,
SysUtils,

   Windows;
var
   AStream: TInputPipeStream;
   AString: string;
   AFile: TextFile;
begin
   Assign(AFile, 'output.txt');
   Rewrite(AFile);
   try
     AStream := TInputPipeStream.Create(StdInputHandle);
     while True do
     begin
       if AStream.NumBytesAvailable > 0 then
       begin
         SetLength(AString, AStream.NumBytesAvailable);
         AStream.ReadBuffer(AString[1], Length(AString));
         WriteLn(AFile, 'I just got this: ', AString);
       end else
       begin
         WriteLn(AFile, 'Waiting...');
         Sleep(1000);
       end;
     end;
   finally
     Close(AFile);
   end;
end.

----- End -----

Reply by email