I'm trying to ensure that users of my C# .NET Core program always unzip it before running, however, I can't seem to find a good solution for this.
I tried using Assembly.GetExecutingAssembly().Location and checking if it contained Temp but it did not work as intended. My understanding is that when running an executable within a zipped file, Windows will automatically unzip (?) to a temp directory and then proceed.
Any ideas on how to go about doing this?
Thanks!
One solution might be that you put another file beside the exe file inside the zip file and make the exe find for this file in the same location of the exe.
When an exe is executed from inside a zip, the unzip program will unzip the exe but not the other file and checking for this file you can test whether just the exe was unzziped or if the whole files where unzipped and the guess it was installed.
(Anyway, I agree with Ian Kemp, as it seems to be an XY problem)..
I can think of two checks that will work in many cases. Obviously, these checks are not bullet-proof.
Most .zip viewers will extract the clicked .exe in a temporary folder.
Windows Explorer zip folder view runs the .exe from a temporary folder and sets the ReadOnly attribute.
Sample app
using System;
using System.IO;
namespace SimpleApp
{
class Program
{
private static bool IsLikelyRunFromZipFolder()
{
var path = System.Reflection.Assembly.GetEntryAssembly().Location;
var fileInfo = new FileInfo(path);
return fileInfo.Attributes.HasFlag(FileAttributes.ReadOnly);
}
private static bool IsRunFromTempFolder()
{
var path = System.Reflection.Assembly.GetEntryAssembly().Location;
var temp = Path.GetTempPath();
return path.IndexOf(temp, StringComparison.OrdinalIgnoreCase) == 0;
}
static void Main(string[] args)
{
var isTemp = IsRunFromTempFolder();
var isZip = IsLikelyRunFromZipFolder();
Console.WriteLine($"Run from TEMP folder? {isTemp}");
Console.WriteLine($"LIKELY run from Windows Explorer ZIP folder? {isZip}");
Console.ReadKey();
}
}
}
I disagree with the sceptics in this thread. Running an exe from a "zip folder" in Windows Explorer is indeed a common source of error. By showing a warning, inexperienced users could get some immediate advice.
Related
At work we use the EmbeddedFileProvider to load SQL files and access the files via relative paths to the resource like so: path/to/file.sql.
Yesterday I worked on a private project and thought I could use this to read some files I need in my program but it did not work and when I checked the output of Assembly.GetManifestResourceNames() I noticed the names of the resources did not include any path so I tried referencing them by just their filenames and it worked.
Today I checked out the same project on my Laptop and tried running it only to get an Exception in my resource loader, that the files could not be found. After checking GetManifestResourceNames() again I saw that the resource names changed the format to path.to.resource.txt. How can this be? I used the same dotnet sdk everytime and the only difference was the machine the code ran on. I even had the exact same OS installed.
public static class EmbeddedResourceLoader
{
public static byte[] Load(string name)
{
var provider = new EmbeddedFileProvider(Assembly.GetCallingAssembly());
using var resourceStream = provider.GetFileInfo(name).CreateReadStream();
using var reader = new BinaryReader(resourceStream);
return reader.ReadBytes((int)resourceStream.Length);
}
}
This is my implementation of the resource loader and the files are located relative to the project directory inside an assets folder.
I am creating a winforms project which checks files in folder. It only works when new file put in the folder. I am using FileSystemWatcher. It works fine on D drive but fails on C drive.
I gave EVERYONE full privilige on that folder
I tried publishing it with click once for full trust application. But it failed also with published edition
Tried to run exe file and visual studio as administrator. Nothing changed
Tried absolute path and Extra filters.
It does not raise any errors. Simply does nothing.
Non Working Code
string desktop = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
teklifwatcher.Path = desktop+"\\XMLTeklif";
teklifwatcher.NotifyFilter = NotifyFilters.LastWrite;
teklifwatcher.Filter = "*.xml";
teklifwatcher.Changed += new FileSystemEventHandler(TeklifXML);
teklifwatcher.EnableRaisingEvents = true;
private void TeklifXML(object sender, FileSystemEventArgs e)
{
//dostuff
}
I solved this problem on my own. I assume filesystemwatcher can't watch c: drive files directly. Because of the security reasons.
But we can use Program Files (X86) folders just like any other application.
Anyone who have similar problems just use filesystemwatcher on a folder at program filex(x86). And give permissions to that folder. Voila! It works
I want to be able to set default folder and file creating inside of the folder where the app is installed? Because this app will be used on multiple machines so I cannot specify path like C://Users/PcName/etc.. Is there any very simple way of doing it?
What you are trying to do is not advisable; if your application is installed using recommended default methods (following Microsoft guidelines) the app will be in a directory under C:\Program Files (or where the program files folder may be redirected) and the user that runs the app will not have write access to that directory, so the directory creation will fail.
That said, you cannot use the Environment.CurrentDirectory, because it may or may not be the directory where your application's executable files reside, neither CurrentDomain.BaseDirectory, because that is not significative too (documentation says it's the directory where the loader will search for assemblies, but that may or may not be the directory of your application's executable files).
Copying from this other answer, the correct way to find the directory of your assembly is
public static string AssemblyDirectory
{
get
{
string codeBase = Assembly.GetExecutingAssembly().CodeBase;
UriBuilder uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
return Path.GetDirectoryName(path);
}
}
Once you have the path, you can try to create the directory with System.IO.Directory.CreateDirectory() and a file with System.IO.File.WriteAllText() or its siblings, or any other standard method of creating files.
You may also want to use the newer Assembly.GetExecutingAssembly().Location property, and use Path.GetDirectoryName() on that.
This applies to both web and windows apps:
Environment.CurrentDirectory = AppDomain.CurrentDomain.BaseDirectory;
You can get the path for the executable using this code (most of the time, actually it returns the current working directory)
System.Environment.CurrentDirectory
If you only specify a path that doesn't start with a drive letter then the path will be relative to where the application is running. e.g. The following program will create a folder and file in the application's local folder.
class Program
{
static void Main(string[] args)
{
var installedLocation = Directory.GetParent(Assembly.GetExecutingAssembly().Location);
var di = installedLocation.CreateSubdirectory("MyFolder");
File.WriteAllText(Path.Combine(di.FullName, "File.txt"), "This will be written to the file");
var installedPath = AppDomain.CurrentDomain.BaseDirectory;
var di2 = Directory.CreateDirectory(Path.Combine(installedPath, "MyFolder2"));
File.WriteAllText(Path.Combine(di.FullName, "File2.txt"), "This will be written to the file");
}
}
I'm running my console app from a plain old command prompt. I'm running it two ways:
using a relative path to the executable from what I think is the working directory. i.e.
C:\Working>.\path\to\my.exe -fileToRead file.txt
using a folder in my $PATH$. I.e.
C:\Working>my.exe -fileToRead file.txt
file.txt is in C:\Working and my.exe is C:\Working\path\to. my.exe will output an XML log file to the working directory. In my mind that, that should be C:\Working, but the file actually ends up in C:\Working\path\to. This doesn't jive with all other command line applications.
I'm not doing anything weird or non-standard (that I know about). I've tried just using the file name for the XML file, "TestResult.xml" and also Path.Combine(Environment.CurrentDirectory, "TestResult.xml"). Both end up in the executable directory, not the directory from which I'm running. The command-line parameter file argument is being read properly, so I know that's working.
Clarification: Basically, my problem is that Environment.CurrentDirectory and Assembly.GetExecutingAssembly().Location are the same directory, but shouldn't be.
What am I doing wrong here? And how do I get the directory from which I execute, not the path to the executable? (I realize I have the exact opposite problem of many questions on stackoverflow)
The results you have using Environment.CurrentDirectory are not the one I get with a very simple program like this
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Environment=" + Environment.CurrentDirectory);
Console.WriteLine("Assembly=" + Assembly.GetExecutingAssembly().Location);
}
}
}
Executing this little app from the command line gives always for the first line the directory where the command prompt is running and the second line always the directory where the assembly is located.
So, I suppose that your problem is caused by something different. Probably a change in the current directory.
In my project \debug directory i have the program exe file for example:
test.exe
Now once i will run this test.exe from c:\
And in the second time i will copy the test.exe to d:\ and run it from there.
In my code i have this line:
string programFilesX86 = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ProgramFilesX86) + "\\Diagnostic Tool\\7z.dll";
Instead the program files x86 how can i get each the directory from where im running the exe file ?
One way (sure fire way also in .Net CE) is
string path = Path.GetDirectoryName(
Assembly.GetEntryAssembly().GetModules()[0].FullyQualifiedName);
or
string path = Path.GetDirectoryName(
Assembly.GetEntryAssembly().Location);
This will prevent Shortcut's from setting the applications CurrentDirectory or StartupPath which could technically be different from it's execution path (ClickOne programs for example).
You can get the running directory by doing:
Application.StartupPath
You can read more about it here
Alternatively you can try Environment.CurrentDirectory but that may not yield you the results you want because of short cuts and other ways of getting to your file.
http://msdn.microsoft.com/en-us/library/system.environment.currentdirectory.aspx
You can also do:
System.IO.Path.GetDirectoryName(Application.ExecutablePath);
Or
System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location);