I'm trying to implement a class wrapper around Powershell
.
It almost works, except that it hangs when it cannot read any more output from the STREAM
.
So it manages to read all output, but after theres no more, it just hangs in IMPORT
statement:
BLOCK-LEVEL ON ERROR UNDO, THROW.
CLASS Powershell:
DEF PRIVATE STREAM stPowershell.
CONSTRUCTOR PUBLIC Powershell():
INPUT-OUTPUT STREAM stPowershell THROUGH VALUE("powershell").
THIS-OBJECT:ReadOutput().
END.
DESTRUCTOR Powershell():
INPUT-OUTPUT STREAM stPowershell CLOSE.
END.
METHOD PUBLIC CHAR Input(i_cInput AS CHAR):
IF i_cInput = ? THEN UNDO, THROW NEW Progress.Lang.AppError(SUBST("&1: 'i_cInput' is 'UNKNOWN'!", PROGRAM-NAME(1))).
PUT STREAM stPowershell UNFORMATTED i_cInput SKIP.
RETURN THIS-OBJECT:ReadOutput().
END.
METHOD PROTECTED CHAR ReadOutput():
DEF VAR cOutputs AS CHAR NO-UNDO.
DEF VAR cOutput AS CHAR NO-UNDO.
DEF VAR lFirst AS LOGICAL NO-UNDO INIT TRUE.
REPEAT:
IF lFirst THEN lFirst = FALSE.
ELSE cOutputs = cOutputs + "~n".
IMPORT STREAM stPowershell UNFORMATTED cOutput NO-ERROR.
cOutputs = cOutputs + cOutput.
END.
RETURN cOutputs.
END.
END.
Usage:
DEF VAR oPowershell AS CLASS Powershell NO-UNDO.
DEF VAR cOutput AS CHAR NO-UNDO.
oPowershell = NEW Powershell().
cOutput = oPowershell:Input("$num = 12").
cOutput = oPowershell:Input("(New-Object -ComObject Wscript.Shell).Popup($num, 0, 'Done', 0x0)").
cOutput = oPowershell:Input("Write-Output 'test output'").
I managed to get this version to work, but I wouldn't call it a perfect solution.
To avoid the output read hanging issue, I made the Powershell
output EOF
character after each command. This way all synchronous execution will always end in EOF
character in the Powershell
output. We will read the output until we reach the EOF
and stop there.
Possible problems can arise if we're running asynchronous commands, or the output contains EOF
characters.
I think the best solution would require us to use platform specific API
s (for example WIN32 API
). We need a way to read all existing output. But I think this version will work for a lot of normal cases.
BLOCK-LEVEL ON ERROR UNDO, THROW.
CLASS Powershell:
&SCOPED-DEFINE POWERSHELL_EOF "[char]26"
&SCOPED-DEFINE PROGRESS_EOF 26
DEF PRIVATE STREAM stPowershell.
CONSTRUCTOR PUBLIC Powershell():
/* Start new 'Powershell' */
INPUT-OUTPUT STREAM stPowershell THROUGH VALUE("powershell") NO-ECHO.
/* Disable prompt */
PUT STREAM stPowershell UNFORMATTED "$global:Prompt = $null" SKIP.
/* Flush startup output */
THIS-OBJECT:Read().
END.
DESTRUCTOR Powershell():
INPUT-OUTPUT STREAM stPowershell CLOSE.
END.
METHOD PUBLIC CHAR Write(i_cInput AS CHAR):
IF i_cInput = ? THEN UNDO, THROW NEW Progress.Lang.AppError(SUBST("&1: 'i_cInput' is 'UNKNOWN'!", PROGRAM-NAME(1))).
/* Write input */
PUT STREAM stPowershell UNFORMATTED i_cInput SKIP.
/* Read output */
RETURN THIS-OBJECT:Read().
END.
METHOD PROTECTED CHAR Read():
DEF VAR cOutputs AS CHAR NO-UNDO.
/* Output 'EOF' manually */
PUT STREAM stPowershell UNFORMATTED {&POWERSHELL_EOF} SKIP.
/* Read all output until 'EOF' */
REPEAT:
READKEY STREAM stPowershell PAUSE 0.
/* If we reached the 'EOF', stop reading */
IF LASTKEY = {&PROGRESS_EOF} THEN DO:
LEAVE.
END.
cOutputs = cOutputs + CHR(LASTKEY).
END.
RETURN cOutputs.
END.
END.