Skip to content

Instantly share code, notes, and snippets.

@santisq
Last active December 8, 2024 18:29
Show Gist options
  • Save santisq/f9a94b15f450c5f350a27fb05da22e1b to your computer and use it in GitHub Desktop.
Save santisq/f9a94b15f450c5f350a27fb05da22e1b to your computer and use it in GitHub Desktop.
Add-Type (Get-Content .\UseObjectCommand.cs -Raw) -PassThru |
Import-Module -Assembly { $_.Assembly }
try {
$tmp = New-TemporaryFile
0..10 | use ($writer = [System.IO.StreamWriter]::new($tmp.FullName)) {
$writer.WriteLine($_)
if ($_ -eq 5) {
throw 'Should correctly dispose `$writer`.'
}
}
}
catch {
# ignore the script terminating error
}
finally {
# StreamWriter __if not disposed correctly__ shouldn't allow this.
# It opens a FileStream with NO shared permissions
Add-Content $tmp.FullName 'Should be able to append content.'
}
# Expected here is `0 to 5 + Should be able to append content.`
Get-Content $tmp.FullName | Write-Host
$tmp.Delete()
using System;
using System.Collections.Generic;
using System.Linq;
using System.Management.Automation;
using System.Management.Automation.Language;
using System.Text;
using System.Threading;
[Cmdlet(VerbsOther.Use, "Object")]
[Alias("use")]
public sealed class UseObjectCommand : PSCmdlet, IDisposable
{
private SteppablePipeline _pipe;
private CancellationTokenSource _src;
[Parameter(ValueFromPipeline = true)]
public object InputObject { get; set; }
[Parameter(Mandatory = true, Position = 0)]
public IDisposable Disposable { get; set; }
[Parameter(Mandatory = true, Position = 1)]
public ScriptBlock ScriptBlock { get; set; }
[Parameter]
public SwitchParameter UseLocalScope { get; set; }
[Parameter]
public SwitchParameter CancellationToken { get; set; }
protected override void BeginProcessing()
{
List<object> args = new List<object>
{
ConvertToProcessBlockIfUnnamed(ScriptBlock)
};
StringBuilder builder = new StringBuilder()
.Append("param($__sb");
if (CancellationToken.IsPresent)
{
_src = new CancellationTokenSource();
builder.Append(", $__token");
args.Add(_src.Token);
}
builder
.Append(") ")
.Append(UseLocalScope.IsPresent ? "." : "&")
.Append(" $__sb")
.Append(_src != null ? " $__token" : null);
_pipe = ScriptBlock
.Create(builder.ToString())
.GetSteppablePipeline(
CommandOrigin.Internal,
args.ToArray());
_pipe.Begin(this);
}
protected override void ProcessRecord()
{
_pipe.Process(InputObject);
}
protected override void EndProcessing()
{
_pipe.End();
}
protected override void StopProcessing()
{
if (_src != null)
{
_src.Cancel();
}
}
private static ScriptBlock ConvertToProcessBlockIfUnnamed(ScriptBlock scriptBlock)
{
ScriptBlockAst sbAst = (ScriptBlockAst)scriptBlock.Ast;
ParamBlockAst param = sbAst.ParamBlock;
NamedBlockAst begin = sbAst.BeginBlock;
NamedBlockAst process = sbAst.ProcessBlock;
NamedBlockAst end = sbAst.EndBlock;
if ((begin != null) || (process != null && !process.Unnamed) || (end != null && !end.Unnamed))
{
return scriptBlock;
}
if (param != null)
{
param = (ParamBlockAst)param.Copy();
}
ScriptBlockAst newSbAst = new ScriptBlockAst(
scriptBlock.Ast.Extent,
paramBlock: param,
beginBlock: null,
processBlock: new NamedBlockAst(
sbAst.EndBlock.Extent,
TokenKind.Process,
new StatementBlockAst(
sbAst.EndBlock.Extent,
sbAst.EndBlock.Statements.Select(s => s.Copy()).Cast<StatementAst>(),
Enumerable.Empty<TrapStatementAst>()),
unnamed: false),
endBlock: null,
dynamicParamBlock: null);
return newSbAst.GetScriptBlock();
}
public void Dispose()
{
if (_src != null)
{
_src.Dispose();
}
_pipe.Dispose();
Disposable.Dispose();
GC.SuppressFinalize(this);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment