locked
C# code to check if two filenames are the same RRS feed

  • Question

  • Hi guys,

    I need to create a function that determines whether two filenames are equivalent. On the face of it this sounds simple (a == b right?) but it's more complicated than that.

    Firstly, there's the issue of case sensitivity. Ok that's easy. But then there's things like subst drives, UNC paths vs. direct shares, etc.

    "\\server\fileshare\file.txt" could actually be equivalent to "s:\Accounts\File.TXT". So could "q:\File.Txt" and "y:\File.txt" if Q: was a subst drive.

    There's also the issue of long file names vs. short file names. They both point to the same file.

    Is there a built-in C# function for testing for filename equivalence or perhaps a Win32 call? Alternatively, do I need to write one?

    I remember years ago TurboPower Software had a function in Pascal for this. Their approach was to remember the 'archive flag' on the file, toggle it using the first filename and then test it again using the second filename. If the archive flag changed, they assumed the two filenames pointed to the same place. (Of course they then set the archive flag back to its original state).

    Is this a fair approach?

    Your comments are greatly appreciated.

    Regards,

    Sigol.


    Thursday, December 18, 2008 2:57 PM

Answers

  • This might work:

    using System; 
    using System.Runtime.InteropServices; 
    using System.ComponentModel; 
     
    namespace Demo 
        public class Program 
        { 
            [STAThread] 
            public static void Main() 
            { 
                try 
                { 
                    Console.WriteLine(GetUniversalName(@"<Your test filename here>")); 
                } 
     
                catch (Exception ex) 
                { 
                    Console.WriteLine("Exception: " + ex); 
                } 
            } 
     
            [DllImport("mpr.dll")] 
            [return: MarshalAs(UnmanagedType.U4)] 
            static extern int WNetGetUniversalName 
            ( 
                string lpLocalPath, 
                [MarshalAs(UnmanagedType.U4)] int dwInfoLevel, 
                IntPtr lpBuffer, 
                [MarshalAs(UnmanagedType.U4)] ref int lpBufferSize 
            ); 
             
            public static string GetUniversalName(string localPath) 
            { 
                string retVal = null
                IntPtr buffer = IntPtr.Zero;    // The pointer in memory to the structure. 
     
                try 
                { 
                    // Pass IntPtr.Size because the API doesn't like null, even though size is zero. 
                    // We know that IntPtr.Size will be aligned correctly. 
     
                    int size = 0
                    int apiRetVal = WNetGetUniversalName(localPath, UNIVERSAL_NAME_INFO_LEVEL, (IntPtr)IntPtr.Size, ref size); 
     
                    if (apiRetVal != ERROR_MORE_DATA) 
                    { 
                        throw new Win32Exception(apiRetVal); 
                    } 
     
                    buffer = Marshal.AllocCoTaskMem(size);  // Allocate the memory. 
                    apiRetVal = WNetGetUniversalName(localPath, UNIVERSAL_NAME_INFO_LEVEL, buffer, ref size); 
     
                    if (apiRetVal != NOERROR) 
                    { 
                        throw new Win32Exception(apiRetVal); 
                    } 
     
                    // Now get the string.  It's all in the same buffer, but the pointer is first,  
                    // so offset the pointer by IntPtr.Size and pass to PtrToStringAnsi 
                     
                    retVal = Marshal.PtrToStringAnsi(new IntPtr(buffer.ToInt64() + IntPtr.Size), size); 
     
                    int actualLength = retVal.IndexOf('\0');  // Returned string has a bunch of 0's at the end. 
     
                    if (actualLength >= 0)  // Remove everything from the first '\0' onwards. 
                    { 
                        retVal = retVal.Substring(0, actualLength); 
                    } 
                } 
     
                finally 
                { 
                    Marshal.FreeCoTaskMem(buffer); 
                } 
     
                return retVal; 
            } 
     
            private const int UNIVERSAL_NAME_INFO_LEVEL = 0x00000001
            private const int REMOTE_NAME_INFO_LEVEL = 0x00000002
            private const int ERROR_MORE_DATA = 234
            private const int NOERROR = 0
        } 
     

    • Proposed as answer by Matthew Watson Thursday, December 18, 2008 3:44 PM
    • Edited by Matthew Watson Thursday, December 18, 2008 4:02 PM
    • Marked as answer by sigol Thursday, December 18, 2008 7:20 PM
    Thursday, December 18, 2008 3:43 PM
  • Deleted
    • Marked as answer by sigol Thursday, December 18, 2008 7:21 PM
    Thursday, December 18, 2008 4:00 PM

All replies

  • This might work:

    using System; 
    using System.Runtime.InteropServices; 
    using System.ComponentModel; 
     
    namespace Demo 
        public class Program 
        { 
            [STAThread] 
            public static void Main() 
            { 
                try 
                { 
                    Console.WriteLine(GetUniversalName(@"<Your test filename here>")); 
                } 
     
                catch (Exception ex) 
                { 
                    Console.WriteLine("Exception: " + ex); 
                } 
            } 
     
            [DllImport("mpr.dll")] 
            [return: MarshalAs(UnmanagedType.U4)] 
            static extern int WNetGetUniversalName 
            ( 
                string lpLocalPath, 
                [MarshalAs(UnmanagedType.U4)] int dwInfoLevel, 
                IntPtr lpBuffer, 
                [MarshalAs(UnmanagedType.U4)] ref int lpBufferSize 
            ); 
             
            public static string GetUniversalName(string localPath) 
            { 
                string retVal = null
                IntPtr buffer = IntPtr.Zero;    // The pointer in memory to the structure. 
     
                try 
                { 
                    // Pass IntPtr.Size because the API doesn't like null, even though size is zero. 
                    // We know that IntPtr.Size will be aligned correctly. 
     
                    int size = 0
                    int apiRetVal = WNetGetUniversalName(localPath, UNIVERSAL_NAME_INFO_LEVEL, (IntPtr)IntPtr.Size, ref size); 
     
                    if (apiRetVal != ERROR_MORE_DATA) 
                    { 
                        throw new Win32Exception(apiRetVal); 
                    } 
     
                    buffer = Marshal.AllocCoTaskMem(size);  // Allocate the memory. 
                    apiRetVal = WNetGetUniversalName(localPath, UNIVERSAL_NAME_INFO_LEVEL, buffer, ref size); 
     
                    if (apiRetVal != NOERROR) 
                    { 
                        throw new Win32Exception(apiRetVal); 
                    } 
     
                    // Now get the string.  It's all in the same buffer, but the pointer is first,  
                    // so offset the pointer by IntPtr.Size and pass to PtrToStringAnsi 
                     
                    retVal = Marshal.PtrToStringAnsi(new IntPtr(buffer.ToInt64() + IntPtr.Size), size); 
     
                    int actualLength = retVal.IndexOf('\0');  // Returned string has a bunch of 0's at the end. 
     
                    if (actualLength >= 0)  // Remove everything from the first '\0' onwards. 
                    { 
                        retVal = retVal.Substring(0, actualLength); 
                    } 
                } 
     
                finally 
                { 
                    Marshal.FreeCoTaskMem(buffer); 
                } 
     
                return retVal; 
            } 
     
            private const int UNIVERSAL_NAME_INFO_LEVEL = 0x00000001
            private const int REMOTE_NAME_INFO_LEVEL = 0x00000002
            private const int ERROR_MORE_DATA = 234
            private const int NOERROR = 0
        } 
     

    • Proposed as answer by Matthew Watson Thursday, December 18, 2008 3:44 PM
    • Edited by Matthew Watson Thursday, December 18, 2008 4:02 PM
    • Marked as answer by sigol Thursday, December 18, 2008 7:20 PM
    Thursday, December 18, 2008 3:43 PM
  • I corrected that error in the original post now. I don't know how that happened, since I copied and pasted the code from one of my projects which compiled ok...
    Thursday, December 18, 2008 4:03 PM
  • Matthew and Ronald - thank you very much for your prompt help. Very much appreciated!
    Thursday, December 18, 2008 7:20 PM