thebitsandthebytes
thebitsandthebytes

Reputation: 1

Launch PowerShell scriptblock asynchronously without dependency on parent process

Below is a small PowerShell script that runs some PowerShell code asynchronously to show a dialog box (for the purpose of demonstrating my issue).

If I close the parent PowerShell process, the child process will also close and the dialog box disappears. Is there any way to launch a PowerShell scriptblock, complete with functions and arguments, asynchronously and without a dependence on the parent PowerShell process?

    $testjob = [PowerShell]::Create().AddScript({ $a = new-object -comobject wscript.shell
    $b = $a.popup('This is a test',5,'Test Message Box',1) })
    $result = $testJob.BeginInvoke()

Update #2

I am trying to execute a script block, rather than an external script. The script block should use functions and variables from the parent script. The problem is, I can't pass those functions or variables in to the new process unless they are contained directly within the script block. Any idea if this is doable?

    Function Show-Prompt {
        Param ($title,$message)
            $a = new-object -comobject wscript.shell
            $b = $a.popup($message,5,$title,1)
    }  

    $scriptContent = {Show-Prompt -Message 'This is a test' -Title 'Test Message Box'}  
    $scriptBlock = [scriptblock]::Create($scriptContent)
    Start-process powershell -argumentlist "-noexit -command &{$scriptBlock}"

Upvotes: 0

Views: 7168

Answers (3)

R Allen
R Allen

Reputation: 1

function GeneratePortableFunction {
    param ([string] $name, [scriptblock] $sb)
    $block = [ScriptBlock]::Create("return `${function:$name};");
    $script = $block.Invoke();
    $block = [ScriptBlock]::Create($script);
    return ("function {0} {{ {1} }}" -f $name, $block.ToString());
}
function RemoteScript {
    param ([string] $header, [string[]] $functions, [string] $footer)
    $sb = New-Object System.Text.StringBuilder;
    [void]$sb.Append("$header`n");
    $functions | % {
        [void]$sb.Append($_);
        [void]$sb.Append("`n");
    }
    [void]$sb.Append($footer);
    return [ScriptBlock]::Create($sb.ToString());
}

$fnc = @();
$fnc += GeneratePortableFunction -name "NameOfYourFunction1";
$fnc += GeneratePortableFunction -name "NameOfYourFunction2(CallsNameOfYourFunction1)";
$script = RemoteScript -header "param([int] `$param1)" -functions @($fnc) -footer "NameOfYourFunction2 -YourParameter `$param1;";
$p1 = 0;
$job = Start-Job -ScriptBlock $script -ArgumentList @($p1);
while($job.State -eq "Running")
{
    write-host "Running...";
    Start-Sleep 5;
}
$result = $job | Receive-Job;
Remove-Job -Id $job.Id;

Upvotes: 0

Keith Hill
Keith Hill

Reputation: 201832

You can use an intermediate PowerShell process. There has to be a direct parent to process the return value from the async script. For instance, put your script in a file called popup.ps1 and try execute like so:

Start-Process PowerShell -Arg c:\popup.ps1

You might want to bump the timeout up a bit from say 5 to 10 seconds. You can close the original PowerShell and the popup will stay. When you close the popup (or it times out) the secondary PowerShell window will disappear.

Upvotes: 2

Start-Automating
Start-Automating

Reputation: 8367

You can do this with WMI. If you use Win32_Process to create the process, it will persist after you close PowerShell.

For instance:

invoke-wmimethod -path win32_process -name create -argumentlist calc

Upvotes: 1

Related Questions