Using WritePrivateProfileString to write path issue - c#

I am using WritePrivateProfileString in c# (through DllImport) to store paths taken from textboxes on the interface. And the .ini file name is hardcoded in my application
string ini_file = ".\\config.ini";
However, when the file writing happens, the configuration file is written to the first path taken from the interface instead of writing it to the exe directory. Which is quite odd.
Debugging shows that the values are sent correctly to the WritePrivateProfileString but it still is written to the wrong location. Anyone knows why is that happenening?

I'd guess that something is changing the working directory of your process, most likely your code in the process. Note that the documentation has this to say:
If the lpFileName parameter does not contain a full path and file name for the file, WritePrivateProfileString searches the Windows directory for the file. If the file does not exist, this function creates the file in the Windows directory.
Now my guess is that this applies if you supply just a file name. Because your file name starts with . I believe that will force the function to start from the current working directory.
Having said all of that, and no matter what the cause of the problem is, you should use a fully-qualified path in order to make sure the file is written where you want it to be written. Whenever you want the file to go in a specific directory, it's always easiest to force that by using fully-qualified paths.
You can find the path to your executable using Application.ExecutablePath and then remove the file name part.
Another point to make is that the same directory as the executable may be a bad choice. If your program is installed under the Program Files directory then the directory which contains the executable will not be generally writeable. I think you should consider using a directory under in the user profile. Look for one of the Environment.SpecialFolder values.

Further to David Heffernan's answer - you can use
Path.GetDirectoryName(Application.ExecutablePath);
to safely get just the running application's folder part.
If you're in a dll rather than an executable, you can use
Path.GetDirectoryName(Assembly.GetAssembly(typeof(MyClass)).CodeBase);
Both require System.IO, and were originally posted here. Second example also requires System.Reflection).

Application data files are supposed to be written to the LocalApplicationData special folder.
string path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData);
You typically will not have permissions to write into the Program Files folder etc.

Related

Get a file path C#

I have a .txt file that I need to read in my program. For the moment I have the directory hardcoded as such:
file = new StreamReader(#"C:\Users\<username>\Documents\File.txt");
However that will (obviously) not work on any other PC that does not have that access to altering the code, or (by some strange happenstance) the same directory as the original code.
How can I get the full file path to set it in my program using C#?
You could create the file in their Application Data directory (they could still find it if they wanted to, but at least it wouldn't be as obvious as the My Documents folder).
When you want to access it, use the Environment class. There are methods for locating special folders for the current user, without resorting to hard-coded paths:
var filePath = Path.Combine(Environment.GetFolderPath(
Environment.SpecialFolder.ApplicationData), "File.txt");
Option 1:
Application.StartupPath can be used for the purpose.
It gets the path for the executable file that started the application, not including the executable name.
Keep File.txt with your executable.
Option 2:
Use Environment.SpecialFolder.ApplicationData
It gives directory that serves as a common repository for application-specific data for the current roaming user.
NOTE: If you want to restrict the user to look into the contents of File.txt then you might need to encrypt the contents.

streamreader paths to file

While in a search to learn more about the streamreader, i came across this from
StreamReader path changes automatically
post #2 by
Hans Passant
var exedir = Path.GetDirectory(Assembly.GetEntryAssembly().Location);
var path = Path.Combine(exedir, #"Config\launcher.txt"); using (var
reader = new StreamReader(path)) {
//... }
Now I understand the "never hard code the file" part, In delphi, I am able to specify a custom file path based on the files in a directory ie.
Read all file names in a directory, user chooses a file name, and then I read the contents of the file
Delphi, I just used a string and added the file name to the end, how does that differ from the code above, and is there a different method to this above?
and btw. Could someone just explain in a bit more detail, the methods and variables used and why (i am still new to c#)
I think what Hans is saying here is that if you are expecting the files to be relative to your application code, then expliclty look relative to your application code. Don't rely on the current directory being the base location of your application, as that is not guaranteed (and can change during your application's execution). So what the code does is:
obtain the entry-point assembly (the assembly with the Main method that was executed at startup), and obtion the file location of that assembly: Assembly.GetEntryAssembly().Location
obtain the directory from this file path: var exedir = Path.GetDirectory(...);
construct a path relative to this directory: var path = Path.Combine(exedir, #"Config\launcher.txt");
create a reader based on the final path: using (var
reader = new StreamReader(path)) ...
GetEntryAssembly returns, according to the documentation:
The assembly that is the process executable in the default
application domain, or the first executable that was
executed by AppDomain.ExecuteAssembly.
And the Location property returns the full path of the assembly. So
Assembly.GetEntryAssembly().Location
is the full path of the executable. In Delphi you would write ParamStr(0) or Application.ExeFileName.
Path.GetDirectory strips off the file name and leaves the directory. In Delphi you'd use ExtractFilePath.
And Path.Combine simple joins two path components, adding a path separator if necessary. In Delphi you'd use TPath.Combine from the IOUtils unit.
So the code in the question constructs the full path to a file named
<exedir>\Config\launcher.txt
where is the directory containing the main executable assembly which is of course known only at runtime.
There's really nothing particularly different between the way things are done in Delphi and C#. To construct the same path in either language you take the exact same steps modulo syntax/method name differences.
Possibly you are in the habit of assuming that the working directory is the directory containing the executable assembly. If so, lose that habit. The working directory is meaningful for a console application and for such an application it can be considered an input to the program. But don't expect it to be stable in a GUI program. And never assume that the working directory contains the executable. That assumption is not valid.
On the other hand, perhaps you really do want a path relative to the working directory. In which case simply supply the filename and let the system take care of the rest.

c# writing to a file without full path

If I use this code
File.AppendAllText("C:/Users/Michael/Documents/Visual Studio 2010/Projects/PuzzleGame/PuzzleGame/PuzzleGameContent/player.TXT", "hi");
The file will save and add "hi" to the end of it. However, if I do something like this:
File.AppendAllText("player.TXT", "what is good?");
The file won't have a "what is good?" at the end of it. I can read files just fine using only the "player.TXT" filename but I can't write to a file using only that. Can anyone help me?
Your working directory is wherever the .exe is (unless you change it). So you see, when you compile, the exe ends up in the bin folder, so your player.txt would need to be there, not with your source.
edit:
I bet you're appending to player.txt THEN you read it and that's why it worked fine, because you created a new one in your bin folder. Otherwise, read would not have worked. If you go in your bin folder and delete player.txt, your readfile shouldn't work.
Both forms are perfectly valid. The likely scenario is that your second version is simply writing to a file at a different location, because not specifying the path will default to the current directory.
If you don't include a path, you'll want to ensure the current directory is valid for accessing the file.
Most likely there are two files on the file system, one in the directory that is explicitly defined in the first example and one where the executable is running in the second example since no explicit path was defined in the parameter of the method call.
From MSDN:
Given a string and a file path, this method opens the specified file,
appends the string to the end of the file, and then closes the file.
The file handle is guaranteed to be closed by this method, even if
exceptions are raised.
The method creates the file if it doesn’t exist, but it doesn't create
new directories. Therefore, the value of the path parameter must
contain existing directories
.
The problem is that AppendAllText is a method which will create the file if it doesn't already exist. So when you use an incomplete path it is unsure whether to create a new file in a base directory or add to an already existing file. If you can't use the full path for whatever reason, you could get the current working directory using something like:
File.AppendAllText(System.Environment.CurrentDirectory + "player.TXT", "what is good?");
So long as the current directory is correct, it'll work the same as your first working example.

File Not Found Exception thrown for XML file possibly caused by Outlook

I'm getting a file not found exception when my application is called by Outlook. It's called when an email is saved to the computer the app is called and performs an action on the saved message.
My app uses a XML file to store configurable settings but this file can't be found when Outlook calls to executes the application. If I run the program manually then it works fine.
The interesting thing about the exception is this:
System.IO.FileNotFoundException: Could not find file 'C:\Program Files\Common Files\System\MSMAPI\1033\settingsOpened.xml'.
Why does Outlook think that the file is here ? This isn't the path for the file but I'm sure is related to Outlook. The way I'm referencing the path in the code is just:
XmlDocument xDoc = new XmlDocument();
xDoc.Load("settingsOpened.xml");
With the file being in the same folder as the .exe. I don't want to hard code the full path in for the XML files either.
Any help would be greatly appreciated.
Thanks,
Ross
From the code shown I would assume that it takes the current path as the location to look for the file. The current path is a bit unpredictable as certain operations effect its value and the value is persisted on each call. I.e. when another part of your application, or even another applciation, sets the current path this value is used the next time around. To set the current path it is enought to use a common dialog to browse for a certain file.
In your case I'd try to either to
specify the path explicitly by deriving it from one of the well known folders (e.g. the user's app path - look for Environment.GetFolderPath and Environment.SpecialFolder)
or
to resolve the path relative to your Dll's assembly path.
To find the assembly path for myType you can use the following code:
String strPath = System.IO.Path.GetDirectoryName(typeof(myType).Assembly.CodeBase);
In either case you should consider that in newer windows operating systems the user does not have write access to all paths of the system drive.

Quick Question: How can I make sure the current directory is not changed because of OpenFileDialog?

say my application is installed in C:\programfiles\xyz\ I've some setting & other data files (*.dat files) in this directory too.
My application uses OpenFileDialog to open an image. Problem is that when ever user browses to some directory (say MyPictures) for an image. The current working directory of the application becomes that directory (MyPictures in this case).
After user inputs the image. I do some processing over it and save some values to imagedata.dat which will be located in the path where original application is installed.(C:\programfiles\xyz here )
In my code I'm just using FileStream("imagedata.dat",...,...) without any path prefix before the filename. Problem here is that application is crashing because it is searching for imagedata.dat in 'MyPictures\imagedata.dat'.
How can I avoid this?
You should be using absolute path names when saving data to files. The current working directory is controlled by the user, not by you (for example, if they launch your process from a shortcut then the working directory could've been changed before your process even starts up).
Also, you should never save anything under C:\Program Files during normal use. Doing this means your program needs to be running as an administrator, and unless you're doing administrator-y things then you should be able to run it as a regular user.
The correct thing to do in your case is to use the Environment.GetFolderPath() function to get the location of the ApplicationData folder and save your data under there. Just choose a sub-directory based on your application's name.
You could save it to GetCurrentDirectory then restore with SetCurrentDirectory. However, I agree wih codeka that using the appropriate GetFolderPath (probably ApplicationData, CommonApplicationData or LocalApplicationData) is a better solution.

Categories