powershelldllpath

Powershell path function not working for ps1 called from ps1


1st ps1:

#$dll = 'C:\Some\Path\To\dll'

$dll = $PSScriptRoot+'\dll'
Write-Host $dll -foreground Green
 
$code = @"
Some code that works
"@

Add-Type -TypeDefinition $code -Language CSharp -ReferencedAssemblies $dll
[System.Reflection.Assembly]::LoadFrom($dll)

[HelloWorld.Program]::Main()

2nd ps1:

$job = Start-Job -FilePath "Myps1.ps1" -Name "aaa"

$dll = $PSScriptRoot+'\Mydll.dll'
Write-Host $dll -foreground Yellow

Start-Sleep 5

$JobStatus = $job.State
Write-Host $job.State -foreground Yellow

if ($JobStatus -like "Running")
    {
        Write-Host "Gresit" -foreground Red
    }
else 
    {
        Write-Host "Corect" -foreground Green
    }

I am calling the first ps1 from the 2nd ps1. The problem arises because when calling the first ps1 from the 2nd, if I use

$dll = 'C:\Some\Path\To\dll'

then the program works. But if I use

$dll = $PSScriptRoot+'\dll'

then the program does nothing.

Needless to say that if I call the first ps1 it works with both versions of dll path.


Solution

  • So this is your problem...

    $job = Start-Job -FilePath "Myps1.ps1" -Name "aaa"
    

    If you look at the documentation for the -FilePath parameter of Start-Job it says:

    -FilePath

    When you use this parameter, PowerShell converts the contents of the specified script file to a script block and runs the script block as a background job.

    So your code is sort-of equivalent to:

    $src = Get-Content "Myps1.ps1"
    $sb  = [scriptblock]::Create($src)
    $job = Start-Job -ScriptBlock $sb -Name "aaa"
    

    And if we run a near-identical cut-n-pasteable example:

    $src = @"
    write-host "root = '$PSScriptRoot'"
    "@
    $sb = [scriptblock]::Create($src)
    $job = Start-Job -ScriptBlock $sb -Name "aaa"
    
    Wait-Job $job
    Receive-Job $job
    

    We see the following output:

    root = ''
    

    That is because $PSScriptRoot is not defined when running inside a ScriptBlock, only when running as a script:

    $PSScriptBlock

    Contains the full path of the executing script's parent directory.

    In PowerShell 2.0, this variable is valid only in script modules (.psm1). Beginning in PowerShell 3.0, it's valid in all scripts.

    A ScriptBlock is an in-memory object, so it doesn't have a "parent directory", and creating a ScriptBlock from the contents of a file doesn't persist the file path into the ScriptBlock, so $PSScriptRoot is undefined in both cases.

    This answer gives some more details:

    https://stackoverflow.com/a/72026978/3156906

    And a suggested workaround there is to make the call to Start-Job a simple wrapper around the Call operator to invoke your script, because then your script executes as a script (not a scriptblock) and has access to the $PSScriptBlock automatic variable:

    $sb = { & ".\first.ps1" }
    $job = Start-Job -ScriptBlock $sb -Name "aaa"
    
    $result = Wait-Job $job
    Receive-Job $job
    

    So the script file runs as a script, not a scriptblock, and it has a $PSScriptRoot automatic variable available as a result, which results in this output:

    C:\src\so\root\dll