PipeToString for PowerBASIC

Windows specific questions.
deltarho[1859]
Posts: 4313
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

PipeToString for PowerBASIC

Post by deltarho[1859] »

I am trying to write a dll in FreeBASIC which uses 'Open Pipe' so that I can use 'Open Pipe' in PowerBASIC.

I have adapted dodicat's PipeToString().

Here is the FreeBASIC code compiled as a dll.

Code: Select all

#cmdline "-gen gcc -O 2 "
#include once "win/ole2.bi"
 
Function StrToBSTR( FB_String As String) As BSTR
  Return  SysAllocStringByteLen( FB_String, Len(FB_String) )
End Function
 
Extern "Windows-MS"
  Public Function PipeToString( ByRef s As ZString ) As BSTR Export
    Dim dummy as string = s
    Dim As Bstr bstrCopy0, bstrCopy1
    Var f=Freefile
    Dim As String tmp
    Open Pipe s For Input As #f
    dummy = ""
    Do Until Eof(f)
      Line Input #f,tmp
      dummy += tmp + Chr(10)
    Loop
    Close #f
    bstrCopy0 = StrToBSTR(dummy)
    bstrCopy1 = bstrCopy0 ' I'm using this so that I can use SysFreeString
    SysFreeString(bstrCopy0)
    Return bstrCopy1
  End Function
End Extern
Here is the PowerBASIC code using PBCC

Code: Select all

#Compile exe
#Dim All
#Include "win32api.inc"
 
Declare Function PipeToString Lib "PipeToString.dll" Alias "PipeToString"( s As StringZ ) As String
 
Function PBMain
 
Print PipeToString("tasklist")
 
Waitkey$
 
End Function

Regarding the parameter, ByRef is used in the FB code because that is what PowerBASIC uses by default. FB's ZString equates with PB's StringZ.

However, FB's strings are not the same as PB's strings, and why I am exporting a BSTR in the FB code.

tasklist gets printed in PB, but the formatting is well out. It is as if Chr(10) in the FB code is being ignored.

I am afraid that I have gone sailing past my pay grade on this one. Either that or senile decay is definitely starting to appear.

Any suggestions?
Josep Roca
Posts: 564
Joined: Sep 27, 2016 18:20
Location: Valencia, Spain

Re: PipeToString for PowerBASIC

Post by Josep Roca »

I don't have time now to test the code, but this is wrong:

Code: Select all

    bstrCopy1 = bstrCopy0 ' I'm using this so that I can use SysFreeString
    SysFreeString(bstrCopy0)
    Return bstrCopy1
You're returning a copy of a BSTR pointer that you have just released (a dangling pointer).

Just use Return bstrCopy0.

The returned BSTR pointer will be released by PowerBasic.
deltarho[1859]
Posts: 4313
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: PipeToString for PowerBASIC

Post by deltarho[1859] »

Thanks, José.

Here is the new FB code.

Code: Select all

#cmdline "-gen gcc -O 2 "
#include once "win/ole2.bi"

Function StrToBSTR( FB_String As String) As BSTR
    Return  SysAllocStringByteLen( FB_String, Len(FB_String) )
End Function

Extern "Windows-MS"
  Public Function PipeToString( ByRef s As ZString ) As BSTR Export
    Dim dummy As String = s ' Convert to a FB string
    Var f=Freefile
    Dim As String tmp
    Open Pipe s For Input As #f 
    dummy = ""
    Do Until Eof(f)
      Line Input #f,tmp
      dummy += tmp + Chr(10)
    Loop
    Close #f
    Return StrToBSTR(dummy)
  End Function
End Extern
However, tasklist still gets printed in PB, but the formatting is still well out.
Last edited by deltarho[1859] on Jun 29, 2023 12:42, edited 1 time in total.
deltarho[1859]
Posts: 4313
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: PipeToString for PowerBASIC

Post by deltarho[1859] »

I wrote an elementary dll using 'As BSTR Export' and that worked fine.

It seems to me that 'dummy += tmp + Chr(10)' is the issue but I cannot see how at the moment.
Josep Roca
Posts: 564
Joined: Sep 27, 2016 18:20
Location: Valencia, Spain

Re: PipeToString for PowerBASIC

Post by Josep Roca »

> However, tasklist still gets printed in PB

Because Windows has not still reused the memory, but it is unsafe to do it.

See the rules for Allocating and Releasing Memory for a BSTR
https://learn.microsoft.com/en-us/cpp/a ... w=msvc-170

"When you implement a function that returns a BSTR, allocate the string but do not free it. The receiving function releases the memory. "
deltarho[1859]
Posts: 4313
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: PipeToString for PowerBASIC

Post by deltarho[1859] »

Thanks, José.

I am not using SysFreeString() anywhere now – in FB or PB.

My problem is 'Print PipeToString("tasklist")' in PowerBASIC. The formatting is wrong compared with using 'tasklist' in a command prompt.
deltarho[1859]
Posts: 4313
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: PipeToString for PowerBASIC

Post by deltarho[1859] »

In PowerBASIC I tried

Code: Select all

Print Val(PipeToString("echo %NUMBER_OF_PROCESSORS%"))
and that came up with '8' – which is correct.

That tells me that there is something wrong with dodicat's PipeToString() handling multiline outputs.
deltarho[1859]
Posts: 4313
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: PipeToString for PowerBASIC

Post by deltarho[1859] »

@dodicat

Have you tried PipeToString() with multiline outputs?
dbickin
Posts: 59
Joined: Aug 03, 2005 16:40

Re: PipeToString for PowerBASIC

Post by dbickin »

deltarho[1859] wrote: Jun 29, 2023 11:47 I wrote an elementary dll using 'As BSTR Export' and that worked fine.

It seems to me that 'dummy += tmp + Chr(10)' is the issue but I cannot see how at the moment.
Have you examined the bytes returned in the string? Are the chr(10) still there as expected?
Does PowerBasic expect lines to end with chr(10), or does it require a chr(13) as well?

David
deltarho[1859]
Posts: 4313
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: PipeToString for PowerBASIC

Post by deltarho[1859] »

Hi David.
dbickin" wrote:Have you examined the bytes returned in the string? Are the chr(10) still there as expected?
Yes.
Does PowerBasic expect lines to end with chr(10), or does it require a chr(13) as well?
I'd already tried Chr(13,10) to no avail.

I have just tried dodicat's PrintToString() in FreeBASIC using

Code: Select all

Print PipeToString("ipconfig/flushdns")
and it printed two lines. So, it can handle multiline.

On the other hand, doing the same in PowerBASIC and only one line is printed.

In PowerBASIC using 'Print PipeToString("tasklist")' I got this
Image
If I Copy the console and Paste into TextPad the formatting is perfect – just like 'tasklist' in the command prompt.

What the blazes is PowerBASIC's Print doing?

It could be worse – I only have grey hair now. :)

Image
hhr
Posts: 211
Joined: Nov 29, 2019 10:41

Re: PipeToString for PowerBASIC

Post by hhr »

This works in FB. It uses 'Binary' and takes 'Input' just like 'Line Input'.

Code: Select all

Function PipeToString(Byval s As String="") As String
   Var f=Freefile
   Open Pipe s For Binary As #f 
   Line Input #f,s
   Close #f
   Return s
End Function

Print PipeToString("tasklist")
Sleep
Edit:
My code with 'Binary' is incorrect. It only seems to work.
I would like to give a correction which I hope is correct:

Code: Select all

Function PipeToString(Byval s As String = "") As String
   Dim As String*16384 d
   Dim As Long f = Freefile
   Open Pipe s For Binary Access Read As #f
   Get #f,,d
   Close #f
   Return d
End Function

Print PipeToString("tasklist")
Sleep
One disadvantage is that you have to specify the length of the string beforehand.
Better to look at the following:
https://www.freebasic.net/wiki/KeyPgOpenPipe
=================================
Edit:
Both programs seem to work the same.

If you remove the 'Print' in both programs, the first program shows the result, the second does not.

You can replace

Code: Select all

Print PipeToString("tasklist")
with

Code: Select all

Dim As String s
s = PipeToString("tasklist")
Print s
Print Len(s)
.
The first program shows the result, but the returned string is empty.
The second program actually prints out the returned string.

If 'Print s' and 'Print Len(s)' are removed, nothing should be displayed.
Last edited by hhr on Jul 03, 2023 8:49, edited 4 times in total.
deltarho[1859]
Posts: 4313
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: PipeToString for PowerBASIC

Post by deltarho[1859] »

@hhr

I don't have any issues with FreeBASIC.

I did try Binary though, but that made no difference.

In FB the parameter must be 'ByRef s As ZString' to 'marry' with PoweBASIC's 's As stringZ'; which defaults to ByRef.

The issue I have is with PowerBASIC's Print.

See my last post.
deltarho[1859]
Posts: 4313
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: PipeToString for PowerBASIC

Post by deltarho[1859] »

Actually the 'Here is the new FB code.' above is wrong. It should have been

Code: Select all

#cmdline "-gen gcc -O 2 "
#include once "win/ole2.bi"

Function StrToBSTR( FB_String As String) As BSTR
    Return  SysAllocStringByteLen( FB_String, Len(FB_String) )
End Function

Extern "Windows-MS"
  Public Function PipeToString( ByRef s As ZString ) As BSTR Export
    Dim dummy As string = s ' Convert to a FB string
    Var f=Freefile
    Dim As String tmp
    Open Pipe dummy For Input As #f 
    dummy = ""
    Do Until Eof(f)
      Line Input #f,tmp
      dummy += tmp + Chr(10)
    Loop
    Close #f
    Return StrToBSTR(dummy)
  End Function
End Extern
:D It is working now. So PowerBASIC was not misbehaving. It was simply a case of 'Rubbish in, rubbish out'. The 'Rubbish in' being my code; no surprises there then. :wink:

I'll keep testing.
deltarho[1859]
Posts: 4313
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: PipeToString for PowerBASIC

Post by deltarho[1859] »

Yep, all is well. I now have PipeToString() in PowerBASIC. Thanks, dodicat. :)
adeyblue
Posts: 301
Joined: Nov 07, 2019 20:08

Re: PipeToString for PowerBASIC

Post by adeyblue »

And here's how you could do it natively in PowerBasic. Which is how FreeBasic does it underneath its waffle, because it's the C way. Don't you love all the languages living in harmony like that.

Code: Select all

Declare Function popen cdecl Lib "msvcrt.dll" Alias "_popen" (ByRef cmd as StringZ, ByRef mode as StringZ) as Dword
Declare Function fread cdecl Lib "msvcrt.dll" Alias "fread" (ByVal buffer as Dword, ByVal elemSize as Dword, ByVal elemCount as Dword, ByVal fp as Dword) As Dword
Declare Function fclose cdecl Lib "msvcrt.dll" Alias "fclose" (ByVal fp as Dword) as Long

Function Pipe(ByRef command as StringZ) as String
    Local cmdOut as IStringBuilderA
    Local buf as StringZ * 1024
    Local bytesRead as Dword
    Local fp as Dword

    fp = popen(command, "rb")
    If fp = 0 Then Exit Function
    cmdOut = Class "StringBuilderA"
    Do
        bytesRead = fread(VarPtr(buf), 1, SizeOf(buf), fp)
        cmdOut.Add(Left$(buf, bytesRead))
    Loop While bytesRead = SizeOf(buf)
    fclose(fp)
    Function = cmdOut.string
End Function
Reading the output as a job lot of bytes rather than line by line means you don't strip the line endings off and have to put them back on again.
We need a StringBuilder, and a Print Using to string, then I think were entirely better than PowerBasic.
Post Reply