4

I have a script function that calls. net to manipulate word documents. It works. Now I want to create a sub-thread to execute it, and then the main thread decides whether it completes or exceeds the specified time, and ends it after that time. As shown in the code,It does not execute functions in the block of $node code, but instead $task1 executes the cmdlet. Why is that? How can I fulfill my needs?

try{
# $cb is a instance of class,scan is the function I want to invoke.
    $code = { $cb.Scan($PrepareFileName, $NailDirName, $HtmlFileName) }
#    $task1 = { Start-Sleep -Seconds 9; Get-Service }
    $newThread = [PowerShell]::Create().AddScript($code)
    $handleTh = $newThread.BeginInvoke()
    $nTimes = 0;
    do
    {
        $nTimes++;
        if($handleTh.IsCompleted -or $nTimes -gt 10)
        {
          break;  
        }
        Start-Sleep -Milliseconds 500

    } while($true)

    $newThread.EndInvoke($handleTh)
    $newThread.Runspace.Close()
    $newThread.Dispose()

}catch{

}
  • Where are beginning the new thread? – Ross Bush May 08 '19 at 01:37
  • Isn't that the sentence? `$handleTh = $newThread.BeginInvoke()` @RossBush – 不知火舞 May 08 '19 at 02:41
  • New PowerShell's `Runspace` that you created known nothing about variables in your current `Runspace`. – user4003407 May 08 '19 at 03:39
  • I think you're right. Is there any good way for the new PowerShell to take variables from the parent's runtime? @PetSerAl – 不知火舞 May 08 '19 at 06:29
  • @不知火舞 Try using PosshRSJob, it lets you handle powershell jobs in different run spaces and can let you access variables from the "parent scope". https://github.com/proxb/PoshRSJob – David Söderlund May 08 '19 at 07:23
  • Thank you very much. I'll try it right away. @DavidSöderlund – 不知火舞 May 08 '19 at 08:50
  • @不知火舞 It should work with the way @Moerwald shown in their answer (with `AddParameter` and/or `AddArgument`). If it does not, then I suggest you should first inspect error stream of created `PowerShell` object to see if anything here. – user4003407 May 08 '19 at 18:31
  • Well, his method is feasible, and later I refer to the link he gave to modify it. However, I encountered a new problem, that is, when the member function of the class called by the new PowerShell calls other member functions, it will get stuck. This function does not set hidden property. @PetSerAl – 不知火舞 May 09 '19 at 01:48

1 Answers1

1

You need to create a runspaceand it to the PowerShell object. Check this microsoft "tutorial" for using runspaces in a correct manner. The link also explains how to use runspace pools, and script block arguments.

try{
    # $cb is a instance of class,scan is the function I want to invoke.
    $code = { 
        # Update 1, added parameter
        param($cb)
        $cb.Scan($PrepareFileName, $NailDirName, $HtmlFileName) 
    }
    # Create a runspace
    $runspace = [runspacefactory]::CreateRunspace()
    # Update 1, inject parameter
    $newThread = [PowerShell]::Create().AddScript($code).AddParameter(‘cb’,$callback)

    # Add the runspace
    $newThread.Runspace = $runspace
    $runspace.Open()
    $handleTh = $newThread.BeginInvoke()
    $nTimes = 0;
    do
    {
        $nTimes++;
        if($handleTh.IsCompleted -or $nTimes -gt 10)
        {
          break;  
        }
        Start-Sleep -Milliseconds 500

    } while($true)

    $newThread.EndInvoke($handleTh)
    $newThread.Dispose()
}
catch{
}

Hope that helps.

enter image description here

Moerwald
  • 10,448
  • 9
  • 43
  • 83
  • Thank you for your answer. I'm doing the same now. But the same result: $handleTh. IsCompleted always equals $true. @Moerwald – 不知火舞 May 08 '19 at 06:34
  • It means that the result of $handleTh.IsCompleted is $true before it has entering the Scan function. @Moerwald – 不知火舞 May 08 '19 at 06:42
  • I've done a test. I replace `$cb.Scan($PrepareFileName, $NailDirName, $HtmlFileName)` with `Start-Sleep -Seconds 30`. During the sleep time `$handleTh.IsCompleted` was `false`. Could it be that `$cb` is `$null` or the `Scan` method return with an error? – Moerwald May 08 '19 at 06:42
  • Replace `$cb.Scan($PrepareFileName, $NailDirName, $HtmlFileName)` with `Start-Sleep -Seconds 30`. Is `$handleTh` now `false` ? – Moerwald May 08 '19 at 06:44
  • That's my question. There's no problem executing cmdlet, but executing functions won't work. It's perfectly okay to run this function without executing it through [PowerShell]:: Create (). – 不知火舞 May 08 '19 at 06:54
  • How do you inject the reference to `$cb`? – Moerwald May 08 '19 at 06:55
  • Replace $cb.Scan($PrepareFileName, $NailDirName, $HtmlFileName) with Start-Sleep -Seconds 30. $handleTh now false. I know there's no problem with this situation. I've commented on a sentence in my code that says #$Task1 = Start-Sleep-Seconds 9; Get-Service}. @Moerwald – 不知火舞 May 08 '19 at 06:58
  • I changed my above example with an additional parameter, see line `$newThread = [PowerShell]::Create().AddScript($code).AddParameter(‘cb’,$callback)`. I think you've to inject `$cb` as described [here](https://devblogs.microsoft.com/scripting/beginning-use-of-powershell-runspaces-part-2/) – Moerwald May 08 '19 at 06:59
  • I'm sorry. I don't know for the moment. I'm trying to figure out how to pass parameters to the new PowerShell thread. – 不知火舞 May 08 '19 at 07:02
  • I UPDATED my answer how to pass arguments to a runspace. Just check the answers code sample. – Moerwald May 08 '19 at 07:04
  • I've changed my code to an example of the answer you gave me, but the result is still the same. – 不知火舞 May 08 '19 at 07:23
  • Well, later I refer to the [link](https://devblogs.microsoft.com/scripting/beginning-use-of-powershell-runspaces-part-2/) you gave me to modify it, and I've made sure it can be invoked. Thank you very much for your kind help. @Moerwald – 不知火舞 May 09 '19 at 01:50
  • @不知火舞, please mark my answer as correct, if it is fine for you. Thx – Moerwald May 09 '19 at 06:24