mirror of
https://github.com/rbalsleyMSFT/FFU.git
synced 2026-06-14 02:09:35 -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()
|
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) {
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user