Related
I try to add some new property in existing category for selected NavisWorks ModelItem`s
There is not so many example over network, and it base on same COM approach.
However there special method to add property available in API.
Only issue that objects is locked.
Is there any way to unlock it?
using ANA = Autodesk.Navisworks.Api;
...
private void addProperty(string category, string prop, string value)
{
var oDoc = Autodesk.Navisworks.Api.Application.ActiveDocument;
ModelItemCollection selectionModelItems = new ModelItemCollection();
ANA.Application.ActiveDocument.CurrentSelection.SelectedItems.CopyTo(selectionModelItems);
//Clear the current selection
ANA.Application.ActiveDocument.CurrentSelection.Clear();
try
{
foreach (ModelItem m in selectionModelItems)
{
foreach (PropertyCategory p in m.PropertyCategories)
{
if (p.DisplayName != category) continue;
var property = new DataProperty(prop, prop, new VariantData(value));
p.Properties.Add(property);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
result of execution:
Properties and categories which are created by Navisworks are read-only.
You can not add or modify properties or categories which are created by Navisworks.
You can only create or add user-defined properties - using COM.
See:
https://adndevblog.typepad.com/aec/2012/05/create-attributes-and-properties-for-model-objects-using-net-api.html
https://adndevblog.typepad.com/aec/2012/08/addmodifyremove-custom-attribute-using-com-api.html
https://forums.autodesk.com/t5/navisworks-api/navisworks-api-add-user-data-tab/td-p/2916866
Here is a code snippet (copied from xiaodong.liang forum post mentioned above) which shows how to add a user-defined property using COM:
private void addProperty() {
ComApi.InwOpState10 state;
state = ComApiBridge.ComApiBridge.State;
ModelItemCollection modelItemCollectionIn = new ModelItemCollection(Autodesk.Navisworks.Api.Application.ActiveDocument.CurrentSelection.SelectedItems);
ComApi.InwOpSelection comSelectionOut =
ComApiBridge.ComApiBridge.ToInwOpSelection(modelItemCollectionIn);
ComApi.InwSelectionPathsColl oPaths = comSelectionOut.Paths();
ComApi.InwOaPath3 oPath = (ComApi.InwOaPath3) oPaths.Last();
ComApi.InwGUIPropertyNode2 propn = (ComApi.InwGUIPropertyNode2) state.GetGUIPropertyNode(oPath, true);
ComApi.InwOaPropertyVec newPvec = (ComApi.InwOaPropertyVec) state.ObjectFactory(Autodesk.Navisworks.Api.Interop.ComApi.nwEObjectType.eObjectType_nwOaPropertyVec, null, null);
ComApi.InwOaProperty newP = (ComApi.InwOaProperty) state.ObjectFactory(Autodesk.Navisworks.Api.Interop.ComApi.nwEObjectType.eObjectType_nwOaProperty, null, null);
newP.name = "LXD_Property_Name";
newP.UserName = "LXD_Property_UserName";
newP.value = "LXD_Property_Value";
newPvec.Properties().Add(newP);
propn.SetUserDefined(0, "LXD_PropertyTab_Name", "LXD_PropertyTab_InteralName", newPvec);
}
I'm trying to develop a plugin for Visual Studio 2015. I have command that gets added to the context menu when a project is right clicked and I can get the project that was right clicked. Now what I am trying to do is determine if the project contains a class that implements a certain interface. So my first step is to get the classes in the project. So I did something like this:
protected IEnumerable<EnvDTE.CodeClass> GetClasses(EnvDTE.CodeElements elements,
EnvDTE.Project project)
{
foreach (EnvDTE.CodeElement element in elements)
{
System.Diagnostics.Debug.WriteLine(element.InfoLocation);
var cls = element as EnvDTE.CodeClass;
if (cls != null)
{
yield return cls;
}
var ns = element as EnvDTE.CodeNamespace;
if (ns != null)
{
foreach (var childCls in GetClasses(ns.Members, project))
{
yield return childCls;
}
}
}
}
So this will go through and pull out classes. The problem is that it will go through everything that is referenced include BCL classes. I thought using InfoLocation might help, but everything returns vsCMInfoLocationExternal (presumably because in the context where the plugin is running, they are external). I've tried things like element.ProjectItem and element.ProjectItem.ContainingProject and element.Parent with the hope of comparing that to the parent project, but those all throw System.Runtime.InteropServices.COMException. So is there a way, given that I know the project, to determine if a particular CodeElement is part of the project, or just referenced by the project?
EDIT: The best I've been able to come up with so far is to do this, first get the default namespace for the project:
var defaultNS = project.Properties.Item("DefaultNamespace").Value.ToString();
And then I can do this:
if (ns != null && ns.Name == defaultNS)
So now I won't go drilling down into System, which is good. The only problem would be if a project had multiple namespaces that I wanted to search. I can't figure out if there is a way to get a list of namespaces that are defined in the project.
Edit: The suggested dupe deals with Type so isn't entirely relevant.
This may suit your needs or it may not but this is what I used to parse code elements and figure out if the definition is in the solution or if it comes in via a reference. There is no way to know if the reference is 3rd party vs BCL however. Some code removed for brevity since this is inside an API and hard to fully break out. You could add a trick once you have the types full name and know its a reference where you reflect all dlls that are signed with the Microsoft key for the type name, if you find one its bcl, otherwise it probably is not.
public static string CodeElementAsTypeFullName(EnvDTE80.CodeElement2 element)
{
if (element == null)
throw new ArgumentNullException(nameof(element));
if (element.Kind == vsCMElement.vsCMElementClass
|| element.Kind == vsCMElement.vsCMElementEnum
|| element.Kind == vsCMElement.vsCMElementStruct)
return element.FullName;
else
return ((dynamic)element).Type.AsFullName;
}
protected void FlattenElement(EnvDTE80.CodeElement2 element)
{
try
{
string varType = CodeElementAsTypeFullName(element);
switch (element.Kind)
{
case vsCMElement.vsCMElementVariable:
case vsCMElement.vsCMElementProperty:
{
EnvDTE80.CodeElement2 defined = null;
///this is API, basically a collection of all the files in the solution with all class/enum/stuct defs parsed out into collections.
foreach (SquishFile file in this.solutionFiles)
{
//next line goes through each solution file one by one to figure out if the file defines the class/enum/struct definition.
defined = file.ContainsCodeElement(varType);
if (defined != null)
break;
}
if (defined != null)
{
if (defined.Kind == vsCMElement.vsCMElementClass
|| defined.Kind == vsCMElement.vsCMElementStruct
|| defined.Kind == vsCMElement.vsCMElementEnum)
//THE ITEM IS DEFINED LOCALLY!
}else
//the item is a reference
}
}
}
public class SquishFile
{
public ConcurrentBag<CodeClass> ClassDefinitions = new ConcurrentBag<CodeClass>();
public ConcurrentBag<CodeEnum> EnumDefinitions = new ConcurrentBag<CodeEnum>();
public ConcurrentBag<CodeStruct> StructDefinitions = new ConcurrentBag<CodeStruct>();
protected ProjectItem _projectItem;
public ProjectItem ProjectItem { get { return _projectItem; } }
public SquishFile(ProjectItem projectItem)
{
if (projectItem.FileCodeModel == null)
throw new Exception("Cannot make a squish file out of a project item with no FileCodeModel!");
_projectItem = projectItem;
foreach (EnvDTE80.CodeElement2 ele in projectItem.FileCodeModel.CodeElements)
Discovery(ele);
}
public EnvDTE80.CodeElement2 ContainsCodeElement(string fullName)
{
foreach(EnvDTE80.CodeElement2 ele in ClassDefinitions)
if (ele.FullName.Equals(fullName))
return ele;
foreach (EnvDTE80.CodeElement2 ele in EnumDefinitions)
if (ele.FullName.Equals(fullName))
return ele;
foreach (EnvDTE80.CodeElement2 ele in StructDefinitions)
if (ele.FullName.Equals(fullName))
return ele;
return null;
}
protected void Discovery(EnvDTE80.CodeElement2 element)
{
if (element.IsCodeType && element.Kind == vsCMElement.vsCMElementClass)
this.ClassDefinitions.Add(element as EnvDTE80.CodeClass2);
else if (element.IsCodeType && element.Kind == vsCMElement.vsCMElementEnum)
this.EnumDefinitions.Add(element as EnvDTE.CodeEnum);
else if (element.IsCodeType && element.Kind == vsCMElement.vsCMElementStruct)
this.StructDefinitions.Add(element as EnvDTE80.CodeStruct2);
foreach (EnvDTE80.CodeElement2 ele in element.Children)
Discovery(ele);
}
}
If I have two DirectoryInfo objects, how can I compare them for semantic equality? For example, the following paths should all be considered equal to C:\temp:
C:\temp
C:\temp\
C:\temp\.
C:\temp\x\..\..\temp\.
The following may or may not be equal to C:\temp:
\temp if the current working directory is on drive C:\
temp if the current working directory is C:\
C:\temp.
C:\temp...\
If it's important to consider the current working directory, I can figure that out myself, so that's not that important. Trailing dots are stripped in windows, so those paths really should be equal - but they aren't stripped in unix, so under mono I'd expect other results.
Case sensitivity is optional. The paths may or may not exist, and the user may or may not have permissions to the path - I'd prefer a fast robust method that doesn't require any I/O (so no permission checking), but if there's something built-in I'd be happy with anything "good enough" too...
I realize that without I/O it's not possible to determine whether some intermediate storage layer happens to have mapped the same storage to the same file (and even with I/O, when things get messy enough it's likely impossible). However, it should be possible to at least positively identify paths that are equivalent, regardless of the underlying filesystem, i.e. paths that necessarily would resolve to the same file (if it exists) on all possible file-systems of a given type. The reason this is sometimes useful is (A) because I certainly want to check this first, before doing I/O, (B) I/O sometimes triggers problematic side-effects, and (C) various other software components sometimes mangle paths provided, and it's helpful to be able to compare in a way that's insensitive to most common transformations of equivalent paths, and finally (D) to prepare deployments it's useful to do some sanity checks beforehand, but those occur before the to-be-deployed-on system is even accessible.
GetFullPath seems to do the work, except for case difference (Path.GetFullPath("test") != Path.GetFullPath("TEST")) and trailing slash.
So, the following code should work fine:
String.Compare(
Path.GetFullPath(path1).TrimEnd('\\'),
Path.GetFullPath(path2).TrimEnd('\\'),
StringComparison.InvariantCultureIgnoreCase)
Or, if you want to start with DirectoryInfo:
String.Compare(
dirinfo1.FullName.TrimEnd('\\'),
dirinfo2.FullName.TrimEnd('\\'),
StringComparison.InvariantCultureIgnoreCase)
From this answer, this method can handle a few edge cases:
public static string NormalizePath(string path)
{
return Path.GetFullPath(new Uri(path).LocalPath)
.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
.ToUpperInvariant();
}
More details in the original answer. Call it like:
bool pathsEqual = NormalizePath(path1) == NormalizePath(path2);
Should work for both file and directory paths.
The question has been edited and clarified since it was originally asked and since this answer was originally posted. As the question currently stands, this answer below is not a correct answer. Essentially, the current question is asking for a purely textual path comparison, which is quite different from wanting to determine if two paths resolve to the same file system object. All the other answers, with the exception of Igor Korkhov's, are ultimately based on a textual comparison of two names.
If one actually wants to know when two paths resolve to the same file system object, you must do some IO. Trying to get two "normalized" names, that take in to account the myriad of possible ways of referencing the same file object, is next to impossible. There are issues such as: junctions, symbolic links, network file shares (referencing the same file object in different manners), etc. etc. In fact, every single answer above, with the exception of Igor Korkhov's, will absolutely give incorrect results in certain circumstances to the question "do these two paths reference the same file system object. (e.g. junctions, symbolic links, directory links, etc.)
The question specifically requested that the solution not require any I/O, but if you are going to deal with networked paths, you will absolutely need to do IO: there are cases where it is simply not possible to determine from any local path-string manipulation, whether two file references will reference the same physical file. (This can be easily understood as follows. Suppose a file server has a windows directory junction somewhere within a shared subtree. In this case, a file can be referenced either directly, or through the junction. But the junction resides on, and is resolved by, the file server, and so it is simply impossible for a client to determine, purely through local information, that the two referencing file names refer to the same physical file: the information is simply not available locally to the client. Thus one must absolutely do some minimal IO - e.g. open two file object handles - to determine if the references refer to the same physical file.)
The following solution does some IO, though very minimal, but correctly determines whether two file system references are semantically identical, i.e. reference the same file object. (if neither file specification refers to a valid file object, all bets are off):
public static bool AreDirsEqual(string dirName1, string dirName2, bool resolveJunctionaAndNetworkPaths = true)
{
if (string.IsNullOrEmpty(dirName1) || string.IsNullOrEmpty(dirName2))
return dirName1==dirName2;
dirName1 = NormalizePath(dirName1); //assume NormalizePath normalizes/fixes case and path separators to Path.DirectorySeparatorChar
dirName2 = NormalizePath(dirName2);
int i1 = dirName1.Length;
int i2 = dirName2.Length;
do
{
--i1; --i2;
if (i1 < 0 || i2 < 0)
return i1 < 0 && i2 < 0;
} while (dirName1[i1] == dirName2[i2]);//If you want to deal with international character sets, i.e. if NormalixePath does not fix case, this comparison must be tweaked
if( !resolveJunctionaAndNetworkPaths )
return false;
for(++i1, ++i2; i1 < dirName1.Length; ++i1, ++i2)
{
if (dirName1[i1] == Path.DirectorySeparatorChar)
{
dirName1 = dirName1.Substring(0, i1);
dirName2 = dirName1.Substring(0, i2);
break;
}
}
return AreFileSystemObjectsEqual(dirName1, dirName2);
}
public static bool AreFileSystemObjectsEqual(string dirName1, string dirName2)
{
//NOTE: we cannot lift the call to GetFileHandle out of this routine, because we _must_
// have both file handles open simultaneously in order for the objectFileInfo comparison
// to be guaranteed as valid.
using (SafeFileHandle directoryHandle1 = GetFileHandle(dirName1), directoryHandle2 = GetFileHandle(dirName2))
{
BY_HANDLE_FILE_INFORMATION? objectFileInfo1 = GetFileInfo(directoryHandle1);
BY_HANDLE_FILE_INFORMATION? objectFileInfo2 = GetFileInfo(directoryHandle2);
return objectFileInfo1 != null
&& objectFileInfo2 != null
&& (objectFileInfo1.Value.FileIndexHigh == objectFileInfo2.Value.FileIndexHigh)
&& (objectFileInfo1.Value.FileIndexLow == objectFileInfo2.Value.FileIndexLow)
&& (objectFileInfo1.Value.VolumeSerialNumber == objectFileInfo2.Value.VolumeSerialNumber);
}
}
static SafeFileHandle GetFileHandle(string dirName)
{
const int FILE_ACCESS_NEITHER = 0;
//const int FILE_SHARE_READ = 1;
//const int FILE_SHARE_WRITE = 2;
//const int FILE_SHARE_DELETE = 4;
const int FILE_SHARE_ANY = 7;//FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE
const int CREATION_DISPOSITION_OPEN_EXISTING = 3;
const int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
return CreateFile(dirName, FILE_ACCESS_NEITHER, FILE_SHARE_ANY, System.IntPtr.Zero, CREATION_DISPOSITION_OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, System.IntPtr.Zero);
}
static BY_HANDLE_FILE_INFORMATION? GetFileInfo(SafeFileHandle directoryHandle)
{
BY_HANDLE_FILE_INFORMATION objectFileInfo;
if ((directoryHandle == null) || (!GetFileInformationByHandle(directoryHandle.DangerousGetHandle(), out objectFileInfo)))
{
return null;
}
return objectFileInfo;
}
[DllImport("kernel32.dll", EntryPoint = "CreateFileW", CharSet = CharSet.Unicode, SetLastError = true)]
static extern SafeFileHandle CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode,
IntPtr SecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool GetFileInformationByHandle(IntPtr hFile, out BY_HANDLE_FILE_INFORMATION lpFileInformation);
[StructLayout(LayoutKind.Sequential)]
public struct BY_HANDLE_FILE_INFORMATION
{
public uint FileAttributes;
public System.Runtime.InteropServices.ComTypes.FILETIME CreationTime;
public System.Runtime.InteropServices.ComTypes.FILETIME LastAccessTime;
public System.Runtime.InteropServices.ComTypes.FILETIME LastWriteTime;
public uint VolumeSerialNumber;
public uint FileSizeHigh;
public uint FileSizeLow;
public uint NumberOfLinks;
public uint FileIndexHigh;
public uint FileIndexLow;
};
Note that in the above code I have included two lines like dirName1 = NormalizePath(dirName1); and have not specified what the function NormalizePath is. NormalizePath can be any path-normalization function - many have been provided in answers elsewhere in this question. Providing a reasonable NormalizePath function means that AreDirsEqual will give a reasonable answer even when the two input paths refer to non-existent file system objects, i.e. to paths that you simply want to compare on a string-level. ( Ishmaeel's comment above should be paid heed as well, and this code does not do that...)
(There may be subtle permissions issues with this code, if a user has only traversal permissions on some initial directories, I am not sure if the file system accesses required by AreFileSystemObjectsEqual are permitted. The parameter resolveJunctionaAndNetworkPaths at least allows the user to revert to pure textual comparison in this case...)
The idea for this came from a reply by Warren Stevens in a similar question I posted on SuperUser: https://superuser.com/a/881966/241981
There are some short comes to the implementation of paths in .NET. There are many complaints about it. Patrick Smacchia, the creator of NDepend, published an open source library that enables handling of common and complex path operations. If you do a lot of compare operations on paths in your application, this library might be useful to you.
It seems that P/Invoking GetFinalPathNameByHandle() would be the most reliable solution.
UPD: Oops, I didn't take into account your desire not to use any I/O
Microsoft has implemented similar methods, although they are not as useful as the answers above:
PathUtil.ArePathsEqual Method (which is just return string.Equals(path1, path2, StringComparison.OrdinalIgnoreCase);)
PathUtil.Normalize Method
PathUtil.NormalizePath Method (which is just return PathUtil.Normalize(path);)
System.IO.Path.GetFullPath(pathA).Equals(System.IO.Path.GetFullPath(PathB));
The "Name" properties are equal. Take:
DirectoryInfo dir1 = new DirectoryInfo("C:\\Scratch");
DirectoryInfo dir2 = new DirectoryInfo("C:\\Scratch\\");
DirectoryInfo dir3 = new DirectoryInfo("C:\\Scratch\\4760");
DirectoryInfo dir4 = new DirectoryInfo("C:\\Scratch\\4760\\..\\");
dir1.Name == dir2.Name and dir2.Name == dir4.Name ("Scratch" in this case. dir3 == "4760".) It's only the FullName properties that are different.
You might be able to do a recursive method to examine the Name properties of each parent given your two DirectoryInfo classes to ensure the complete path is the same.
EDIT: does this work for your situation? Create a Console Application and paste this over the entire Program.cs file. Provide two DirectoryInfo objects to the AreEquals() function and it will return True if they're the same directory. You might be able to tweak this AreEquals() method to be an extension method on DirectoryInfo if you like, so you could just do myDirectoryInfo.IsEquals(myOtherDirectoryInfo);
using System;
using System.Diagnostics;
using System.IO;
using System.Collections.Generic;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(AreEqual(
new DirectoryInfo("C:\\Scratch"),
new DirectoryInfo("C:\\Scratch\\")));
Console.WriteLine(AreEqual(
new DirectoryInfo("C:\\Windows\\Microsoft.NET\\Framework"),
new DirectoryInfo("C:\\Windows\\Microsoft.NET\\Framework\\v3.5\\1033\\..\\..")));
Console.WriteLine(AreEqual(
new DirectoryInfo("C:\\Scratch\\"),
new DirectoryInfo("C:\\Scratch\\4760\\..\\..")));
Console.WriteLine("Press ENTER to continue");
Console.ReadLine();
}
private static bool AreEqual(DirectoryInfo dir1, DirectoryInfo dir2)
{
DirectoryInfo parent1 = dir1;
DirectoryInfo parent2 = dir2;
/* Build a list of parents */
List<string> folder1Parents = new List<string>();
List<string> folder2Parents = new List<string>();
while (parent1 != null)
{
folder1Parents.Add(parent1.Name);
parent1 = parent1.Parent;
}
while (parent2 != null)
{
folder2Parents.Add(parent2.Name);
parent2 = parent2.Parent;
}
/* Now compare the lists */
if (folder1Parents.Count != folder2Parents.Count)
{
// Cannot be the same - different number of parents
return false;
}
bool equal = true;
for (int i = 0; i < folder1Parents.Count && i < folder2Parents.Count; i++)
{
equal &= folder1Parents[i] == folder2Parents[i];
}
return equal;
}
}
}
You can use Minimatch, a port of Node.js' minimatch.
var mm = new Minimatcher(searchPattern, new Options { AllowWindowsPaths = true });
if (mm.IsMatch(somePath))
{
// The path matches! Do some cool stuff!
}
var matchingPaths = mm.Filter(allPaths);
See why the AllowWindowsPaths = true option is necessary:
On Windows-style paths
Minimatch's syntax was designed for Linux-style paths (with forward slashes only). In particular, it uses the backslash as an escape character, so it cannot simply accept Windows-style paths. My C# version preserves this behavior.
To suppress this, and allow both backslashes and forward slashes as path separators (in patterns or input), set the AllowWindowsPaths option:
var mm = new Minimatcher(searchPattern, new Options { AllowWindowsPaths = true });
Passing this option will disable escape characters entirely.
Nuget: http://www.nuget.org/packages/Minimatch/
GitHub: https://github.com/SLaks/Minimatch
using System;
using System.Collections.Generic;
using System.Text;
namespace EventAnalysis.IComparerImplementation
{
public sealed class FSChangeElemComparerByPath : IComparer<FSChangeElem>
{
public int Compare(FSChangeElem firstPath, FSChangeElem secondPath)
{
return firstPath.strObjectPath == null ?
(secondPath.strObjectPath == null ? 0 : -1) :
(secondPath.strObjectPath == null ? 1 : ComparerWrap(firstPath.strObjectPath, secondPath.strObjectPath));
}
private int ComparerWrap(string stringA, string stringB)
{
int length = 0;
int start = 0;
List<string> valueA = new List<string>();
List<string> valueB = new List<string>();
ListInit(ref valueA, stringA);
ListInit(ref valueB, stringB);
if (valueA.Count != valueB.Count)
{
length = (valueA.Count > valueB.Count)
? valueA.Count : valueB.Count;
if (valueA.Count != length)
{
for (int i = 0; i < length - valueA.Count; i++)
{
valueA.Add(string.Empty);
}
}
else
{
for (int i = 0; i < length - valueB.Count; i++)
{
valueB.Add(string.Empty);
}
}
}
else
length = valueA.Count;
return RecursiveComparing(valueA, valueB, length, start);
}
private void ListInit(ref List<string> stringCollection, string stringToList)
{
foreach (string s in stringToList.Remove(0, 2).Split('\\'))
{
stringCollection.Add(s);
}
}
private int RecursiveComparing(List<string> valueA, List<string> valueB, int length, int start)
{
int result = 0;
if (start != length)
{
if (valueA[start] == valueB[start])
{
result = RecursiveComparing(valueA, valueB, length, ++start);
}
else
{
result = String.Compare(valueA[start], valueB[start]);
}
}
else
return 0;
return result;
}
}
}
I used recursion to solve this problem for myself.
public bool PathEquals(string Path1, string Path2)
{
FileInfo f1 = new FileInfo(Path1.Trim('\\','/','.'));
FileInfo f2 = new FileInfo(Path2.Trim('\\', '/','.'));
if(f1.Name.ToLower() == f2.Name.ToLower())
{
return DirectoryEquals(f1.Directory, f2.Directory);
}
else
{
return false;
}
}
public bool DirectoryEquals(DirectoryInfo d1, DirectoryInfo d2)
{
if(d1.Name.ToLower() == d2.Name.ToLower())
{
if((d1.Parent != null) && (d2.Parent != null))
{
return DirectoryEquals(d1.Parent, d2.Parent);
}
else
{
return true;//C:\Temp1\Temp2 equals \Temp1\Temp2
//return (d1.Parent == null) && (d2.Parent == null);//C:\Temp1\Temp2 does not equal \Temp1\Temp2
}
}
else
{
return false;
}
}
Note: new FileInfo(path) returns a valid FileInfo even if path is not a file (the name field is equal to the directory name)
Thank you, #Andy Shellam and #VladV and #Eamon Nerbonne
I found the other solution:
private static bool AreEqual(DirectoryInfo dir1, DirectoryInfo dir2)
{
return AreEqual(dir1.FullName, dir2.FullName);
}
private static bool AreEqual(string folderPath1, string folderPath2)
{
folderPath1 = Path.GetFullPath(folderPath1);
folderPath2 = Path.GetFullPath(folderPath2);
if (folderPath1.Length == folderPath2.Length)
{
return string.Equals(folderPath1, folderPath2/*, StringComparison.OrdinalIgnoreCase*/);
}
else if (folderPath1.Length == folderPath2.Length + 1 && IsEndWithAltDirectorySeparatorChar(folderPath1))
{
// folderPath1 = #"F:\temp\"
// folderPath2 = #"F:\temp"
return folderPath1.Contains(folderPath2 /*, StringComparison.OrdinalIgnoreCase*/);
}
else if (folderPath1.Length + 1 == folderPath2.Length && IsEndWithAltDirectorySeparatorChar(folderPath2))
{
// folderPath1 = #"F:\temp"
// folderPath2 = #"F:\temp\"
return folderPath2.Contains(folderPath1 /*, StringComparison.OrdinalIgnoreCase*/);
}
return false;
static bool IsEndWithAltDirectorySeparatorChar(string path)
{
var lastChar = path[path.Length - 1];
return lastChar == Path.DirectorySeparatorChar;
}
}
It can work well.
static void Main(string[] args)
{
Console.WriteLine(AreEqual(
new DirectoryInfo("C:\\Scratch"),
new DirectoryInfo("C:\\Scratch\\")));
Console.WriteLine(AreEqual(
new DirectoryInfo("C:\\Windows\\Microsoft.NET\\Framework"),
new DirectoryInfo("C:\\Windows\\Microsoft.NET\\Framework\\v3.5\\1033\\..\\..")));
Console.WriteLine(AreEqual(
new DirectoryInfo("C:\\Scratch\\"),
new DirectoryInfo("C:\\Scratch\\4760\\..\\..")));
Debug.WriteLine(AreEqual(#"C:/Temp", #"C:\Temp2")); // False
Debug.WriteLine(AreEqual(#"C:\Temp\", #"C:\Temp2"));// False
Debug.WriteLine(AreEqual(#"C:\Temp\", #"C:\Temp")); // True
Debug.WriteLine(AreEqual(#"C:\Temp/", #"C:\Temp")); // True
Debug.WriteLine(AreEqual(#"C:/Temp/", #"C:\Temp\"));// True
Console.WriteLine("Press ENTER to continue");
Console.ReadLine();
}
bool equals = myDirectoryInfo1.FullName == myDirectoryInfo2.FullName;
?
bool Equals(string path1, string path2)
{
return new Uri(path1) == new Uri(path2);
}
Uri constructor normalizes the path.
In one of my solutions, when I right click a symbol and choose "Go To Implementation" for an object defined in one of the other solution projects, it lists the reference twice and forces me to choose one.
Based on the icons, it appears that one of the items in the list represents the project, and the other represents a dll. It doesn't matter which one I click - it goes to the same source file.
I only have the library reference once in this particular project - it is referencing the project.
What would cause this to happen? Some sort of circular reference issue perhaps?
As far as I can tell, this can also happen if you have a solution with several projects, where a certain project is referenced as project and also as pure file by two other projects in the solution.
Another advice that I can give if something is broken with ReSharper, is to clear the cache.
I had this problem and I just fixed it.
First, try do a Clean Solution and then a Build.
In my case, one rogue Project in my solution was compiled using an older version of the .NET framework than the other Projects, so when Resharper added a reference to my other Projects for me, it must have added it as a dll reference instead of as a Project reference.
My fix was
Upgrade old Project to the same version of .NET framework as the other Projects
Remove references to other Projects from that old Project
Add references to the other Projects again (as Project references this time)
Clean solution
Build solution
Done.
I've found a couple different cases that cause this problem, and got so annoyed that I wrote a little console app to scan my solution and find the problems for me. Here it is for anyone who might find this useful. To run it pass it the path to your solution folder and it will print out the issues on the console. It's very "quick and dirty" but it found the issues for me.
class Program
{
static void Main(string[] args)
{
if (args != null && args.Any())
{
foreach (var s in args)
{
Console.WriteLine("Checking " + s);
DirectoryInfo dir = new DirectoryInfo(s);
var files = dir.GetFiles("*.csproj", SearchOption.AllDirectories);
var projects = files.Select(x => new Project(x)).ToList();
var grouped = projects.GroupBy(x => x.TargetFrameworkVersion);
if(grouped.Count()>1)
{
Console.WriteLine("Solution contains multiple versions of Target Frameworks, this may cause duplicate assemblies in R# cache");
foreach (var group in grouped)
{
Console.WriteLine(group.Key);
foreach (var project in group)
{
Console.WriteLine(project.AssemblyName);
}
}
}
//loop through for debugging
foreach (var project in projects)
{
foreach (var reference in project.References)
{
foreach (var checkProject in projects)
{
if (checkProject.AssemblyName == reference)
{
Console.WriteLine("Reference in" + project.FileName + " referencing " +
reference+" that should be a ProjectReference, this may cause duplicate entries in R# Cache");
}
}
}
}
}
Console.WriteLine("Complete");
Console.ReadLine();
}
else
{
Console.WriteLine("You must provide a path to scan for csproj files");
}
}
}
public class Project
{
public string FileName { get; set; }
public string AssemblyName { get; set; }
public string ProjectGuid { get; set; }
public string TargetFrameworkVersion { get; set; }
public IList<string> References { get; set; }
private FileInfo _file;
private XmlDocument _document;
private XmlNamespaceManager _namespaceManager;
public Project(FileInfo file)
{
_file = file;
FileName = _file.FullName;
_document = new XmlDocument();
_document.Load(_file.FullName);
_namespaceManager = new XmlNamespaceManager(_document.NameTable);
_namespaceManager.AddNamespace("msbld", "http://schemas.microsoft.com/developer/msbuild/2003");
var projectGuidNode = _document.SelectSingleNode("//msbld:ProjectGuid", _namespaceManager);
ProjectGuid = projectGuidNode.InnerText;
var assemblyNameNode = _document.SelectSingleNode("//msbld:AssemblyName", _namespaceManager);
AssemblyName = assemblyNameNode.InnerText;
var targetFrameworkNode = _document.SelectSingleNode("//msbld:TargetFrameworkVersion", _namespaceManager);
TargetFrameworkVersion = targetFrameworkNode.InnerText;
References = new List<string>();
var referenceNodes = _document.SelectNodes("//msbld:Reference", _namespaceManager);
foreach (var node in referenceNodes)
{
var element = (XmlElement) node;
//file references
if (element.HasChildNodes)
{
foreach (var child in element.ChildNodes)
{
var childElement = (XmlElement)child;
if (childElement.Name == "HintPath")
{
var value = childElement.InnerText;
value = value.Substring(value.LastIndexOf("\\") + 1);
value = value.Replace(".dll", "");
References.Add(value);
}
}
}
//gac references
else
{
foreach (var attr in element.Attributes)
{
var attribute = (XmlAttribute)attr;
if (attribute.Name == "Include")
{
var value = attribute.Value;
string reference = value;
if (value.Contains(','))
{
reference = value.Substring(0, value.IndexOf(','));
}
References.Add(reference);
break;
}
}
}
}
}
}
If I have two DirectoryInfo objects, how can I compare them for semantic equality? For example, the following paths should all be considered equal to C:\temp:
C:\temp
C:\temp\
C:\temp\.
C:\temp\x\..\..\temp\.
The following may or may not be equal to C:\temp:
\temp if the current working directory is on drive C:\
temp if the current working directory is C:\
C:\temp.
C:\temp...\
If it's important to consider the current working directory, I can figure that out myself, so that's not that important. Trailing dots are stripped in windows, so those paths really should be equal - but they aren't stripped in unix, so under mono I'd expect other results.
Case sensitivity is optional. The paths may or may not exist, and the user may or may not have permissions to the path - I'd prefer a fast robust method that doesn't require any I/O (so no permission checking), but if there's something built-in I'd be happy with anything "good enough" too...
I realize that without I/O it's not possible to determine whether some intermediate storage layer happens to have mapped the same storage to the same file (and even with I/O, when things get messy enough it's likely impossible). However, it should be possible to at least positively identify paths that are equivalent, regardless of the underlying filesystem, i.e. paths that necessarily would resolve to the same file (if it exists) on all possible file-systems of a given type. The reason this is sometimes useful is (A) because I certainly want to check this first, before doing I/O, (B) I/O sometimes triggers problematic side-effects, and (C) various other software components sometimes mangle paths provided, and it's helpful to be able to compare in a way that's insensitive to most common transformations of equivalent paths, and finally (D) to prepare deployments it's useful to do some sanity checks beforehand, but those occur before the to-be-deployed-on system is even accessible.
GetFullPath seems to do the work, except for case difference (Path.GetFullPath("test") != Path.GetFullPath("TEST")) and trailing slash.
So, the following code should work fine:
String.Compare(
Path.GetFullPath(path1).TrimEnd('\\'),
Path.GetFullPath(path2).TrimEnd('\\'),
StringComparison.InvariantCultureIgnoreCase)
Or, if you want to start with DirectoryInfo:
String.Compare(
dirinfo1.FullName.TrimEnd('\\'),
dirinfo2.FullName.TrimEnd('\\'),
StringComparison.InvariantCultureIgnoreCase)
From this answer, this method can handle a few edge cases:
public static string NormalizePath(string path)
{
return Path.GetFullPath(new Uri(path).LocalPath)
.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
.ToUpperInvariant();
}
More details in the original answer. Call it like:
bool pathsEqual = NormalizePath(path1) == NormalizePath(path2);
Should work for both file and directory paths.
The question has been edited and clarified since it was originally asked and since this answer was originally posted. As the question currently stands, this answer below is not a correct answer. Essentially, the current question is asking for a purely textual path comparison, which is quite different from wanting to determine if two paths resolve to the same file system object. All the other answers, with the exception of Igor Korkhov's, are ultimately based on a textual comparison of two names.
If one actually wants to know when two paths resolve to the same file system object, you must do some IO. Trying to get two "normalized" names, that take in to account the myriad of possible ways of referencing the same file object, is next to impossible. There are issues such as: junctions, symbolic links, network file shares (referencing the same file object in different manners), etc. etc. In fact, every single answer above, with the exception of Igor Korkhov's, will absolutely give incorrect results in certain circumstances to the question "do these two paths reference the same file system object. (e.g. junctions, symbolic links, directory links, etc.)
The question specifically requested that the solution not require any I/O, but if you are going to deal with networked paths, you will absolutely need to do IO: there are cases where it is simply not possible to determine from any local path-string manipulation, whether two file references will reference the same physical file. (This can be easily understood as follows. Suppose a file server has a windows directory junction somewhere within a shared subtree. In this case, a file can be referenced either directly, or through the junction. But the junction resides on, and is resolved by, the file server, and so it is simply impossible for a client to determine, purely through local information, that the two referencing file names refer to the same physical file: the information is simply not available locally to the client. Thus one must absolutely do some minimal IO - e.g. open two file object handles - to determine if the references refer to the same physical file.)
The following solution does some IO, though very minimal, but correctly determines whether two file system references are semantically identical, i.e. reference the same file object. (if neither file specification refers to a valid file object, all bets are off):
public static bool AreDirsEqual(string dirName1, string dirName2, bool resolveJunctionaAndNetworkPaths = true)
{
if (string.IsNullOrEmpty(dirName1) || string.IsNullOrEmpty(dirName2))
return dirName1==dirName2;
dirName1 = NormalizePath(dirName1); //assume NormalizePath normalizes/fixes case and path separators to Path.DirectorySeparatorChar
dirName2 = NormalizePath(dirName2);
int i1 = dirName1.Length;
int i2 = dirName2.Length;
do
{
--i1; --i2;
if (i1 < 0 || i2 < 0)
return i1 < 0 && i2 < 0;
} while (dirName1[i1] == dirName2[i2]);//If you want to deal with international character sets, i.e. if NormalixePath does not fix case, this comparison must be tweaked
if( !resolveJunctionaAndNetworkPaths )
return false;
for(++i1, ++i2; i1 < dirName1.Length; ++i1, ++i2)
{
if (dirName1[i1] == Path.DirectorySeparatorChar)
{
dirName1 = dirName1.Substring(0, i1);
dirName2 = dirName1.Substring(0, i2);
break;
}
}
return AreFileSystemObjectsEqual(dirName1, dirName2);
}
public static bool AreFileSystemObjectsEqual(string dirName1, string dirName2)
{
//NOTE: we cannot lift the call to GetFileHandle out of this routine, because we _must_
// have both file handles open simultaneously in order for the objectFileInfo comparison
// to be guaranteed as valid.
using (SafeFileHandle directoryHandle1 = GetFileHandle(dirName1), directoryHandle2 = GetFileHandle(dirName2))
{
BY_HANDLE_FILE_INFORMATION? objectFileInfo1 = GetFileInfo(directoryHandle1);
BY_HANDLE_FILE_INFORMATION? objectFileInfo2 = GetFileInfo(directoryHandle2);
return objectFileInfo1 != null
&& objectFileInfo2 != null
&& (objectFileInfo1.Value.FileIndexHigh == objectFileInfo2.Value.FileIndexHigh)
&& (objectFileInfo1.Value.FileIndexLow == objectFileInfo2.Value.FileIndexLow)
&& (objectFileInfo1.Value.VolumeSerialNumber == objectFileInfo2.Value.VolumeSerialNumber);
}
}
static SafeFileHandle GetFileHandle(string dirName)
{
const int FILE_ACCESS_NEITHER = 0;
//const int FILE_SHARE_READ = 1;
//const int FILE_SHARE_WRITE = 2;
//const int FILE_SHARE_DELETE = 4;
const int FILE_SHARE_ANY = 7;//FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE
const int CREATION_DISPOSITION_OPEN_EXISTING = 3;
const int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
return CreateFile(dirName, FILE_ACCESS_NEITHER, FILE_SHARE_ANY, System.IntPtr.Zero, CREATION_DISPOSITION_OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, System.IntPtr.Zero);
}
static BY_HANDLE_FILE_INFORMATION? GetFileInfo(SafeFileHandle directoryHandle)
{
BY_HANDLE_FILE_INFORMATION objectFileInfo;
if ((directoryHandle == null) || (!GetFileInformationByHandle(directoryHandle.DangerousGetHandle(), out objectFileInfo)))
{
return null;
}
return objectFileInfo;
}
[DllImport("kernel32.dll", EntryPoint = "CreateFileW", CharSet = CharSet.Unicode, SetLastError = true)]
static extern SafeFileHandle CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode,
IntPtr SecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool GetFileInformationByHandle(IntPtr hFile, out BY_HANDLE_FILE_INFORMATION lpFileInformation);
[StructLayout(LayoutKind.Sequential)]
public struct BY_HANDLE_FILE_INFORMATION
{
public uint FileAttributes;
public System.Runtime.InteropServices.ComTypes.FILETIME CreationTime;
public System.Runtime.InteropServices.ComTypes.FILETIME LastAccessTime;
public System.Runtime.InteropServices.ComTypes.FILETIME LastWriteTime;
public uint VolumeSerialNumber;
public uint FileSizeHigh;
public uint FileSizeLow;
public uint NumberOfLinks;
public uint FileIndexHigh;
public uint FileIndexLow;
};
Note that in the above code I have included two lines like dirName1 = NormalizePath(dirName1); and have not specified what the function NormalizePath is. NormalizePath can be any path-normalization function - many have been provided in answers elsewhere in this question. Providing a reasonable NormalizePath function means that AreDirsEqual will give a reasonable answer even when the two input paths refer to non-existent file system objects, i.e. to paths that you simply want to compare on a string-level. ( Ishmaeel's comment above should be paid heed as well, and this code does not do that...)
(There may be subtle permissions issues with this code, if a user has only traversal permissions on some initial directories, I am not sure if the file system accesses required by AreFileSystemObjectsEqual are permitted. The parameter resolveJunctionaAndNetworkPaths at least allows the user to revert to pure textual comparison in this case...)
The idea for this came from a reply by Warren Stevens in a similar question I posted on SuperUser: https://superuser.com/a/881966/241981
There are some short comes to the implementation of paths in .NET. There are many complaints about it. Patrick Smacchia, the creator of NDepend, published an open source library that enables handling of common and complex path operations. If you do a lot of compare operations on paths in your application, this library might be useful to you.
It seems that P/Invoking GetFinalPathNameByHandle() would be the most reliable solution.
UPD: Oops, I didn't take into account your desire not to use any I/O
Microsoft has implemented similar methods, although they are not as useful as the answers above:
PathUtil.ArePathsEqual Method (which is just return string.Equals(path1, path2, StringComparison.OrdinalIgnoreCase);)
PathUtil.Normalize Method
PathUtil.NormalizePath Method (which is just return PathUtil.Normalize(path);)
System.IO.Path.GetFullPath(pathA).Equals(System.IO.Path.GetFullPath(PathB));
The "Name" properties are equal. Take:
DirectoryInfo dir1 = new DirectoryInfo("C:\\Scratch");
DirectoryInfo dir2 = new DirectoryInfo("C:\\Scratch\\");
DirectoryInfo dir3 = new DirectoryInfo("C:\\Scratch\\4760");
DirectoryInfo dir4 = new DirectoryInfo("C:\\Scratch\\4760\\..\\");
dir1.Name == dir2.Name and dir2.Name == dir4.Name ("Scratch" in this case. dir3 == "4760".) It's only the FullName properties that are different.
You might be able to do a recursive method to examine the Name properties of each parent given your two DirectoryInfo classes to ensure the complete path is the same.
EDIT: does this work for your situation? Create a Console Application and paste this over the entire Program.cs file. Provide two DirectoryInfo objects to the AreEquals() function and it will return True if they're the same directory. You might be able to tweak this AreEquals() method to be an extension method on DirectoryInfo if you like, so you could just do myDirectoryInfo.IsEquals(myOtherDirectoryInfo);
using System;
using System.Diagnostics;
using System.IO;
using System.Collections.Generic;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(AreEqual(
new DirectoryInfo("C:\\Scratch"),
new DirectoryInfo("C:\\Scratch\\")));
Console.WriteLine(AreEqual(
new DirectoryInfo("C:\\Windows\\Microsoft.NET\\Framework"),
new DirectoryInfo("C:\\Windows\\Microsoft.NET\\Framework\\v3.5\\1033\\..\\..")));
Console.WriteLine(AreEqual(
new DirectoryInfo("C:\\Scratch\\"),
new DirectoryInfo("C:\\Scratch\\4760\\..\\..")));
Console.WriteLine("Press ENTER to continue");
Console.ReadLine();
}
private static bool AreEqual(DirectoryInfo dir1, DirectoryInfo dir2)
{
DirectoryInfo parent1 = dir1;
DirectoryInfo parent2 = dir2;
/* Build a list of parents */
List<string> folder1Parents = new List<string>();
List<string> folder2Parents = new List<string>();
while (parent1 != null)
{
folder1Parents.Add(parent1.Name);
parent1 = parent1.Parent;
}
while (parent2 != null)
{
folder2Parents.Add(parent2.Name);
parent2 = parent2.Parent;
}
/* Now compare the lists */
if (folder1Parents.Count != folder2Parents.Count)
{
// Cannot be the same - different number of parents
return false;
}
bool equal = true;
for (int i = 0; i < folder1Parents.Count && i < folder2Parents.Count; i++)
{
equal &= folder1Parents[i] == folder2Parents[i];
}
return equal;
}
}
}
You can use Minimatch, a port of Node.js' minimatch.
var mm = new Minimatcher(searchPattern, new Options { AllowWindowsPaths = true });
if (mm.IsMatch(somePath))
{
// The path matches! Do some cool stuff!
}
var matchingPaths = mm.Filter(allPaths);
See why the AllowWindowsPaths = true option is necessary:
On Windows-style paths
Minimatch's syntax was designed for Linux-style paths (with forward slashes only). In particular, it uses the backslash as an escape character, so it cannot simply accept Windows-style paths. My C# version preserves this behavior.
To suppress this, and allow both backslashes and forward slashes as path separators (in patterns or input), set the AllowWindowsPaths option:
var mm = new Minimatcher(searchPattern, new Options { AllowWindowsPaths = true });
Passing this option will disable escape characters entirely.
Nuget: http://www.nuget.org/packages/Minimatch/
GitHub: https://github.com/SLaks/Minimatch
using System;
using System.Collections.Generic;
using System.Text;
namespace EventAnalysis.IComparerImplementation
{
public sealed class FSChangeElemComparerByPath : IComparer<FSChangeElem>
{
public int Compare(FSChangeElem firstPath, FSChangeElem secondPath)
{
return firstPath.strObjectPath == null ?
(secondPath.strObjectPath == null ? 0 : -1) :
(secondPath.strObjectPath == null ? 1 : ComparerWrap(firstPath.strObjectPath, secondPath.strObjectPath));
}
private int ComparerWrap(string stringA, string stringB)
{
int length = 0;
int start = 0;
List<string> valueA = new List<string>();
List<string> valueB = new List<string>();
ListInit(ref valueA, stringA);
ListInit(ref valueB, stringB);
if (valueA.Count != valueB.Count)
{
length = (valueA.Count > valueB.Count)
? valueA.Count : valueB.Count;
if (valueA.Count != length)
{
for (int i = 0; i < length - valueA.Count; i++)
{
valueA.Add(string.Empty);
}
}
else
{
for (int i = 0; i < length - valueB.Count; i++)
{
valueB.Add(string.Empty);
}
}
}
else
length = valueA.Count;
return RecursiveComparing(valueA, valueB, length, start);
}
private void ListInit(ref List<string> stringCollection, string stringToList)
{
foreach (string s in stringToList.Remove(0, 2).Split('\\'))
{
stringCollection.Add(s);
}
}
private int RecursiveComparing(List<string> valueA, List<string> valueB, int length, int start)
{
int result = 0;
if (start != length)
{
if (valueA[start] == valueB[start])
{
result = RecursiveComparing(valueA, valueB, length, ++start);
}
else
{
result = String.Compare(valueA[start], valueB[start]);
}
}
else
return 0;
return result;
}
}
}
I used recursion to solve this problem for myself.
public bool PathEquals(string Path1, string Path2)
{
FileInfo f1 = new FileInfo(Path1.Trim('\\','/','.'));
FileInfo f2 = new FileInfo(Path2.Trim('\\', '/','.'));
if(f1.Name.ToLower() == f2.Name.ToLower())
{
return DirectoryEquals(f1.Directory, f2.Directory);
}
else
{
return false;
}
}
public bool DirectoryEquals(DirectoryInfo d1, DirectoryInfo d2)
{
if(d1.Name.ToLower() == d2.Name.ToLower())
{
if((d1.Parent != null) && (d2.Parent != null))
{
return DirectoryEquals(d1.Parent, d2.Parent);
}
else
{
return true;//C:\Temp1\Temp2 equals \Temp1\Temp2
//return (d1.Parent == null) && (d2.Parent == null);//C:\Temp1\Temp2 does not equal \Temp1\Temp2
}
}
else
{
return false;
}
}
Note: new FileInfo(path) returns a valid FileInfo even if path is not a file (the name field is equal to the directory name)
Thank you, #Andy Shellam and #VladV and #Eamon Nerbonne
I found the other solution:
private static bool AreEqual(DirectoryInfo dir1, DirectoryInfo dir2)
{
return AreEqual(dir1.FullName, dir2.FullName);
}
private static bool AreEqual(string folderPath1, string folderPath2)
{
folderPath1 = Path.GetFullPath(folderPath1);
folderPath2 = Path.GetFullPath(folderPath2);
if (folderPath1.Length == folderPath2.Length)
{
return string.Equals(folderPath1, folderPath2/*, StringComparison.OrdinalIgnoreCase*/);
}
else if (folderPath1.Length == folderPath2.Length + 1 && IsEndWithAltDirectorySeparatorChar(folderPath1))
{
// folderPath1 = #"F:\temp\"
// folderPath2 = #"F:\temp"
return folderPath1.Contains(folderPath2 /*, StringComparison.OrdinalIgnoreCase*/);
}
else if (folderPath1.Length + 1 == folderPath2.Length && IsEndWithAltDirectorySeparatorChar(folderPath2))
{
// folderPath1 = #"F:\temp"
// folderPath2 = #"F:\temp\"
return folderPath2.Contains(folderPath1 /*, StringComparison.OrdinalIgnoreCase*/);
}
return false;
static bool IsEndWithAltDirectorySeparatorChar(string path)
{
var lastChar = path[path.Length - 1];
return lastChar == Path.DirectorySeparatorChar;
}
}
It can work well.
static void Main(string[] args)
{
Console.WriteLine(AreEqual(
new DirectoryInfo("C:\\Scratch"),
new DirectoryInfo("C:\\Scratch\\")));
Console.WriteLine(AreEqual(
new DirectoryInfo("C:\\Windows\\Microsoft.NET\\Framework"),
new DirectoryInfo("C:\\Windows\\Microsoft.NET\\Framework\\v3.5\\1033\\..\\..")));
Console.WriteLine(AreEqual(
new DirectoryInfo("C:\\Scratch\\"),
new DirectoryInfo("C:\\Scratch\\4760\\..\\..")));
Debug.WriteLine(AreEqual(#"C:/Temp", #"C:\Temp2")); // False
Debug.WriteLine(AreEqual(#"C:\Temp\", #"C:\Temp2"));// False
Debug.WriteLine(AreEqual(#"C:\Temp\", #"C:\Temp")); // True
Debug.WriteLine(AreEqual(#"C:\Temp/", #"C:\Temp")); // True
Debug.WriteLine(AreEqual(#"C:/Temp/", #"C:\Temp\"));// True
Console.WriteLine("Press ENTER to continue");
Console.ReadLine();
}
bool equals = myDirectoryInfo1.FullName == myDirectoryInfo2.FullName;
?
bool Equals(string path1, string path2)
{
return new Uri(path1) == new Uri(path2);
}
Uri constructor normalizes the path.