8

when I run a .net exe, a corresponding folder is created is created C:\Users\UserName\AppData\Local\AppName

inside this folder, another folder is created AppName_Url_ABCXYZ

inside this folder, yet another folder is created for the assembly version of the .net exe 0.2.28.0

can anyone explain how windows decide to create the second level folder AppName_Url_ABCXYZ?

i am having an issue when i increment my assembly version and perform a single file publish and run the resulting exe, a new AppName_Url_ABCXYZ folder is created containing the new assembly version folder.

this causes issues because it breaks the functionality of Properties.Settings.Default.Upgrade() as the settings to upgrade from are no longer in the expected directory

Good:

-AppData
 -Local
  -MyApp
   -MyApp_Url_ABCXYZ
    -0.2.13.0
    -0.2.14.0
    -0.2.15.0

Bad:

-AppData
 -Local
  -MyApp
   -MyApp_Url_ABC1
    -0.2.13.0
   -MyApp_Url_ABC2
    -0.2.14.0
   -MyApp_Url_ABC3
    -0.2.15.0

enter image description here

Update:

The information provided by @Richard Deeming indicates that the hash portion of the appdata folder is generated like so:

var uri = "file:///" + fullExePath; //or 'assemblyName.CodeBase' if vshost (you can check the 'FriendlyName')
uri = uri.ToUpperInvariant();

var ms = new MemoryStream();
var bSer = new BinaryFormatter();
bSer.Serialize(ms, uri);
ms.Position = 0;
var sha1 = new SHA1CryptoServiceProvider();
var hash = sha1.ComputeHash(ms);
var hashstring = ToBase32StringSuitableForDirName(hash);

which makes no sense as the exe path is not changing

this issue does not occur with a brand new WPF .net 6 app and incrementing the assembly version, so its something specific to my application.

examining the resulting exe's in windows explorer does not help, they seem identical.

Update:

I have been unable to determine why this is occurring in my project. I don't even know how to debug it. I've never made any intentional changes to this behavior.

Julien
  • 212
  • 1
  • 18
  • 53
  • are you using click once? – Daniel A. White Jan 16 '22 at 15:50
  • no. just the publish option in VS – Julien Jan 16 '22 at 16:06
  • AFAIK it should work as such changes are considered in the funcionality of `Upgrade()`. According to [this answer](https://stackoverflow.com/questions/534261/how-do-you-keep-user-config-settings-across-different-assembly-versions-in-net/534335#534335) you need a flag value in your user settings to upgrade only when a new version is released; have you tried that? can you post exactly how're you calling the `Upgrade()` method? – Josh Part Jan 18 '22 at 21:13
  • exactly as described in that answer. the issue is that the appdata folder that is generated is put into a new folder which contains -only- that version. the previous version is in its own folder. the `Upgrade()` method seems to only apply to versions in the same folder, hence my issue – Julien Jan 19 '22 at 01:23
  • Are you using .NET Framework or the new stack (aka .NET Core)? – mu88 Jan 25 '22 at 11:04
  • Does [this thread](https://stackoverflow.com/questions/24660121/how-to-get-hash-value-in-user-config-path) answer your question? – Richard Deeming Jan 25 '22 at 17:34
  • @RichardDeeming thanks. this certainly sheds some light on how that directory name hash is constructed. i guess the .net single file publish is changing one of those values? i will need to experiment – Julien Jan 25 '22 at 17:43
  • @mu88 new stack, and a big reason for that is specifically the single file publish – Julien Jan 25 '22 at 17:43
  • @Julien so would you please add some more information about your publish process? Which .NET version do you use? How do you publish your file? Because I made a test with .NET 6 and `dotnet publish -r win-x64` yesterday and was not able to reproduce it – mu88 Jan 26 '22 at 07:46
  • @mu88 right click the project in solution explorer, click Publish, click Publish – Julien Jan 26 '22 at 13:44
  • I tried it with VS 2022 and the same settings as in your screenshots, but I'm not able to reproduce it: my app gets published into `"C:\source\temp\ConsoleApp1\bin\Release\net6.0\publish\win-x64\ConsoleApp1.exe"`. Can you provide a sample project on GitHub? – mu88 Jan 26 '22 at 13:57
  • the folder the app is published into doesn't matter. i am referring to the appdata folder that gets created when you run the app – Julien Jan 26 '22 at 14:02
  • By itself, running the executable does not install the application. As far as I understand your question, you either use the ClickOnce technology, or another way to install the application. The publication shown in your screenshots does not lead to the installation of the application and we can reproduce your problem only by knowing which application installation technology you are using. – Maxim Jan 29 '22 at 05:09
  • @Maxim there is no installation involved. just run the exe. – Julien Jan 30 '22 at 11:55
  • @Julien, I used the information you provided (as well as the commenters above) and when I ran the executable it didn't install anywhere. What installer did you use? By itself, the executable file will not be installed anywhere without an installer. – Maxim Jan 30 '22 at 13:43
  • @Maxim once again, there is no installation. you can run a .exe without installing anything. I never mentioned anything regarding installation in my question. – Julien Jan 31 '22 at 05:24
  • Are you using strong names for your assemblies? Or are you perhaps including the version number in the publish path? – l33t Feb 11 '22 at 10:16
  • @l33t certainly not intentionally. how do i check these things? – Julien Feb 11 '22 at 20:42

2 Answers2

1

Download the entire project package here – ConsoleApp1.zip

To demonstrate, let's create a simple console application:

enter image description here

and add a link to it:

<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.0" />

We will also add the Settings.settings project properties file. As a result, we get a project in the following configuration:

enter image description here

After compiling in debug mode and running the result of ConsoleApp1.exe, we get the following picture along the path C:\Users\Administrator\AppData\Local\ConsoleApp1:

enter image description here

inside this folder there will be a folder with the version number of the program:

enter image description here

When compiling the release version and running the result of ConsoleApp1.exe, we get the following picture along the path C:\Users\Administrator\AppData\Local\ConsoleApp1:

enter image description here

inside this new folder there will also be a folder with the version number of the program:

enter image description here

It should be noted that the debug and release versions of the program create different folders. 

When you change the project version, new versions will appear in the debug and release folders, respectively:

enter image description here

enter image description here

The debug and release programs are different compilation "versions" of the same project, and different URLs are created to separate them.

Download the entire project package here – ConsoleApp1.zip

qqNade
  • 1,942
  • 8
  • 10
  • thanks for the comprehensive write up. unfortunately it doesn't solve the issue :( `when i increment my assembly version and perform a single file publish and run the resulting exe, a new AppName_Url_ABCXYZ folder is created containing the new assembly version folder.` – Julien Feb 02 '22 at 00:13
  • You need to make sure that 1. Visual Studio 2022 is updated to the latest version; 2. Then I need more detailed information about the environment: windows, version of Visual Studio; 3. It would be good to get your complete test project. – qqNade Feb 02 '22 at 10:02
  • Windows 10 Pro 20H2 19042.1466 VS 2022 64-bit 4.8.04084 i do not have a test project, only my real application, which i cannot share. – Julien Feb 02 '22 at 12:15
  • Please check how the project I provided earlier works. It does work and seems to avoid the issues that you are trying to avoid. You can download it from the links at the top and bottom of my original answer. To help solve your problem, I first need to reproduce the behavior you describe. This is not possible without your help. If you can't share your project, you can create a test project that you can share and isolate the problem you are trying to solve. – qqNade Feb 02 '22 at 15:22
  • thanks again. if i knew how to recreate the problem in a new project, i would probably know how to fix the issue. i do not know why this behavior occurs in my project and not in a fresh sample project. i am asking for help to discover why this is occuring in my project - i am not asking for an explanation of how it is meant to work. – Julien Feb 02 '22 at 18:13
1

If my understanding is correct, the folder in question (C:\Users\UserName\AppData\Local\AppName\AppName_Url_ABCXYZ)is used to store the user.config settings. Please check the following threads:

How to change the predefined userconfig directory of my .NET application?

Custom path of the user.config

Here is the quote from the FAQ: https://learn.microsoft.com/en-us/archive/blogs/rprabhu/client-settings-faq

The exact path of the user.config files looks something like this:

<Profile Directory>\<Company Name>\<App Name>_<Evidence Type>_<Evidence Hash>\<Version>\user.config

where

<Profile Directory> - is either the roaming profile directory or the local one. Settings are stored by default in the local user.config file. To store a setting in the roaming user.config file, you need to mark the setting with the SettingsManageabilityAttribute with SettingsManageability set to Roaming.

<Company Name> - is typically the string specified by the AssemblyCompanyAttribute (with the caveat that the string is escaped and truncated as necessary, and if not specified on the assembly, we have a fallback procedure).

<App Name> - is typically the string specified by the AssemblyProductAttribute (same caveats as for company name).

<Evidence Type> and <Evidence Hash> - information derived from the app domain evidence to provide proper app domain and assembly isolation.

<Version> - typically the version specified in the AssemblyVersionAttribute. This is required to isolate different versions of the app deployed side by side.

The file name is always simply user.config.

If you want to get to the path programmatically, you can do it using the Configuration Management API (you need to add a reference to System.Configuration.dll).

The path construction algorithm has to meet certain rigorous requirements in terms of security, isolation and robustness. While we tried to make the path as easily discoverable as possible by making use of friendly, application supplied strings, it is not possible to keep the path totally simple without running into issues like collisions with other apps, spoofing etc.

The LocalFileSettingsProvider does not provide a way to change the files in which settings are stored. Note that the provider itself doesn't determine the config file locations in the first place - it is the configuration system. If you need to store the settings in a different location for some reason, the recommended way is to write your own SettingsProvider.

It looks like Microsoft intentionally did not provide the way to configure the path to the config file to avoid collisions. The SO threads mentioned above seem to provide some instructions on implementing the custom SettingsProvider.

In your case, the <Evidence Hash> part is different after updating the version. Here's one more thread describing the possible cause: https://social.msdn.microsoft.com/Forums/vstudio/en-US/d87a0add-00ba-4074-8ef3-cf085e1122eb/userscope-settings-path-changed-after-upgrade?forum=netfxbcl

the Evidence Hash is changed, because this values is affected by two things, for a details, please check this link,

1.StrongName

2.URL

If neither of these is available, use the .exe path.

<Evidence Type> can be the URL, StrongName, or Path, based on the evidence available to hash. In the path in question, I can see it set to Url. As I have found in the book "Programming .NET Security", the Url evidence represents the URL from which the assembly was loaded from. If it was loaded from the disk, it would be the file:// URL. Have not checked it with the example though.

Probably, the URL change or EXE path change affected the hash value.

In addition, it's necessary to check if the assembly strong name has changed.

A strong name consists of the assembly's identity—its simple text name, version number, and culture information (if provided)—plus a public key and a digital signature. It is generated from an assembly file using the corresponding private key. (The assembly file contains the assembly manifest, which contains the names and hashes of all the files that make up the assembly.) https://learn.microsoft.com/en-us/dotnet/standard/assembly/create-use-strong-named

Anna Dolbina
  • 1
  • 1
  • 8
  • 9
  • thank you this is great information – Julien Feb 11 '22 at 20:44
  • what is `URL` referencing exactly? this is a WPF desktop project – Julien Feb 11 '22 at 20:46
  • 1
    Here's one more thread on calculating the hash value: https://stackoverflow.com/questions/24660121/how-to-get-hash-value-in-user-config-path To my mind, it's better to check path differences first. – Anna Dolbina Feb 11 '22 at 21:19
  • still weird. the path to my app doesn't change. `C:\some\dir\MyApp.exe` is constant. – Julien Feb 11 '22 at 21:22
  • As I have found in the book "Programming .NET Security", the Url evidence represents the URL from which the assembly was loaded from. If it was loaded from the disk, it would be the file:// URL. Have not checked it with the example though. – Anna Dolbina Feb 11 '22 at 21:32
  • You also should check the strong name, if you use any. – Anna Dolbina Feb 11 '22 at 21:35