gkngkc / UnityStandaloneFileBrowser

A native file browser for unity standalone platforms
MIT License
2.01k stars 317 forks source link

"Multiple assemblies with equivalent identity have been imported" error for System.Windows.Forms.dll after importing unity package #122

Open The-MAZZTer opened 2 years ago

The-MAZZTer commented 2 years ago

I suspect the error indicates this package is not compatible with Unity 2021.3.

However I have figured out how to work around it by importing a different version of System.Windows.Forms.

I copied C:\Program Files\Unity\Hub\Editor\2021.3.0f1\Editor\Data\MonoBleedingEdge\lib\mono\unityjit-win32\System.Windows.Forms.dll and replaced the DLL the SFB package provided with that one. This resolved the error and the file picker works properly (at least in the editor).

Not sure if I selected the proper DLL as there appear to be several candidates in subfolders of lib\mono. There are some that are ~700kb which I think are native code (eg not .NET binaries so Unity can't use them as such) and there are a few that are ~2-3MB which are probably workable choices. I selected one that sounded plausible and my first choice seems to be working.

The-MAZZTer commented 2 years ago

It looks like trying to use C:\ as the starting directory for a file open dialog does not work properly with this DLL (I did trace the problem to System.Windows.Forms). The starting directory is the default and C:\ is shown in the file name box. So that is a potential complication with this workaround.

rafcsoares commented 2 years ago

It looks like trying to use C:\ as the starting directory for a file open dialog does not work properly with this DLL (I did trace the problem to System.Windows.Forms). The starting directory is the default and C:\ is shown in the file name box. So that is a potential complication with this workaround.

The final solution for this problem is remove System.Windows.Forms.dll from your project and use ShellFileDialog (a wrapper to user32.dll from native c++ windows folder).

----------------- Solution Below will work with all Unity Versions (NetStandard and/or NetFramework) ---------------------------

Drop the DLL below to your unity project and remove old System.Windows.Forms.dll from your project.

DLL Link: ShellFileDialogs.zip PS: Edit DLL Inspector in Unity to only compile dll in Editor Windows/Standalone Windows x84|x64

Replace the source of StandaloneFileBrowserWindows.cs to the code below.

#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN

using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Linq;
using System.Collections.Generic;

namespace SFB {

    // For fullscreen support
    // - "PlayerSettings/Visible In Background" should be enabled, otherwise when file dialog opened app window minimizes automatically.
    public class StandaloneFileBrowserWindows : IStandaloneFileBrowser 
    {

        [DllImport("user32.dll")]
        private static extern IntPtr GetActiveWindow();

        public string[] OpenFilePanel(string title, string directory, ExtensionFilter[] extensions, bool multiselect) {

            var shellFilters = GetShellFilterFromFileExtensionList(extensions);
            if (multiselect)
            {
                var paths = ShellFileDialogs.FileOpenDialog.ShowMultiSelectDialog(GetActiveWindow(), title, directory, string.Empty, shellFilters, null);
                return paths == null || paths.Count == 0 ? new string[0] : paths.ToArray();
            }
            else
            {
                var path = ShellFileDialogs.FileOpenDialog.ShowSingleSelectDialog(GetActiveWindow(), title, directory, string.Empty, shellFilters, null);
                return string.IsNullOrEmpty(path) ? new string[0] : new[] { path };
            }
        }

        public void OpenFilePanelAsync(string title, string directory, ExtensionFilter[] extensions, bool multiselect, Action<string[]> cb) {
            cb.Invoke(OpenFilePanel(title, directory, extensions, multiselect));
        }

        public string[] OpenFolderPanel(string title, string directory, bool multiselect) {

            var path = ShellFileDialogs.FolderBrowserDialog.ShowDialog(GetActiveWindow(), title, directory);
            return string.IsNullOrEmpty(path) ? new string[0] : new[] { path };
        }

        public void OpenFolderPanelAsync(string title, string directory, bool multiselect, Action<string[]> cb) {
            cb.Invoke(OpenFolderPanel(title, directory, multiselect));
        }

        public string SaveFilePanel(string title, string directory, string defaultName, ExtensionFilter[] extensions) {

            var finalFilename = "";
            if (!string.IsNullOrEmpty(directory))
            {
                finalFilename = GetDirectoryPath(directory);
            }

            if (!string.IsNullOrEmpty(defaultName))
            {
                finalFilename += defaultName;
            }

            var shellFilters = GetShellFilterFromFileExtensionList(extensions);
            if(shellFilters.Length > 0 && !string.IsNullOrEmpty(finalFilename))
            {
                var extension = $".{shellFilters[0].Extensions[0]}";
                if (!string.IsNullOrEmpty(extension) && !finalFilename.EndsWith(extension, StringComparison.CurrentCultureIgnoreCase))
                    finalFilename += extension;
            }

            var path = ShellFileDialogs.FileSaveDialog.ShowDialog(GetActiveWindow(), title, directory, finalFilename, shellFilters, 0);
            return path;
        }

        public void SaveFilePanelAsync(string title, string directory, string defaultName, ExtensionFilter[] extensions, Action<string> cb) {
            cb.Invoke(SaveFilePanel(title, directory, defaultName, extensions));
        }

        private static ShellFileDialogs.Filter[] GetShellFilterFromFileExtensionList(ExtensionFilter[] extensions)
        {
            var shellFilters = new List<ShellFileDialogs.Filter>();
            if (extensions != null)
            {
                foreach (var extension in extensions)
                {
                    if (extension.Extensions == null || extension.Extensions.Length == 0)
                        continue;

                    var displayName = extension.Name;
                    if (string.IsNullOrEmpty(displayName))
                    {
                        System.Text.StringBuilder extensionFormatted = new System.Text.StringBuilder();
                        foreach (var extensionStr in extension.Extensions)
                        {
                            if (extensionFormatted.Length > 0)
                                extensionFormatted.Append(";");
                            extensionFormatted.Append($"*.{extensionStr}");
                        }
                        displayName = $"({extensionFormatted})";
                    }
                    var filter = new ShellFileDialogs.Filter(displayName, extension.Extensions);
                    shellFilters.Add(filter);
                }
            }
            if (shellFilters.Count == 0)
            {
                shellFilters.AddRange(ShellFileDialogs.Filter.ParseWindowsFormsFilter(@"All files (*.*)|*.*"));
            }

            return shellFilters.ToArray();
        }

        private static string GetDirectoryPath(string directory) {
            var directoryPath = Path.GetFullPath(directory);
            if (!directoryPath.EndsWith("\\")) {
                directoryPath += "\\";
            }
            if (Path.GetPathRoot(directoryPath) == directoryPath) {
                return directory;
            }
            return Path.GetDirectoryName(directoryPath) + Path.DirectorySeparatorChar;
        }
    }
}

#endif

Best Regards Rafael

thnewlands commented 2 years ago

Hey @rafcsoares thank you for putting this together! I'm getting a missing directive error when I follow your instructions. Do you have any troubleshooting tips?

Assets\Scripts\Thirdparty\StandaloneFileBrowser\StandaloneFileBrowserWindows.cs(78,24): error CS0246: The type or namespace name 'ShellFileDialogs' could not be found (are you missing a using directive or an assembly reference?

rafcsoares commented 2 years ago

You need to import dll to project buddy.

@thnewlands i put the link for dll zip in my previous comment.. you just need to drop it inside your project and select to work with UnityEditor Windows x86/x64 and Standalone Windows x86/x64

thnewlands commented 2 years ago

Turns out I had a competing .asmdef -- works perfectly! Thanks for sharing your solution.

TheYellowArchitect commented 2 years ago

Thank you for this. I was testing SFB on the editor and it was good, but days later when I built with System.Windows.Forms, I just couldn't build!

I use Unity Version 2018.1.1f1, and so, can confirm it works there. It needed .NET 4.0 though, but luckily that was the last version possible through Project Settings -> Player -> Other Settings

Profy commented 2 years ago

Hi @rafcsoares. I would like to know which version of ShellFileDialogs you used to make the library you gave in link. Your version does not match with the last version of ShellFileDialogs.

rafcsoares commented 2 years ago

@Profy i don't know... i just pick the master branch and compiled the DLL by myself to share.

In my project i use the raw code instead

Profy commented 2 years ago

@rafcsoares thank you for your reply. I have already contacted the developer to make some fixes.

cmwolke-gamedesign commented 1 year ago

It seems ShellFileDialogs doesn't respect the directory parameter, at least for me it always opens in the last location instead of where I want it to (test: BasicSampleScene, "Open file directory" button) Edit: Can easily be fixed by running

if (!string.IsNullOrEmpty) { directory = GetDirectoryPath(directory); }