diff --git a/src/AssemblyCrawler.Tests/AssemblyCrawler.Tests.csproj b/src/AssemblyCrawler.Tests/AssemblyCrawler.Tests.csproj new file mode 100644 index 0000000..b449488 --- /dev/null +++ b/src/AssemblyCrawler.Tests/AssemblyCrawler.Tests.csproj @@ -0,0 +1,27 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + + diff --git a/src/AssemblyCrawler.Tests/AssemblyExtensionTests.cs b/src/AssemblyCrawler.Tests/AssemblyExtensionTests.cs new file mode 100644 index 0000000..3b1ba80 --- /dev/null +++ b/src/AssemblyCrawler.Tests/AssemblyExtensionTests.cs @@ -0,0 +1,67 @@ +using AssemblyCrawler; +using System.IO; + +namespace AssemblyCrawler.Tests; + +public class AssemblyExtensionTests : IDisposable +{ + private readonly string _tempDir; + + public AssemblyExtensionTests() + { + _tempDir = Path.Combine(Path.GetTempPath(), "AssemblyExtensionTests_" + Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(_tempDir); + } + + public void Dispose() + { + if (Directory.Exists(_tempDir)) + Directory.Delete(_tempDir, recursive: true); + } + + // IsManagedAssembly uses FName (lowercased), so copy the DLL with a lowercase filename + private FileInfo GetManagedDllFileInfo() + { + string location = typeof(AssemblyExtensionTests).Assembly.Location; + string lowercaseName = Path.GetFileName(location).ToLowerInvariant(); + string dest = Path.Combine(_tempDir, lowercaseName); + File.Copy(location, dest, overwrite: true); + return new FileInfo(dest); + } + + [Fact] + public void IsManagedAssembly_ManagedDll_ReturnsTrue() + { + var fileInfo = GetManagedDllFileInfo(); + var assemblyInfo = new AssemblyInfo(fileInfo); + + Assert.True(assemblyInfo.IsManagedAssembly()); + } + + [Fact] + public void IsManagedAssembly_NonManagedFile_ReturnsFalse() + { + var tempFile = Path.Combine(_tempDir, "notmanaged.dll"); + File.WriteAllBytes(tempFile, new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 }); + var fileInfo = new FileInfo(tempFile); + var assemblyInfo = new AssemblyInfo(fileInfo); + + Assert.False(assemblyInfo.IsManagedAssembly()); + } + + [Fact] + public void SortByHashCode_GroupsIdenticalAssembliesTogether() + { + var fileInfo = GetManagedDllFileInfo(); + var a1 = new AssemblyInfo(fileInfo); + var a2 = new AssemblyInfo(fileInfo); + var a3 = new AssemblyInfo(fileInfo); + + var list = new List { a1, a2, a3 }; + var sorted = list.SortByHashCode(); + + // All three should have the same hash code and be in the same group + Assert.Single(sorted); + Assert.Equal(3, sorted.Values.First().Count); + } +} diff --git a/src/AssemblyCrawler.Tests/AssemblyInfoTests.cs b/src/AssemblyCrawler.Tests/AssemblyInfoTests.cs new file mode 100644 index 0000000..77270f8 --- /dev/null +++ b/src/AssemblyCrawler.Tests/AssemblyInfoTests.cs @@ -0,0 +1,89 @@ +using AssemblyCrawler; +using System.IO; +using System.Reflection; + +namespace AssemblyCrawler.Tests; + +public class AssemblyInfoTests : IDisposable +{ + private readonly string _tempDir; + + public AssemblyInfoTests() + { + _tempDir = Path.Combine(Path.GetTempPath(), "AssemblyInfoTests_" + Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(_tempDir); + } + + public void Dispose() + { + if (Directory.Exists(_tempDir)) + Directory.Delete(_tempDir, recursive: true); + } + + // IsManagedAssembly uses FName (lowercased), so copy the DLL with a lowercase filename + private FileInfo GetManagedDllFileInfo() + { + string location = typeof(AssemblyInfoTests).Assembly.Location; + string lowercaseName = Path.GetFileName(location).ToLowerInvariant(); + string dest = Path.Combine(_tempDir, lowercaseName); + File.Copy(location, dest, overwrite: true); + return new FileInfo(dest); + } + + [Fact] + public void Constructor_ManagedDll_PopulatesFName() + { + var fileInfo = GetManagedDllFileInfo(); + var assemblyInfo = new AssemblyInfo(fileInfo); + + Assert.Equal(fileInfo.Name.ToLowerInvariant(), assemblyInfo.FName.Value); + } + + [Fact] + public void Constructor_ManagedDll_IsManaged_ReturnsTrue() + { + var fileInfo = GetManagedDllFileInfo(); + var assemblyInfo = new AssemblyInfo(fileInfo); + + Assert.True(assemblyInfo.IsManaged.Value); + } + + [Fact] + public void Constructor_ManagedDll_AName_IsNotEmpty() + { + var fileInfo = GetManagedDllFileInfo(); + var assemblyInfo = new AssemblyInfo(fileInfo); + + Assert.NotEmpty(assemblyInfo.AName.Value); + } + + [Fact] + public void Constructor_ManagedDll_AssemblyVersion_IsNotNull() + { + var fileInfo = GetManagedDllFileInfo(); + var assemblyInfo = new AssemblyInfo(fileInfo); + + Assert.NotNull(assemblyInfo.AssemblyVersion.Value); + } + + [Fact] + public void Constructor_NonManagedBinary_IsManaged_ReturnsFalse() + { + var tempFile = Path.Combine(_tempDir, "notadll.dll"); + File.WriteAllBytes(tempFile, new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 }); + var fileInfo = new FileInfo(tempFile); + var assemblyInfo = new AssemblyInfo(fileInfo); + + Assert.False(assemblyInfo.IsManaged.Value); + } + + [Fact] + public void GetHashCode_SameAssembly_ReturnsConsistentValue() + { + var fileInfo = GetManagedDllFileInfo(); + var assemblyInfo1 = new AssemblyInfo(fileInfo); + var assemblyInfo2 = new AssemblyInfo(fileInfo); + + Assert.Equal(assemblyInfo1.GetHashCode(), assemblyInfo2.GetHashCode()); + } +} diff --git a/src/AssemblyCrawler.Tests/CrawlerTests.cs b/src/AssemblyCrawler.Tests/CrawlerTests.cs new file mode 100644 index 0000000..6e3dcbe --- /dev/null +++ b/src/AssemblyCrawler.Tests/CrawlerTests.cs @@ -0,0 +1,102 @@ +using AssemblyCrawler; +using System.IO; + +namespace AssemblyCrawler.Tests; + +public class CrawlerTests : IDisposable +{ + private readonly string _tempDir; + + public CrawlerTests() + { + _tempDir = Path.Combine(Path.GetTempPath(), "CrawlerTests_" + Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(_tempDir); + } + + public void Dispose() + { + if (Directory.Exists(_tempDir)) + Directory.Delete(_tempDir, recursive: true); + } + + private void CopyTestDllToDir(string dir, string fileName) + { + string sourceDll = typeof(CrawlerTests).Assembly.Location; + // Use lowercase filename to match FName.Value (which lowercases the name) + File.Copy(sourceDll, Path.Combine(dir, fileName.ToLowerInvariant()), overwrite: true); + } + + [Fact] + public void Crawl_ValidDirectory_PopulatesAssemblyListAndCounts() + { + CopyTestDllToDir(_tempDir, "test1.dll"); + CopyTestDllToDir(_tempDir, "test2.dll"); + + var crawler = new Crawler(_tempDir); + crawler.Crawl(); + + Assert.True(crawler.TotalFileCount >= 2); + Assert.True(crawler.TotalAssemblyCount >= 2); + Assert.True(crawler.AssemblyList.Count >= 2); + } + + [Fact] + public void Crawl_CalledTwice_DoesNotReCrawl() + { + CopyTestDllToDir(_tempDir, "test1.dll"); + + var crawler = new Crawler(_tempDir); + crawler.Crawl(); + int countAfterFirstCrawl = crawler.TotalAssemblyCount; + + // Copy another DLL to the dir between crawls (shouldn't be picked up) + CopyTestDllToDir(_tempDir, "test2.dll"); + crawler.Crawl(); + + Assert.Equal(countAfterFirstCrawl, crawler.TotalAssemblyCount); + } + + [Fact] + public void Sort_BeforeCrawl_DoesNotCrash() + { + var crawler = new Crawler(_tempDir); + + // Should print "Must parse directory first." but not throw + var exception = Record.Exception(() => crawler.Sort()); + Assert.Null(exception); + } + + [Fact] + public void Sort_AfterCrawl_SeparatesManagedAndUnmanaged() + { + CopyTestDllToDir(_tempDir, "managed1.dll"); + CopyTestDllToDir(_tempDir, "managed2.dll"); + + // Create a non-managed binary file + var nonManagedPath = Path.Combine(_tempDir, "native.dll"); + File.WriteAllBytes(nonManagedPath, new byte[128]); + + var crawler = new Crawler(_tempDir); + crawler.Crawl(); + crawler.Sort(); + + // AllAssemblies contains assemblies with duplicates (>1 copy) + // AllManagedAssemblies is the filtered managed subset + // Both should be valid without exception + Assert.NotNull(crawler.AllAssemblies); + Assert.NotNull(crawler.AllManagedAssemblies); + } + + [Fact] + public void CrawlDirectory_IgnoresResourceDlls() + { + CopyTestDllToDir(_tempDir, "regular.dll"); + CopyTestDllToDir(_tempDir, "resources.resources.dll"); + + var crawler = new Crawler(_tempDir); + crawler.Crawl(); + + // The .resources.dll should be ignored; only regular.dll counted + Assert.Equal(1, crawler.TotalAssemblyCount); + } +} diff --git a/src/AssemblyCrawler.Tests/StringExtensionTests.cs b/src/AssemblyCrawler.Tests/StringExtensionTests.cs new file mode 100644 index 0000000..841c781 --- /dev/null +++ b/src/AssemblyCrawler.Tests/StringExtensionTests.cs @@ -0,0 +1,30 @@ +using AssemblyCrawler; + +namespace AssemblyCrawler.Tests; + +public class StringExtensionTests +{ + [Fact] + public void GetStableHashCode_SameInput_ReturnsSameValue() + { + var hash1 = "hello".GetStableHashCode(); + var hash2 = "hello".GetStableHashCode(); + Assert.Equal(hash1, hash2); + } + + [Fact] + public void GetStableHashCode_DifferentInputs_ReturnsDifferentValues() + { + var hash1 = "hello".GetStableHashCode(); + var hash2 = "world".GetStableHashCode(); + Assert.NotEqual(hash1, hash2); + } + + [Fact] + public void GetStableHashCode_EmptyString_ReturnsConsistentValue() + { + var hash1 = "".GetStableHashCode(); + var hash2 = "".GetStableHashCode(); + Assert.Equal(hash1, hash2); + } +} diff --git a/src/AssemblyCrawler.sln b/src/AssemblyCrawler.sln index 1185269..13c30c3 100644 --- a/src/AssemblyCrawler.sln +++ b/src/AssemblyCrawler.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 17.2.32630.192 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssemblyCrawler", "AssemblyCrawler\AssemblyCrawler.csproj", "{F4896D2F-EC6F-426D-9321-F874423D7DE5}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssemblyCrawler.Tests", "AssemblyCrawler.Tests\AssemblyCrawler.Tests.csproj", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +17,10 @@ Global {F4896D2F-EC6F-426D-9321-F874423D7DE5}.Debug|Any CPU.Build.0 = Debug|Any CPU {F4896D2F-EC6F-426D-9321-F874423D7DE5}.Release|Any CPU.ActiveCfg = Release|Any CPU {F4896D2F-EC6F-426D-9321-F874423D7DE5}.Release|Any CPU.Build.0 = Release|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/AssemblyCrawler/Assembly.cs b/src/AssemblyCrawler/Assembly.cs index b502fe9..8dd1384 100644 --- a/src/AssemblyCrawler/Assembly.cs +++ b/src/AssemblyCrawler/Assembly.cs @@ -6,35 +6,6 @@ using System.Reflection; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; -using System.Security.Cryptography; - - -/* NOTES - -// Get - - - // Any CPU = x86 + COMIMAGE_FLAGS_ILONLY will give the flag - - (dwManagedImageFlags &COMIMAGE_FLAGS_ILONLY) != 0; - - - // How you get the core header - - if (image_optional_header.h32.NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR) - { - *ManagedCode = image_optional_header.h32.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].Size ? TRUE : FALSE; - if (*ManagedCode) - { - fSucc = ReadCORHeader(hImage, - image_optional_header.h32.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress, - image_optional_header.h32.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].Size, - image_section_headers, - section_count, - &ich); - } - } -*/ namespace AssemblyCrawler { @@ -44,11 +15,11 @@ internal class AssemblyInfo public readonly Lazy AName; public readonly Lazy AssemblyVersion; public readonly Lazy FileVersion; - public Lazy FileSize; - public Lazy PublicKeyToken; - public Lazy FrameworkVersion; + public readonly Lazy FileSize; + public readonly Lazy PublicKeyToken; + public readonly Lazy FrameworkVersion; - public Lazy IsManaged; + public readonly Lazy IsManaged; public string Path => file.DirectoryName; @@ -92,8 +63,10 @@ private AssemblyName GetAssemblyName() { return AssemblyName.GetAssemblyName(file.FullName); } - catch - { } + catch (Exception ex) + { + Debug.WriteLine($"GetAssemblyName failed for '{file.FullName}': {ex.Message}"); + } } return default; } @@ -163,31 +136,6 @@ private string InitializeFramework() return "Unknown"; } - public bool IsReadyToRun() - { - using var stream = File.OpenRead(file.FullName); - using var reader = new PEReader(stream); - - // From Concord: https://devdiv.visualstudio.com/DevDiv/_git/Concord?path=/src/impl/Common/PEFile.cpp&version=GBmain&line=1761&lineEnd=1770&lineStartColumn=1&lineEndColumn=18&lineStyle=plain&_a=contents - //DWORD dwSignature = 0; - //HRESULT hr = ReadRVA(m_IMAGE_COR20_HEADER.ManagedNativeHeader.VirtualAddress, &dwSignature, sizeof(DWORD)); - //if (hr != S_OK) - //{ - // VSFAIL("failed to read ManagedNativeHeader Signature???"); - //} - //else - //{ - // m_fManagedReadyToRun = dwSignature == 0x00525452; // 'RTR' - //} - - return false; - } - - private string GetPublicKey() - { - return HexByteArrayToString(assemblyName.Value?.GetPublicKey()); - } - private string GetPublicKeyToken() { return HexByteArrayToString(assemblyName.Value?.GetPublicKeyToken()); diff --git a/src/AssemblyCrawler/AssemblyCrawler.csproj b/src/AssemblyCrawler/AssemblyCrawler.csproj index b4974ce..8f59415 100644 --- a/src/AssemblyCrawler/AssemblyCrawler.csproj +++ b/src/AssemblyCrawler/AssemblyCrawler.csproj @@ -2,12 +2,17 @@ Exe - net6.0 + net8.0 preview enable - + + + + + <_Parameter1>AssemblyCrawler.Tests + diff --git a/src/AssemblyCrawler/AssemblyExtension.cs b/src/AssemblyCrawler/AssemblyExtension.cs index f928ec1..6de13bc 100644 --- a/src/AssemblyCrawler/AssemblyExtension.cs +++ b/src/AssemblyCrawler/AssemblyExtension.cs @@ -3,8 +3,6 @@ using System.Diagnostics; using System.IO; using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace AssemblyCrawler { diff --git a/src/AssemblyCrawler/Crawler.cs b/src/AssemblyCrawler/Crawler.cs index ebd49cc..ca930d1 100644 --- a/src/AssemblyCrawler/Crawler.cs +++ b/src/AssemblyCrawler/Crawler.cs @@ -4,8 +4,6 @@ using System.Diagnostics; using System.IO; using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace AssemblyCrawler { @@ -56,10 +54,10 @@ public void CrawlDirectory(DirectoryInfo d) var assemblyFiles = files.Where(file => string.Equals(file.Extension, ".dll", StringComparison.OrdinalIgnoreCase) && !file.Name.EndsWith(".resources.dll", StringComparison.OrdinalIgnoreCase) // Ignore Resource dlls - && !file.Attributes.HasFlag(FileAttributes.ReparsePoint)); // Ignore symlinks + && !file.Attributes.HasFlag(FileAttributes.ReparsePoint)).ToList(); // Ignore symlinks - TotalFileCount += files.Count(); - TotalAssemblyCount += assemblyFiles.Count(); + TotalFileCount += files.Count; + TotalAssemblyCount += assemblyFiles.Count; assemblies.AddRange(assemblyFiles.Select(a => new AssemblyInfo(a))); @@ -122,7 +120,7 @@ public void Sort() } } - sortedAssemblies_managed = new Dictionary>>(sortedAssemblies_all); + sortedAssemblies_managed = new Dictionary>>(sortedAssemblies_all, StringComparer.OrdinalIgnoreCase); keyList = sortedAssemblies_managed.Keys.ToList(); foreach (var key in keyList) @@ -132,141 +130,12 @@ public void Sort() if (sortedAssemblies_managed[key][key2][0].IsManaged.Value == false) { sortedAssemblies_managed.Remove(key); + break; } } } Console.WriteLine($"Sort complete in {sw.ElapsedMilliseconds}ms"); } - - private const string AssemblyCacheFolderName = "AssemblyCache"; - private const string PreCacheFileName = "files.txt"; - //public void CreateSymlinks(string assemblyName) - //{ - // //copy each specific assembly - // if (string.IsNullOrWhiteSpace(assemblyName) || !sortedAssemblies_managed.ContainsKey(assemblyName)) - // { - // Console.WriteLine("Empty or invalid assembly name"); - // } - - // var list = sortedAssemblies_managed[assemblyName]; - // if (!list.Any()) - // { - // Console.WriteLine($"No duplicates found for ${assemblyName}."); - // return; - // } - - // var assemblyByHashCode = list.SortByHashCode(); - - // // create new folder - // var vsInstallationFolder = System.IO.Path.Combine(ParsePath, "Common7", "IDE"); - // while (!Directory.Exists(vsInstallationFolder)) - // { - // Console.WriteLine($"Installation not found at '{vsInstallationFolder}'"); - // Console.WriteLine("Enter location of Visual Studio Installation:"); - // vsInstallationFolder = Console.ReadLine(); - - // if (String.IsNullOrWhiteSpace(vsInstallationFolder)) - // { - // return; - // } - // } - - // var assemblyCacheFolder = Path.Combine(vsInstallationFolder, AssemblyCacheFolderName); - // CreateDirectoryIfNotExists(assemblyCacheFolder); - - // var assemblyNameFolder = Path.Combine(assemblyCacheFolder, assemblyName); - // CreateDirectoryIfNotExists(assemblyNameFolder); - - // var keys = assemblyByHashCode.Keys.ToList(); - // //create symlinks - // foreach (var key in keys) - // { - // try - // { - // var hashCodeFolder = Path.Combine(assemblyNameFolder, key.ToString("X8")); - // CreateDirectoryIfNotExists(hashCodeFolder); - - // var instances = assemblyByHashCode[key].Where(item => !Path.GetFullPath(item.Path).StartsWith(Path.GetFullPath(hashCodeFolder), StringComparison.OrdinalIgnoreCase)).ToList(); - - // if (!instances.Any()) - // { - // continue; - // } - - // var paths = instances.Select(item => item.Path).ToList(); - - // var precacheFileName = Path.Combine(hashCodeFolder, PreCacheFileName); - - // { - // using FileStream fs = new FileStream(precacheFileName, FileMode.Append); - // using StreamWriter sw = new StreamWriter(fs); - - // //don't pick up the ones in the cache folder - // paths.ForEach(item => sw.WriteLine(item)); - // sw.Flush(); - // sw.Close(); - // } - - // var fileName = instances[0].FName.Value; - // var cacheAssemblyFileName = Path.Combine(hashCodeFolder, assemblyName); - - // // copy one into it if it doesn't exist - // if (!File.Exists(cacheAssemblyFileName)) - // { - // //these should all be the same so we don't care which one we're copying - // File.Copy(Path.Combine(paths[0], fileName), cacheAssemblyFileName); - // } - - // foreach (var path in paths) - // { - // var target = Path.Combine(path, fileName); - // try - // { - // File.Delete(target); - // MakeSymLink(target, cacheAssemblyFileName); - // } - // catch - // { - // //undo the delete if soemthing bad happens - // if (!File.Exists(target)) - // { - // File.Copy(cacheAssemblyFileName, target); - // } - // throw; - // } - // } - // } - // catch (UnauthorizedAccessException) - // { - // Console.WriteLine("Don't have access"); - // throw; - // } - // } - //} - - private static void CreateDirectoryIfNotExists(string path) - { - if (!Directory.Exists(path)) - { - Directory.CreateDirectory(path); - } - } - - private static void MakeSymLink(string symLinkPath, string destinationFile) - { - ProcessStartInfo psi = new ProcessStartInfo("cmd.exe", $"/c mklink \"{symLinkPath}\" \"{destinationFile}\""); - var process = Process.Start(psi); - while (!process.HasExited) - { - process.WaitForExit(); - } - - if (process.ExitCode != 0) - { - Console.WriteLine($"Failed to create symlink {symLinkPath} pointing to {destinationFile}. ExitCode {process.ExitCode}."); - throw new InvalidOperationException(); - } - } } } diff --git a/src/AssemblyCrawler/Program.cs b/src/AssemblyCrawler/Program.cs index e009ee4..cd8afc0 100644 --- a/src/AssemblyCrawler/Program.cs +++ b/src/AssemblyCrawler/Program.cs @@ -15,7 +15,7 @@ public static void Main(string[] args) { if (args.Length > 0) { - Console.Error.WriteLine("Argument count not correct"); + Console.Error.WriteLine("This program does not accept command-line arguments."); return; } @@ -25,6 +25,14 @@ public static void Main(string[] args) public static void InnerLoop(string? path = null) { + if (!string.IsNullOrWhiteSpace(path) && Directory.Exists(path)) + { + Crawler c = new Crawler(path); + c.Crawl(); + crawler = c; + c.Sort(); + } + bool exit = false; while (!exit) { @@ -130,9 +138,6 @@ private static void List() case "r": GenerateRyansList(useManaged); break; - case "s": - CreateSymLinkForAssembly(); - break; case "q": exit = true; break; @@ -145,22 +150,6 @@ private static void List() } } - private static void CreateSymLinkForAssembly() - { - //// only symlink managed assemblies - //var list = ListToUse(true); - //Console.WriteLine("Enter assembly name for SymLinking:"); - //var assemblyName = Console.ReadLine(); - //if (string.IsNullOrWhiteSpace(assemblyName) || !list.Keys.Contains(assemblyName, StringComparer.OrdinalIgnoreCase)) - //{ - // Console.WriteLine("Invalid assemblyname or assemblyname not found. Only Managed Assembly names supported."); - // return; - //} - - //crawler.CreateSymlinks(assemblyName); - - } - private static void GenerateRyansList(bool useManaged) { var sortedList = ListToUse(useManaged); @@ -178,7 +167,7 @@ private static void GenerateRyansList(bool useManaged) } else { - FileStream fs = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Write); + FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.Write); sw = new StreamWriter(fs); } @@ -250,7 +239,7 @@ private static void GetDetail(bool useManaged) var keyItems = sortedList.Where(item => item.Value.Count() == count).ToList(); var keyItemCount = keyItems.Count(); - Console.WriteLine($"{keyItemCount} itemsfound for target count {count}."); + Console.WriteLine($"{keyItemCount} items found for target count {count}."); for (int i = 0; i < keyItemCount; i++) { Console.WriteLine($" {keyItems[i].Key}");