Website may be up and down over next few months. I'm currently doing a complete overhaul of everything. Going back to simple individual .htm pages, new overall site theme, sanitizing and cleaning up html of all pages and blog posts, attempting to implement a new tooling and publishing system etc etc.

Below is a post I made on stack overflow.

--------------------------

Stack Overflow. We meet again.  /Sigh

Is it just me or is anybody else getting wacky messages from Directory.CreateDirectory.

Is it just me or do other people have code that has been running flawlessly for years only to suddenly go poof at random for no reason even, when that code has not been changed at all?

Running with windows 10, VS2019. All projects are .NET Core 3.1 or .NET Std 2.0/2.1.

Anyway, I have this code ISettingsProvider that gets implemented by my class for managing app settings. (Stored in xml format) In that class constructor I pass in a string representing the file path to the xml file were the settings will be stored. If the file does not already exist I grab the folder path ala Path.GetDirectoryName and check if it exists and if it does not create it, then write out a empty setting file to that folder. Other wise do nothing because we are about to read the data from the file immediately afterwards.

Below is the constructor for that class. Again this code has been working flawlessly for me for years, across multiple platforms versions and projects, .net pcl 3.5 4.6 .NET Std, Unity3D etc etc

    /// <summary>
    /// Initializes a new instance of the <see cref="XmlSettingsProvider"/> class.
    /// </summary>
    /// <param name="fileName">
    /// The file name.
    /// </param>
    /// <param name="create"><c>true</c> Create the settings file immediately if one does not exist.</param>
    /// <exception cref="ArgumentNullException">
    /// If <see cref="fileName"/> is null or empty.
    /// </exception>
    public XmlSettingsProvider(string fileName, bool create)
    {
        if (string.IsNullOrEmpty(fileName))
        {
            throw new ArgumentNullException(nameof(fileName));
        }

        var directoryName = Path.GetDirectoryName(fileName);
        if (directoryName != null && directoryName.IndexOfAny(Path.GetInvalidPathChars()) != -1)
        {
            throw new Exception("Invalid path characters detected!");
        }

        var name = Path.GetFileName(fileName);
        if (name != null && name.IndexOfAny(Path.GetInvalidFileNameChars()) != -1)
        {
            throw new Exception("Invalid filename characters detected!");
        }

        this.readDelayInSeconds = 5;
        this.FileName = fileName;

        if (create && !File.Exists(fileName))
        {
            var doc = new XmlDocument();
            var declaration = doc.CreateXmlDeclaration("1.0", null, null);

            var settings = doc.CreateElement("settings");
            doc.AppendChild(settings);
            doc.InsertBefore(declaration, doc.DocumentElement);

            Directory.CreateDirectory(directoryName);
            using (var stream = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))
            {
                doc.Save(stream);
            }
        }

        this.Read();
    }

The problem is with Directory.CreateDirectory. I am trying to create a directory at "d:\documents\StockWatchWpfCore" where "d:\documents" exists and is my windows 10 "My Documents" folder and the "d:\documents\StockWatchWpfCore" does not yet exist.

I thought maybe it was windows 10 Ransomware detection preventing my app from creating a folder in "d:\documents" because I have "d:\documents" added as a protected folder. But I am not getting any messages in Block History stating my app is being blocked.

To make matters worse, or more confusing, the exception info that is being raised is not what you would expect.

The exception data being thrown.

Call Stack

at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor)
at System.IO.Directory.CreateDirectory(String path)
at Codefarts.AppCore.SettingProviders.Xml.XmlSettingsProvider..ctor(String fileName, Boolean create) in P:\Code Projects\Codefarts.AppCore\Setting Providers\Codefarts.AppCore.SettingProviders.Xml\XmlSettingsProvider.cs:line 142
at Codefarts.AppCore.SettingProviders.Xml.XmlSettingsProvider..ctor(String fileName) in P:\Code Projects\Codefarts.AppCore\Setting Providers\Codefarts.AppCore.SettingProviders.Xml\XmlSettingsProvider.cs:line 162
at Codefarts.WpfAppBootstrapper.BootstrappedApp.OnStartup(StartupEventArgs e) in P:\Code Projects\Codefarts.WpfAppBootstrapper\BootstrappedApp.cs:line 63
at System.Windows.Application.<.ctor>b__1_0(Object unused)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
at System.Windows.Threading.DispatcherOperation.InvokeImpl()
at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
at MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(Object obj)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)

Source

"System.IO.FileSystem"

Message

"Could not find file 'd:\\Documents\\StockWatchWpfCore'."

InnerException

null

Did you notice the error message? "Could not find file 'd:\\Documents\\StockWatchWpfCore'."  File? I'm working with directories not files?!?!

The plot thickens ......

So in another part of my code I have hooked into the AppDomain.ResolveAssemblies event. Interestingly as soon as I step into Directory.CreateDirectory the ResolveAssemblies event gets fired 2 times both with the same request

Name

    "System.IO.FileSystem.resources, Version=4.1.2.0, Culture=en-US, PublicKeyToken=b03f5f7f11d50a3a"

RequestingAssembly

    {System.IO.FileSystem, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a}

My assembly resolve event code

private Assembly ResolveAssemblies(object sender, System.ResolveEventArgs args)
    {
            var folderPaths = new List<string>(this.AssemblySearchFolders);
            folderPaths.Add(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
            var filter = new AssemblyName(args.Name);

            foreach (var folderPath in folderPaths)
            {
                if (!Directory.Exists(folderPath))
                {
                    continue;
                }

                var fileMatches = Directory.GetFiles(folderPath, filter.Name + ".dll", SearchOption.AllDirectories);
                var assemblyPath = fileMatches.FirstOrDefault();
                if (!string.IsNullOrWhiteSpace(assemblyPath) && File.Exists(assemblyPath))
                {
                    return Assembly.LoadFrom(assemblyPath);
                }
            }

        return null;
    }

BTW "this.AssemblySearchFolders" is an empty collection. It's just there in case I want to search other locations other then the app install folder.

Ok so at this point it seems like .net is trying to resolve a missing assembly reference that it can't find on it's own. So I opened up my project containing the XmlSettingsProvider implementation. That project is a .NET Core 3.1 project type with this project info

<Project Sdk="Microsoft.NET.Sdk">
    
        <PropertyGroup>
            <TargetFramework>netcoreapp3.1</TargetFramework>
            <AssemblyVersion>2020.5.10.9</AssemblyVersion>
            <FileVersion>2020.5.10.9</FileVersion>
        </PropertyGroup>
    
        <PropertyGroup>
            <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
        </PropertyGroup>       
    
        <ItemGroup>
            <ProjectReference Include="..\..\Codefarts.AppCoreNETStd\Codefarts.AppCoreNETStd2.csproj" />
        </ItemGroup> 
    </Project>

Notice that after I started having this issue I made sure to include the CopyLocalLockFileAssemblies property in my .csproj file. But it does not copy any references to the bin folder like "System.IO.FileSystem".

Ok, so then I tried to do a nuget search for System.IO.FileSystem and found this https://www.nuget.org/packages/System.IO.FileSystem/4.3.0?_src=template

It appears to support .NET Standard 1.3 but no mention of .NET Core. I added the nuget reference anyway and did a rebuild then ran my app again and still the same issue. No System.IO.FileSystem.dll being copied to the bin folder and AssemblyResolve event still being fired still looking for missing assembly references.

It's at this point I'm running out of ideas and am at the limit of my understandings. Also the googles is not helping. It's frustrating because the XmlSettingsProvider code has not changed and has been working fine for the last two weeks.. All of a sudden it decides to pipe up and start giving my this error last two days.

As a simplified test I started a new .Net Core 3.1 console app with this code and I get the same issue with not being able to resolve System.IO.FileSystem

using System;
    using System.IO;
    
    namespace assembly_resolve
    {
        class Program
        {
            static void Main(string[] args)
            {
                AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
                Directory.CreateDirectory(@"d:\documents\wpfcore");
            }
    
            private static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
            {
                return args.RequestingAssembly;
            }
        }
    }

Also as a test of my sanity I wrote this linqPad6 test code.

void Main()
    {
        AppDomain.CurrentDomain.AssemblyResolve += (s, e) =>
        {        
            return e.RequestingAssembly;
        };
    
        Directory.CreateDirectory(@"d:\documents\WpfCoreTest");
    }

The test code does not fire the AssemblyResolve event as I new it would not. Also tried obligatory system shutdown and reboot to no avail.  So it would seem that there are architecture/behaviour changes moving from .NET Framework to .NET Core. Ala -> https://docs.microsoft.com/en-us/dotnet/api/system.appdomain.assemblyresolve?view=netcore-3.1

Also tried a hail marry via this (Shameless plug) http://www.createdbyx.com/createdbyx/post/2016/07/02/MSBuild-Recursively-Copy-Indirect-Project-Dependencies-to-bin.aspx  But still no success.

So in conclusion I need to somehow instruct .net where to find the assembly "System.IO.FileSystem" digery do so it can "Do the thing"

Pweaze Halp!

==========================

UPDATE: The next day. Was up thinking about it all night. My whole mindset was toward something going wrong with .NET Core, or some kind of user permissions etc so I created additional console projects in C# and VS2019 targeting .NET Framework 4 Client Profile, 4.6, 4.7, .NET Core 3.1 all of them did not work and throw the same error when trying to create a directory. Now I know they have worked in the past but could not figure out why it has stopped working. I also tried running vs2019 in administrator mode to no avail. What is going on?

I noticed the version number of the assembly it was trying to look for "System.IO.FileSystem.resources, Version=4.1.2.0, ..." When I look at the [nuget package][1] that package has no version 4.1.2.0. Additionally when I look in my "C:\Users\Dean\.nuget\packages\system.io.filesystem" folder there is no corresponding folder for 4.1.2.

So my thinking now is that something has gone horribly wrong on my system and is not my code at all.

Then I tried turning off windows ransomware protection and .... what the hell ... it started working. Which makes things more interesting because of the fact that there is no block history reports in windows ransomware protection indicating that it is protecting my system from my own application code?!!?! But I get block history notifications and reports every other day from other applications on my system. As a side note I do have vs2019 devenv.exe added as an allowed app.

Added my application .exe to "Add an app through Controlled folder access" and voila it's working. Also modified the application code and re ran it to check if ransomware protection would detect a different version of my app and it did not. So that suggests it's not doing any type of crc/file contents comparison check from when the block rule was first created.

I'm wondering if it's because it's a .net application that ransomware protection does not report block history. In any case the issue has been tracked down, my sanity has been restored, and I am in the process of filing a bug report via the "Give us feedback" link located on the right of the ransomware protection window.


New helpful utilities list

Published 4/4/2020 by createdbyx in News
Tags:

Added a new page containing a list of helpfull utilities.


I was recently working on a WPF MVVM application that used MEF to load plugins. I had broken up the main WPF application into smaller more manageable compartmentalized projects to help ensure more cross platform code. But when I tried to run the program it would start up fine but as soon as it tried to utilize logic from my plugins I was getting "assembly or one of it’s dependencies not found" exceptions.

Main Application
    -> LibraryA (References to Xceed.WPF.Toolkit)

Turns out when visual studio compiled my solution it was not copying Xceed.Wpf.Tookit into the main application bin/Debug folder. To solve this took a lot of searching on the interwebz before coming across this handy solution. Recursively Copying Indirect Project Dependencies in MSBuild.

Just download the CopyIndirectDependencies.targets CopyIndirectDependencies.targets (9.19 kb) and put it in your main application folder. Then edit your projects .csproj file to include this line "<Import Project="CopyIndirectDependencies.targets" />"

 

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  ...

  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <Import Project="CopyIndirectDependencies.targets" />
  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
       Other similar extension points exist, see Microsoft.Common.targets.
  <Target Name="BeforeBuild">
  </Target>
  <Target Name="AfterBuild">
  </Target>
  -->
</Project>

Now when you build your main application project it should scan for indirect dependancies and copy them to the projects bin/Debug folder.


Full repo available at https://bitbucket.org/createdbyx/codefarts.utilities-extension-methods-only

/// <summary>Determines whether a value in within a certain range.</summary>
/// <param name="value">The value.</param>
/// <param name="min">The minimum value.</param>
/// <param name="max">The maximum value.</param>
/// <returns>True if the value is in range.</returns>
public static bool IsInRange(this BaseType value, BaseType min, BaseType max)
{
    return value >= min && value <= max;
}

/// <summary>Determines whether a value in within a certain range.</summary>
/// <param name="value">The value.</param>
/// <param name="min">The minimum value.</param>
/// <param name="max">The maximum value.</param>
/// <param name="throwException">if set to <c>true</c> will throw a <see cref="IndexOutOfRangeException"/> if the value is out of range.</param>
/// <returns>True if the value is in range.</returns>
/// <exception cref="System.IndexOutOfRangeException">Is thrown if the value is out of range.</exception>
public static bool IsInRange(this BaseType value, BaseType min, BaseType max, bool throwException)
{
    if (!(value >= min && value <= max) && throwException)
    {
        throw new IndexOutOfRangeException();
    }

    return true;
}

/// <summary>Determines whether a value in within a certain range.</summary>
/// <param name="value">The value.</param>
/// <param name="min">The minimum value.</param>
/// <param name="max">The maximum value.</param>
/// <param name="throwException">if set to <c>true</c> will throw a <see cref="IndexOutOfRangeException"/> if the value is out of range.</param>
/// <param name="message">The message for the <see cref="IndexOutOfRangeException"/> if it is thrown.</param>
/// <returns>True if the value is in range.</returns>
/// <exception cref="System.IndexOutOfRangeException">Is thrown if the value is out of range.</exception>
public static bool IsInRange(this BaseType value, BaseType min, BaseType max, bool throwException, string message)
{
    if (!(value >= min && value <= max) && throwException)
    {
        throw new IndexOutOfRangeException(message);
    }

    return true;
}

Full repo available at https://bitbucket.org/createdbyx/codefarts.utilities-extension-methods-only

/// <summary>
/// Clamps the specified value.
/// </summary>
/// <param name="value">The value to be clamped.</param>
/// <param name="min">The minimum value.</param>
/// <param name="max">The maximum value.</param>
/// <returns>The clamped value.</returns>
public static BaseType Clamp(this BaseType value, BaseType min, BaseType max)
{
    if (value < min)
    {
        return min;
    }

    if (value > max)
    {
        return max;
    }

    return value;
}

Full repo available at https://bitbucket.org/createdbyx/codefarts.utilities-extension-methods-only

/// <summary>
/// Determines weather or not all the characters in a string are all the same.
/// </summary>
/// <param name="value">The value to check for.</param>
/// <returns>true is all characters are the same, otherwise false.</returns>
public static bool AllTheSame(this string value)
{
#if UNITY3D
    if (!StringExtensionMethods.IsNullOrWhiteSpace(value))
#else
    if (!string.IsNullOrWhiteSpace(value))
#endif
    {
        var clone = new string(value[0], value.Length);
        return clone == value;
    }

    return false;
}

#if UNITY3D
public static bool IsNullOrWhiteSpace(this string value)
{
    if (value == null)
    {
        return true;
    }

    for (var i = 0; i < value.Length; i++)
    {
        if (!char.IsWhiteSpace(value[i]))
        {
            return false;
        }
    }

    return true;
}
#endif
}

Full repo available at https://bitbucket.org/createdbyx/codefarts.utilities-extension-methods-only

namespace System.Collections
{
    /// <summary>
    /// Provides extension methods for the IList interface.
    /// </summary>
    public static class IListExtensionMethods
    {
        /// <summary>Swaps the specified items in a list.</summary>
        /// <param name="list">The list.</param>
        /// <param name="indexA">The index of item A.</param>
        /// <param name="indexB">The index of item B.</param>
        /// <param name="remove">If set to <c>true</c> items will be removed and re-inserted.</param>
        public static void Swap(this IList list, int indexA, int indexB, bool remove)
        {
            if (indexA == indexB)
            {
                return;
            }

            indexA.IsInRange(0, list.Count - 1, true, "indexA is out of range.");
            indexB.IsInRange(0, list.Count - 1, true, "indexB is out of range.");

            if (remove)
            {
                var first = Math.Min(indexA, indexB);
                var second = Math.Max(indexA, indexB);
                
                var tempA = list[first];
                var tempB = list[second];

                list.RemoveAt(second);
                list.RemoveAt(first);
               
                list.Insert(first, tempB);
                list.Insert(second, tempA);
            }
            else
            {
                var temp = list[indexA];
                list[indexA] = list[indexB];
                list[indexB] = temp;
            }
        }

        /// <summary>Swaps the specified items in a list.</summary>
        /// <param name="list">The list.</param>
        /// <param name="indexA">The index of item A.</param>
        /// <param name="indexB">The index of item B.</param>
        /// <remarks>Items are swapped and not removed or inserted.</remarks>
        public static void Swap(this IList list, int indexA, int indexB)
        {
            Swap(list, indexA, indexB, false);
        }

        /// <summary>Swaps the specified items in a list and return true if successful.</summary>
        /// <param name="list">The list.</param>
        /// <param name="indexA">The index of item A.</param>
        /// <param name="indexB">The index of item B.</param>
        /// <remarks>Items are swapped and not removed or inserted.</remarks>
        /// <returns>true if successful.</returns>
        public static bool TrySwap(this IList list, int indexA, int indexB)
        {
            try
            {
                Swap(list, indexA, indexB);
            }
            catch
            {
                return false;
            }

            return true;
        }

        /// <summary>Swaps the specified items in a list and return true if successful.</summary>
        /// <param name="list">The list.</param>
        /// <param name="indexA">The index of item A.</param>
        /// <param name="indexB">The index of item B.</param>
        /// <param name="remove">If set to <c>true</c> items will be removes and re-inserted.</param>
        /// <returns>true if successful.</returns>
        public static bool TrySwap(this IList list, int indexA, int indexB, bool remove)
        {
            try
            {
                Swap(list, indexA, indexB, remove);
            }
            catch
            {
                return false;
            }

            return true;
        }
    }
}

Full repo available at https://bitbucket.org/createdbyx/codefarts.utilities-extension-methods-only

/// <summary>
/// Removes a range of entries inside an array.
/// </summary>
/// <typeparam name="T">Specifies the generic type of the array.</typeparam>
/// <param name="array">The destination array.</param>
/// <param name="index">The start index where entries will be removed from.</param>
/// <param name="length">The number of entries to be removed.</param>
/// <returns>
/// Returns the resized and updated destination array.
/// </returns>
/// <exception cref="ArgumentOutOfRangeException">index</exception>
public static T[] RemoveRange<T>(this T[] array, int index, int length)
{
    if (length < 1)
    {
        return array;
    }

    if (index < 0 || index > array.Length - 1)
    {
        throw new ArgumentOutOfRangeException("index");
    }

    if (index + length > array.Length - 1)
    {
        Array.Resize(ref array, index);
        return array;
    }

    var endLength = Math.Max(0, Math.Min(array.Length - index, array.Length - (index + length)));
    var tempArray = new T[endLength];
    Array.Copy(array, index + length, tempArray, 0, endLength);
    Array.Resize(ref array, array.Length - length);
    tempArray.CopyTo(array, array.Length - endLength);
    return array;
}

Full repo available at https://bitbucket.org/createdbyx/codefarts.utilities-extension-methods-only

/// <summary>
/// Moves the specified entries in the array by a set ammount.
/// </summary>
/// <typeparam name="T">Specifies the generic type of the array.</typeparam>
/// <param name="array">The destination array.</param>
/// <param name="index">The start index where entries will be moved from.</param>
/// <param name="length">The number of entries to be moved.</param>
/// <param name="shift">The ammount and direction to move the specified entries.</param>
/// <returns>
/// Returns the resized and updated destination array.
/// </returns>
/// <exception cref="ArgumentOutOfRangeException">length;'length' argument must be greater then 0.</exception>
/// <remarks><p>To move entries to the left (towards 0) specify a negative shift value and a positive shift value to move entries to the right.</p>
/// <example>
/// <code>
/// var items = new[] { 0, 1, 2, 3, 4 };
/// items = items.Move(3, 2, -1);
/// </code>
/// Result should be { 0, 1, 3, 4, 4 }
/// </example></remarks>
public static T[] Move<T>(this T[] array, int index, int length, int shift)
{
    if (length <= 0)
    {
        throw new ArgumentOutOfRangeException("length", "'length' argument must be greater then 0.");
    }

    if (shift > 0 && index + length + shift > array.Length - 1)
    {
        Array.Resize(ref array, array.Length + (index + length + shift - array.Length));
    }

    if (index + shift < 0)
    {
        length += index + shift;
        index = -(index + shift);
    }

    length = Math.Min(array.Length - index, length);
    if (length > 0)
    {
        Array.Copy(array, index, array, index + shift, length);
    }        

    return array;
}

Full repo available at https://bitbucket.org/createdbyx/codefarts.utilities-extension-methods-only

/// <summary>
/// Crops an array to a specified length.
/// </summary>
/// <typeparam name="T">Specifies the generic type of the array.</typeparam>
/// <param name="array">The destination array.</param>
/// <param name="length">The length that the destination array will be set to.</param>
/// <returns>Returns the resized and updated destination array.</returns>
public static T[] Crop<T>(this T[] array, int length)
{
    if (array == null)
    {
        return array;
    }

    Array.Resize(ref array, Math.Max(length, 0));
    return array;
}

/// <summary>
/// Crops an array to a specified length.
/// </summary>
/// <typeparam name="T">Specifies the generic type of the array.</typeparam>
/// <param name="array">The destination array.</param>
/// <param name="index">The index in the destination array where cropping begins at.</param>
/// <param name="length">The length that the destination array will be set to.</param>
/// <returns>
/// Returns the resized and updated destination array.
/// </returns>
/// <exception cref="ArgumentOutOfRangeException">length;'length' argument must be greater then 0.</exception>
public static T[] Crop<T>(this T[] array, int index, int length)
{
    if (array == null)
    {
        return array;
    }

    if (length <= 0)
    {
        throw new ArgumentOutOfRangeException("length", "'length' argument must be greater then 0.");
    }
                 
    if (index < 0 || index > array.Length - 1)
    {
        return array;
    }
                        
    length = Math.Min(length, array.Length - index);
    if (index > 0)
    {
        Array.Copy(array, index, array, 0, length);
    }

    Array.Resize(ref array, length);
    return array;
}

Created by: X

Just another personal website in this crazy online world

Name of author Dean Lunz (aka Created by: X)
Computer programming nerd, and tech geek.
About Me -- Resume