Private/Await.ps1

# 1. Laad eerst de basis die nodig is voor WinRT
Add-Type -AssemblyName "System.Runtime.WindowsRuntime"
Add-Type -AssemblyName "PresentationFramework"
Add-Type -AssemblyName "WindowsBase"

# 2. Maak een lijst van de WinRT types die je nodig hebt
[Windows.Devices.Geolocation.Geolocator, Windows.Devices.Geolocation, ContentType = WindowsRuntime] | Out-Null

[Windows.Devices.Radios.Radio, Windows.System.Devices, ContentType = WindowsRuntime] | Out-Null
[Windows.Devices.Radios.RadioState, Windows.System.Devices, ContentType = WindowsRuntime] | Out-Null
[Windows.Devices.Radios.RadioAccessStatus, Windows.System.Devices, ContentType = WindowsRuntime] | Out-Null

[Windows.Devices.WiFi.WiFiAdapter, Windows.Devices.Wifi, ContentType = WindowsRuntime] | Out-Null
[Windows.Devices.WiFi.WiFiReconnectionKind, Windows.Devices.Wifi, ContentType = WindowsRuntime] | Out-Null

[Windows.Devices.Enumeration.DeviceInformation, Windows.Devices.Enumeration, ContentType = WindowsRuntime] | Out-Null

[Windows.Security.Credentials.PasswordCredential, Windows.Security.Credentials, ContentType = WindowsRuntime] | Out-Null

[Windows.Foundation.IAsyncInfo, Windows.Foundation, ContentType = WindowsRuntime] | Out-Null
[Windows.Foundation.IAsyncOperation``1, Windows.Foundation, ContentType = WindowsRuntime] | Out-Null

[Windows.Networking.Connectivity.NetworkInformation, Windows.Networking.Connectivity, ContentType = WindowsRuntime] | Out-Null

<#$WinRTTypes = @(
  "Windows.Devices.Geolocation.Geolocator, Windows.Devices.Geolocation",
  "Windows.Devices.Radios.Radio, Windows.System.Devices",
  "Windows.Devices.Radios.RadioState, Windows.System.Devices",
  "Windows.Devices.WiFi.WiFiAdapter, Windows.Devices.Wifi",
  "Windows.Devices.WiFi.WiFiReconnectionKind, Windows.Devices.Wifi",
  "Windows.Devices.Radios.RadioAccessStatus, Windows.System.Devices",
  "Windows.Devices.Enumeration.DeviceInformation, Windows.Devices.Enumeration",
  "Windows.Security.Credentials.PasswordCredential, Windows.Security.Credentials",
  "Windows.Networking.Connectivity.NetworkInformation, Windows.Networking.Connectivity",
  "Windows.Foundation.IAsyncInfo, Windows.Foundation",
  "Windows.Foundation.IAsyncOperation``1, Windows.Foundation"
)#>



# 3. Laad ze allemaal in één keer via de loop
#foreach ($Type in $WinRTTypes) {
# Dit forceert PowerShell om de WinRT metadata te laden
# $RuntimeLoaded = ([Type]::GetType("$Type, ContentType=WindowsRuntime")) #| Out-Null
# if ($null -eq $RuntimeLoaded.Name) {
# Write-Warning "Kon WinRT type niet laden: $Type"
# }
#}

<#
#For LocationPermissions
[Windows.Devices.Geolocation.Geolocator, Windows.Devices.Geolocation, ContentType = WindowsRuntime] | Out-Null
#For Radio On Off
[Windows.Devices.Radios.Radio, Windows.System.Devices, ContentType = WindowsRuntime] | Out-Null
[Windows.Devices.Radios.RadioState, Windows.System.Devices, ContentType = WindowsRuntime] | Out-Null
[Windows.Devices.Radios.RadioAccessStatus, Windows.System.Devices, ContentType = WindowsRuntime] | Out-Null
#Wifi
[Windows.Devices.WiFi.WiFiAdapter, Windows.Devices.Wifi, ContentType = WindowsRunTime] | Out-Null
[Windows.Devices.WiFi.WiFiReconnectionKind, Windows.DEvices.Wifi, ContentType = WindowsRuntime] | Out-Null
#Device
[Windows.Devices.Enumeration.DeviceInformation, Windows.Devices.Enumeration, ContentType = WindowsRunTime] | Out-Null
#password
[Windows.Security.Credentials.PasswordCredential, Windows.Security.Credentials, ContentType = WindowsRunTime] | Out-Null
#networkConnection
[Windows.Networking.Connectivity.NetworkInformation, Windows.Networking.Connectivity, Contenttype = WindowsRuntime] | Out-Null
#>


$asTaskGeneric = [System.WindowsRuntimeSystemExtensions].GetMethods() | Where-Object { 
  $_.Name -eq 'AsTask' #-and $_.GetParameters().Count -eq 1
}

$asTaskOperation = $asTaskGeneric | Where-Object { $_.GetParameters().Count -eq 1 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1' }
$asTaskAction = $asTaskGeneric    | Where-Object { $_.GetParameters().Count -eq 1 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncAction' }

$asTaskOperationCancel = $asTaskGeneric | Where-Object { $_.GetParameters().Count -eq 2 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1' -and $_.GetParameters()[1].ParameterType.Name -eq 'CancellationToken' }
$asTaskActionCancel = $asTaskGeneric | Where-Object { $_.GetParameters().Count -eq 2 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncAction' -and $_.GetParameters()[1].ParameterType.Name -eq 'CancellationToken' }

Function Await($WinRtTask, $ResultType, [int]$TimeoutMs = 10000, [string]$Activity, [int]$ParentId = 0) {
  process {
    # Forceer de timeout tussen 1000 en 60000 ms
    if ($TimeoutMs -lt 1000) { $TimeoutMs = 1000 }
    if ($TimeoutMs -gt 60000) { $TimeoutMs = 120000 }
    if ($ResultType) {
      $asTask = $asTaskOperation.MakeGenericMethod($ResultType)
      $NetTask = $asTask.Invoke($null, @($WinRtTask))
    } else {
      $NetTask = $asTaskAction.Invoke($null, @($WinRtTask))
    }
    if ($Activity) {
      $elapsed = 0
      $interval = 500
      while (-not ( 
          $NetTask.IsCompleted -or 
          ($elapsed -ge $TimeoutMs))) {
        # Bereken percentage op basis van de voortgang van de TIMEOUT
        $percent = [int](($elapsed / $TimeoutMs) * 100)
      
        Write-Progress -Activity $Activity `
          -Status "Wachten op respons $($NetTask.IsCompleted)... ($($elapsed)ms / $($TimeoutMs)ms)" `
          -PercentComplete $percent
        -ParentId $ParentId
      
        Start-Sleep -Milliseconds $interval
        $elapsed += $interval
      }
    } else {
      # Zonder naam blokkeren we gewoon voor de ingestelde tijd
      [Void]$NetTask.Wait($TimeoutMs)
    }
    # Resultaat afhandeling
    if ($NetTask.IsCompleted) {
      if ($ResultType) { return $NetTask.Result }
      return $true
    } else {
      Write-Error "De WinRT terminated after $($TimeoutMs)ms."
      return $null
    }
  }
}

#AwaitWithCancel with a Global Anker <- otherwise we get an error 0xc00000005
#wel still struggeling with connecting wit wps pushbutton some how it fails if you release lock ?!

Function AwaitWithCancel {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)]$WinRtTask,
    [type] $ResultType,
    [int] $TimeoutMs = 120000,
    [string] $Activity,
    [int] $ParentId
  )

  Begin {
    # 1. Gebruik de script-scope om de variabelen 'vast te pinnen' voor het End-blok
    [System.Threading.CancellationTokenSource]$script:cts = New-Object System.Threading.CancellationTokenSource
    $script:timer = [System.Diagnostics.Stopwatch]::StartNew()
        
    # Gebruik de automatische hardware-stop timer
    $script:cts.CancelAfter($TimeoutMs)

    # De WinRT-methode selector (moet vooraf gedefinieerd zijn in je module)
    if ($ResultType) {
      [System.Reflection.MethodInfo]$script:Method = $asTaskOperationCancel.MakeGenericMethod($ResultType)
    } else {
      $script:Method = $asTaskActionCancel
    }
    #write-host ("Method:{0}" -f $script:Method.Gettype().Name)
  }
  Process {
    write-Debug ("ParentId in Parameters:{0}" -f  $PSBoundParameters.ContainsKey("ParentId") )
    if ($PSBoundParameters.ContainsKey("ParentId")) { $id = $ParentId + 1 } else { $Id = 0 }
    # Start de Task-bridge en koppel de Token
    $script:NetTask = $script:Method.Invoke($null, @($WinRtTask, $script:cts.Token))
    Write-debug ("NetTask:" -f $script:NetTask.gettype().Name)
    try {
      # Hoofd-loop: Wacht op de hardware OF de automatische annulering
      while (-not (
          $script:NetTask.IsCompleted -or 
          $script:cts.Token.IsCancellationRequested -or
          ($script:NetTask.Status -eq 'RanToCompletion')
        )) {
        if ($script:NetTask.IsCompleted) { Write-Host "Connected" }
        if ($Activity) {
          $elapsed = $script:timer.ElapsedMilliseconds
          $percent = [Math]::Min(100, [int](($elapsed / $TimeoutMs) * 100))
          if ($ParentId) {
            Write-Progress -id $Id -Activity $Activity -Status "Wachten op WinRT... ($($elapsed)ms)" -ParentId $parentId -PercentComplete $percent
          } else {
            Write-Progress -id $Id -Activity $Activity -Status "Wachten op WinRT... ($($elapsed)ms)" -PercentComplete $percent
          }
       
        }
        # Gebruik een korte sleep om de thread niet te blokkeren
        Start-Sleep -Milliseconds 200
      }
    } catch {
      Write-Error "Fout in de WinRT-communicatie: $($_.Exception.Message)"
    }
    # 3. Resultaat veilig ophalen
    $FinalResult = $null
    Write-Debug ("{1} - Status:{0}" -f $script:NetTask.Status, $ResultType.Name)
    if ($script:NetTask.Status -eq 'WaitingForActivation') {
      $Script:cts.Token
    }
    While (-not ($Script:NetTask.IsCompleted) ) {
      Write-Debug ("{1} - Status:{0} NetTask:{2}" -f $script:NetTask.Status, $ResultType.Name, $Script:NetTask.IsCompleted)
      Start-Sleep -Milliseconds 100
    }
    Write-Debug ("{1} - Status:{0} NetTask:{2}" -f $script:NetTask.Status, $ResultType.Name, $Script:NetTask.IsCompleted)
    if ($null -ne $script:NetTask -and $script:NetTask.Status -eq 'RanToCompletion') {
      $FinalResult = $script:NetTask.Result
      #$script:cts.Dispose()
    }
    # 4. Schoonmaken pas NA bevestiging van de hardware
    $script:timer.Stop()
    # De Dispose is nu veilig omdat de driver 'IsCompleted' is
  # if($Activity){ Write-Progress -Id $Id -Completed -Activity $Activity}
    return $FinalResult
  }
  End {
    $script:cts.Cancel()
  }
}
<#Function AwaitWithCancel {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)] $WinRtTask,
    [type] $ResultType,
    [int] $TimeoutMs = 10000,
    [string] $ProgressBarName
  )
  begin {
    if ($TimeoutMs -lt 1000) { $TimeoutMs = 1000 }
    if ($TimeoutMs -gt 120000) { $TimeoutMs = 120000 }
 
    # Gebruik script-scope zodat 'End' erbij kan
    $script:cts = New-Object System.Threading.CancellationTokenSource
    $script:timer = [System.Diagnostics.Stopwatch]::StartNew()
    $script:cts.CancelAfter($TimeoutMs)
 
    $script:method = if ($ResultType) { $asTaskOperationCancel.MakeGenericMethod($ResultType) } else { $asTaskActionCancel }
  }
  Process {
    try {
      # De taak MOET in de script-scope staan
      $script:NetTask = $script:method.Invoke($null, @($WinRtTask, $script:cts.Token))
$method.delay( )
      while (-not $script:NetTask.IsCompleted) {
        if ($ProgressBarName) {
          $elapsed = $script:timer.ElapsedMilliseconds
          $percent = [Math]::Min(100, [int](($elapsed / $TimeoutMs) * 100))
          Write-Progress -Activity $ProgressBarName -Status "Wachten op WinRT... ($elapsed ms)" -PercentComplete $percent
        }
        Start-Sleep -Milliseconds 200
      }
    } catch {
      # Fouten vangen, maar NIET hier afsluiten
    }
  }
  end {
    # DE HARDWARE LANDING
    if ($null -ne $script:NetTask -and -not $script:NetTask.IsCompleted) {
      $script:cts.Cancel()
      $safetyLoop = 0
      # Houd de terminal OPEN totdat de driver de scan staakt
      while (-not $script:NetTask.IsCompleted -and $safetyLoop -lt 5000) {
        [System.Threading.Thread]::Sleep(100)
        $safetyLoop += 100
      }
    }
 
    # Resultaat ophalen VOORDAT we disposen
    $FinalResult = $null
    if ($script:NetTask.Status -eq 'RanToCompletion') {
      $FinalResult = $script:NetTask.Result
    }
 
    # Alles opruimen
    $script:timer.Stop()
    $script:cts.Dispose()
    #Write-Progress -Activity $ProgressBarName -Completed
     
    return $FinalResult
  }
}#>