Writing to a subfolder in c# - c#

I'm having a little bit of trouble writing to a text file within a folder I tried to create. It said I didn't have access to the path 'C:\'
Could anyone tell me why and how to fix it? Thanks!
string file_name = Environment.CurrentDirectory;
file_name += #"\.";
file_name = (string)combobox1.SelectedValue;
file_name += #"\.";
file_name += (string)combobox2.SelectedValue;
TextWriter name = new StreamWriter(file_name);
EDIT: Here's the new code after revisions...
var location = Path.Combine(Environment.CurrentDirectory, (string)combobox1.SelectedItem);
Directory.CreateDirectory(location);
var path = Path.Combine(location, combobox2.SelectedItem);
TextWriter name = new StreamWriter(path, true);
My goal is to write a text file to \\.txt
Could anyone tell me how? Thanks!

have you checked the value of file_name to make sure is a valid Path?
you have missed a concatenation anyway at line number 3
string file_name = Environment.CurrentDirectory;
file_name += #"\.";
file_name += (string)combobox1.SelectedValue; // <--
file_name += #"\.";
file_name += (string)combobox2.SelectedValue;
TextWriter name = new StreamWriter(file_name);

The account that the application is running under does not have write permissions in the location you are trying to save the file to.
This article goes over how to resolve this issue:
http://www.phdcc.com/findinsite/instperm.htm

You should be using Path.Combine():
var fileName = Path.Combine(Environment.CurrentDirectory, (string)comboBox1.SelectedValue,
(string)comboBox2.SelectedValue);
If at that point it still doesn't work, at least you'll know it's actually a permissions/existence/etc. issue, rather than an issue with the way you've constructed the file name.

The solution here is a combination of what everyone else has said.
As has already been pointed out, this line:
file_name = (string)combobox1.SelectedValue;
is incorrectly doing an assignment (=) instead of a concatenation (+=). This means that if comboxbo1.SelectedValue is null, your path becomes \., which is the root directory of the drive.
You need to remember that it's legal for SelectedValue to be null, because a combo box can have an empty selection. You need to handle that case, perhaps by disabling your save functionality until the combo boxes have valid selections.
This isn't really a problem with permissions; it's unlikely that you actually need or intend to write to the root directory, which is why you aren't given that permission in the first place.

Related

Which directory opens when ShowDialog() is performed [duplicate]

using (var openFileDialog1 = new OpenFileDialog())
{
openFileDialog1.Reset();
if (!string.IsNullOrEmpty(ExcelFilePath))
{
string fileName = Path.GetFileName(ExcelFilePath);
string fileExt = Path.GetExtension(ExcelFilePath);
//Avoid "you can't open this location using this program file" dialog
//if there is a file name in the path strip it )
if (!string.IsNullOrEmpty(fileName))
initialDirectory = Path.GetDirectoryName(ExcelFilePath);
//if not let it be
else
initialDirectory = ExcelFilePath;
openFileDialog1.InitialDirectory = initialDirectory;
}
else
openFileDialog1.InitialDirectory = "c:\\";
openFileDialog1.Filter = "Excel files (*.xls or *.xlsx)|*.xls;*.xlsx";
//openFileDialog1.Filter = "xls files (*.xls)|*.xls|xlsx files(*.xlsx)|.xlsx";
openFileDialog1.FilterIndex = 2;
openFileDialog1.RestoreDirectory = false;
openFileDialog1.CheckFileExists = true;
openFileDialog1.CheckPathExists = true;
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
var browseSelectionMade = BrowseSelectionMade;
if (browseSelectionMade!=null)
browseSelectionMade(this, new DataEventArgs<string>(openFileDialog1.FileName));
}
}
Regardless of whether or not I set RestoreDirectory to true, I will always browse to the LAST used directory if my initial directory is set to a path that doesn't exist. Where is the last used directory saved by OpenFileDialog? And is there a way to override this behavior? (e.g. I always want to set it to C:\ if the initial directory doesn't exist?)
It seems like all you need to do is the following:
string path; // this is the path that you are checking.
if(Directory.Exists(path)) {
openFileDialog1.InitialDirectory = path;
} else {
openFileDialog1.InitialDirectory = #"C:\";
}
That is unless I'm missing something.
Where is the last used directory saved?
It is stored in the registry. The exact location depends on the Windows version, for Win7 it is HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32. A quick look with regedit ought to convince you that you don't want to mess with that.
The simple workaround is to provide a valid path. If the one you calculate isn't valid, Directory.Exists returns false, then provide a valid one. Like the Documents folder returned by Environment.GetFolderPath(). Then again, nothing wrong with the last used one either, the user will easily recognize it with good odds that it happens to be close to the desired one.
I don't think there is anything built in for that. Just check before you open the dialog:
if (!Directory.Exists(initialDirectory))
{
openFileDialog1.InitialDirectory = #"C:\";
}
Check to see if the ExcelFilePath exists, you check to see if it's null or empty, however if before your block you check to see if the directory exists, and if it doesn't reset the value to an empty string you should be golden.
(yes you'll need to apply your file name logic etc earlier) however once you've parsed all of that out, it's trivial to determine if the directory exits
if (!Directory.Exists(excelPath))
{
ExcelFilePath = String.Empty;
}
Also, to set the default extension you should set FilterIndex property instead of DefaultExt. see: https://stackoverflow.com/a/6104319/381082
Here's a good article on the OpenFileDialog in C#: http://www.c-sharpcorner.com/uploadfile/mahesh/openfiledialog-in-C-Sharp/
For future me
remember to do:
try
{
result = dialog.ShowDialog(Window);
}
catch
{
dialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
result = dialog.ShowDialog(Window);
}
This helps in the situation when user opened file from location, that does not longer exists (ex. USB stick, mapped network drive) - ShowDialog throws exception if InitialDirectory is invalid.
Edit: After consulting with a more knowledgeable friend, perhaps the better solution is, in hindsight, obvious. Just store your own registry key in HKEY_CURRENT_USER\SOFTWARE\YourCompanyOrAppName\Whatevs or something similar (not sure on best practices, or which folders you have read/write access to, do your own research in that) and avoid this problem altogether. By simply letting the user navigate to where they want once, and then storing the path in the registry (as a normal string, and not a PIDL) and retrieving that path the next time. For reference, see the MSDN articles on the Registry and RegistryKey classes, and their example in the RegistryKey/Methods/SetValue article. Still, I'll leave this post as is, as a point of curiosity, or if someone has a very specific problem and needs this solution. As always, good luck!
For any poor soul wandering through here in the future, it seems I figured out how to find the last used directory. Just like stated previously, it's stored in the registry, more specifically in HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\OpenSavePidlMRU\
Here is a set of folders for each file extension, including a "*" for unknown file extensions. I'll do this for txt files, change the path as needed. To access this path we make a RegistryKey and call OpenSubKey (BTW, full code below)
string RegistryPath = #"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ComDlg32\\OpenSavePidlMRU\\txt";
RegistryKey rk = Registry.CurrentUser.OpenSubKey(RegistryPath);
In here is a set of entries which all contain PIDLs (we'll get to that) of the last opened or saved items.
DO NOTE: the folder name OpenSavePidlMRU, i've seen called just OpenSaveMRU, and seems to be for versions older than win 10.
Also in here is an entry called "MRUListEx", MRU stands for "Most Recently Used". In this entry is an index of which item was... well, most recently used. So if I have 10 entries, named 0 to 9, and 9 was the last used, the first byte in MRUListEx will be 0x09. So for:
byte[] mrulistex = (byte[])rk.GetValue("MRUListEx");
byte Last = mrulistex[0];
Last will equal 0x09 (on my system)
Then we call GetValue again but for that entry
byte[] LastPathByteArray = (byte[])rk.GetValue(Last.ToString());
And here's where things get problematic, as this won't return a byte array where each byte is a character in our filepath, it returns what's known as a PIDL. While the byte array will seem to contain the path, in both char and wide char, it also contains a bunch of gibberish that can't be easily converted.
I won't pretend to understand it, but https://stackoverflow.com/a/4318663 provides a way to convert this to a string. (see code below)
string LastPath = GetPathFromPIDL(LastPathByteArray);
And we're done. PLEASE NOTE this doesn't necessarily represent a good solution, but I wasn't able to find much official documentation on this in my half hour of digging. And obviously this code doesn't check if the registry path is correct, if the registry keys exist, or do much error checking at all, but this does at least work.
using Microsoft.Win32; //for the registry class
using System.Runtime.InteropServices; //for converting the PIDL
//GetPathFromPIDL from matt.schechtman at https://stackoverflow.com/a/4318663
[DllImport("shell32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SHGetPathFromIDListW(IntPtr pidl, MarshalAs(UnmanagedType.LPTStr)] StringBuilder pszPath);
private string GetPathFromPIDL(byte[] byteCode)
{
//MAX_PATH = 260
StringBuilder builder = new StringBuilder(260);
IntPtr ptr = IntPtr.Zero;
GCHandle h0 = GCHandle.Alloc(byteCode, GCHandleType.Pinned);
try
{
ptr = h0.AddrOfPinnedObject();
}
finally
{
h0.Free();
}
SHGetPathFromIDListW(ptr, builder);
return builder.ToString();
}
public void OnClick_Button_OpenFile(object sender, RoutedEventArgs e)
{
string RegistryPath = #"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ComDlg32\\OpenSavePidlMRU\\txt";
RegistryKey rk = Registry.CurrentUser.OpenSubKey(RegistryPath);
byte[] mrulistex = (byte[])rk.GetValue("MRUListEx");
byte Last = mrulistex[0];
byte[] LastPathByteArray = (byte[])rk.GetValue(Last.ToString());
string LastPath = GetPathFromPIDL(LastPathByteArray);
// Configure open file dialog box
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();`
dlg.InitialDirectory = LastPath;
result = dlg.ShowDialog();
if (result == true)
{
string filename = dlg.FileName;
}
//etc etc, rest of your code
}
Good luck.
In case you're using file name stored in some string, it's better to use Path to cut the file name (on my W10 the open dialog doesn't open in initial directory, if I supply just file name):
if (!System.IO.Directory.Exists(filename))
{
openDlg.InitialDirectory =
System.IO.Path.GetDirectoryName(filename);
}

Apply Folder Icon Change

I am attempting to change the icon of a folder. The code below does all what I found online says to do but the icon never changes. Am I maybe not "Applying" the change?
string createdFile = Path.Combine(#"C:\Users\np\Desktop\PUTEST", "desktop.ini");
if (File.Exists(createdFile))
{
var di = new DirectoryInfo(createdFile);
di.Attributes &= ~FileAttributes.ReadOnly;
File.Delete(createdFile);
File.Create(createdFile).Dispose();
}
else
{
File.Create(createdFile).Dispose();
}
//string iconPath = #"%SystemRoot%\system32\SHELL32.dll";
string iconPath = Environment.ExpandEnvironmentVariables(#"%SystemRoot%\system32\SHELL32.dll");
string iconIndex = "-183";
using (TextWriter tw = new StreamWriter(createdFile))
{
tw.WriteLine("[.ShellClassInfo]");
tw.WriteLine("IconResource=" + iconPath + "," + iconIndex);
//tw.WriteLine("IconFile=" + iconPath);
//tw.WriteLine("IconIndex=" + iconIndex);
}
File.SetAttributes(createdFile, System.IO.FileAttributes.ReadOnly);
File.SetAttributes(createdFile, System.IO.FileAttributes.System);
File.SetAttributes(createdFile, System.IO.FileAttributes.Hidden);
When crafting a file like this it's always good to do so using Explorer or Notepad first, then write/adjust your code to match whatever was produced. Otherwise, it's harder to figure out if the problem is with your file or your code.
I believe the minimum requirements to make this work is Desktop.ini must be marked System and the parent directory must be marked ReadOnly (System may work there as well, but I know ReadOnly definitely does). So, your code is working with the right attributes, but there are still a few problems.
Your if ... else ... block is saying "If a file exists at this path, create a directory at that path, then delete the file at that path, then create a file at that path." Of course, the directory should not and cannot have the same path as the file. I assume you are deleting and recreating the file to clear the contents when it already exists, however File.Create() overwrites (truncates) existing files, making the calls to both File.Delete() and File.Exists() unnecessary.
More importantly is this line...
di.Attributes &= ~FileAttributes.ReadOnly;
...with which there are two problems. First, you are ANDing the directory's attributes with the negation of ReadOnly, which has the effect of removing ReadOnly and keeping the other attributes the same. You want to ensure ReadOnly is set on the directory, so you want to do the opposite of the code you used: OR the directory's attributes with ReadOnly (not negated)...
di.Attributes |= FileAttributes.ReadOnly;
Also, you need that attribute set regardless of whether you created the directory or not, so that line should be moved outside of the if ... else ....
Another issue is the successive calls to File.SetAttributes(). After those three calls the file's attributes will be only Hidden, since that was the value of the last call. Instead, you need to combine (bitwise OR) those attributes in a single call.
A couple of other minor tweaks...
As you know since you are calling Dispose() on it, File.Create() returns a FileStream to that file. Instead of throwing it away, you could use it to create your StreamWriter, which will have to create one, anyways, under the covers. Better yet, call File.CreateText() instead and it will create the StreamWriter for you.
Environment variables are supported in Desktop.ini files, so you don't have to expand them yourself. This would make the file portable between systems if, say, you copied it from one system to another, or the directory is on a network share accessed by multiple systems with different %SystemRoot% values.
Incorporating all of the above changes your code becomes...
// Create a new directory, or get the existing one if it exists
DirectoryInfo directory = Directory.CreateDirectory(#"C:\Users\np\Desktop\PUTEST");
directory.Attributes |= FileAttributes.ReadOnly;
string filePath = Path.Combine(directory.FullName, "desktop.ini");
string iconPath = #"%SystemRoot%\system32\SHELL32.dll";
string iconIndex = "-183";
using (TextWriter tw = File.CreateText(filePath))
{
tw.WriteLine("[.ShellClassInfo]");
tw.WriteLine("IconResource=" + iconPath + "," + iconIndex);
//tw.WriteLine("IconFile=" + iconPath);
//tw.WriteLine("IconIndex=" + iconIndex);
}
File.SetAttributes(filePath, FileAttributes.ReadOnly | FileAttributes.System | FileAttributes.Hidden);
One catch is that the above code throws an exception if you run it twice in succession. This is because the File.Create*() methods fail if the input file is Hidden or ReadOnly. We could use new FileStream() as an alternative, but that still throws an exception if the file is ReadOnly. Instead, we'll just have to remove those attributes from any existing input file before opening it...
// Create a new directory, or get the existing one if it exists
DirectoryInfo directory = Directory.CreateDirectory(#"C:\Users\np\Desktop\PUTEST");
directory.Attributes |= FileAttributes.ReadOnly;
string filePath = Path.Combine(directory.FullName, "desktop.ini");
FileInfo file = new FileInfo(filePath);
try
{
// Remove the Hidden and ReadOnly attributes so file.Create*() will succeed
file.Attributes = FileAttributes.Normal;
}
catch (FileNotFoundException)
{
// The file does not yet exist; no extra handling needed
}
string iconPath = #"%SystemRoot%\system32\SHELL32.dll";
string iconIndex = "-183";
using (TextWriter tw = file.CreateText())
{
tw.WriteLine("[.ShellClassInfo]");
tw.WriteLine("IconResource=" + iconPath + "," + iconIndex);
//tw.WriteLine("IconFile=" + iconPath);
//tw.WriteLine("IconIndex=" + iconIndex);
}
file.Attributes = FileAttributes.ReadOnly | FileAttributes.System | FileAttributes.Hidden;
I changed from using File to FileInfo since that makes this a little easier.

Copy a file from local drive to shared drive using C#

I want to copy a file from my local drive to a shared network path.
I have tried the following way:
string remoteUserName =
WebConfigurationManager.AppSettings["remoteUsername"].ToString();
string remotePassword =
WebConfigurationManager.AppSettings["remotePassword"].ToString();
string remoteDomain =
WebConfigurationManager.AppSettings["remoteDomain"].ToString();
string remoteFilePath =
WebConfigurationManager.AppSettings["remoteFilePath"].ToString();
using (var impersonation = new
ImpersonatedUser(remoteUserName, remoteDomain, remotePassword))
{
CreateErrorLog("Logged in successfully - User and password are correct.",
"Action" + " - " + "controllerName");
string filePath = remoteFilePath;
string fileName = "txt.txt";
StreamWriter SW1;
FileIOPermission myPerm = new
FileIOPermission(FileIOPermissionAccess.AllAccess, filePath + fileName);
myPerm.Assert();
SW1 = System.IO.File.CreateText(filePath + fileName);
}
Ok, let's work on this code a little. First let's simplify building the paths. We have a network path and a local path. According to your current code the network path is built with a few variables comboBox1, comboBox2, and Environment.UserName, so let's do it a little different:
var networkPath = Path.Combine(#"\\network",
comboBox1.SelectedItem as string,
comboBox2.SelectedItem as string,
Environment.UserName);
that's going to place the \ in between each of those strings properly (i.e. if there were already a back slash it wouldn't add one, but would if necessary).
Now let's do the same for the local path:
var localPath = Path.Combine(#"C:\Users",
Environment.UserName,
"test",
label5.Text);
ok, we're almost there, but we also have an alternative network path:
var alternativeNetworkPath = Path.Combine(#"\\atlanta2-0\it-documents\filestroage",
comboBox1.SelectedItem as string,
comboBox2.SelectedItem as string,
Environment.UserName,
label5.Text);
now, one thing about this path that's already suspect to me is this, \filestroage, that's actually spelled wrong. Now, if the folder is spelled that way fine, but I'm wondering if it's spelled wrong. So just take a look. Alright, let's continue on, now we have all three paths built, it's a little easier to read, and we can easily output those strings to ensure they are right. Let's take a look at the logic. It says this, if the networkPath exists then save it there, however, if it does not exist then create it and save it to the alternativeNetworkPath. So let's do that:
if (Directory.Exists(networkPath))
{
File.Copy(localPath, networkPath);
}
else
{
Directory.CreateDirectory(networkPath);
File.Copy(localPath, alternativeNetworkPath);
}
alright, simple enough yes? But you stated that the Directory.Exists is returning true even if it exists. That's pretty much expected isn't it? If the directory exists then this method would certainly return true, if not then it would return false. You then stated with the Directory.CreateDirectory that The line above says the network name cannot be found - that can only mean that the path was constructed wrong.
So after breaking it down, the bottom line is this, the paths being constructed have to be off a tidge. However, with this new model you should be able to pull those paths out a lot easier. So the entire method, in my mind, would look something like this:
var networkPath = Path.Combine(#"\\network",
comboBox1.SelectedItem as string,
comboBox2.SelectedItem as string,
Environment.UserName);
var localPath = Path.Combine(#"C:\Users",
Environment.UserName,
"test",
label5.Text);
var alternativeNetworkPath = Path.Combine(#"\\atlanta2-0\it-documents\filestroage",
comboBox1.SelectedItem as string,
comboBox2.SelectedItem as string,
Environment.UserName,
label5.Text);
if (Directory.Exists(networkPath))
{
File.Copy(localPath, networkPath);
}
else
{
Directory.CreateDirectory(networkPath);
File.Copy(localPath, alternativeNetworkPath);
}
and so now let's have a look at those paths in those variables and your problem should come right out.
Network paths are accessed by full Universal Naming Convention-UNC \\Server\Share\drive\file paths. If you have these type credentials or rights to access network, You could use File.Copy method to move your files.

C# Targeting a directory of which a mapname is partially known

I am trying to send a file using the smtp from gmail, but I have stumbled upon a problem.
The file will be stored in the windows appdata folder.
To add the file to the e-mail, I'm using:
attachment = new System.Net.Mail.Attachment((Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "/Folder1/Folder2/Folder3/result.txt"));
The code as above works, BUT:
The issue I currently have, is that Folder2 as seen above, will be a random name containing numbers, letters, and the word TEMP.
For example a12TEMP34b
I have tried and searched if I'm able to use * somehow, but can't seem to get it working.
Any ideas?
You can use Directory.EnumerateDirectories to search for a specific folder :
var folder1 = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Folder1");
var folder2 = Directory.EnumerateDirectories(folder1, "*TEMP*").Single();
var path = Path.Combine(folder2, "Folder3/result.txt");
attachment = new System.Net.Mail.Attachment(path)
You could parse Directory.GetDirectory into a string array and grab the first element of that array if you're sure it will always be that path.
So:
string staticPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "/Folder1/";
string dynamicFolder = Directory.GetDirectory(staticPath, "*TEMP*")[0];
string finalPath = dynamicFolder + "/Folder3/result.txt"

Replacing delimiter characters in file path

I'm developing a C# web application in VS 2008. I let the user select an input file and then I store the file path in a string variable. However, it stores this path as "C:\\folder\\...". So my question is how do I convert this file path into single "\"?
Thank you guys for all your helps! Please forgive me as I am a newbie to ASP.NET development. This is more of my code in context. First I want to see if the directory exists. I guess I don't have to check this if I check if the file exists. But this should still work right? And currently my "path" string variable is not showing up the way I need it to. I'm not sure how to formulate this statement. Eventually I want to execute the ReadAllText statement (see the last line).
protected void btnAppend_Click(object sender, EventArgs e)
{
string fullpath = Page.Request.PhysicalPath;
string fullPath2 = fullpath.Replace(#"\\", #"\");
if (!Directory.Exists(fullpath2))
{
string msg = "<h1>The upload path doesn't exist: {0}</h1>";
Response.Write(String.Format(msg, fullpath2));
Response.End();
}
string path = "#" + fullpath2 + uploadFile.PostedFile.FileName;
if (File.Exists(path))
{
// Create a file to write to.
try
{
StreamReader sr = new StreamReader(path);
string s = "";
while(sr.Peek() > 0)
s = sr.ReadLine();
sr.Close();
}
catch (IOException exc)
{
Console.WriteLine(exc.Message + "Cannot open file.");
return;
}
}
if (uploadFile.PostedFile.ContentLength > 0)
{
inputfile = System.IO.File.ReadAllText(path);
Are you sure the problem is the backslashes? Backslash is an escape character in strings, such that if you were adding it in a string you have to type it as "\\" rather than "\". (if you don't use #) Note that the debugger frequently displays the string the way you would put it in code, with the escape characters, rather than direct.
According to the documentation, Page.Request.PhysicalPath returns the path to the specific file you are in, not the directory. Directory.Exists is only true if you give it a directory, not a file. Does File.Exists() return true?
For a start, calling fullpath.Replace() does nothing to fullpath; it returns a new string. Also, when your string literals have a \ (backslash) in them, you need to tell the compiler that you're not trying to use an escape sequence:
fullpath = fullpath.Replace(#"\\", #"\");
The # means "please treat this string literally (verbatim)". In other words, "when I say backslash, I mean backslash!"
See http://msdn.microsoft.com/en-us/library/362314fe.aspx.
Edit:
As LeBleu mentioned, you are calling Directory.Exists() on a full filepath. This won't work; you need to extract the directory part from the path. Try this:
if (!Directory.Exists(Path.GetDirectoryName(fullpath)))
{
...
}
You might want to consider replacing it with the Path.DirectorySeparatorChar rather than \ on the offchance that your code may end up running on a different platform one day (mono.net allows it to be run on linux or possibly more likely it might end up on some wierd mobile platform)
http://msdn.microsoft.com/en-us/library/system.io.path.directoryseparatorchar.aspx

Categories