Add application information section for 'Bring Your Own Applications' feature - added code for modern folder browser and added the code behind for the browse folder buttons

This commit is contained in:
rbalsleyMSFT
2025-02-21 16:18:04 -08:00
parent 9f3ee8d963
commit c0fdd102e3
2 changed files with 301 additions and 5 deletions
+257 -4
View File
@@ -3,7 +3,7 @@
param() param()
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
# SECTION 1: Variables & Constants # SECTION: Variables & Constants
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
$FFUDevelopmentPath = $PSScriptRoot $FFUDevelopmentPath = $PSScriptRoot
$AppsPath = Join-Path $FFUDevelopmentPath "Apps" $AppsPath = Join-Path $FFUDevelopmentPath "Apps"
@@ -24,6 +24,172 @@ function Get-USBDrives {
} }
} }
} }
# --------------------------------------------------------------------------
# SECTION: Modern folder dialog
# --------------------------------------------------------------------------
# 1) Define a C# class that uses the correct GUIDs for IFileDialog, IFileOpenDialog, and FileOpenDialog,
# while omitting conflicting "GetResults/GetSelectedItems" from IFileDialog.
Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
public static class ModernFolderBrowser
{
// Flags for IFileDialog
[Flags]
private enum FileDialogOptions : uint
{
OverwritePrompt = 0x00000002,
StrictFileTypes = 0x00000004,
NoChangeDir = 0x00000008,
PickFolders = 0x00000020,
ForceFileSystem = 0x00000040,
AllNonStorageItems = 0x00000080,
NoValidate = 0x00000100,
AllowMultiSelect = 0x00000200,
PathMustExist = 0x00000800,
FileMustExist = 0x00001000,
CreatePrompt = 0x00002000,
ShareAware = 0x00004000,
NoReadOnlyReturn = 0x00008000,
NoTestFileCreate = 0x00010000,
DontAddToRecent = 0x02000000,
ForceShowHidden = 0x10000000
}
// IFileDialog (GUID from Windows SDK)
// - Omitting GetResults / GetSelectedItems to avoid overshadow.
[ComImport]
[Guid("42F85136-DB7E-439C-85F1-E4075D135FC8")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IFileDialog
{
[PreserveSig]
int Show(IntPtr parent);
void SetFileTypes(uint cFileTypes, IntPtr rgFilterSpec);
void SetFileTypeIndex(uint iFileType);
void GetFileTypeIndex(out uint piFileType);
void Advise(IntPtr pfde, out uint pdwCookie);
void Unadvise(uint dwCookie);
void SetOptions(FileDialogOptions fos);
void GetOptions(out FileDialogOptions pfos);
void SetDefaultFolder(IShellItem psi);
void SetFolder(IShellItem psi);
void GetFolder(out IShellItem ppsi);
void GetCurrentSelection(out IShellItem ppsi);
void SetFileName([MarshalAs(UnmanagedType.LPWStr)] string pszName);
void GetFileName(out IntPtr pszName);
void SetTitle([MarshalAs(UnmanagedType.LPWStr)] string pszTitle);
void SetOkButtonLabel([MarshalAs(UnmanagedType.LPWStr)] string pszText);
void SetFileNameLabel([MarshalAs(UnmanagedType.LPWStr)] string pszLabel);
void GetResult(out IShellItem ppsi);
void AddPlace(IShellItem psi, int fdap);
void SetDefaultExtension([MarshalAs(UnmanagedType.LPWStr)] string pszDefaultExtension);
void Close(int hr);
void SetClientGuid(ref Guid guid);
void ClearClientData();
void SetFilter(IntPtr pFilter);
// NOTE: We intentionally do NOT define GetResults and GetSelectedItems here,
// because they cause overshadow warnings in IFileOpenDialog.
}
// IFileOpenDialog extends IFileDialog by adding 2 new methods with the same name,
// which otherwise cause overshadow warnings. We'll define them only here.
[ComImport]
[Guid("D57C7288-D4AD-4768-BE02-9D969532D960")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IFileOpenDialog : IFileDialog
{
// These two come after the parent's vtable:
void GetResults(out IntPtr ppenum);
void GetSelectedItems(out IntPtr ppsai);
}
// The coclass for creating an IFileOpenDialog
[ComImport]
[Guid("DC1C5A9C-E88A-4DDE-A5A1-60F82A20AEF7")]
private class FileOpenDialog
{
}
// IShellItem
[ComImport]
[Guid("43826D1E-E718-42EE-BC55-A1E261C37BFE")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IShellItem
{
void BindToHandler(IntPtr pbc, ref Guid bhid, ref Guid riid, out IntPtr ppv);
void GetParent(out IShellItem ppsi);
void GetDisplayName(uint sigdnName, out IntPtr ppszName);
void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs);
void Compare(IShellItem psi, uint hint, out int piOrder);
}
private const uint SIGDN_FILESYSPATH = 0x80058000;
public static string ShowDialog(string title, IntPtr parentHandle)
{
// Create COM dialog instance
IFileOpenDialog dialog = (IFileOpenDialog)(new FileOpenDialog());
// Get current options
FileDialogOptions opts;
dialog.GetOptions(out opts);
// Add flags for picking folders
opts |= FileDialogOptions.PickFolders | FileDialogOptions.PathMustExist | FileDialogOptions.ForceFileSystem;
dialog.SetOptions(opts);
// Set title
if (!string.IsNullOrEmpty(title))
{
dialog.SetTitle(title);
}
// Show the dialog
int hr = dialog.Show(parentHandle);
// 0 = S_OK. 1 or 0x800704C7 often means user canceled. Return null if so.
if (hr != 0)
{
if ((uint)hr == 0x800704C7 || hr == 1)
{
return null; // Canceled
}
else
{
Marshal.ThrowExceptionForHR(hr);
}
}
// Retrieve the selection (IShellItem)
IShellItem shellItem;
dialog.GetResult(out shellItem);
if (shellItem == null) return null;
// Convert to file system path
IntPtr pszPath = IntPtr.Zero;
shellItem.GetDisplayName(SIGDN_FILESYSPATH, out pszPath);
if (pszPath == IntPtr.Zero) return null;
string folderPath = Marshal.PtrToStringAuto(pszPath);
Marshal.FreeCoTaskMem(pszPath);
return folderPath;
}
}
"@ -Language CSharp
# 2) Define a PowerShell function that invokes our C# wrapper
function Show-ModernFolderPicker {
param(
[string]$Title = "Select a folder"
)
# For a simple test, pass IntPtr.Zero as the parent window handle
return [ModernFolderBrowser]::ShowDialog($Title, [IntPtr]::Zero)
}
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
# SECTION: Winget Management Functions # SECTION: Winget Management Functions
@@ -850,7 +1016,7 @@ $window.Add_Loaded({
# Add keyboard handler # Add keyboard handler
$script:lstUSBDrives.Add_KeyDown({ $script:lstUSBDrives.Add_KeyDown({
param($eventSrc, $keyEvent) param($eventSource, $keyEvent)
if ($keyEvent.Key -eq 'Space') { if ($keyEvent.Key -eq 'Space') {
$selectedItem = $script:lstUSBDrives.SelectedItem $selectedItem = $script:lstUSBDrives.SelectedItem
if ($selectedItem) { if ($selectedItem) {
@@ -865,7 +1031,7 @@ $window.Add_Loaded({
# Add selection change handler # Add selection change handler
$script:lstUSBDrives.Add_SelectionChanged({ $script:lstUSBDrives.Add_SelectionChanged({
param($eventSrc, $selChangeEvent) param($eventSource, $selChangeEvent)
# Update Select All checkbox state # Update Select All checkbox state
$allSelected = -not ($script:lstUSBDrives.Items | Where-Object { -not $_.IsSelected }) $allSelected = -not ($script:lstUSBDrives.Items | Where-Object { -not $_.IsSelected })
$script:chkSelectAllUSBDrives.IsChecked = $allSelected $script:chkSelectAllUSBDrives.IsChecked = $allSelected
@@ -953,6 +1119,38 @@ $window.Add_Loaded({
$script:chkBringYourOwnApps.Visibility = 'Collapsed' $script:chkBringYourOwnApps.Visibility = 'Collapsed'
}) })
# Bring Your Own Applications checkbox should only show if Install Applications is checked
$script:chkBringYourOwnApps = $window.FindName('chkBringYourOwnApps')
$script:byoApplicationPanel = $window.FindName('byoApplicationPanel')
$script:chkBringYourOwnApps.Visibility = if ($script:chkInstallApps.IsChecked) { 'Visible' } else { 'Collapsed' }
# Show/Hide Bring Your Own Applications based on Install Apps state
$script:chkInstallApps.Add_Checked({
$script:chkBringYourOwnApps.Visibility = 'Visible'
})
$script:chkInstallApps.Add_Unchecked({
$script:chkBringYourOwnApps.IsChecked = $false
$script:chkBringYourOwnApps.Visibility = 'Collapsed'
$script:byoApplicationPanel.Visibility = 'Collapsed'
})
# Show/Hide Application Information section based on Bring Your Own Applications state
$script:chkBringYourOwnApps.Add_Checked({
$script:byoApplicationPanel.Visibility = 'Visible'
})
$script:chkBringYourOwnApps.Add_Unchecked({
$script:byoApplicationPanel.Visibility = 'Collapsed'
$window.FindName('txtAppName').Text = ''
$window.FindName('txtAppCommandLine').Text = ''
$window.FindName('txtAppSource').Text = ''
})
# Show/Hide Winget panel based on checkbox state
$script:chkInstallWingetApps.Add_Checked({
$script:wingetPanel.Visibility = 'Visible'
# Don't show search panel here - it should only show after validation
})
# Show/Hide Winget panel based on checkbox state # Show/Hide Winget panel based on checkbox state
$script:chkInstallWingetApps.Add_Checked({ $script:chkInstallWingetApps.Add_Checked({
$script:wingetPanel.Visibility = 'Visible' $script:wingetPanel.Visibility = 'Visible'
@@ -1028,7 +1226,7 @@ $window.Add_Loaded({
$script:lstWingetResults.AddHandler( $script:lstWingetResults.AddHandler(
[System.Windows.Controls.GridViewColumnHeader]::ClickEvent, [System.Windows.Controls.GridViewColumnHeader]::ClickEvent,
[System.Windows.RoutedEventHandler] { [System.Windows.RoutedEventHandler] {
param($sender, $e) param($eventSource, $e)
$header = $e.OriginalSource $header = $e.OriginalSource
if ($header -is [System.Windows.Controls.GridViewColumnHeader] -and $header.Tag) { if ($header -is [System.Windows.Controls.GridViewColumnHeader] -and $header.Tag) {
Invoke-ListViewSort -listView $script:lstWingetResults -property $header.Tag Invoke-ListViewSort -listView $script:lstWingetResults -property $header.Tag
@@ -1090,6 +1288,60 @@ $window.Add_Loaded({
$script:txtStatus.Text = "Cleared all applications from the list" $script:txtStatus.Text = "Cleared all applications from the list"
} }
}) })
# Add Browse button handler for App Source
$script:btnBrowseAppSource = $window.FindName('btnBrowseAppSource')
$script:btnBrowseAppSource.Add_Click({
$selectedPath = Show-ModernFolderPicker -Title "Select Application Source Folder"
if ($selectedPath) {
$window.FindName('txtAppSource').Text = $selectedPath
}
})
# Add Browse button handler for FFU Development Path
$script:btnBrowseFFUDevPath = $window.FindName('btnBrowseFFUDevPath')
$script:btnBrowseFFUDevPath.Add_Click({
$selectedPath = Show-ModernFolderPicker -Title "Select FFU Development Path"
if ($selectedPath) {
$window.FindName('txtFFUDevPath').Text = $selectedPath
}
})
# Add Browse button handler for FFU Capture Location
$script:btnBrowseFFUCaptureLocation = $window.FindName('btnBrowseFFUCaptureLocation')
$script:btnBrowseFFUCaptureLocation.Add_Click({
$selectedPath = Show-ModernFolderPicker -Title "Select FFU Capture Location"
if ($selectedPath) {
$window.FindName('txtFFUCaptureLocation').Text = $selectedPath
}
})
# Add Browse button handler for Office Path
$script:btnBrowseOfficePath = $window.FindName('btnBrowseOfficePath')
$script:btnBrowseOfficePath.Add_Click({
$selectedPath = Show-ModernFolderPicker -Title "Select Office Path"
if ($selectedPath) {
$window.FindName('txtOfficePath').Text = $selectedPath
}
})
# Add Browse button handler for Drivers Folder
$script:btnBrowseDriversFolder = $window.FindName('btnBrowseDriversFolder')
$script:btnBrowseDriversFolder.Add_Click({
$selectedPath = Show-ModernFolderPicker -Title "Select Drivers Folder"
if ($selectedPath) {
$window.FindName('txtDriversFolder').Text = $selectedPath
}
})
# Add Browse button handler for PE Drivers Folder
$script:btnBrowsePEDriversFolder = $window.FindName('btnBrowsePEDriversFolder')
$script:btnBrowsePEDriversFolder.Add_Click({
$selectedPath = Show-ModernFolderPicker -Title "Select PE Drivers Folder"
if ($selectedPath) {
$window.FindName('txtPEDriversFolder').Text = $selectedPath
}
})
}) })
# Function to search for Winget apps # Function to search for Winget apps
@@ -1336,6 +1588,7 @@ $btnLoadConfig.Add_Click({
# Applications tab # Applications tab
$window.FindName('chkInstallApps').IsChecked = $configContent.InstallApps $window.FindName('chkInstallApps').IsChecked = $configContent.InstallApps
$window.FindName('chkInstallWingetApps').IsChecked = $configContent.InstallWingetApps $window.FindName('chkInstallWingetApps').IsChecked = $configContent.InstallWingetApps
$window.FindName('chkBringYourOwnApps').IsChecked = $configContent.BringYourOwnApps
# Update USB Drive selection if present in config # Update USB Drive selection if present in config
if ($configContent.USBDriveList) { if ($configContent.USBDriveList) {
+44 -1
View File
@@ -660,6 +660,49 @@
Content="Bring Your Own Applications" Content="Bring Your Own Applications"
Margin="5" Margin="5"
ToolTip="Enable to bring your own applications during the build process"/> ToolTip="Enable to bring your own applications during the build process"/>
<!-- Application Information Section -->
<StackPanel x:Name="byoApplicationPanel"
Visibility="Collapsed"
Margin="25,0,5,20">
<TextBlock Text="Application Information"
FontWeight="Bold"
Margin="0,5,0,10"/>
<!-- Name -->
<TextBlock Text="Name:"
Margin="0,0,0,5"/>
<TextBox x:Name="txtAppName"
Margin="0,0,0,10"
ToolTip="Enter the name of the application"/>
<!-- Command Line -->
<TextBlock Text="Command Line:"
Margin="0,0,0,5"/>
<TextBox x:Name="txtAppCommandLine"
Margin="0,0,0,10"
ToolTip="Enter the command line to install the application"/>
<!-- Source -->
<TextBlock Text="Source:"
Margin="0,0,0,5"/>
<Grid Margin="0,0,0,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="txtAppSource"
Grid.Column="0"
VerticalAlignment="Center"
ToolTip="Enter the source folder path of the application installation files"/>
<Button x:Name="btnBrowseAppSource"
Grid.Column="1"
Content="Browse..."
Width="80"
Margin="5,0,0,0"
VerticalAlignment="Center"/>
</Grid>
</StackPanel>
</StackPanel> </StackPanel>
</Grid> </Grid>
</TabItem> </TabItem>
@@ -776,7 +819,7 @@
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<TextBox x:Name="txtPEDriversFolder" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Stretch" ToolTip="Path to the PE drivers folder. Default is $FFUDevelopmentPath\PEDrivers."/> <TextBox x:Name="txtPEDriversFolder" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Stretch" ToolTip="Path to the PE drivers folder. Default is $FFUDevelopmentPath\PEDrivers."/>
<Button x:Name="btnBrowsePEDrivers" Grid.Column="1" Content="Browse..." Width="80" Margin="5,0,0,0" VerticalAlignment="Center"/> <Button x:Name="btnBrowsePEDriversFolder" Grid.Column="1" Content="Browse..." Width="80" Margin="5,0,0,0" VerticalAlignment="Center"/>
</Grid> </Grid>
</Grid> </Grid>
</TabItem> </TabItem>