Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions MilestonePSTools/Private/DialogOwnerHelpers.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Copyright 2025 Milestone Systems A/S
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

function Invoke-WithDialogOwner {
[CmdletBinding()]
param(
[IntPtr] $Handle = [IntPtr]::Zero,
[switch] $TopMostFallback,
[Parameter(Mandatory)][ScriptBlock] $ScriptBlock
)

Add-Type -AssemblyName System.Windows.Forms | Out-Null

$owner = $null
$topMostOwnerForm = $null
$dialogOwner = $null
try {
if ($Handle -eq [IntPtr]::Zero -and $global:UiOwnerHandle) {
$Handle = [IntPtr]$global:UiOwnerHandle
}

if ($Handle -ne [IntPtr]::Zero) {
$owner = New-Object System.Windows.Forms.NativeWindow
$owner.AssignHandle($Handle)
} elseif ($TopMostFallback) {
$topMostOwnerForm = New-Object System.Windows.Forms.Form
$topMostOwnerForm.TopMost = $true
$topMostOwnerForm.ShowInTaskbar = $false
$topMostOwnerForm.StartPosition = [System.Windows.Forms.FormStartPosition]::Manual
$topMostOwnerForm.Size = [System.Drawing.Size]::new(1, 1)
}

if ($owner) {
$dialogOwner = $owner
} elseif ($topMostOwnerForm) {
$dialogOwner = $topMostOwnerForm
}

return & $ScriptBlock $dialogOwner
}
finally {
if ($owner) {
$owner.ReleaseHandle()
}
if ($topMostOwnerForm) {
$topMostOwnerForm.Dispose()
}
}
}

function Invoke-WithWpfDialogOwner {
[CmdletBinding()]
param(
[Parameter(Mandatory)][System.Windows.Window] $Window,
[IntPtr] $Handle = [IntPtr]::Zero,
[switch] $TopMostFallback,
[Parameter(Mandatory)][ScriptBlock] $ScriptBlock
)

Add-Type -AssemblyName PresentationFramework | Out-Null

$dialogOwnerHandle = $Handle
if ($dialogOwnerHandle -eq [IntPtr]::Zero -and $global:UiOwnerHandle) {
$dialogOwnerHandle = [IntPtr]$global:UiOwnerHandle
}

if ($dialogOwnerHandle -ne [IntPtr]::Zero) {
$interop = [System.Windows.Interop.WindowInteropHelper]::new($Window)
$interop.Owner = $dialogOwnerHandle
} elseif ($TopMostFallback) {
$Window.Topmost = $true
}

return & $ScriptBlock $Window
}
13 changes: 9 additions & 4 deletions MilestonePSTools/Private/Find-XProtectDeviceDialog.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
function Find-XProtectDeviceDialog {
[CmdletBinding()]
[RequiresInteractiveSession()]
param ()
param (
[IntPtr]
$OwnerHandle = [IntPtr]::Zero
)

begin {
Assert-VmsRequirementsMet
Expand Down Expand Up @@ -201,7 +204,7 @@ function Find-XProtectDeviceDialog {
$saveDialog.Title = "Save As CSV"
$saveDialog.Filter = "Comma delimited (*.csv)|*.csv"

$saveAs = $saveDialog.ShowDialog()
$saveAs = $saveDialog.ShowDialog($window)

if ($saveAs -eq $true) {
$script:searchResults | Export-Csv -Path $saveDialog.FileName -NoTypeInformation
Expand Down Expand Up @@ -238,7 +241,10 @@ function Find-XProtectDeviceDialog {
$var_lblPropertyValueBlank.Visibility = "Hidden"
})

$null = $window.ShowDialog()
Invoke-WithWpfDialogOwner -Window $window -Handle $OwnerHandle -TopMostFallback -ScriptBlock {
param($dialogWindow)
$null = $dialogWindow.ShowDialog()
}
}
}

Expand Down Expand Up @@ -311,4 +317,3 @@ function Find-XProtectDeviceSearch {
return $results
}
}

41 changes: 30 additions & 11 deletions MilestonePSTools/Private/ImportVmsHardwareExcelFunctions.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ function Show-FileDialog {

[Parameter(Mandatory, ParameterSetName = 'SaveFile')]
[switch]
$SaveFile
$SaveFile,

[Parameter()]
[IntPtr]
$OwnerHandle = [IntPtr]::Zero
)

process {
Expand All @@ -49,10 +53,12 @@ function Show-FileDialog {
}

try {
$form = [form]@{
TopMost = $true
$dialogResult = Invoke-WithDialogOwner -Handle $OwnerHandle -TopMostFallback -ScriptBlock {
param($ownerHandle)
return $dialog.ShowDialog($ownerHandle)
}
if ($dialog.ShowDialog($form) -eq 'OK') {

if ($dialogResult -eq 'OK') {
$dialog.FileName
} else {
throw "$($PSCmdlet.ParameterSetName) aborted."
Expand All @@ -61,9 +67,6 @@ function Show-FileDialog {
if ($dialog) {
$dialog.Dispose()
}
if ($form) {
$form.Dispose()
}
}
}
}
Expand Down Expand Up @@ -885,6 +888,10 @@ function Export-VmsHardwareExcel {
.PARAMETER Force
Overwrite an existing file if the file specified in `Path` already exists.

.PARAMETER OwnerHandle
Optional UI owner handle to make the save-file dialog modal to an existing
GUI window.

.EXAMPLE
Export-VmsHardwareExcel -Path ~\Documents\hardware.xlsx -Verbose

Expand Down Expand Up @@ -927,12 +934,16 @@ function Export-VmsHardwareExcel {

[Parameter()]
[switch]
$Force
$Force,

[Parameter()]
[IntPtr]
$OwnerHandle = [IntPtr]::Zero
)

begin {
if ([string]::IsNullOrWhiteSpace($Path)) {
$Path = Show-FileDialog -SaveFile
$Path = Show-FileDialog -SaveFile -OwnerHandle $OwnerHandle
}
if (Test-Path $Path) {
throw ([io.ioexception]::new("File $Path already exists."))
Expand Down Expand Up @@ -1586,6 +1597,10 @@ function Import-VmsHardwareExcel {
modified by default. If you wish to update the settings for existing
hardware during an import, this switch can be used.

.PARAMETER OwnerHandle
Optional UI owner handle to make the open-file dialog modal to an existing
GUI window.

.EXAMPLE
Import-VmsHardwareExcel -Path ~\Desktop\hardware.xlsx -Verbose

Expand Down Expand Up @@ -1618,15 +1633,19 @@ function Import-VmsHardwareExcel {

[Parameter()]
[switch]
$UpdateExisting
$UpdateExisting,

[Parameter()]
[IntPtr]
$OwnerHandle = [IntPtr]::Zero
)

begin {
if ($null -eq (Get-VmsManagementServer -ErrorAction 'SilentlyContinue')) {
Connect-Vms -ShowDialog -AcceptEula -ErrorAction Stop
}
if ([string]::IsNullOrWhiteSpace($Path)) {
$Path = Show-FileDialog -OpenFile
$Path = Show-FileDialog -OpenFile -OwnerHandle $OwnerHandle
}
try {
$excelPackage = Open-ExcelPackage -Path $Path
Expand Down
9 changes: 6 additions & 3 deletions MilestonePSTools/Public/Find-XProtectDevice.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@ function Find-XProtectDevice {

[Parameter(ParameterSetName = 'ShowDialog')]
[switch]
$ShowDialog
$ShowDialog,

[Parameter(ParameterSetName = 'ShowDialog')]
[IntPtr]
$OwnerHandle = [IntPtr]::Zero
)

begin {
Expand All @@ -63,7 +67,7 @@ function Find-XProtectDevice {

process {
if ($ShowDialog) {
Find-XProtectDeviceDialog
Find-XProtectDeviceDialog -OwnerHandle $OwnerHandle
return
}
if ($MyInvocation.BoundParameters.ContainsKey('Address')) {
Expand Down Expand Up @@ -107,4 +111,3 @@ function Find-XProtectDevice {
}
}
}

14 changes: 10 additions & 4 deletions MilestonePSTools/Public/Tools/Select-VideoOSItem.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ function Select-VideoOSItem {
$HideGroupsTab,
[Parameter()]
[switch]
$HideServerTab
$HideServerTab,
[Parameter()]
[IntPtr]
$OwnerHandle = [IntPtr]::Zero
)

begin {
Expand All @@ -69,12 +72,16 @@ function Select-VideoOSItem {
$form.ServerTabVisable = -not $HideServerTab
$form.Icon = [System.Drawing.Icon]::FromHandle([VideoOS.Platform.UI.Util]::ImageList.Images[[VideoOS.Platform.UI.Util]::SDK_GeneralIx].GetHicon())
$form.Text = $Title
$form.TopMost = $true
$form.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterScreen
$form.BringToFront()
$form.Activate()

if ($form.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
$dialogResult = Invoke-WithDialogOwner -Handle $OwnerHandle -TopMostFallback -ScriptBlock {
param($owner)
$form.ShowDialog($owner)
}

if ($dialogResult -eq [System.Windows.Forms.DialogResult]::OK) {
if ($FlattenOutput) {
Write-Output $form.ItemsSelectedFlattened
}
Expand All @@ -84,4 +91,3 @@ function Select-VideoOSItem {
}
}
}

19 changes: 18 additions & 1 deletion docs/commands/en-US/Find-XProtectDevice.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ Finds devices and provides the names of the parent hardware and recording server

```
Find-XProtectDevice [-ItemType <String[]>] [-Name <String>] [-Address <String>] [-MacAddress <String>]
[-EnableFilter <String>] [-Properties <Hashtable>] [-ShowDialog] [<CommonParameters>]
[-EnableFilter <String>] [-Properties <Hashtable>] [-ShowDialog] [[-OwnerHandle] <IntPtr>]
[<CommonParameters>]
```

## DESCRIPTION
Expand Down Expand Up @@ -164,6 +165,22 @@ Accept pipeline input: False
Accept wildcard characters: False
```

### -OwnerHandle

Specifies an optional UI owner window handle when using -ShowDialog so the dialog is modal to an existing GUI window.

```yaml
Type: IntPtr
Parameter Sets: ShowDialog
Aliases:

Required: False
Position: Named
Default value: 0
Accept pipeline input: False
Accept wildcard characters: False
```

### CommonParameters
This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216).

Expand Down
18 changes: 17 additions & 1 deletion docs/commands/en-US/Select-VideoOSItem.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Offers a UI dialog for selecting items, similar to the item selection interface
```
Select-VideoOSItem [[-Title] <String>] [[-Kind] <Guid[]>] [[-Category] <Category[]>] [-SingleSelect]
[-AllowFolders] [-AllowServers] [-KindUserSelectable] [-CategoryUserSelectable] [-FlattenOutput]
[-HideGroupsTab] [-HideServerTab] [<CommonParameters>]
[-HideGroupsTab] [-HideServerTab] [[-OwnerHandle] <IntPtr>] [<CommonParameters>]
```

## DESCRIPTION
Expand Down Expand Up @@ -167,6 +167,22 @@ Accept pipeline input: False
Accept wildcard characters: False
```

### -OwnerHandle

Specifies an optional UI owner window handle to make the dialog modal to an existing GUI window.

```yaml
Type: IntPtr
Parameter Sets: (All)
Aliases:

Required: False
Position: Named
Default value: 0
Accept pipeline input: False
Accept wildcard characters: False
```

### -Kind

One or more Guids representing a type of object in Milestone.
Expand Down