diff --git a/AppControl Manager/Excluded Code/FileDialogHelper unmanaged version.cs b/AppControl Manager/Excluded Code/FileDialogHelper unmanaged version.cs
deleted file mode 100644
index 7753f6a8c..000000000
--- a/AppControl Manager/Excluded Code/FileDialogHelper unmanaged version.cs
+++ /dev/null
@@ -1,532 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Runtime.InteropServices;
-using Windows.Win32;
-using Windows.Win32.Foundation;
-using Windows.Win32.System.Com;
-using Windows.Win32.UI.Shell;
-using Windows.Win32.UI.Shell.Common;
-
-namespace WDACConfig
-{
- ///
- /// https://learn.microsoft.com/en-us/uwp/api/windows.storage.pickers.filesavepicker?view=winrt-26100
- /// This one currently has problems
- ///
- internal static class FileDialogHelper
- {
- ///
- /// Opens a file picker dialog to select a single file.
- ///
- ///
- ///
- internal unsafe static string? ShowFilePickerDialog(string filter)
- {
- // Create an instance of the file open dialog
- int hr = PInvoke.CoCreateInstance(
- typeof(FileOpenDialog).GUID, // CLSID for FileOpenDialog
- null, // No outer object for aggregation
- CLSCTX.CLSCTX_INPROC_SERVER, // In-process server context
- out IFileOpenDialog* fileOpenDialog // Explicitly specify the output type
- );
-
- // If creation fails, throw an exception with the corresponding HRESULT code.
- if (hr < 0)
- {
- Marshal.ThrowExceptionForHR(hr);
- }
-
- // Prepare the list of file type filters based on the input string.
- List extensions = [];
-
- if (!string.IsNullOrEmpty(filter)) // Check if filter is provided.
- {
- // Split the filter into name and pattern pairs (e.g., "Text Files|*.txt").
- string[] tokens = filter.Split('|');
-
- // Ensure the pairs are valid.
- if (tokens.Length % 2 == 0)
- {
- for (int i = 0; i < tokens.Length; i += 2)
- {
- COMDLG_FILTERSPEC extension;
- extension.pszName = (char*)Marshal.StringToHGlobalUni(tokens[i]);
- extension.pszSpec = (char*)Marshal.StringToHGlobalUni(tokens[i + 1]);
- extensions.Add(extension);
- }
- }
- }
-
- // Apply the filters to the file open dialog.
- fileOpenDialog->SetFileTypes(extensions.ToArray());
-
- // Set the default folder to "My Documents".
- hr = PInvoke.SHCreateItemFromParsingName(
- Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
- null,
- typeof(IShellItem).GUID,
- out void* pDirectoryShellItem
- );
-
- if (hr >= 0) // Proceed only if the default folder creation succeeds.
- {
- IShellItem* directoryShellItem = (IShellItem*)pDirectoryShellItem;
- fileOpenDialog->SetFolder(directoryShellItem);
- fileOpenDialog->SetDefaultFolder(directoryShellItem);
-
- // Release the IShellItem after use
- _ = directoryShellItem->Release();
- }
-
- try
- {
-
- try
- {
- // Display the dialog to the user.
- fileOpenDialog->Show(new HWND(GlobalVars.hWnd)); // Pass the parent window handle.
- }
- catch (Exception e)
- {
- if (e.HResult == -2147023673) // Specific HRESULT for "Operation Canceled".
- {
- return null;
- }
-
- throw; // Re-throw unexpected exceptions.
- }
- finally
- {
- if (fileOpenDialog != null)
- {
- _ = fileOpenDialog->Release();
- }
- }
-
- // Retrieve the result of the dialog (selected file).
- IShellItem* ppsi = null;
- fileOpenDialog->GetResult(&ppsi);
-
- // Retrieve the file path
- PWSTR filename;
- ppsi->GetDisplayName(SIGDN.SIGDN_FILESYSPATH, &filename);
-
- // Convert to managed string
- string selectedFilePath = new(filename);
-
- // Free the allocated memory for filename
- if (filename.Value != null)
- {
- Marshal.FreeCoTaskMem((IntPtr)filename.Value);
- }
-
- // Release COM objects
- _ = ppsi->Release();
-
- return selectedFilePath;
- }
-
- finally
- {
- if (fileOpenDialog != null)
- {
- _ = fileOpenDialog->Release();
- }
-
- // Clean up extensions memory
- foreach (var extension in extensions)
- {
- if (extension.pszName.Value != null)
- {
- Marshal.FreeHGlobal((IntPtr)extension.pszName.Value);
- }
- if (extension.pszSpec.Value != null)
- {
- Marshal.FreeHGlobal((IntPtr)extension.pszSpec.Value);
- }
- }
- }
- }
-
-
-
- ///
- /// Opens a file picker dialog to select multiple files.
- ///
- /// A file filter string in the format "Description|Extension" pairs
- /// (e.g., "Text Files|*.txt|All Files|*.*").
- /// A list of selected file paths or null if the operation is cancelled.
- internal unsafe static List? ShowMultipleFilePickerDialog(string filter)
- {
- // Create the file open dialog
- int hr = PInvoke.CoCreateInstance(
- typeof(FileOpenDialog).GUID, // CLSID for FileOpenDialog
- null, // No aggregation
- CLSCTX.CLSCTX_INPROC_SERVER, // In-process COM server
- out IFileOpenDialog* fileOpenDialog // Interface for the dialog
- );
-
- // If the HRESULT indicates failure, throw a corresponding .NET exception.
- if (hr < 0)
- {
- Marshal.ThrowExceptionForHR(hr);
- }
-
- // Initialize a list to store file type filters for the dialog.
- List extensions = [];
-
- if (!string.IsNullOrEmpty(filter))
- {
- // Split the filter string by '|' and process description-extension pairs.
- string[] tokens = filter.Split('|');
-
- // Ensure there is a valid description-extension pair for every two tokens.
- if (tokens.Length % 2 == 0)
- {
- for (int i = 1; i < tokens.Length; i += 2)
- {
- COMDLG_FILTERSPEC extension;
- extension.pszName = (char*)Marshal.StringToHGlobalUni(tokens[i - 1]);
- extension.pszSpec = (char*)Marshal.StringToHGlobalUni(tokens[i]);
- extensions.Add(extension);
- }
- }
- }
-
- // Apply the file type filters to the dialog.
- fileOpenDialog->SetFileTypes(extensions.ToArray());
-
- // Set default folder to My Documents
- hr = PInvoke.SHCreateItemFromParsingName(
- Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
- null,
- typeof(IShellItem).GUID,
- out var directoryShellItem
- );
-
- // If the "My Documents" folder is successfully retrieved, set it as the default.
- if (hr >= 0)
- {
- fileOpenDialog->SetFolder((IShellItem*)directoryShellItem);
- fileOpenDialog->SetDefaultFolder((IShellItem*)directoryShellItem);
- }
-
- // Configure dialog options for multiple selection
- FILEOPENDIALOGOPTIONS options = FILEOPENDIALOGOPTIONS.FOS_ALLOWMULTISELECT;
- fileOpenDialog->SetOptions(options);
-
- try
- {
- // Show the dialog
- fileOpenDialog->Show(new HWND(GlobalVars.hWnd));
- }
- catch (Exception e)
- {
- if (e.HResult == -2147023673) // Operation Canceled HRESULT
- {
- return null;
- }
-
- throw;
- }
- finally
- {
- if (fileOpenDialog != null)
- {
- _ = fileOpenDialog->Release();
- }
- }
-
- // Retrieve the collection of selected items
- IShellItemArray* ppsiCollection = null;
-
- try
- {
-
- fileOpenDialog->GetResults(&ppsiCollection);
-
- // Get the number of selected files
- uint fileCount = 0;
- ppsiCollection->GetCount(&fileCount);
-
- // Initialize the list to store file paths
- List selectedFiles = [];
-
- // Iterate through selected items
- for (uint i = 0; i < fileCount; i++)
- {
- IShellItem* ppsi = null;
- ppsiCollection->GetItemAt(i, &ppsi);
-
- // Retrieve the file path
- PWSTR filename;
- ppsi->GetDisplayName(SIGDN.SIGDN_FILESYSPATH, &filename);
-
- // Convert to managed string and add to the list
- string filePath = new(filename.Value);
- selectedFiles.Add(filePath);
-
- // Free the unmanaged memory allocated for the file path
- if (filename.Value != null)
- {
- Marshal.FreeCoTaskMem((IntPtr)filename.Value);
- }
-
-
- // Release the IShellItem COM object
-
- _ = ppsi->Release();
-
- }
-
- return selectedFiles;
- }
- finally
- {
- // Clean up extensions memory
- foreach (var extension in extensions)
- {
- if (extension.pszName.Value != null)
- {
- Marshal.FreeHGlobal((IntPtr)extension.pszName.Value);
- }
- if (extension.pszSpec.Value != null)
- {
- Marshal.FreeHGlobal((IntPtr)extension.pszSpec.Value);
- }
- }
-
- // Release COM objects
- _ = ppsiCollection->Release();
- if (fileOpenDialog != null)
- {
- _ = fileOpenDialog->Release();
- }
- }
- }
-
-
-
-
-
- ///
- /// Opens a folder picker dialog to select a single folder.
- ///
- /// The selected directory path as a string, or null if the operation is cancelled.
- internal unsafe static string? ShowDirectoryPickerDialog()
- {
- // Create the file open dialog
- int hr = PInvoke.CoCreateInstance(
- typeof(FileOpenDialog).GUID, // CLSID for FileOpenDialog
- null, // No aggregation
- CLSCTX.CLSCTX_INPROC_SERVER, // In-process COM server
- out IFileOpenDialog* fileOpenDialog // Interface for the dialog
- );
-
- if (hr < 0)
- {
- Marshal.ThrowExceptionForHR(hr);
- }
-
- // Configure dialog options to enable folder selection
- FILEOPENDIALOGOPTIONS options = FILEOPENDIALOGOPTIONS.FOS_PICKFOLDERS;
- fileOpenDialog->SetOptions(options);
-
- // Set default folder to "My Documents"
- hr = PInvoke.SHCreateItemFromParsingName(
- Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
- null,
- typeof(IShellItem).GUID,
- out var directoryShellItem
- );
-
- // If the "My Documents" folder is successfully retrieved, set it as the default.
- if (hr >= 0)
- {
- fileOpenDialog->SetFolder((IShellItem*)directoryShellItem);
- fileOpenDialog->SetDefaultFolder((IShellItem*)directoryShellItem);
- }
-
- try
- {
- // Show the dialog
- fileOpenDialog->Show(new HWND(GlobalVars.hWnd));
- }
- catch (Exception e)
- {
- if (e.HResult == -2147023673) // Operation Canceled HRESULT
- {
- return null;
- }
-
- throw;
- }
- finally
- {
- // Release the IFileOpenDialog COM object
- if (fileOpenDialog != null)
- {
- _ = fileOpenDialog->Release();
- }
- }
-
- // Retrieve the selected folder
- IShellItem* ppsi = null;
-
- try
- {
-
- fileOpenDialog->GetResult(&ppsi);
-
- // Get the file system path of the folder
- PWSTR folderPath;
- ppsi->GetDisplayName(SIGDN.SIGDN_FILESYSPATH, &folderPath);
-
- // Convert to managed string
- string selectedFolderPath = new(folderPath.Value);
-
- // Free unmanaged memory for the folder path
- Marshal.FreeCoTaskMem((IntPtr)folderPath.Value);
-
- return selectedFolderPath;
-
- }
- finally
- {
- // Clean up the IShellItem COM object
- _ = ppsi->Release();
-
-
- // Release the IFileOpenDialog COM object
- if (fileOpenDialog != null)
- {
- _ = fileOpenDialog->Release();
- }
- }
- }
-
-
-
-
-
- ///
- /// Opens a folder picker dialog to select multiple folders.
- ///
- /// A list of selected directory paths or null if cancelled.
- internal unsafe static List? ShowMultipleDirectoryPickerDialog()
- {
- // Create the file open dialog
- int hr = PInvoke.CoCreateInstance(
- typeof(FileOpenDialog).GUID, // CLSID for FileOpenDialog
- null, // No aggregation
- CLSCTX.CLSCTX_INPROC_SERVER, // In-process COM server
- out IFileOpenDialog* fileOpenDialog // Interface for the dialog
- );
-
- if (hr < 0)
- {
- Marshal.ThrowExceptionForHR(hr);
- }
-
- // Configure dialog options to enable folder picking and multiple selection
- FILEOPENDIALOGOPTIONS options = FILEOPENDIALOGOPTIONS.FOS_PICKFOLDERS | FILEOPENDIALOGOPTIONS.FOS_ALLOWMULTISELECT;
- fileOpenDialog->SetOptions(options);
-
- // Set default folder to "My Documents"
- hr = PInvoke.SHCreateItemFromParsingName(
- Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
- null,
- typeof(IShellItem).GUID,
- out var directoryShellItem
- );
-
- if (hr >= 0)
- {
- fileOpenDialog->SetFolder((IShellItem*)directoryShellItem);
- fileOpenDialog->SetDefaultFolder((IShellItem*)directoryShellItem);
- }
-
-
-
- try
- {
- // Show the dialog
- fileOpenDialog->Show(new HWND(GlobalVars.hWnd));
- }
- catch (Exception e)
- {
- if (e.HResult == -2147023673) // Operation Canceled HRESULT
- {
- return null;
- }
-
- throw;
- }
- finally
- {
- // Release the IFileOpenDialog COM object
- if (fileOpenDialog != null)
- {
- _ = fileOpenDialog->Release();
- }
- }
-
- // Retrieve the collection of selected items
- IShellItemArray* ppsiCollection = null;
-
- try
- {
-
- fileOpenDialog->GetResults(&ppsiCollection);
-
- // Get the number of selected folders
- uint folderCount;
- ppsiCollection->GetCount(&folderCount);
-
- // Initialize the list to store selected folder paths
- List selectedFolders = [];
-
- // Iterate through each selected folder
- for (uint i = 0; i < folderCount; i++)
- {
- IShellItem* ppsi = null;
- ppsiCollection->GetItemAt(i, &ppsi); // Retrieve IShellItem for the folder
-
- // Get the file system path of the folder
- PWSTR folderPath;
- ppsi->GetDisplayName(SIGDN.SIGDN_FILESYSPATH, &folderPath);
-
- // Convert the unmanaged string (PWSTR) to a managed string and add to the list
- string selectedFolderPath = new(folderPath.Value);
- selectedFolders.Add(selectedFolderPath);
-
- // Free unmanaged memory for the folder path
- Marshal.FreeCoTaskMem((IntPtr)folderPath.Value);
-
- // Clean up the IShellItem COM object
-
- _ = ppsi->Release();
-
- }
-
- return selectedFolders;
- }
- finally
- {
-
- // Clean up the IShellItemArray COM object
- _ = ppsiCollection->Release();
-
- // Release the IFileOpenDialog COM object
- if (fileOpenDialog != null)
- {
- _ = fileOpenDialog->Release();
- }
-
- }
- }
-
-
-
- }
-}
diff --git a/AppControl Manager/Excluded Code/FileDialogHelper.cs b/AppControl Manager/Excluded Code/FileDialogHelper.cs
new file mode 100644
index 000000000..66329407a
--- /dev/null
+++ b/AppControl Manager/Excluded Code/FileDialogHelper.cs
@@ -0,0 +1,392 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using Windows.Win32;
+using Windows.Win32.Foundation;
+using Windows.Win32.System.Com;
+using Windows.Win32.UI.Shell;
+using Windows.Win32.UI.Shell.Common;
+
+namespace WDACConfig
+{
+ ///
+ /// https://learn.microsoft.com/en-us/uwp/api/windows.storage.pickers.filesavepicker?view=winrt-26100
+ /// This class uses managed code, "allowMarshaling" should be "true" for CsWin32 JSON settings.
+ ///
+ internal static class FileDialogHelper
+ {
+ ///
+ /// Opens a file picker dialog to select a single file.
+ ///
+ ///
+ ///
+ internal unsafe static string? ShowFilePickerDialog(string filter)
+ {
+ // Create an instance of the file open dialog using COM interface.
+ int hr = PInvoke.CoCreateInstance(
+ typeof(FileOpenDialog).GUID, // GUID for FileOpenDialog COM object
+ null, // No outer object for aggregation
+ CLSCTX.CLSCTX_INPROC_SERVER, // Context specifies in-process server execution
+ out IFileOpenDialog fileOpenDialog // Output reference to the created IFileOpenDialog instance
+ );
+
+ // If creation fails, throw an exception with the corresponding HRESULT code.
+ if (hr < 0)
+ {
+ Marshal.ThrowExceptionForHR(hr);
+ }
+
+ // Prepare the list of file type filters based on the input string.
+ List extensions = [];
+
+ if (!string.IsNullOrEmpty(filter)) // Check if filter is provided.
+ {
+ // Split the filter into name and pattern pairs (e.g., "Text Files|*.txt").
+ string[] tokens = filter.Split('|');
+ if (tokens.Length % 2 == 0) // Ensure the pairs are valid.
+ {
+ for (int i = 1; i < tokens.Length; i += 2)
+ {
+ // Populate the filter specification structure for each pair.
+ COMDLG_FILTERSPEC extension;
+ extension.pszName = (char*)Marshal.StringToHGlobalUni(tokens[i - 1]); // Filter name.
+ extension.pszSpec = (char*)Marshal.StringToHGlobalUni(tokens[i]); // Filter pattern.
+ extensions.Add(extension);
+ }
+ }
+ }
+
+ // Apply the filters to the file open dialog.
+ fileOpenDialog.SetFileTypes(extensions.ToArray());
+
+ // Set the default folder to "My Documents".
+ hr = PInvoke.SHCreateItemFromParsingName(
+ GlobalVars.UserConfigDir, // Path to the folder.
+ null, // No binding context needed.
+ typeof(IShellItem).GUID, // GUID for the IShellItem interface.
+ out var directoryShellItem // Output reference to the IShellItem instance.
+ );
+
+ if (hr >= 0) // Proceed only if the default folder creation succeeds.
+ {
+ // Set the initial and default folder for the dialog.
+ fileOpenDialog.SetFolder((IShellItem)directoryShellItem);
+ fileOpenDialog.SetDefaultFolder((IShellItem)directoryShellItem);
+ }
+
+ try
+ {
+ // Display the dialog to the user.
+ fileOpenDialog.Show(new HWND(GlobalVars.hWnd)); // Pass the parent window handle.
+ }
+ catch (Exception e)
+ {
+ // Handle exceptions, such as when the user cancels the dialog.
+ if (e.HResult != -2147023673) // Specific HRESULT for "Operation Canceled".
+ {
+ throw; // Re-throw unexpected exceptions.
+ }
+ else
+ {
+ return null; // Return null when the dialog is canceled.
+ }
+ }
+
+ // Retrieve the result of the dialog (selected file).
+ fileOpenDialog.GetResult(out IShellItem ppsi); // Get the IShellItem representing the selected file.
+
+ // Get the file path as a PWSTR.
+ PWSTR filename;
+ ppsi.GetDisplayName(SIGDN.SIGDN_FILESYSPATH, &filename); // Retrieve the file's full path.
+
+ // Convert the unmanaged PWSTR to a managed string and return it.
+ string selectedFilePath = filename.ToString();
+ return selectedFilePath;
+ }
+
+
+
+ ///
+ /// Opens a file picker dialog to select multiple files.
+ ///
+ /// A file filter string in the format "Description|Extension" pairs
+ /// (e.g., "Text Files|*.txt|All Files|*.*").
+ /// A list of selected file paths or null if the operation is cancelled.
+ internal unsafe static List? ShowMultipleFilePickerDialog(string filter)
+ {
+ // Create the file open dialog using COM's CoCreateInstance method.
+ // CLSCTX.CLSCTX_INPROC_SERVER ensures the dialog runs in the same process.
+ int hr = PInvoke.CoCreateInstance(
+ typeof(FileOpenDialog).GUID, // GUID of the FileOpenDialog class.
+ null, // No aggregation.
+ CLSCTX.CLSCTX_INPROC_SERVER, // In-process COM server.
+ out IFileOpenDialog fileOpenDialog // Interface for the dialog.
+ );
+
+ // If the HRESULT indicates failure, throw a corresponding .NET exception.
+ if (hr < 0)
+ {
+ Marshal.ThrowExceptionForHR(hr);
+ }
+
+ // Initialize a list to store file type filters for the dialog.
+ List extensions = [];
+
+ if (!string.IsNullOrEmpty(filter))
+ {
+ // Split the filter string by '|' and process description-extension pairs.
+ string[] tokens = filter.Split('|');
+
+ // Ensure there is a valid description-extension pair for every two tokens.
+ if (tokens.Length % 2 == 0)
+ {
+ for (int i = 1; i < tokens.Length; i += 2)
+ {
+ COMDLG_FILTERSPEC extension;
+
+ // Marshal the description and extension strings to unmanaged memory.
+ extension.pszName = (char*)Marshal.StringToHGlobalUni(tokens[i - 1]); // Filter description.
+ extension.pszSpec = (char*)Marshal.StringToHGlobalUni(tokens[i]); // File extension(s).
+
+ // Add the filter specification to the list.
+ extensions.Add(extension);
+ }
+ }
+ }
+
+ // Apply the file type filters to the dialog.
+ fileOpenDialog.SetFileTypes(extensions.ToArray());
+
+ // Optionally set a default folder and starting directory.
+ // Retrieves a shell item representing the "My Documents" directory.
+ hr = PInvoke.SHCreateItemFromParsingName(
+ GlobalVars.UserConfigDir, // Path to the folder.
+ null, // No binding context.
+ typeof(IShellItem).GUID, // GUID for IShellItem interface.
+ out var directoryShellItem // Output shell item.
+ );
+
+ // If the "My Documents" folder is successfully retrieved, set it as the default.
+ if (hr >= 0)
+ {
+ fileOpenDialog.SetFolder((IShellItem)directoryShellItem); // Set starting folder.
+ fileOpenDialog.SetDefaultFolder((IShellItem)directoryShellItem); // Set default folder.
+ }
+
+ // Configure dialog options to allow multiple file selection.
+ FILEOPENDIALOGOPTIONS options = FILEOPENDIALOGOPTIONS.FOS_ALLOWMULTISELECT;
+ fileOpenDialog.SetOptions(options);
+
+ try
+ {
+ // Display the file picker dialog to the user.
+ fileOpenDialog.Show(new HWND(GlobalVars.hWnd)); // Owner window handle.
+ }
+ catch (Exception e)
+ {
+ // Handle user cancellation of the dialog (specific HRESULT -2147023673).
+ if (e.HResult != -2147023673)
+ {
+ throw; // Rethrow for unexpected errors.
+ }
+ else
+ {
+ return null; // User cancelled; return null.
+ }
+ }
+
+ // Retrieve the collection of selected items from the dialog.
+ fileOpenDialog.GetResults(out IShellItemArray ppsiCollection);
+
+ // Get the number of selected files in the collection.
+ ppsiCollection.GetCount(out uint fileCount);
+
+ // Initialize the list to store the paths of the selected files.
+ List selectedFiles = [];
+
+ // Iterate through each selected file.
+ for (uint i = 0; i < fileCount; i++)
+ {
+ ppsiCollection.GetItemAt(i, out IShellItem ppsi); // Get the IShellItem for the file.
+
+ // Get the file system path of the file.
+ PWSTR filename;
+ ppsi.GetDisplayName(SIGDN.SIGDN_FILESYSPATH, &filename); // Retrieve the full filesystem path.
+
+ // Convert the unmanaged string (PWSTR) to a managed string and add it to the list.
+ string selectedFilePath = filename.ToString();
+ selectedFiles.Add(selectedFilePath);
+ }
+
+ // Return the list of selected file paths.
+ return selectedFiles;
+ }
+
+
+
+
+
+ ///
+ /// Opens a folder picker dialog to select a single folder.
+ ///
+ /// The selected directory path as a string, or null if the operation is cancelled.
+ internal unsafe static string? ShowDirectoryPickerDialog()
+ {
+ // Create the file open dialog using COM's CoCreateInstance method.
+ // CLSCTX.CLSCTX_INPROC_SERVER ensures the dialog runs in the same process.
+ int hr = PInvoke.CoCreateInstance(
+ typeof(FileOpenDialog).GUID, // GUID of the FileOpenDialog class.
+ null, // No aggregation.
+ CLSCTX.CLSCTX_INPROC_SERVER, // In-process COM server.
+ out IFileOpenDialog fileOpenDialog // Interface for the dialog.
+ );
+
+ // If the HRESULT indicates failure, throw a corresponding .NET exception.
+ if (hr < 0)
+ {
+ Marshal.ThrowExceptionForHR(hr);
+ }
+
+ // Configure dialog options to enable folder selection.
+ FILEOPENDIALOGOPTIONS options = FILEOPENDIALOGOPTIONS.FOS_PICKFOLDERS;
+ fileOpenDialog.SetOptions(options);
+
+ // Optionally set a default folder and starting directory.
+ // Retrieves a shell item representing the "My Documents" directory.
+ hr = PInvoke.SHCreateItemFromParsingName(
+ GlobalVars.UserConfigDir, // Path to the folder.
+ null, // No binding context.
+ typeof(IShellItem).GUID, // GUID for IShellItem interface.
+ out var directoryShellItem // Output shell item.
+ );
+
+ // If the "My Documents" folder is successfully retrieved, set it as the default.
+ if (hr >= 0)
+ {
+ fileOpenDialog.SetFolder((IShellItem)directoryShellItem); // Set starting folder.
+ fileOpenDialog.SetDefaultFolder((IShellItem)directoryShellItem); // Set default folder.
+ }
+
+ try
+ {
+ // Display the folder picker dialog to the user.
+ fileOpenDialog.Show(new HWND(GlobalVars.hWnd)); // Owner window handle.
+ }
+ catch (Exception e)
+ {
+ // Handle user cancellation of the dialog (specific HRESULT -2147023673).
+ if (e.HResult != -2147023673)
+ {
+ throw; // Rethrow for unexpected errors.
+ }
+ else
+ {
+ return null; // User cancelled; return null.
+ }
+ }
+
+ // Retrieve the selected folder from the dialog.
+ fileOpenDialog.GetResult(out IShellItem ppsi);
+
+ // Get the file system path of the selected folder.
+ PWSTR folderPath;
+ ppsi.GetDisplayName(SIGDN.SIGDN_FILESYSPATH, &folderPath); // Retrieve the full filesystem path.
+
+ // Convert the unmanaged string (PWSTR) to a managed string.
+ string selectedFolderPath = folderPath.ToString();
+
+ // Return the selected folder path as a managed string.
+ return selectedFolderPath;
+ }
+
+
+
+
+ ///
+ /// Opens a folder picker dialog to select multiple folders.
+ ///
+ /// A list of selected directory paths or null if cancelled.
+ internal unsafe static List? ShowMultipleDirectoryPickerDialog()
+ {
+ // Create the file open dialog using COM's CoCreateInstance method.
+ // CLSCTX.CLSCTX_INPROC_SERVER ensures the dialog runs in the same process.
+ int hr = PInvoke.CoCreateInstance(
+ typeof(FileOpenDialog).GUID, // GUID of the FileOpenDialog class.
+ null, // No aggregation.
+ CLSCTX.CLSCTX_INPROC_SERVER, // In-process COM server.
+ out IFileOpenDialog fileOpenDialog // Interface for the dialog.
+ );
+
+ // If the HRESULT indicates failure, throw a corresponding .NET exception.
+ if (hr < 0)
+ {
+ Marshal.ThrowExceptionForHR(hr);
+ }
+
+ // Configure dialog options to enable folder picking and multiple selection.
+ FILEOPENDIALOGOPTIONS options = FILEOPENDIALOGOPTIONS.FOS_PICKFOLDERS | FILEOPENDIALOGOPTIONS.FOS_ALLOWMULTISELECT;
+ fileOpenDialog.SetOptions(options);
+
+ // Optionally set a default folder and starting directory.
+ // Retrieves a shell item representing the "My Documents" directory.
+ hr = PInvoke.SHCreateItemFromParsingName(
+ GlobalVars.UserConfigDir, // Path to the folder.
+ null, // No binding context.
+ typeof(IShellItem).GUID, // GUID for IShellItem interface.
+ out var directoryShellItem // Output shell item.
+ );
+
+ // If the "My Documents" folder is successfully retrieved, set it as the default.
+ if (hr >= 0)
+ {
+ fileOpenDialog.SetFolder((IShellItem)directoryShellItem); // Set starting folder.
+ fileOpenDialog.SetDefaultFolder((IShellItem)directoryShellItem); // Set default folder.
+ }
+
+ try
+ {
+ // Display the folder picker dialog to the user.
+ fileOpenDialog.Show(new HWND(GlobalVars.hWnd)); // Owner window handle.
+ }
+ catch (Exception e)
+ {
+ // Handle user cancellation of the dialog (specific HRESULT -2147023673).
+ if (e.HResult != -2147023673)
+ {
+ throw; // Rethrow for unexpected errors.
+ }
+ else
+ {
+ return null; // User cancelled; return null.
+ }
+ }
+
+ // Retrieve the collection of selected items from the dialog.
+ fileOpenDialog.GetResults(out IShellItemArray ppsiCollection);
+
+ // Get the number of selected folders in the collection.
+ ppsiCollection.GetCount(out uint folderCount);
+
+ // Initialize the list to store the paths of the selected folders.
+ List selectedFolders = [];
+
+ // Iterate through each selected folder.
+ for (uint i = 0; i < folderCount; i++)
+ {
+ ppsiCollection.GetItemAt(i, out IShellItem ppsi); // Get the IShellItem for the folder.
+
+ // Get the file system path of the folder.
+ PWSTR folderPath;
+ ppsi.GetDisplayName(SIGDN.SIGDN_FILESYSPATH, &folderPath); // Retrieve the full filesystem path.
+
+ // Convert the unmanaged string (PWSTR) to a managed string and add it to the list.
+ string selectedFolderPath = folderPath.ToString();
+ selectedFolders.Add(selectedFolderPath);
+ }
+
+ // Return the list of selected folder paths.
+ return selectedFolders;
+ }
+
+ }
+}
diff --git a/AppControl Manager/Logic/FileDialogHelper.cs b/AppControl Manager/Logic/FileDialogHelper.cs
index 6f924f7fb..8022ee20b 100644
--- a/AppControl Manager/Logic/FileDialogHelper.cs
+++ b/AppControl Manager/Logic/FileDialogHelper.cs
@@ -11,7 +11,7 @@ namespace WDACConfig
{
///
/// https://learn.microsoft.com/en-us/uwp/api/windows.storage.pickers.filesavepicker?view=winrt-26100
- /// This class uses managed code, "allowMarshaling" is "true" for CsWin32 JSON settings.
+ /// This class uses unmanaged code, "allowMarshaling" should be "false" for CsWin32 JSON settings.
///
internal static class FileDialogHelper
{
@@ -22,12 +22,12 @@ internal static class FileDialogHelper
///
internal unsafe static string? ShowFilePickerDialog(string filter)
{
- // Create an instance of the file open dialog using COM interface.
+ // Create an instance of the file open dialog
int hr = PInvoke.CoCreateInstance(
- typeof(FileOpenDialog).GUID, // GUID for FileOpenDialog COM object
+ typeof(FileOpenDialog).GUID, // CLSID for FileOpenDialog
null, // No outer object for aggregation
- CLSCTX.CLSCTX_INPROC_SERVER, // Context specifies in-process server execution
- out IFileOpenDialog fileOpenDialog // Output reference to the created IFileOpenDialog instance
+ CLSCTX.CLSCTX_INPROC_SERVER, // In-process server context
+ out IFileOpenDialog* fileOpenDialog // Explicitly specify the output type
);
// If creation fails, throw an exception with the corresponding HRESULT code.
@@ -43,65 +43,103 @@ out IFileOpenDialog fileOpenDialog // Output reference to the created IFileOpenD
{
// Split the filter into name and pattern pairs (e.g., "Text Files|*.txt").
string[] tokens = filter.Split('|');
- if (tokens.Length % 2 == 0) // Ensure the pairs are valid.
+
+ // Ensure the pairs are valid.
+ if (tokens.Length % 2 == 0)
{
- for (int i = 1; i < tokens.Length; i += 2)
+ for (int i = 0; i < tokens.Length; i += 2)
{
- // Populate the filter specification structure for each pair.
COMDLG_FILTERSPEC extension;
- extension.pszName = (char*)Marshal.StringToHGlobalUni(tokens[i - 1]); // Filter name.
- extension.pszSpec = (char*)Marshal.StringToHGlobalUni(tokens[i]); // Filter pattern.
+ extension.pszName = (char*)Marshal.StringToHGlobalUni(tokens[i]);
+ extension.pszSpec = (char*)Marshal.StringToHGlobalUni(tokens[i + 1]);
extensions.Add(extension);
}
}
}
// Apply the filters to the file open dialog.
- fileOpenDialog.SetFileTypes(extensions.ToArray());
+ fileOpenDialog->SetFileTypes(extensions.ToArray());
// Set the default folder to "My Documents".
hr = PInvoke.SHCreateItemFromParsingName(
- GlobalVars.UserConfigDir, // Path to the folder.
- null, // No binding context needed.
- typeof(IShellItem).GUID, // GUID for the IShellItem interface.
- out var directoryShellItem // Output reference to the IShellItem instance.
+ Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
+ null,
+ typeof(IShellItem).GUID,
+ out void* pDirectoryShellItem
);
if (hr >= 0) // Proceed only if the default folder creation succeeds.
{
- // Set the initial and default folder for the dialog.
- fileOpenDialog.SetFolder((IShellItem)directoryShellItem);
- fileOpenDialog.SetDefaultFolder((IShellItem)directoryShellItem);
+ IShellItem* directoryShellItem = (IShellItem*)pDirectoryShellItem;
+ fileOpenDialog->SetFolder(directoryShellItem);
+ fileOpenDialog->SetDefaultFolder(directoryShellItem);
+
+ // Release the IShellItem after use
+ _ = directoryShellItem->Release();
}
try
{
- // Display the dialog to the user.
- fileOpenDialog.Show(new HWND(GlobalVars.hWnd)); // Pass the parent window handle.
- }
- catch (Exception e)
- {
- // Handle exceptions, such as when the user cancels the dialog.
- if (e.HResult != -2147023673) // Specific HRESULT for "Operation Canceled".
+
+ try
+ {
+ // Display the dialog to the user.
+ fileOpenDialog->Show(new HWND(GlobalVars.hWnd)); // Pass the parent window handle.
+ }
+ catch (Exception e)
{
+ if (e.HResult == -2147023673) // Specific HRESULT for "Operation Canceled".
+ {
+ return null;
+ }
+
throw; // Re-throw unexpected exceptions.
}
- else
+
+
+ // Retrieve the result of the dialog (selected file).
+ IShellItem* ppsi = null;
+ fileOpenDialog->GetResult(&ppsi);
+
+ // Retrieve the file path
+ PWSTR filename;
+ ppsi->GetDisplayName(SIGDN.SIGDN_FILESYSPATH, &filename);
+
+ // Convert to managed string
+ string selectedFilePath = new(filename);
+
+ // Free the allocated memory for filename
+ if (filename.Value != null)
{
- return null; // Return null when the dialog is canceled.
+ Marshal.FreeCoTaskMem((IntPtr)filename.Value);
}
- }
- // Retrieve the result of the dialog (selected file).
- fileOpenDialog.GetResult(out IShellItem ppsi); // Get the IShellItem representing the selected file.
+ // Release COM objects
+ _ = ppsi->Release();
- // Get the file path as a PWSTR.
- PWSTR filename;
- ppsi.GetDisplayName(SIGDN.SIGDN_FILESYSPATH, &filename); // Retrieve the file's full path.
+ return selectedFilePath;
+ }
- // Convert the unmanaged PWSTR to a managed string and return it.
- string selectedFilePath = filename.ToString();
- return selectedFilePath;
+ finally
+ {
+ if (fileOpenDialog != null)
+ {
+ _ = fileOpenDialog->Release();
+ }
+
+ // Clean up extensions memory
+ foreach (COMDLG_FILTERSPEC extension in extensions)
+ {
+ if (extension.pszName.Value != null)
+ {
+ Marshal.FreeHGlobal((IntPtr)extension.pszName.Value);
+ }
+ if (extension.pszSpec.Value != null)
+ {
+ Marshal.FreeHGlobal((IntPtr)extension.pszSpec.Value);
+ }
+ }
+ }
}
@@ -114,13 +152,12 @@ out var directoryShellItem // Output reference to the IShellItem instance.
/// A list of selected file paths or null if the operation is cancelled.
internal unsafe static List? ShowMultipleFilePickerDialog(string filter)
{
- // Create the file open dialog using COM's CoCreateInstance method.
- // CLSCTX.CLSCTX_INPROC_SERVER ensures the dialog runs in the same process.
+ // Create the file open dialog
int hr = PInvoke.CoCreateInstance(
- typeof(FileOpenDialog).GUID, // GUID of the FileOpenDialog class.
- null, // No aggregation.
- CLSCTX.CLSCTX_INPROC_SERVER, // In-process COM server.
- out IFileOpenDialog fileOpenDialog // Interface for the dialog.
+ typeof(FileOpenDialog).GUID, // CLSID for FileOpenDialog
+ null, // No aggregation
+ CLSCTX.CLSCTX_INPROC_SERVER, // In-process COM server
+ out IFileOpenDialog* fileOpenDialog // Interface for the dialog
);
// If the HRESULT indicates failure, throw a corresponding .NET exception.
@@ -143,83 +180,115 @@ out var directoryShellItem // Output reference to the IShellItem instance.
for (int i = 1; i < tokens.Length; i += 2)
{
COMDLG_FILTERSPEC extension;
-
- // Marshal the description and extension strings to unmanaged memory.
- extension.pszName = (char*)Marshal.StringToHGlobalUni(tokens[i - 1]); // Filter description.
- extension.pszSpec = (char*)Marshal.StringToHGlobalUni(tokens[i]); // File extension(s).
-
- // Add the filter specification to the list.
+ extension.pszName = (char*)Marshal.StringToHGlobalUni(tokens[i - 1]);
+ extension.pszSpec = (char*)Marshal.StringToHGlobalUni(tokens[i]);
extensions.Add(extension);
}
}
}
// Apply the file type filters to the dialog.
- fileOpenDialog.SetFileTypes(extensions.ToArray());
+ fileOpenDialog->SetFileTypes(extensions.ToArray());
- // Optionally set a default folder and starting directory.
- // Retrieves a shell item representing the "My Documents" directory.
+ // Set default folder to My Documents
hr = PInvoke.SHCreateItemFromParsingName(
- GlobalVars.UserConfigDir, // Path to the folder.
- null, // No binding context.
- typeof(IShellItem).GUID, // GUID for IShellItem interface.
- out var directoryShellItem // Output shell item.
+ Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
+ null,
+ typeof(IShellItem).GUID,
+ out void* directoryShellItem
);
// If the "My Documents" folder is successfully retrieved, set it as the default.
if (hr >= 0)
{
- fileOpenDialog.SetFolder((IShellItem)directoryShellItem); // Set starting folder.
- fileOpenDialog.SetDefaultFolder((IShellItem)directoryShellItem); // Set default folder.
+ fileOpenDialog->SetFolder((IShellItem*)directoryShellItem);
+ fileOpenDialog->SetDefaultFolder((IShellItem*)directoryShellItem);
}
- // Configure dialog options to allow multiple file selection.
+ // Configure dialog options for multiple selection
FILEOPENDIALOGOPTIONS options = FILEOPENDIALOGOPTIONS.FOS_ALLOWMULTISELECT;
- fileOpenDialog.SetOptions(options);
+ fileOpenDialog->SetOptions(options);
try
{
- // Display the file picker dialog to the user.
- fileOpenDialog.Show(new HWND(GlobalVars.hWnd)); // Owner window handle.
+ // Show the dialog
+ fileOpenDialog->Show(new HWND(GlobalVars.hWnd));
}
catch (Exception e)
{
- // Handle user cancellation of the dialog (specific HRESULT -2147023673).
- if (e.HResult != -2147023673)
+ if (e.HResult == -2147023673) // Operation Canceled HRESULT
{
- throw; // Rethrow for unexpected errors.
- }
- else
- {
- return null; // User cancelled; return null.
+ return null;
}
+
+ throw;
}
- // Retrieve the collection of selected items from the dialog.
- fileOpenDialog.GetResults(out IShellItemArray ppsiCollection);
+ // Retrieve the collection of selected items
+ IShellItemArray* ppsiCollection = null;
- // Get the number of selected files in the collection.
- ppsiCollection.GetCount(out uint fileCount);
+ try
+ {
- // Initialize the list to store the paths of the selected files.
- List selectedFiles = [];
+ fileOpenDialog->GetResults(&ppsiCollection);
- // Iterate through each selected file.
- for (uint i = 0; i < fileCount; i++)
- {
- ppsiCollection.GetItemAt(i, out IShellItem ppsi); // Get the IShellItem for the file.
+ // Get the number of selected files
+ uint fileCount = 0;
+ ppsiCollection->GetCount(&fileCount);
- // Get the file system path of the file.
- PWSTR filename;
- ppsi.GetDisplayName(SIGDN.SIGDN_FILESYSPATH, &filename); // Retrieve the full filesystem path.
+ // Initialize the list to store file paths
+ List selectedFiles = [];
+
+ // Iterate through selected items
+ for (uint i = 0; i < fileCount; i++)
+ {
+ IShellItem* ppsi = null;
+ ppsiCollection->GetItemAt(i, &ppsi);
+
+ // Retrieve the file path
+ PWSTR filename;
+ ppsi->GetDisplayName(SIGDN.SIGDN_FILESYSPATH, &filename);
- // Convert the unmanaged string (PWSTR) to a managed string and add it to the list.
- string selectedFilePath = filename.ToString();
- selectedFiles.Add(selectedFilePath);
+ // Convert to managed string and add to the list
+ string filePath = new(filename.Value);
+ selectedFiles.Add(filePath);
+
+ // Free the unmanaged memory allocated for the file path
+ if (filename.Value != null)
+ {
+ Marshal.FreeCoTaskMem((IntPtr)filename.Value);
+ }
+
+
+ // Release the IShellItem COM object
+ _ = ppsi->Release();
+
+ }
+
+ return selectedFiles;
}
+ finally
+ {
+ // Clean up extensions memory
+ foreach (COMDLG_FILTERSPEC extension in extensions)
+ {
+ if (extension.pszName.Value != null)
+ {
+ Marshal.FreeHGlobal((IntPtr)extension.pszName.Value);
+ }
+ if (extension.pszSpec.Value != null)
+ {
+ Marshal.FreeHGlobal((IntPtr)extension.pszSpec.Value);
+ }
+ }
- // Return the list of selected file paths.
- return selectedFiles;
+ // Release COM objects
+ _ = ppsiCollection->Release();
+ if (fileOpenDialog != null)
+ {
+ _ = fileOpenDialog->Release();
+ }
+ }
}
@@ -232,160 +301,198 @@ out var directoryShellItem // Output shell item.
/// The selected directory path as a string, or null if the operation is cancelled.
internal unsafe static string? ShowDirectoryPickerDialog()
{
- // Create the file open dialog using COM's CoCreateInstance method.
- // CLSCTX.CLSCTX_INPROC_SERVER ensures the dialog runs in the same process.
+ // Create the file open dialog
int hr = PInvoke.CoCreateInstance(
- typeof(FileOpenDialog).GUID, // GUID of the FileOpenDialog class.
- null, // No aggregation.
- CLSCTX.CLSCTX_INPROC_SERVER, // In-process COM server.
- out IFileOpenDialog fileOpenDialog // Interface for the dialog.
+ typeof(FileOpenDialog).GUID, // CLSID for FileOpenDialog
+ null, // No aggregation
+ CLSCTX.CLSCTX_INPROC_SERVER, // In-process COM server
+ out IFileOpenDialog* fileOpenDialog // Interface for the dialog
);
- // If the HRESULT indicates failure, throw a corresponding .NET exception.
if (hr < 0)
{
Marshal.ThrowExceptionForHR(hr);
}
- // Configure dialog options to enable folder selection.
+ // Configure dialog options to enable folder selection
FILEOPENDIALOGOPTIONS options = FILEOPENDIALOGOPTIONS.FOS_PICKFOLDERS;
- fileOpenDialog.SetOptions(options);
+ fileOpenDialog->SetOptions(options);
- // Optionally set a default folder and starting directory.
- // Retrieves a shell item representing the "My Documents" directory.
+ // Set default folder to "My Documents"
hr = PInvoke.SHCreateItemFromParsingName(
- GlobalVars.UserConfigDir, // Path to the folder.
- null, // No binding context.
- typeof(IShellItem).GUID, // GUID for IShellItem interface.
- out var directoryShellItem // Output shell item.
+ Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
+ null,
+ typeof(IShellItem).GUID,
+ out void* directoryShellItem
);
// If the "My Documents" folder is successfully retrieved, set it as the default.
if (hr >= 0)
{
- fileOpenDialog.SetFolder((IShellItem)directoryShellItem); // Set starting folder.
- fileOpenDialog.SetDefaultFolder((IShellItem)directoryShellItem); // Set default folder.
+ fileOpenDialog->SetFolder((IShellItem*)directoryShellItem);
+ fileOpenDialog->SetDefaultFolder((IShellItem*)directoryShellItem);
}
try
{
- // Display the folder picker dialog to the user.
- fileOpenDialog.Show(new HWND(GlobalVars.hWnd)); // Owner window handle.
+ // Show the dialog
+ fileOpenDialog->Show(new HWND(GlobalVars.hWnd));
}
catch (Exception e)
{
- // Handle user cancellation of the dialog (specific HRESULT -2147023673).
- if (e.HResult != -2147023673)
+ if (e.HResult == -2147023673) // Operation Canceled HRESULT
{
- throw; // Rethrow for unexpected errors.
- }
- else
- {
- return null; // User cancelled; return null.
+ return null;
}
+
+ throw;
}
- // Retrieve the selected folder from the dialog.
- fileOpenDialog.GetResult(out IShellItem ppsi);
- // Get the file system path of the selected folder.
- PWSTR folderPath;
- ppsi.GetDisplayName(SIGDN.SIGDN_FILESYSPATH, &folderPath); // Retrieve the full filesystem path.
+ // Retrieve the selected folder
+ IShellItem* ppsi = null;
- // Convert the unmanaged string (PWSTR) to a managed string.
- string selectedFolderPath = folderPath.ToString();
+ try
+ {
+
+ fileOpenDialog->GetResult(&ppsi);
+
+ // Get the file system path of the folder
+ PWSTR folderPath;
+ ppsi->GetDisplayName(SIGDN.SIGDN_FILESYSPATH, &folderPath);
+
+ // Convert to managed string
+ string selectedFolderPath = new(folderPath.Value);
+
+ // Free unmanaged memory for the folder path
+ Marshal.FreeCoTaskMem((IntPtr)folderPath.Value);
+
+ return selectedFolderPath;
- // Return the selected folder path as a managed string.
- return selectedFolderPath;
+ }
+ finally
+ {
+ // Clean up the IShellItem COM object
+ _ = ppsi->Release();
+
+
+ // Release the IFileOpenDialog COM object
+ if (fileOpenDialog != null)
+ {
+ _ = fileOpenDialog->Release();
+ }
+ }
}
+
///
/// Opens a folder picker dialog to select multiple folders.
///
/// A list of selected directory paths or null if cancelled.
internal unsafe static List? ShowMultipleDirectoryPickerDialog()
{
- // Create the file open dialog using COM's CoCreateInstance method.
- // CLSCTX.CLSCTX_INPROC_SERVER ensures the dialog runs in the same process.
+ // Create the file open dialog
int hr = PInvoke.CoCreateInstance(
- typeof(FileOpenDialog).GUID, // GUID of the FileOpenDialog class.
- null, // No aggregation.
- CLSCTX.CLSCTX_INPROC_SERVER, // In-process COM server.
- out IFileOpenDialog fileOpenDialog // Interface for the dialog.
+ typeof(FileOpenDialog).GUID, // CLSID for FileOpenDialog
+ null, // No aggregation
+ CLSCTX.CLSCTX_INPROC_SERVER, // In-process COM server
+ out IFileOpenDialog* fileOpenDialog // Interface for the dialog
);
- // If the HRESULT indicates failure, throw a corresponding .NET exception.
if (hr < 0)
{
Marshal.ThrowExceptionForHR(hr);
}
- // Configure dialog options to enable folder picking and multiple selection.
+ // Configure dialog options to enable folder picking and multiple selection
FILEOPENDIALOGOPTIONS options = FILEOPENDIALOGOPTIONS.FOS_PICKFOLDERS | FILEOPENDIALOGOPTIONS.FOS_ALLOWMULTISELECT;
- fileOpenDialog.SetOptions(options);
+ fileOpenDialog->SetOptions(options);
- // Optionally set a default folder and starting directory.
- // Retrieves a shell item representing the "My Documents" directory.
+ // Set default folder to "My Documents"
hr = PInvoke.SHCreateItemFromParsingName(
- GlobalVars.UserConfigDir, // Path to the folder.
- null, // No binding context.
- typeof(IShellItem).GUID, // GUID for IShellItem interface.
- out var directoryShellItem // Output shell item.
+ Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
+ null,
+ typeof(IShellItem).GUID,
+ out void* directoryShellItem
);
- // If the "My Documents" folder is successfully retrieved, set it as the default.
if (hr >= 0)
{
- fileOpenDialog.SetFolder((IShellItem)directoryShellItem); // Set starting folder.
- fileOpenDialog.SetDefaultFolder((IShellItem)directoryShellItem); // Set default folder.
+ fileOpenDialog->SetFolder((IShellItem*)directoryShellItem);
+ fileOpenDialog->SetDefaultFolder((IShellItem*)directoryShellItem);
}
+
try
{
- // Display the folder picker dialog to the user.
- fileOpenDialog.Show(new HWND(GlobalVars.hWnd)); // Owner window handle.
+ // Show the dialog
+ fileOpenDialog->Show(new HWND(GlobalVars.hWnd));
}
catch (Exception e)
{
- // Handle user cancellation of the dialog (specific HRESULT -2147023673).
- if (e.HResult != -2147023673)
- {
- throw; // Rethrow for unexpected errors.
- }
- else
+ if (e.HResult == -2147023673) // Operation Canceled HRESULT
{
- return null; // User cancelled; return null.
+ return null;
}
+
+ throw;
}
- // Retrieve the collection of selected items from the dialog.
- fileOpenDialog.GetResults(out IShellItemArray ppsiCollection);
+ // Retrieve the collection of selected items
+ IShellItemArray* ppsiCollection = null;
+
+ try
+ {
- // Get the number of selected folders in the collection.
- ppsiCollection.GetCount(out uint folderCount);
+ fileOpenDialog->GetResults(&ppsiCollection);
- // Initialize the list to store the paths of the selected folders.
- List selectedFolders = [];
+ // Get the number of selected folders
+ uint folderCount;
+ ppsiCollection->GetCount(&folderCount);
- // Iterate through each selected folder.
- for (uint i = 0; i < folderCount; i++)
- {
- ppsiCollection.GetItemAt(i, out IShellItem ppsi); // Get the IShellItem for the folder.
+ // Initialize the list to store selected folder paths
+ List selectedFolders = [];
- // Get the file system path of the folder.
- PWSTR folderPath;
- ppsi.GetDisplayName(SIGDN.SIGDN_FILESYSPATH, &folderPath); // Retrieve the full filesystem path.
+ // Iterate through each selected folder
+ for (uint i = 0; i < folderCount; i++)
+ {
+ IShellItem* ppsi = null;
+ ppsiCollection->GetItemAt(i, &ppsi); // Retrieve IShellItem for the folder
+
+ // Get the file system path of the folder
+ PWSTR folderPath;
+ ppsi->GetDisplayName(SIGDN.SIGDN_FILESYSPATH, &folderPath);
+
+ // Convert the unmanaged string (PWSTR) to a managed string and add to the list
+ string selectedFolderPath = new(folderPath.Value);
+ selectedFolders.Add(selectedFolderPath);
+
+ // Free unmanaged memory for the folder path
+ Marshal.FreeCoTaskMem((IntPtr)folderPath.Value);
+
+ // Clean up the IShellItem COM object
+ _ = ppsi->Release();
+
+ }
- // Convert the unmanaged string (PWSTR) to a managed string and add it to the list.
- string selectedFolderPath = folderPath.ToString();
- selectedFolders.Add(selectedFolderPath);
+ return selectedFolders;
}
+ finally
+ {
+
+ // Clean up the IShellItemArray COM object
+ _ = ppsiCollection->Release();
+
+ // Release the IFileOpenDialog COM object
+ if (fileOpenDialog != null)
+ {
+ _ = fileOpenDialog->Release();
+ }
- // Return the list of selected folder paths.
- return selectedFolders;
+ }
}
}
diff --git a/AppControl Manager/NativeMethods.json b/AppControl Manager/NativeMethods.json
index ad006028f..dacdfc705 100644
--- a/AppControl Manager/NativeMethods.json
+++ b/AppControl Manager/NativeMethods.json
@@ -1,4 +1,4 @@
{
"$schema": "https://aka.ms/CsWin32.schema.json",
- "allowMarshaling": true
+ "allowMarshaling": false
}
\ No newline at end of file