浏览代码

Fix startup to register file associations #157

Ruben 7 月之前
父节点
当前提交
b9f0130c5f

+ 4 - 2
src/PicView.Avalonia.MacOS/App.axaml.cs

@@ -16,6 +16,7 @@ using PicView.Avalonia.WindowBehavior;
 using PicView.Core.FileHandling;
 using PicView.Core.Localization;
 using PicView.Core.MacOS;
+using PicView.Core.MacOS.FileAssociation;
 using PicView.Core.MacOS.Wallpaper;
 using PicView.Core.ViewModels;
 
@@ -485,8 +486,9 @@ public class App : Application, IPlatformSpecificService
         return MacOsKeybindings.DefaultKeybindings;
     }
 
-    public Task AssociateFileTypes(string s)
+    public void InitiateFileAssociationService()
     {
-        throw new NotImplementedException();
+        var iIFileAssociationService = new MacFileAssociationService();
+        FileAssociationManager.Initialize(iIFileAssociationService);
     }
 }

+ 7 - 0
src/PicView.Avalonia.Win32/App.axaml.cs

@@ -19,6 +19,7 @@ using PicView.Core.Localization;
 using PicView.Core.ProcessHandling;
 using PicView.Core.ViewModels;
 using PicView.Core.WindowsNT;
+using PicView.Core.WindowsNT.FileAssociation;
 using PicView.Core.WindowsNT.FileHandling;
 using PicView.Core.WindowsNT.Taskbar;
 using PicView.Core.WindowsNT.Wallpaper;
@@ -513,6 +514,12 @@ public partial class App : Application, IPlatformSpecificService
         return WindowsKeybindings.DefaultKeybindings;
     }
 
+    public void InitiateFileAssociationService()
+    {
+        var iIFileAssociationService = new WindowsFileAssociationService();
+        FileAssociationManager.Initialize(iIFileAssociationService);
+    }
+
     public void DisableScreensaver()
     {
         NativeMethods.DisableScreensaver();

+ 0 - 1
src/PicView.Avalonia.Win32/Views/SettingsWindow.axaml.cs

@@ -134,5 +134,4 @@ public partial class SettingsWindow : Window
         var iIFileAssociationService = new WindowsFileAssociationService();
         FileAssociationManager.Initialize(iIFileAssociationService);
     }
-
 }

+ 2 - 0
src/PicView.Avalonia/Interfaces/IPlatformSpecificService.cs

@@ -52,4 +52,6 @@ public interface IPlatformSpecificService
     Task<bool> ExtractWithLocalSoftwareAsync(string path, string tempDirectory);
 
     string DefaultJsonKeyMap();
+
+    void InitiateFileAssociationService();
 }

+ 31 - 21
src/PicView.Avalonia/StartUp/StartUpHelper.cs

@@ -1,4 +1,5 @@
-using System.Runtime.InteropServices;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
 using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls.ApplicationLifetimes;
@@ -35,35 +36,44 @@ public static class StartUpHelper
         }
         else
         {
-            if (Settings.UIProperties.OpenInSameWindow &&
-                ProcessHelper.CheckIfAnotherInstanceIsRunning())
-            {
-                HandleMultipleInstances(args);
-            }
-            else if (args.Length > 1)
+            if (args.Length > 1)
             {
                 var arg = args[1];
                 if (arg.StartsWith("associate:", StringComparison.OrdinalIgnoreCase) ||
                     arg.StartsWith("unassociate:", StringComparison.OrdinalIgnoreCase))
                 {
-                    for (int i = 1; i < args.Length; i++)
+                    // This is important: we need to process the complete original argument,
+                    // not just the first part
+                    Task.Run(async () =>
                     {
-                        var currentArg = args[i];
-                        if (currentArg.StartsWith("associate:", StringComparison.OrdinalIgnoreCase) || 
-                            currentArg.StartsWith("unassociate:", StringComparison.OrdinalIgnoreCase))
+                        try
                         {
-                            Task.Run(async () =>
+                            vm.PlatformService.InitiateFileAssociationService();
+                            Debug.WriteLine($"Processing file association argument: {arg}");
+                            await FileTypeHelper.ProcessFileAssociationArguments(arg);
+
+                            // Exit if this was just a file association command
+                            // and no other files were specified to be opened
+                            if (args.Length <= 2)
                             {
-                                // Use the helper to process the association commands
-                                await FileTypeHelper.ProcessFileAssociationArguments(currentArg);
-                                if (args.Length <= 2)
-                                {
-                                    Environment.Exit(0);
-                                }
-                            });
+                                Debug.WriteLine("Exiting after processing file association");
+                                Environment.Exit(0);
+                            }
                         }
-                    }
-                    Environment.Exit(0);
+                        catch (Exception ex)
+                        {
+                            Debug.WriteLine($"Error in file association processing: {ex.Message}");
+                        }
+                        finally
+                        {
+                            Environment.Exit(0);
+                        }
+                    });
+                }
+                else if (Settings.UIProperties.OpenInSameWindow &&
+                    ProcessHelper.CheckIfAnotherInstanceIsRunning())
+                {
+                    HandleMultipleInstances(args);
                 }
             }
         }

+ 168 - 82
src/PicView.Core/FileAssociations/FileTypeHelper.cs

@@ -25,7 +25,7 @@ public static class FileTypeHelper
                 new FileTypeItem("Advanced Video Interlace Format", [".avif"]),
                 new FileTypeItem("Icon", [".ico"])
             ]),
-            
+
             new FileTypeGroup(TranslationManager.Translation.Graphics, [
                 new FileTypeItem("Scalable Vector Graphics", [".svg", ".svgz"], null),
                 new FileTypeItem("Photoshop", [".psd", ".psb"], null),
@@ -40,7 +40,7 @@ public static class FileTypeHelper
                 new FileTypeItem("Truevision Targa", [".tga"]),
                 new FileTypeItem("Industrial Light & Magic OpenEXR", [".exr"])
             ]),
-            
+
             new FileTypeGroup(TranslationManager.Translation.RawCamera, [
                 new FileTypeItem("Raw", [".raw"]),
                 new FileTypeItem("Framed Raster", [".3fr"]),
@@ -63,9 +63,9 @@ public static class FileTypeHelper
                 new FileTypeItem("Kodak FlashPix Bitmap", [".fpx"]),
                 new FileTypeItem("Kodak PhotoCD Bitmap", [".pcd"]),
                 new FileTypeItem("Kodak Raw", [".dcr"]),
-                new FileTypeItem("Windows Metafile Image", [".wmf", ".emf"]),
+                new FileTypeItem("Windows Metafile Image", [".wmf", ".emf"])
             ]),
-            
+
             new FileTypeGroup(TranslationManager.Translation.Uncommon, [
                 new FileTypeItem("Wordperfect Graphics", [".wpg"]),
                 new FileTypeItem("Paintbrush bitmap graphics", [".pcx"], null),
@@ -77,7 +77,7 @@ public static class FileTypeHelper
                 new FileTypeItem("Portable PixMap Bitmap", [".pbm"]),
                 new FileTypeItem("Base64", [".b64"])
             ]),
-            
+
             new FileTypeGroup(TranslationManager.Translation.Archives, [
                 new FileTypeItem("Zip", [".zip"], null),
                 new FileTypeItem("Rar", [".rar"], null),
@@ -88,139 +88,225 @@ public static class FileTypeHelper
 
         return groups;
     }
-    
-   
+
+
     public static async Task<bool> SetFileAssociations(ReadOnlyObservableCollection<FileTypeGroup> groups)
-{
-    try
     {
-        // If we're on Windows, check for admin permissions
-        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && !IsAdministrator())
+        try
         {
-            // Build list of extensions to associate with descriptions
-            var extensionsToAssociate = new List<string>();
-            
+            // If we're on Windows, check for admin permissions
+            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && !IsAdministrator())
+            {
+                // Build list of extensions to associate with descriptions
+                var extensionsToAssociate = new List<string>();
+                var extensionsToUnassociate = new List<string>();
+
+                foreach (var group in groups)
+                {
+                    foreach (var fileType in group.FileTypes)
+                    {
+                        if (!fileType.IsSelected.HasValue)
+                        {
+                            continue; // Skip null selections
+                        }
+
+                        foreach (var extension in fileType.Extensions)
+                        {
+                            // Make sure to properly handle extensions that contain commas
+                            var individualExtensions =
+                                extension.Split([',', ' '], StringSplitOptions.RemoveEmptyEntries);
+
+                            foreach (var ext in individualExtensions)
+                            {
+                                var cleanExt = ext.Trim();
+                                if (!cleanExt.StartsWith('.'))
+                                {
+                                    cleanExt = "." + cleanExt;
+                                }
+
+                                if (fileType.IsSelected.Value)
+                                {
+                                    // Add to association list
+                                    extensionsToAssociate.Add($"{cleanExt}|{fileType.Description}");
+                                }
+                                else
+                                {
+                                    // Add to unassociation list
+                                    extensionsToUnassociate.Add($"{cleanExt}");
+                                }
+                            }
+                        }
+                    }
+                }
+
+                // Build arguments for the elevated process
+                var args = new List<string>();
+
+                if (extensionsToAssociate.Count > 0)
+                {
+                    // Create command arguments for associations
+                    args.Add("associate:" + string.Join(";", extensionsToAssociate));
+                }
+
+                if (extensionsToUnassociate.Count > 0)
+                {
+                    // Create command arguments for unassociations
+                    args.Add("unassociate:" + string.Join(";", extensionsToUnassociate));
+                }
+
+                if (args.Count == 0)
+                {
+                    return true; // Nothing to do
+                }
+
+                // Start new process with elevated permissions
+                return ProcessHelper.StartProcessWithElevatedPermission(string.Join(" ", args));
+            }
+
+            // Standard processing path (non-Windows or already has admin rights)
             foreach (var group in groups)
             {
                 foreach (var fileType in group.FileTypes)
                 {
-                    if (!fileType.IsSelected.HasValue || !fileType.IsSelected.Value) 
+                    if (!fileType.IsSelected.HasValue)
+                    {
                         continue;
-                    
+                    }
+
                     foreach (var extension in fileType.Extensions)
                     {
-                        // Make sure to properly handle extensions that contain commas
                         var individualExtensions = extension.Split([',', ' '], StringSplitOptions.RemoveEmptyEntries);
-    
+
                         foreach (var ext in individualExtensions)
                         {
                             var cleanExt = ext.Trim();
                             if (!cleanExt.StartsWith('.'))
+                            {
                                 cleanExt = "." + cleanExt;
-                            
-                            // Add the extension with its description, separated by a special delimiter
-                            extensionsToAssociate.Add($"{cleanExt}|{fileType.Description}");
+                            }
+
+                            if (fileType.IsSelected.Value)
+                            {
+                                await FileAssociationManager.AssociateFile(cleanExt, fileType.Description);
+                            }
+                            else
+                            {
+                                await FileAssociationManager.UnassociateFile(cleanExt);
+                            }
                         }
                     }
                 }
             }
 
-            if (extensionsToAssociate.Count <= 0)
-            {
-                return true; // Nothing to do
-            }
-
-            // Create command arguments - separate extensions with semicolons to avoid issues
-            var associateArg = "associate:" + string.Join(";", extensionsToAssociate);
-                
-            // Start new process with elevated permissions
-            return ProcessHelper.StartProcessWithElevatedPermission(associateArg);
+            return true;
         }
-        
-        // Standard processing path (non-Windows or already has admin rights)
-        foreach (var group in groups)
+        catch (Exception ex)
         {
-            foreach (var fileType in group.FileTypes)
-            {
-                if (!fileType.IsSelected.HasValue) 
-                    continue;
-                
-                foreach (var extension in fileType.Extensions)
-                {
-                    var individualExtensions = extension.Split([',', ' '], StringSplitOptions.RemoveEmptyEntries);
-    
-                    foreach (var ext in individualExtensions)
-                    {
-                        var cleanExt = ext.Trim();
-                        if (!cleanExt.StartsWith('.'))
-                            cleanExt = "." + cleanExt;
-            
-                        if (fileType.IsSelected.Value)
-                            await FileAssociationManager.AssociateFile(cleanExt, fileType.Description);
-                        else
-                            await FileAssociationManager.UnassociateFile(cleanExt);
-                    }
-                }
-            }
+            // Log the exception or handle it appropriately
+            Debug.WriteLine($"Error in SetFileAssociations: {ex}");
+            return false;
         }
-        
-        return true;
-    }
-    catch (Exception ex)
-    {
-        // Log the exception or handle it appropriately
-        Debug.WriteLine($"Error in SetFileAssociations: {ex}");
-        return false;
     }
-}
-    
+
     private static bool IsAdministrator()
     {
         if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+        {
             return false;
-            
+        }
+
         // Check if running as administrator
         using var identity = WindowsIdentity.GetCurrent();
         var principal = new WindowsPrincipal(identity);
         return principal.IsInRole(WindowsBuiltInRole.Administrator);
     }
-    
+
     public static async Task ProcessFileAssociationArguments(string arg)
     {
         try
         {
             if (arg.StartsWith("associate:", StringComparison.OrdinalIgnoreCase))
             {
+                // Process associations
                 var extensionsString = arg["associate:".Length..];
                 if (string.IsNullOrWhiteSpace(extensionsString))
+                {
+                    Debug.WriteLine("No extensions to associate found in arguments.");
                     return;
-                
+                }
+
                 // Split by semicolons for different extensions
                 var extensions = extensionsString
                     .Split(';', StringSplitOptions.RemoveEmptyEntries)
                     .Select(ext => ext.Trim())
                     .ToArray();
-                
+
+                Debug.WriteLine($"Found {extensions.Length} extensions to associate");
+
                 foreach (var extension in extensions)
                 {
-                    // Each extension may have a description after a pipe |
-                    var parts = extension.Split('|', 2);
-                    var ext = parts[0].Trim();
-                
-                    // Get description if available
-                    string? description = null;
-                    if (parts.Length > 1)
+                    try
+                    {
+                        // Each extension may have a description after a pipe |
+                        var parts = extension.Split('|', 2);
+                        var ext = parts[0].Trim();
+
+                        // Get description if available
+                        string? description = null;
+                        if (parts.Length > 1)
+                        {
+                            description = parts[1].Trim();
+                        }
+
+                        Debug.WriteLine($"Associating {ext} with description '{description}'");
+                        await FileAssociationManager.AssociateFile(ext, description);
+                    }
+                    catch (Exception extEx)
+                    {
+                        Debug.WriteLine($"Error processing extension '{extension}': {extEx.Message}");
+                    }
+                }
+            }
+            else if (arg.StartsWith("unassociate:", StringComparison.OrdinalIgnoreCase))
+            {
+                // Process unassociations
+                var extensionsString = arg["unassociate:".Length..];
+                if (string.IsNullOrWhiteSpace(extensionsString))
+                {
+                    Debug.WriteLine("No extensions to unassociate found in arguments.");
+                    return;
+                }
+
+                // Split by semicolons for different extensions
+                var extensions = extensionsString
+                    .Split(';', StringSplitOptions.RemoveEmptyEntries)
+                    .Select(ext => ext.Trim())
+                    .ToArray();
+
+                Debug.WriteLine($"Found {extensions.Length} extensions to unassociate");
+
+                foreach (var extension in extensions)
+                {
+                    try
+                    {
+                        // For unassociate, we just need the extension (ignore any description)
+                        var ext = extension.Split('|')[0].Trim();
+
+                        Debug.WriteLine($"Unassociating {ext}");
+                        await FileAssociationManager.UnassociateFile(ext);
+                    }
+                    catch (Exception extEx)
                     {
-                        description = parts[1].Trim();
+                        Debug.WriteLine($"Error unassociating extension '{extension}': {extEx.Message}");
                     }
-                
-                    await FileAssociationManager.AssociateFile(ext, description);
                 }
             }
         }
         catch (Exception ex)
         {
-            Debug.WriteLine($"Error processing file association arguments: {ex}");
+            Debug.WriteLine($"Error processing file association arguments: {ex.Message}");
+            Debug.WriteLine($"Argument was: {arg}");
+            Debug.WriteLine($"Stack trace: {ex.StackTrace}");
         }
     }
-}
+}