Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.0k views
in Technique[技术] by (71.8m points)

visual studio - UseLegacyPathHandling is not loaded properly from app.config runtime element

I'm trying to use the new long path support at my app. In order to use it, without forcing clients to have the newest .net 4.6.2 version instelled on their machines, one should only add those elements to his app.config (see the link for more info https://blogs.msdn.microsoft.com/dotnet/2016/08/02/announcing-net-framework-4-6-2/) :

<startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1"/>
</startup>
<runtime>
    <AppContextSwitchOverrides value="Switch.System.IO.UseLegacyPathHandling=false" />
</runtime>

When I use it in my execution project it works perfectly. The problem is in my testing projects (which use Nunit). I've added app.config to my test project in the same way I've added it to the execution project.

Using the ConfigurationManager class I've managed to ensure that app config indeed loaded (in short: using an app setting which i was able to retrieve in a unit test).

Using ConfigurationManager.GetSection("runtime"), I even managed to ensure the runtime element has been loaded properly (_rawXml value is the same as in app.config).

But (!) for some reason the app config runtime element is not influence the UseLegacyPathHandling variable and therefore all of my calls with long path fail.

I guess the problem is somehow relates to the fact that testing projects become dll's that are loaded using the Nunit engine, which is the execution entry point.

I'm facing the exact same problem in another project I have, which is a dll loaded by Office Word application. I believe the problem is the same in both cases and derived from the fact that the projects are not meant to be an execution entry point.

It's important to understand that I've no access to the executions their self (Word Office or Nunit) and therefore I can't configure them myself.

Is there an option to somehow make the AppContextSwitchOverrides get loaded from scratch dynamically? Other ideas will be most welcome. Thanks.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I've been having the same issue, and have noted the same lack of loading of that particular setting.

So far, what I've got is that the caching of settings is at least partly to blame. If you check out how it's implemented, disabling the cache has no effect on future calls to values (i.e. if caching is enabled and something is accessed during that time, then it will always be cached).

https://referencesource.microsoft.com/#mscorlib/system/AppContext/AppContext.cs

This doesn't seem to be an issue for most of the settings, but for some reason the UseLegacyPathHandling and BlockLongPaths settings are getting cached by the time I can first step into the code.

At this time, I don't have a good answer, but if you need something in the interim, I've got a highly suspect temporary fix for you. Using reflection, you can fix the setting in the assembly init. It writes to private variables by name, and uses the specific value of 0 to invalidate the cache, so it's a very delicate fix and not appropriate for a long term solution.

That being said, if you need something that 'just works' for the time being, you can check the settings, and apply the hack as needed. Here's a simple example code. This would be a method you'll need in your test class.

    [AssemblyInitialize]
    public static void AssemblyInit(TestContext context)
    {
        // Check to see if we're using legacy paths
        bool stillUsingLegacyPaths;
        if (AppContext.TryGetSwitch("Switch.System.IO.UseLegacyPathHandling", out stillUsingLegacyPaths) && stillUsingLegacyPaths)
        {
            // Here's where we trash the private cached field to get this to ACTUALLY work.
            var switchType = Type.GetType("System.AppContextSwitches"); // <- internal class, bad idea.
            if (switchType != null)
            {
                AppContext.SetSwitch("Switch.System.IO.UseLegacyPathHandling", false);   // <- Should disable legacy path handling

                // Get the private field that is used for caching the path handling value (bad idea).
                var legacyField = switchType.GetField("_useLegacyPathHandling", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
                legacyField?.SetValue(null, (Int32)0); // <- caching uses 0 to indicate no value, -1 for false, 1 for true.

                // Ensure the value is set.  This changes the backing field, but we're stuck with the cached value for now.
                AppContext.TryGetSwitch("Switch.System.IO.UseLegacyPathHandling", out stillUsingLegacyPaths);
                TestAssert.False(stillUsingLegacyPaths, "Testing will fail if we are using legacy path handling.");
            }
        }
    }

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...