Invisible subfolder under AppData - c#

I'm working on a project that involves the rewriting of legacy Windows applications for a modern .NET environment. The structure of these applications requires that there is one "Master" application running locally on the user's machine that fetches the others from a network share, along with required DLLs, and then runs them locally. This is not a problem per se, but I'm running into issues when I try to use the current user's AppData folder structure for this, whether it's local or roaming.
Thing is though, I can create a subfolder without a problem, I can copy files over (via a call to System.IO.File.Copy) and run them (via creating and starting an instance of System.Diagnostics.Process), but still the applications complain that they can't find the needed DLLs. This probably is related to the fact that the subfolder doesn't show up in the file system at all, which leads me to believe that files are not being copied over there in a fully conventional sense, but rather exist in some sort of limbo that prevents them from being "aware" of each other. When I do the copying and running to a standard filesystem folder, it all works fine. I've been trying to Google this issue as I was sure that this was a known problem, but I'm having a hard time finding anything on this subject. Hence this question.
Any ideas as to what might be going on? Or is my approach of using AppData for this particular purpose perhaps misguided to begin with?
EDIT: Comments asked for a viable code sample that reproduces the problem. I did have some interesting results trying to reproduce the issue as simply as I could. I created a fresh console app and could not reproduce it there. I then tried a fresh WinUI 3 app (which is what I'm trying to work with for my project) and I reproduced it there - the code seems to create the directory and then the file, without complaints (I run without the subfolder being there), but they don't show up in Explorer. For comparison, I then tried a fresh WPF app that does the exact same thing but this behaves the same as the console app, no issue with the subfolder and file showing up after running the app. So my issue seems to be confined to WinUI only.
You should be able to reproduce by placing this code inside the main window of a fresh application (and creating a button called myButton if VS doesn't do that automatically) and comparing WinUI 3 with WPF.
public MainWindow()
{
InitializeComponent();
}
private void myButton_Click(object sender, RoutedEventArgs e)
{
// See https://aka.ms/new-console-template for more information
var destinationPath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Subfolder");
string sourceFilename = "C:\\Data\\Textfile.txt";
CopyFileToLocalExecutionPath(sourceFilename, destinationPath);
}
private string CopyFileToLocalExecutionPath(string sourceFilename, string destinationPath)
{
// Make sure that destination path exists before copying file
if (!Directory.Exists(destinationPath))
Directory.CreateDirectory(destinationPath);
// Assign full destination filename - local path + same as source filename
var destinationFilename = System.IO.Path.Combine(destinationPath, System.IO.Path.GetFileName(sourceFilename));
// Do not overwrite file if it's already there
if (!File.Exists(destinationFilename))
File.Copy(sourceFilename, destinationFilename);
return destinationFilename;
}

Related

Dealing with files in ASP.NET MVC

Im working on an ASP.NET MVC project that compiles input code to .exe file. Then, my code run this .exe. After success, I just delete that .exe file.
To relaese input code I'm using CSharpCodeProvider class. To run it, I'm using Process class.
Making and deleting exe files seems a little bit tricky to me, because I can't save that files into project directory directly while I'm debugging my program. I need to give special permissions to IIS_USERS. But what to do, when I want to release my project into production? How to deal with filepath? Where to save it?
Now, it looks like this:
string exeName = Path.Combine("C:\\Users\\User\\source\\repos\\proj\\solution\\obj\\Debug", "test.exe");
To deal with paths you should be using Server.MapPath("~") - This returns the physical Path to the root of the web application.
For example if you have a folder called "MyOutput" in the solution (in parallel to the Controller, Views etc. folders) then you can write Server.MapPath("~/MyOutput/"). This will resolve to a physical path like C:\Users\User\source\repos\proj\solution\MyOutput\
This way you do not have to bother about changing paths in local machines or web servers.
Also you should not be putting stuffs in the Obj or Bin folders which are not directly related to the web application.
More samples are available here: Server.MapPath("."), Server.MapPath("~"), Server.MapPath(#"\"), Server.MapPath("/"). What is the difference?
How about asking user where to save it?
You could have a form that ask for this path, username, password etc. Or even in a config <appSettings></appSettings>
Then you can use Impersonate to save it, execute it without any permission issue. You could use this to save it locally or to any network drive

Why cannot libspotify save to cache?

Running my C# application from Visual studio works fine (in this respect)
But when installing the application in my system (win7, .NET 4.0) I get problems with the cache.
These are the errors I get:
LogMessageCallback. Message:20:43:03.988 E [playlist:1978] Unable to save file: playlist.bnk
LogMessageCallback. Message:20:43:03.988 E [social-mgr:830] Unable to save file: social_stream.bnk
LogMessageCallback. Message:20:46:31.034 E [user_cache:107] Unable to save file: user-cache.bnk
LogMessageCallback. Message:20:43:04.988 I [c:/Users/spotify-buildagent/BuildAgent/work/1e0ce8a77adfb2dc/client/core/protocol/file_streamer_simple.cpp:769] Request for file 57a6ab34bad26645e2345a610ae652fe77f82afb complete (code: 0)
I have tried to deleted the entire cache library and it gets recreated when I start the app, so it can't be a matter of file privilege.
Since the cache does not seem to be valid my playlists are not accessible to me at startup.
I do log out properly.
Any explanation/workaround?
I think I've got it...
I search for the playlist.bnk file on my disk and found one under Spotify\bin\Debug\cache_location\Users\bes51659-user, that is from where I run my project with visual studio. "cache_location" in the path directed me to the settings_location argument in the config struct when creating the session. I had set it to const string "cache_location". I must have understood the explanation wrong:
https://developer.spotify.com/docs/libspotify/12.1.51/structsp__session__config.html#a342532432040d476aaaf73f10893d23b
The location where Spotify will write setting files and per-user cache items. This includes playlists, track metadata, etc. 'settings_location' may be the same path as 'cache_location'. 'settings_location' folder will not be created (unlike 'cache_location'), if you don't want to create the folder yourself, you can set 'settings_location' to 'cache_location'.
(a bit contradictory that the "cache_location" catalog was actually created under debug!)
The comment must mean that if I reuse the same location for setting_location as for cache_location I do not have to create it as it has already been created!
I do not know if libspotify did not have permissions to create the catalog "cache_location" under "program files", or if it expected it to be there and did not find it. But it does not matter. I have now changed both the locations to "c:\mySpotify" in the config struct and problem solved...
My only excuse is that google tells me that I'm not the first to have fallen into this pithole.

Finding Install path of a program

I am new to C# and I have made a simple Windows Forms Application that basically updates the persons files for a game.
They have to manually move and delete certain folders just to change version every time. I have successfully accomplished this.
However before I start giving it out I really should improve it. I know I need to change the name of the processes and remove my descriptions ETC.
I have stumbled onto an error and instead of me taking a guess I think it is best to get an opinion from a more experienced person about how to do this.
I am going to use Inno Setup to make the installer for my application, this way I can be sure it will go into their program files 32 and 64 bit. So I know this will be in program files.
So now I am wondering if I have done this the correct way or not? I was using this format to find their program files:
string programFilesFolder = Environment.GetEnvironmentVariable("PROGRAMFILES(X86)") ?? Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
However, would this work on all windows systems(XP, Vista, Win7, Win8) and is it completely accurate? I was going to use the above, and then use this:
string PATCHSELECTOR = Path.Combine(programFiles, #"PATCH SELECTOR");
if (Directory.Exists(PATCHSELECTOR))
{
string GamereliteFolder = Path.Combine(programFiles, #"GAMERELITE~1");
if (Directory.Exists(GamereliteFolder))
And then I move the files using the string method. If the file exists it is deleted before I copy the file over from PATCH SELECTOR to GAMERELITE.
Also will windows XP support using the .exe with an assembly resource embedded which is making the program need to be ran as administrator? I previously was making the assembly work through UAC however that wouldnt always work if they have UAC off or if it is XP so I thought I would try the admin assembly instead.
Can anyone possibly give me some insight, ideas or links?
For executables (not sure for websites & web application) this returns the directory where the executable lives (it's actually the base path where the framework will probe for Assemblies to load, 99% of the the that's the same thing).
System.AppDomain.CurrentDomain.BaseDirectory
This method works for any executable located in a folder which is defined in the windows PATH variable:
private string LocateEXE(String fileName)
{
string path = Environment.GetEnvironmentVariable("path");
string[] folders = path.Split(';');
foreach (var folder in folders)
{
if (File.Exists(Path.Combine(folder, fileName)))
{
return Path.Combine(folder, fileName);
}
}
return String.Empty;
}
Usage:
string pathToEXE = LocateEXE("Example.exe");
Reference:
how to find the execution path of a installed software
How can I get another application's installation path programmatically?
Couple things:
Among the already stated answers, Assembly.GetExecutingAssembly().Location will also give you the full file path of the currently "executing" Assembly. (Alternatively, GetCurrentAssembly)
If I'm reading your question correctly, you're trying to find both your own location as well as another application's. I would highly recommend seeing if the other application has a registry key that specifies the exact location - it'll make your copy step WAY more stable.

Using ClickOnce when i need to generate several large files?

I'm building a digital signage application and I want to deploy it using ClickOnce. (I feel this is the best approach.) When I start the application from Visual Studio (VS) it works great. The application downloads a lot of images from my web service and saves them to disk:
string saveDir = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory.FullName;
When I start my deployed application, it shows the splash screen and then disappears. The process keeps running, but the UI doesn't display. I'm wondering if my saveDir as shown above is giving me trouble?
How do I locate my installed application? (I need to make license files, etc.)
I'm not sure if this is the root of your problem, but I highly recommend you change the structure of how you store your application information.
When an application is installed through ClickOnce, the application is installed within the User's folder, and it's considerably obfuscated. Furthermore, locations may change with subsequent application updates, so you can not be guarantee than any cached, downloaded file will exist from update to update.
To solve this problem, ClickOnce does provide a Data directory, that is not obfuscated and can be used for caching local data. The only caveat is this directory is not available for non-ClickOnce instances of your application (like the version that is running within the VS debugger.)
To get around this, you should write a function that you can use to get your data directory, regardless of your method of distribution or execution. The following code is a sample of what the function should look like:
//This reference is necessary if you want to discover information regarding
// the current instance of a deployed application.
using System.Deployment.Application;
//Method to obtain your applications data directory
public static string GetAppDataDirectory()
{
//The static, IsNetworkDeployed property let's you know if
// an application has been deployed via ClickOnce.
if (ApplicationDeployment.IsNetworkDeployed)
//In case of a ClickOnce install, return the deployed apps data directory
// (This is located within the User's folder, but differs between
// versions of Windows.)
return ApplicationDeployment.CurrentDeployment.DataDirectory;
//Otherwise, return another location. (Application.StartupPath works well with debugging.)
else return Application.StartupPath;
}

Rename a running executable (exe) file [duplicate]

This question already has answers here:
Why does rename a loaded .net assembly work?
(3 answers)
Closed 5 years ago.
We are trying to push updates to multiple servers at once and my manager has found that it is possible to rename running .exe file. Using that knowledge he wants to rename a running exe and copy over a new version of said exe such that anyone running their in memory copy of foo.exe are fine and anybody who opens a shortcut pointing to foo.exe will get a new copy with updates applied.
I guess I need to clarify, He doesn't expect the old copy to magically update, he just expects them to keep running the old copy until they open the exe again, in which case it will then open the new one that has the name of the old one.
It sometimes throws an exception that the file is in use on his program but if he tries renaming it in a loop it will eventually succeed. On my machine I have yet to be able to get it to work even in a loop.
My first and main question is this: Is it ever acceptable to do this. Should renaming a running executable ever be a valid scenario?
Secondly, if it is a valid scenario then how could one reliably do this? Our current thoughts are try a bunch of times using File.Move (C#) to do a rename and if it doesn't work then write out to an error log so it can be handled manually.
An airplane mechanic and a surgeon meet in a bar. The mechanic says "you know, we have basically the same job. We take broken stuff out and put new, better parts in." The surgeon says "yeah, but you don't have to keep the plane flying as you're making the repairs!"
Trying to update an application by moving files while the application is running seems about as dangerous as trying to fix an airplane in flight. Possible? Sure. Greatly increased risk of catestrophic crash? Yep.
If the application you are updating is a managed application, consider using ClickOnce Deployment. That way, the next time someone runs the application, if there is a new version available it will be copied down and installed automatically. That's much more safe and pleasant than trying to mess with an application while its still running.
No, this is not acceptable. Do not do this. This is not a valid deployment mechanism. This should have been yours or his first clue:
It sometimes throws an exception that the file is in use on his program but if he tries renaming it in a loop it will eventually succeed.
And it won't work, anyway. His theory is quite wrong:
Using that knowledge he wants to rename a running exe and copy over a new version of said exe such that anyone running their in memory copy of foo.exe are fine and anybody who opens a shortcut pointing to foo.exe will get a new copy with updates applied.
Specifically, the copy in memory will not be automatically replaced with the new executable just because it has the same name. The reason that you're allowed to rename the executable in the first place is because the operating system is not using the file name to find the application. The original executable will still be loaded, and it will remain loaded until you explicitly unload it and load the new, modified executable.
Notice how even modern web browsers like Chrome and Firefox with their super fancy automatic, in the background, no one ever notices that they exist, updaters still have to close and relaunch the application in order to apply the updates.
Don't worry about shooting the messenger here. It's more likely that your customers and your tech support department will shoot you first.
See number 1.
In our organization, we solved the problem of Updates by having two release folders say EXE_A and EXE_B. We also have a release folder called EXE which only has links ALL of which points to either to EXE_A or EXE_B from which the user runs the applications.
When we publish a new version of the program, we publish it to the folder that is not referenced in the links and then update the links (EXE). In this way, you do not get into exceptions that users are holding the application / assemblies. Also if a user wants to run the updated version, all he need to do is close / re-execute the link in EXE folder.
If you use Windows Vista/Server2k8 or newer you could use mklink to create a symbolic link to the folder containing your application and start the application out of the "symblic linked folder" and then at the update create a new folder, e.g. "AppV2" and change the SymLink to that folder, so the next time the user restarts the application he starts it out of the new folder without noticing.
Renaming open files is ALWAYS a bad choice!
But in general I would think of a better deployment strategy anyway, because if you need to use such "hacks" it is always a messy situation. I don't know your application, but maybee ClickOnce would be a point to start, because you can configure it to check for updates on every start...

Categories