mirror of
https://github.com/rbalsleyMSFT/FFU.git
synced 2026-06-14 10:19:36 -06:00
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:
@@ -3,7 +3,7 @@
|
||||
param()
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# SECTION 1: Variables & Constants
|
||||
# SECTION: Variables & Constants
|
||||
# --------------------------------------------------------------------------
|
||||
$FFUDevelopmentPath = $PSScriptRoot
|
||||
$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
|
||||
@@ -850,7 +1016,7 @@ $window.Add_Loaded({
|
||||
|
||||
# Add keyboard handler
|
||||
$script:lstUSBDrives.Add_KeyDown({
|
||||
param($eventSrc, $keyEvent)
|
||||
param($eventSource, $keyEvent)
|
||||
if ($keyEvent.Key -eq 'Space') {
|
||||
$selectedItem = $script:lstUSBDrives.SelectedItem
|
||||
if ($selectedItem) {
|
||||
@@ -865,7 +1031,7 @@ $window.Add_Loaded({
|
||||
|
||||
# Add selection change handler
|
||||
$script:lstUSBDrives.Add_SelectionChanged({
|
||||
param($eventSrc, $selChangeEvent)
|
||||
param($eventSource, $selChangeEvent)
|
||||
# Update Select All checkbox state
|
||||
$allSelected = -not ($script:lstUSBDrives.Items | Where-Object { -not $_.IsSelected })
|
||||
$script:chkSelectAllUSBDrives.IsChecked = $allSelected
|
||||
@@ -953,6 +1119,38 @@ $window.Add_Loaded({
|
||||
$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
|
||||
$script:chkInstallWingetApps.Add_Checked({
|
||||
$script:wingetPanel.Visibility = 'Visible'
|
||||
@@ -1028,7 +1226,7 @@ $window.Add_Loaded({
|
||||
$script:lstWingetResults.AddHandler(
|
||||
[System.Windows.Controls.GridViewColumnHeader]::ClickEvent,
|
||||
[System.Windows.RoutedEventHandler] {
|
||||
param($sender, $e)
|
||||
param($eventSource, $e)
|
||||
$header = $e.OriginalSource
|
||||
if ($header -is [System.Windows.Controls.GridViewColumnHeader] -and $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"
|
||||
}
|
||||
})
|
||||
|
||||
# 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
|
||||
@@ -1336,6 +1588,7 @@ $btnLoadConfig.Add_Click({
|
||||
# Applications tab
|
||||
$window.FindName('chkInstallApps').IsChecked = $configContent.InstallApps
|
||||
$window.FindName('chkInstallWingetApps').IsChecked = $configContent.InstallWingetApps
|
||||
$window.FindName('chkBringYourOwnApps').IsChecked = $configContent.BringYourOwnApps
|
||||
|
||||
# Update USB Drive selection if present in config
|
||||
if ($configContent.USBDriveList) {
|
||||
|
||||
@@ -660,6 +660,49 @@
|
||||
Content="Bring Your Own Applications"
|
||||
Margin="5"
|
||||
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>
|
||||
</Grid>
|
||||
</TabItem>
|
||||
@@ -776,7 +819,7 @@
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<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>
|
||||
</TabItem>
|
||||
|
||||
Reference in New Issue
Block a user