question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Handling for really long file/folder names

See original GitHub issue

I’m guessing this doesn’t come up very often but when you try and read a message file with a really long file name (over 259 characters) or in my case in a very deep folder structure over 259 characters it returns the error “The provided file is not a valid IStorage\r\nParameter name: storageFilePath” although the file is a valid Outlook message file that will open in Outlook from that folder.

Here is an example folder structure I tested that I made deliberately complex to check other things in my code

Directory of C:\Temp\EmailTesting\Specific\Very long folder name,  # , complex name\Very long folder name,  # , complex name 1\Very long folder name,  # , complex name 2\Very long folder name,  # , complex name 3\Very long folder name,  # , complex name 4\Samples-01

08/12/2010  16:57            50,176 Test File.msg

I did pass it in what I believe is the support .net structure for very long names with \?\ added to the start of the path e.g. \?\C:\Temp (mentioned here https://blogs.msdn.microsoft.com/jeremykuhne/2016/07/30/net-4-6-2-and-long-paths-on-windows-10/ )

As mentioned I’m sure this is an unusual use case but a fix would be appreciated.

Thanks very much, Dom

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:12 (6 by maintainers)

github_iconTop GitHub Comments

1reaction
mlsad3commented, Jan 15, 2018

@domOrielton @FuchsiaSoft , In case you’re curious, I have the following unit-test in my code. There’s probably a better way to do this, but what it does is:

  • Iterate over all the code files in my solution
  • Makes sure that if my code ever uses ‘Path’, ‘Directory’, etc., that I have specified “using Path = Pri.LongPath.Path” somewhere in the file, just as you did in your example.
  • It has inline waiver support, just add ‘waiveTest_UsageOfPath’ to a comment on the same line where you use File/Path/Directory unsafely. Also useful for corner-case lines that confuse my test.
  • It skips certain projects (that I maybe have no control over or I know they don’t need longpath support)
  • It skips certain files that I don’t want to add a bunch of inline waivers
  • It ignores any ‘Path’ usage in comments and quotes

Note: You’ll probably have to change the depth of my code solution. This unit test’s bin/ directory is 4 directories above the solution, so I navigate down 4 “…/…/…/…/” when doing the GetFiles()

    using System;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using System.IO;
    using System.Linq;
    using System.Text.RegularExpressions;
    using Directory = Pri.LongPath.Directory;
    using File = Pri.LongPath.File;
    using Path = Pri.LongPath.Path;

    namespace My.Tools.Test
    {
        [TestClass]
        public class UnitTest_Tools
        {
            readonly Regex _regexFindComments = new Regex( @"//.*", RegexOptions.Compiled );
            readonly Regex _regexFindVerbatimString = new Regex( "@\"[^\"]*\"", RegexOptions.Compiled );
            readonly Regex _regexFindEscapedQuotes = new Regex( @"\\" + "\"", RegexOptions.Compiled );
            readonly Regex _regexFindQuotedSections = new Regex( "\"[^\"]*\"", RegexOptions.Compiled );

            readonly Regex _regexHasPriPath = new Regex( @"^\s*using\s+Path\s*=\s*Pri.LongPath.Path\s*;", RegexOptions.Compiled );

            readonly Regex _regexHasPriDirectory = new Regex(
                @"^\s*using\s+(?:Directory\s*=\s*Pri.LongPath.Directory|Lucene.Net.Store)\s*;", RegexOptions.Compiled );

            readonly Regex _regexHasPriDirectoryInfo = new Regex(
                @"^\s*using\s+DirectoryInfo\s*=\s*Pri.LongPath.DirectoryInfo\s*;", RegexOptions.Compiled );

            readonly Regex _regexHasPriFile = new Regex( @"^\s*using\s+File\s*=\s*Pri.LongPath.File\s*;", RegexOptions.Compiled );
            readonly Regex _regexHasPriFileInfo = new Regex( @"^\s*using\s+FileInfo\s*=\s*Pri.LongPath.FileInfo\s*;", RegexOptions.Compiled );

            readonly Regex _regexHasPriFileSystemInfo = new Regex(
                @"^\s*using\s+FileSystemInfo\s*=\s*Pri.LongPath.FileSystemInfo\s*;", RegexOptions.Compiled );

            readonly Regex _regexNewFileStream = new Regex( @"\bnew\s+(?:(?:System\.)?IO\.)?FileStream\s*\(", RegexOptions.Compiled );

            readonly Regex _regexUsingSystemIo = new Regex( @"^\s*using System\.IO\b", RegexOptions.Compiled );
            readonly Regex _regexUsingPriLongPath = new Regex( @"^\s*using Pri.LongPath\b", RegexOptions.Compiled );
            readonly Regex _regexUsesSystemIoPath = new Regex( @"\b(?:System\.)?IO\.Path\b", RegexOptions.Compiled );
            readonly Regex _regexUsesSystemIoDirectory = new Regex( @"\b(?:System\.)?IO\.Directory\b", RegexOptions.Compiled );
            readonly Regex _regexUsesSystemIoDirectoryInfo = new Regex( @"\b(?:System\.)?IO\.DirectoryInfo\b", RegexOptions.Compiled );
            readonly Regex _regexUsesSystemIoFile = new Regex( @"\b(?:System\.)?IO\.File\b", RegexOptions.Compiled );
            readonly Regex _regexUsesSystemIoFileInfo = new Regex( @"\b(?:System\.)?IO\.FileInfo\b", RegexOptions.Compiled );
            readonly Regex _regexUsesSystemIoFileSystemInfo = new Regex( @"\b(?:System\.)?IO\.FileSystemInfo\b", RegexOptions.Compiled );

            readonly Regex _regexUsageOfPath = new Regex( @"\bPath\b", RegexOptions.Compiled );
            readonly Regex _regexUsageOfDirectory = new Regex( @"\bDirectory\b", RegexOptions.Compiled );
            readonly Regex _regexUsageOfDirectoryInfo = new Regex( @"\bDirectoryInfo\b", RegexOptions.Compiled );
            readonly Regex _regexUsageOfFile = new Regex( @"\bFile\b", RegexOptions.Compiled );
            readonly Regex _regexUsageOfFileInfo = new Regex( @"\bFileInfo\b", RegexOptions.Compiled );
            readonly Regex _regexUsageOfFileSystemInfo = new Regex( @"\bFileSystemInfo\b", RegexOptions.Compiled );

            readonly Regex _regexUsageOfPathWaiver = new Regex( @"waiveTest_UsageOfPath", RegexOptions.Compiled );

            /// <summary>
            /// This test checks to make sure certain filepath-length libraries are not being used
            /// </summary>
            [Timeout( 10000 )]
            [TestMethod]
            public void Test_SourceCode_SystemIOCheck()
            {
                // Test Sanity check: make sure assumption about where the test is running from is correct
                Assert.IsTrue( Directory.Exists( @"../../../../InternalLibs" ) ); // This is a directory in my solution
                var pathToSolution = @"../../../../";

                // Search all source code, and make sure "new FileStream(" is not seen)
                var files = Directory.GetFiles(
                    pathToSolution, "*.cs", SearchOption.AllDirectories ).ToList();

                // Sanity check: We should have at least 5 .cs files in solution
                Assert.IsTrue( files.Count > 5 );
                foreach ( var file in files )
                {
                    // Skip files in certain paths
                    if (
                         file.Contains( @"ExternalLibs\NotifyIconWpf" ) ||
                         file.Contains( @"ExternalLibs\Pri.LongPath" ) ||
                         file.Contains( @"WebSites\" ) ||
                         file.EndsWith( @".g.cs" ) ||
                         file.EndsWith( @".g.i.cs" ) ||
                         file.EndsWith( @"UnitTestsThatUseSystemIO.cs" ) || // This file requires use of System.IO.Directory
                         file.EndsWith( @"MyProject\FileUsingSystemIO.cs" )
                        )
                        continue;

                    // Read in the text and then do regex checks on it
                    using ( StreamReader r = new StreamReader( file ) )
                    {
                        bool usesSystemIO = false;
                        bool usesPriLongPath = false;
                        bool hasPriPath = false;
                        bool hasPriDirectory = false;
                        bool hasPriDirectoryInfo = false;
                        bool hasPriFile = false;
                        bool hasPriFileInfo = false;
                        bool hasPriFileSystemInfo = false;
                        string line;
                        while ( (line = r.ReadLine()) != null )
                        {
                            var lineWithoutComments = _regexFindComments.Replace( line, "" );
                            var lineWithoutVerbatimString = _regexFindVerbatimString.Replace( lineWithoutComments, "" );
                            var lineWithoutEscapedQuotes = _regexFindEscapedQuotes.Replace( lineWithoutVerbatimString, "" );
                            var codeText = _regexFindQuotedSections.Replace( lineWithoutEscapedQuotes, "" );

                            var currentLineHasWaiver = _regexUsageOfPathWaiver.IsMatch( line );

                            // Check if following are used
                            // using System.IO;
                            // using Pri.LongPath;
                            usesSystemIO |= _regexUsingSystemIo.IsMatch( codeText );
                            usesPriLongPath |= _regexUsingPriLongPath.IsMatch( codeText );

                            // Check if the following are used
                            // using Path = Pri.LongPath.Path;
                            // using Directory = Pri.LongPath.Directory;
                            // using DirectoryInfo = Pri.LongPath.DirectoryInfo;
                            // using File = Pri.LongPath.File;
                            // using FileInfo = Pri.LongPath.FileInfo;
                            // using FileSystemInfo = Pri.LongPath.FileSystemInfo;
                            hasPriPath |= _regexHasPriPath.IsMatch( codeText );
                            hasPriDirectory |= _regexHasPriDirectory.IsMatch( codeText );
                            hasPriDirectoryInfo |= _regexHasPriDirectoryInfo.IsMatch( codeText );
                            hasPriFile |= _regexHasPriFile.IsMatch( codeText );
                            hasPriFileInfo |= _regexHasPriFileInfo.IsMatch( codeText );
                            hasPriFileSystemInfo |= _regexHasPriFileSystemInfo.IsMatch( codeText );

                            // Instead of "new FileStream()" it should be "File.Open()" to do the same thing but with Pri.LongPath
                            Assert.IsFalse( _regexNewFileStream.IsMatch( codeText ) );

                            // Make sure "System.IO.Path" or "IO.Path" is not used
                            Assert.IsTrue( !_regexUsesSystemIoPath.IsMatch( codeText ) || currentLineHasWaiver );
                            Assert.IsTrue( !_regexUsesSystemIoDirectory.IsMatch( codeText ) || currentLineHasWaiver );
                            Assert.IsTrue( !_regexUsesSystemIoDirectoryInfo.IsMatch( codeText ) || currentLineHasWaiver );
                            Assert.IsTrue( !_regexUsesSystemIoFile.IsMatch( codeText ) || currentLineHasWaiver );
                            Assert.IsTrue( !_regexUsesSystemIoFileInfo.IsMatch( codeText ) || currentLineHasWaiver );
                            Assert.IsTrue( !_regexUsesSystemIoFileSystemInfo.IsMatch( codeText ) || currentLineHasWaiver );

                            if ( !usesSystemIO && usesPriLongPath )
                                continue;

                            // Make sure that "Path" is not used without having declared "using Path = Pri.LongPath.Path"
                            Assert.IsTrue( hasPriPath || currentLineHasWaiver || !_regexUsageOfPath.IsMatch( codeText ) );
                            Assert.IsTrue( hasPriDirectory || currentLineHasWaiver || !_regexUsageOfDirectory.IsMatch( codeText ) );
                            Assert.IsTrue( hasPriDirectoryInfo || currentLineHasWaiver || !_regexUsageOfDirectoryInfo.IsMatch( codeText ) );
                            Assert.IsTrue( hasPriFile || currentLineHasWaiver || !_regexUsageOfFile.IsMatch( codeText ) );
                            Assert.IsTrue( hasPriFileInfo || currentLineHasWaiver || !_regexUsageOfFileInfo.IsMatch( codeText ) );
                            Assert.IsTrue( hasPriFileSystemInfo || currentLineHasWaiver || !_regexUsageOfFileSystemInfo.IsMatch( codeText ) );
                        }
                    }
                }
            }
        }
    }
0reactions
Sicos1977commented, Jan 26, 2018

I’m going to close this issue since I’m not planning to change the MSGReader to support very long (because you are the only one who asked for it) filenames, because this probably gives issues elsewhere. If you want to get this into MSGReader then implement it and send me a pull request.

Read more comments on GitHub >

github_iconTop Results From Across the Web

How do I change how long file & folder names can be
It depends on the length of the complete path to the file (such as C:\Program Files\filename.txt). Windows limits a single path to 260 ......
Read more >
Understanding long folder and file names in Windows
The simplest solution is to shrink the Folder or Sub-Folder(s) name(s) and leave the actual File name alone.
Read more >
Fixing long Filenames
When it comes to names and paths, folders (directories) and filenames are exactly the same: all rules that apply to filenames also apply...
Read more >
Enable Long File Name Support in Windows 10
Enable Long File Name Support in Windows 10 · Start the registry editor (regedit.exe) · Navigate to HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\ ...
Read more >
How to copy files that have too long of a filepath in Windows?
(if file names are too long) First try to zip/rar/7z them with an archive application and then copy the archive file to your...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found