Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OpenFileDialog returns empty string on paths over 260 characters (or doesn't return at all)

I'm writing a program that needs to read in files from anywhere on the system. Some users of the program have paths over the 260 character limit. The OpenFileDialog doesn't work with files with paths over 260 characters.

I've tried using both System.Windows.Forms.OpenFileDialog and Microsoft.Win32.OpenFileDialog. In the case of the former, when I click "open" after I've navigated to and selected the file, the window doesn't close and the program doesn't continue. In the case of the latter, the window will close when I click "open", but the path is an empty string.

I've updated the registry on my computer. I've edited the application manifest file. I would try to prepend the "//?/" string to my paths, but there are no paths to which to prepend.

var dialog = new OpenFileDialog
{
  // initialize dialog
}

if (dialog.ShowDialog() == DialogResult.OK) // DialogResult.OK replaced with true if using Microsoft.Win32.OpenFileDialog
{
  // if when using System.Windows.Forms.OpenFileDialog, I will never get to this point
  // if using Microsoft.Win32.OpenFileDialog, I will get here but dialog.FileNames will be empty
}

I'd expect the above code to work the same with long and short paths if I've updated the registry and the app manifest. I'm suspecting that this just isn't supported, but all of my searches have revealed people providing a solution that either doesn't work or only works in specific cases.

like image 962
cdicker Avatar asked Nov 07 '22 12:11

cdicker


1 Answers

In the case of System.Windows.Forms.OpenFileDialog I was able to get this to work by setting ValidateNames to false to overcome ShowDialog() not returning when the user clicks "open",

    System.Windows.Forms.OpenFileDialog openFileDialog_WindowsForms = new System.Windows.Forms.OpenFileDialog
    {
        CheckFileExists = true,
        CheckPathExists = true,
        ValidateNames = false // this will allow paths over 260 characters
    };

    if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
    {
        string[] fileNames = openFileDialog_WindowsForms.getFileNames_WindowsForms();

        foreach (var file in fileNames)
        {
            try
            {
                Console.WriteLine(File.ReadAllText(file));
            }
            catch (Exception ex)
            {
                Console.WriteLine("Couldn't open file from Windows.Forms.OpenFileDialog:" + ex.Message);
            }
        }

    };

and reflection to overcome the paths not being accessible from the FilePath or FilePaths properties. It turns out the paths were present in a private property which I could access using reflection.

public static class OpenFileDialogLongPathExtension
{
    public static string[] getFileNames_WindowsForms(this System.Windows.Forms.OpenFileDialog dialog)
    {
        var fileNamesProperty = dialog.GetType().GetProperty("FileNamesInternal", BindingFlags.NonPublic | BindingFlags.Instance);
        var fileNamesFromProperty = (string[])fileNamesProperty?.GetValue(dialog);
        return fileNamesFromProperty;
    }
}

I tried something similar for the Microsoft.Win32.OpenFileDialog, but it seems like that private property is still invalid, so the same solution wouldn't work.

Either way, I hope this helps someone else. This example was created using .NET Framework 4.8.

like image 163
cdicker Avatar answered Nov 15 '22 07:11

cdicker