Mono executes program with wrong current directory - c#

I have a strange problem. I have written a custom spamfilter in C# .NET 4.6 for personal use. And I put this program up to my Raspberry Pi.
I have tested many times the program, and everything worked fine, but when I created a cronjob I have noticed that the program never writes log.
As I started to test again I have find out, that the problem is, when I start the program from the program's base dir, it works fine:
> cd path/to/program
> mono program.exe
but when I start it from another directory with absolute path:
> mono /absolute/path/to/program/program.exe
The required files' relative paths go wrong.
For example if I'm in the /home directory, and my program is in /home/user/program directory, and I run this:
> mono /home/user/program/program.exe
I get " Could not find file "/home/xyz.txt" "
I have tried to create absolute paths from relative paths in the program using Path.GetFullPath() and Environment.CurrentDirectory but nothing changed.
So my question is: Is there any option to run a mono program in another directory with correct current directory?
I know that a bash script could execute my first solution (cd and then mono) and it could be used in cron, but I want to know if there is a simplier solution.

The current directory is correct, it is the current directory of where you are starting the process, and that process is mono in this case, and not the location of the CIL-based assembly that mono is loading.
What it sounds like you want is the path of the ExecutingAssembly.
This works on x-plat (Windows/Linux/macOS):
string path = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);
Console.WriteLine(path);
Ref: https://msdn.microsoft.com/en-us/library/aa457089.aspx

Related

How do I keep my application's working directory consistent even when being run from a batch file? [duplicate]

I see that there are some ways to get the application folder path:
Application.StartupPath
System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().Location)
AppDomain.CurrentDomain.BaseDirectory
System.IO.Directory.GetCurrentDirectory()
Environment.CurrentDirectory
System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase)
System.IO.Path.GetDirectory(Application.ExecutablePath)
What is the best way depending on the situation?
AppDomain.CurrentDomain.BaseDirectory is probably the most useful for accessing files whose location is relative to the application install directory.
In an ASP.NET application, this will be the application root directory, not the bin subfolder - which is probably what you usually want. In a client application, it will be the directory containing the main executable.
In a VSTO 2005 application, it will be the directory containing the VSTO managed assemblies for your application, not, say, the path to the Excel executable.
The others may return different directories depending on your environment - for example see #Vimvq1987's answer.
CodeBase is the place where a file was found and can be a URL beginning with http://. In which case Location will probably be the assembly download cache. CodeBase is not guaranteed to be set for assemblies in the GAC.
UPDATE
These days (.NET Core, .NET Standard 1.3+ or .NET Framework 4.6+) it's better to use AppContext.BaseDirectory rather than AppDomain.CurrentDomain.BaseDirectory. Both are equivalent, but multiple AppDomains are no longer supported.
Application.StartupPathand 7. System.IO.Path.GetDirectoryName(Application.ExecutablePath) - Is only going to work for Windows Forms application
System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().Location)
Is going to give you something like: "C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\Temporary ASP.NET Files\\legal-services\\e84f415e\\96c98009\\assembly\\dl3\\42aaba80\\bcf9fd83_4b63d101" which is where the page that you are running is.
AppDomain.CurrentDomain.BaseDirectory for web application could be useful and will return something like "C:\\hg\\Services\\Services\\Services.Website\\" which is base directory and is quite useful.
System.IO.Directory.GetCurrentDirectory() and 5. Environment.CurrentDirectory
will get you location of where the process got fired from - so for web app running in debug mode from Visual Studio something like "C:\\Program Files (x86)\\IIS Express"
System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase)
will get you location where .dll that is running the code is, for web app that could be "file:\\C:\\hg\\Services\\Services\\Services.Website\\bin"
Now in case of for example console app points 2-6 will be directory where .exe file is.
Hope this saves you some time.
Note that not all of these methods will return the same value. In some cases, they can return the same value, but be careful, their purposes are different:
Application.StartupPath
returns the StartupPath parameter (can be set when run the application)
System.IO.Directory.GetCurrentDirectory()
returns the current directory, which may or may not be the folder where the application is located. The same goes for Environment.CurrentDirectory. In case you are using this in a DLL file, it will return the path of where the process is running (this is especially true in ASP.NET).
For a web application, to get the current web application root directory, generally call by web page for the current incoming request:
HttpContext.Current.Server.MapPath();
System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath;
Above code description
I started a process from a Windows Service over the Win32 API in the session from the user which is actually logged in (in Task Manager session 1 not 0). In this was we can get to know, which variable is the best.
For all 7 cases from the question above, the following are the results:
Path1: C:\Program Files (x86)\MyProgram
Path2: C:\Program Files (x86)\MyProgram
Path3: C:\Program Files (x86)\MyProgram\
Path4: C:\Windows\system32
Path5: C:\Windows\system32
Path6: file:\C:\Program Files (x86)\MyProgram
Path7: C:\Program Files (x86)\MyProgram
Perhaps it's helpful for some of you, doing the same stuff, when you search the best variable for your case.
In my experience, the best way is a combination of these.
System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase
Will give you the bin folder
Directory.GetCurrentDirectory()
Works fine on .Net Core but not .Net and will give you the root directory of the project
System.AppContext.BaseDirectory and AppDomain.CurrentDomain.BaseDirectory
Works fine in .Net but not .Net core and will give you the root directory of the project
In a class library that is supposed to target.Net and .Net core I check which framework is hosting the library and pick one or the other.
To get the path to .exe for simple desktop applications I use
Path.GetDirectoryName(Assembly.GetEntryAssembly().Location)
which returns path to .exe.
Also be aware that for several domains the .exe for default domain will be returned, or the .exe executed by first call of ExecuteAssembly(String) and that if the entry is unmanaged, the null will be returned.
Be careful with GetExecutingAssembly(), naming was confusing for me, as I have expected to get the .exe, but it returns the .dll or .exe, where the code is placed, so in case of GetExecutingAssembly() placed in library it returns the library.
I have used this one successfully
System.IO.Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName)
It works even inside linqpad.
Root directory:
DriveInfo cDrive = new DriveInfo(System.Environment.CurrentDirectory);
var driverPath = cDrive.RootDirectory;
If you know to get the root directory:
string rootPath = Path.GetPathRoot(Application.StartupPath)
this one System.IO.Path.GetDirectory(Application.ExecutablePath) changed to System.IO.Path.GetDirectoryName(Application.ExecutablePath)

batch works fine but "windows cannot find devcon.exe" in c# program

I have a batch file which disables and enables some driver using windows devcon.
when I run this batch file by double-clicking on it it works fine.
However, I tried to run it from a C# program that I wrote using this line:
System.Diagnostics.Process.Start("C:/*path to file*/file.bat");
it runs fine until it tries to open the devcon.exe and I get the following message:
after that it continues to run smoothly.
any ideas why it doesn't work from the C# program?
p.s
I can't post the batch code due to IP issues...
The problem is - as often - the "working directory". When you double-click something in the Explorer, the working directory is set to the current folder, so from the batch file's point of view it's current directory is its own directory.
When you execute a C# application, usually the working directory is the directory of the application's exe file, but not necessarily (for example if the application is run using a link, you can specify a different working directory). That's why, to find the application EXE file's directory it is not save to use GetCurrentDirectory.
So what happens is that the application runs the batch file, but with the application's directory, not the batch file's directory, as working directory. An alternative to an explicit cd within the batch file would be to specify the working directory when calling Process.Start.
ok, after a little bit of research I found this simple solution:
simply changing to the directory of the devcon.exe (using cd command) at the beginning of the batch code, i.e:
cd "C:/*path to the directory of devcon.exe*"
#rest of the code

Command line current working directory in a console app [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How do I find out what directory my console app is running in with C#?
How to get current working directory of a console that runs a program so I could resolve relative paths passed as program args?
Lets say I've put my program here: c:\tools\program.exe
But I'm invoking it from various places. Lets say I'm here: C:\Users\Me\Documents\ and I run this command program.exe --src=somefile.txt --dest=subdir\otherfile.txt
Environment.CurrentDirectory and System.Reflection.Assembly.GetExecutingAssembly().Location will return c:\tools but I would like to be able to resolve somefile.txt and subdir\oterfile.txt paths that are relative to C:\Users\Me\Documents\.
====== UPDATE ======
Thank you for your help. It seems that Environment.CurrentDirectory works as expected. It turned out that in my case the problem was caused by Xenocode's Postbuild tool (now called Spoon Virtual Application Studio) that I occasionally use to "pack" all program's files (including dlls, config, etc.) into one executable. It's very handy, but in this case the "virtualization" feature messed up my program's Environment variables. I've managed to solve that issue.
Environment.CurrentDirectory gives you the Current Working Directory
Let me show a simple example:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Startup: " + Environment.CurrentDirectory);
Environment.CurrentDirectory = #"D:\temp";
Console.WriteLine("After:" + Environment.CurrentDirectory);
}
}
Now I create two folders named D:\temp and D:\temp2
I put the executable in D:\temp
Open a command prompt and set the current working directory with cd D:\temp2
From that directory I run ..\temp\mytestapp.exe
the output is
Startup: D:\temp2
After: D:\temp
As a curiosity:
this was the documentation for Environment.CurrentDirectory in Net 1.1
Gets and sets the fully qualified path of the current directory; that
is, the directory from which this process starts.
and this is the documentation in NET 4.0
Gets or sets the fully qualified path of the current working
directory.
Use the Environment and Path classes. http://msdn.microsoft.com/en-us/library/fyy7a5kt.aspx
var fqn = Path.Combine(Environment.CurrentDirectory, "somefile.txt")
But to play with your documents you need:
var fqn = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.Person),
"somefile.txt");
`fqn' is TLA for fully qualified name

C# Unit testing - Weird project directory

I have a situation where I am running a unit test and trying to get the project base path and it is giving me a odd output.
D:\VSCode\Bob.Smith\Projects\MySolution\DEVELOPMENT\MyClasses\MyProject\TestResults\Jacob.Freeman_P1004 2012-11-26 09_21_33\Out
Is this a known issue with VS or just something that I have missed out? I am using the following line with no other code to get the directory information.
string output = null;
output = Environment.CurrentDirectory;
I have tried different various ways to get the directory information such as the following, but it still gives me the same output.
output = System.IO.Directory.GetCurrentDirectory()
Thanks in advance.
As documentation states Environment.CurrentDirectory:
Gets or sets the fully qualified path of the current working directory.
The current working directory is not necessarily the projects base path - It is when you start an exe. While running test this path is set by test runner. This variable can change while program is running.
In application you should use Assembly.GetEntryAssembly().Location to get location of you exe file. This will not work for tests as they are executed by test runner.
If for test purposes you simply need location of tested dll file you can use typeof(SomeTypeThatIsDeclaredInThatDLL).Assembly.Location.
It works as designed. Your tests executes in different directory against bin.

Image.Save to relative path doesn't save where expected

The documentation doesn't explain the behavior when passing in a path such as "myFile_temp.jpg" but I would assume that it would save the the application directory because this is a relative path, relative to the application we are currently running.
I think that the problem can be solved by prepending the current application directory to my temp file name using
string appPath = Path.GetDirectoryName(Application.ExecutablePath);
Sure there are lots of ways to do it, but this should work.
My issue is I'd like to know why this is happening rather than just throw a patch on it and ship it back out to the users.
Code is WPF, C# project compiled with .NET 4.0 and Visual Studio 2010 and runs on a lot of different machines. Mostly 32-bit XP,while the dev machine is a 64-bit Windows 7.
Can any one explain this behavior and why it's occuring?
Edit
The files will on occasion be saved to the directory the user selected files from to manipulate. They resize them, the program keeps track of the size percent for each of the file paths. When the user is finished they will click done and the program will go through each of the file paths, create a copy, resize the image and then save it with a _temp on the end.
Take note that it doesn't always do it and it when it does it doesn't do it for all the files they touched.
It works as s expected. You just didn't expect valid behavior. Lets assume that your app is placed in c:/superapps/myapp.exe. You opened command line and you're in C:\ which means that this your current working directory.
You can still run your app by ./superapps/myapp but your working directory is still C:\. And this will be working directory of your app in this case, not the directory you placed the binaries.
That is why it may not have permission or save data in some unexpected by you location. You should always think that your app could be run just like any other command like dir. It will be working in the place where user is currently standing (his current working dir) not in the place it's binaries are stored in

Categories

Resources