Can't rename directory in C# but manually - c#

I'm using Directory.Move(oldDir, newDir) to rename a directory. Every now and then I get a IOException saying "Access to the path "oldDir" is denied". However if I right click the directory in the explorer I can rename it without any issues. How's that and how can I get it to work?
EDIT
The program is still running, I get the exception and can rename it manually while my cursor is paused on the breakpoint. I also tried setting a breakpoint at Directory.Move, successfully renamed the directory in explorer (and back again), stepped over Directory.Move and ended up in the catch (IOException) again. So I don't see why my program should lock the directory at all. There must be something else.
Any ideas?
EDIT 2
Here is my code
public bool Copy()
{
string destPathRelease = ThisUser.DestPath + "\\Release";
if (Directory.Exists(destPathRelease))
{
try
{
string newPath = ThisUser.DestPath + '\\' + (string.IsNullOrEmpty(currBuildLabel) ? ("Release" + '_' + DateTime.Now.ToString("yyyyMMdd_HHmmss")) : currBranchName) + '.' + currBuildLabel;
Directory.Move(destPathRelease, newPath);
catch (IOException)
{
// Breakpoint
}
}
}
}
As you can see I just entered the method. I never touched the directory in my program before. Is there another way to rename a directory?

Without seeing more code I'd say your application is locking a file within the directory, you can see what is accessing the directory using Process explorer
from the intro to process explorer:
Ever wondered which program has a particular file or directory open? Now you can find out. Process Explorer shows you information about which handles and DLLs processes have opened or loaded.
It might also be worth making sure nothing else is copying files from/to that directory - e.g. dropbox. I had an issue recently where visual studio would stop debugging because of a file lock - in the end it was indexing on the drive which was temporarily locking the file. Process explorer only partially helped in that it showed 'system' had the file lock and not another application.

You need to check the User that is running the .net application. It don't have the right permission to execute the rename.
This is:
the user running the application pool for web applications
the logged application for console/winforms application
the configured user for services or scheduled tasks

If the parent directory of your destination directory does not exist, The Directory.Move operation with fail. I've just been trying to figure out something loosely similar to this.

This is the safest method to rename a directory in the C# .NET Core with cross-platform.
/// <summary>
/// Renames a folder name
/// </summary>
/// <param name="directory">The full directory of the folder</param>
/// <param name="newFolderName">New name of the folder</param>
/// <returns>Returns true if rename is successfull</returns>
public static bool RenameFolder(string directory, string newFolderName)
{
try
{
if (string.IsNullOrWhiteSpace(directory) ||
string.IsNullOrWhiteSpace(newFolderName))
{
return false;
}
var oldDirectory = new DirectoryInfo(directory);
if (!oldDirectory.Exists)
{
return false;
}
if (string.Equals(oldDirectory.Name, newFolderName, StringComparison.OrdinalIgnoreCase))
{
//new folder name is the same with the old one.
return false;
}
string newDirectory;
if (oldDirectory.Parent == null)
{
//root directory
newDirectory = Path.Combine(directory, newFolderName);
}
else
{
newDirectory = Path.Combine(oldDirectory.Parent.FullName, newFolderName);
}
if (Directory.Exists(newDirectory))
{
//target directory already exists
return false;
}
oldDirectory.MoveTo(newDirectory);
return true;
}
catch
{
//ignored
return false;
}
}

Related

C# WPF Application don't write a file after publishing

First of all, the forum has given me very good ideas and solutions just by reading along.
To my current case I willnot really looking for it.
I have written a WPF application that works perfectly in debug and release mode. However, as soon as I publish you, no more.
I tried different ways to publish.
The application is provided by CD, DVD or stick.
The clickonce method was once used sometimes not.
It should be created as independently executable. And the framework is included.
My problem is that after publishing via the release build, I have no permission to write to files in Environment.CurrentDirectory or outside.
In one case, the affected file is supplied but cannot be changed. As soon as I exchange the file after installation, I can change it.
In another case, I can't create a file using the directive. The StreamWriter is used. But also creating folders using System.IO is not possible.
The application is run by an administrator. Only when the application is executed with RunAsAdmin does it run without problems.
The file is a txt file that acts as a log file. However, users without administrator rights should also be able to update the log file if necessary.
What can I do so that my application can generally generate files, regardless of the user rights?
Thank you very much.
You can check my code snippet:
private bool HasPathAccessable(string path)
{
DirectorySecurity sec = Directory.GetAccessControl(path);
WindowsIdentity currentIdentity = WindowsIdentity.GetCurrent();
foreach (FileSystemAccessRule acr in sec.GetAccessRules(true, true, typeof(System.Security.Principal.NTAccount)))
{
//Console.WriteLine("{0} | {1} | {2} | {3} | {4}", acr.IdentityReference.Value, acr.FileSystemRights, acr.InheritanceFlags, acr.PropagationFlags, acr.AccessControlType);
if (acr.IdentityReference.Value == currentIdentity.Name)
{
if (acr.FileSystemRights == FileSystemRights.FullControl && acr.AccessControlType == AccessControlType.Allow)
{
Console.WriteLine($"{currentIdentity.Name} accessable to {path}");
return true;
}
}
}
Console.WriteLine($"{currentIdentity.Name} inaccessable to {path}");
return false;
}
private void ChangePathAccessControlCurrentUser(string path, bool isFile)
{
WindowsIdentity currentIdentity = WindowsIdentity.GetCurrent();
if (isFile)
{
var pathAccessControl = File.GetAccessControl(path);
pathAccessControl.AddAccessRule(new FileSystemAccessRule(currentIdentity.Name, FileSystemRights.FullControl, AccessControlType.Allow));
File.SetAccessControl(path, pathAccessControl);
Console.WriteLine($"AddAccessRule File : {path}");
}
else
{
var pathAccessControl = Directory.GetAccessControl(path);
pathAccessControl.AddAccessRule(new FileSystemAccessRule(currentIdentity.Name, FileSystemRights.FullControl, AccessControlType.Allow));
Directory.SetAccessControl(path, pathAccessControl);
Console.WriteLine($"AddAccessRule Directory : {path}");
}
}

Exception related to relative paths

I am working on a C# sharp console application. I ran into the following error related to relative paths tried changing the debug settings from anyCPU to x86 as well, but that didn't work. Can someone point me in the right direction. Thanks
public static void ReadOrders(string pOrderDirectory)
{
// This exception is already thrown by <code>Directory.GetFiles()</code> but caught earlier here to allow
// the option of throwing an app-specific exception
if (!Directory.Exists(pOrderDirectory))
{
throw new DirectoryNotFoundException("Unable to find input directory for orders: " + pOrderDirectory); //ran into the exception here.
}
// Process the list of files found in the directory.
string[] oOrderFilenames = Directory.GetFiles(pOrderDirectory, SalesTaxHelper.GetConfigurationValue(CONFIG_KEY_FILE_SEARCH_PATTERN));
if (oOrderFilenames.Length < 1)
{
throw new IOException("No orders found in input directory");
}
foreach (var oOrderFile in oOrderFilenames)
{
var oOrderProcessor = new Order();
var oOrderLineItems = File.ReadAllLines(oOrderFile);
foreach (var oLineItem in oOrderLineItems)
{
oOrderProcessor.AddLineItem(oLineItem);
}
Console.WriteLine(oOrderProcessor.PrintInvoice());
}
Console.WriteLine("======================================");
Console.WriteLine("PROCESSED ALL INPUT FILES IN DIRECTORY");
}
Exception: Unable to find input directory for orders:
Thanks,
Hari
Okay so make sure your folder named exactly input is placed in the bin > Debug folder of your project and if you set
pOrderDirectory = "input"
it should find the folder. The reason it will find the folder is because you're building in Debug mode so when you specify a path with just a simple string like I did it will be the default location where it will go search for the folder.
Note that it won't find your folder if you change to Release because your folder is in Debug.

UNC path is recognised as valid on an application deployed on most machines apart from two.

UNC path is recognised as valid on an application deployed on most machines apart from two. They have read or read/write permissions. How can I fix this?
It is the Directory Exists in C#:
For example, using a hypothetical UNC path #"\test server\test first folder\second folder\third folder\Final destination";
then with all but two users this code works:
bool exists;
if (textBox1.Text == string.Empty)
{
exists = false;
}
else
{
Directory.GetAccessControl(textBox1.Text);
exists = Directory.Exists(Path.GetDirectoryName(textBox1.Text));
// MessageBox.Show(exists.ToString(),"Directory", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
//
// The user selected a folder and pressed the OK button.
// A message pops up and identifies the number of files found within that folder.
//
//textBox1.Text = f.FileName; //OpenFile dialog f FileName
if (textBox1.Text == string.Empty )
{
MessageBox.Show("No directory selected","Directory",MessageBoxButtons.OK,MessageBoxIcon.Error);
}
else if (exists == false)
{
MessageBox.Show("Directory does not exist", "Directory", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
it finds the path (it is valid). But with the two users they get a "Path does not exist. Verify that the path is correct" error.
Any assistance greatly appreciated.
Thanks
Your code looks fine.
If you say it is working on most machines, most likely, the issue is not with your code but rather with your underlying network setup or the string you are entering in the textbox (keep in mind that a UNC path should always start with 2 backslashes).
As a suggestion you might try to access the UNC path trough explorer (assuming you're talking about Windows machines) on the two machines where your code is not working. If you can't access it this way, you're code won't be able to either.

Unable to launch shortcut (lnk) files from 32-bit C# application when the full path resolves to a 64-bit directory

I'm trying to launch programs in the Start Menu from a C# application, and nearly all of the items in the Start Menu are shortcut (lnk) files. When using Process.Start to launch these files, I found that I was getting "The system cannot find the path specified" error if the full path of the lnk file pointed to the C:\Program Files directory. I did some research with File System Redirection in Windows, so I tried disabling it, but I'm still getting the same error:
// disable file system redirection:
IntPtr ptr = new IntPtr();
bool isWow64FsRedirectionDisabled = Wow64DisableWow64FsRedirection(ref ptr);
// run the file:
System.Diagnostics.Process.Start("c:\\splitter.lnk");
This returns "The system cannot find the path specified." However, if I launch c:\splitter.lnk from the Start > Run dialog box, the program runs just fine. You can reproduce this issue on any 64-bit machine by creating a shortcut for any 64-bit app, placing it on the C drive, and attempting to run it using the code above.
Is there a better way to launch .lnk files to avoid this problem? Or am I not disabling file redirection properly?
EDIT: I also tried setting UseShellExecute to true to have the operating system run the file, but that still fails, which is interesting because running the same path from the Start > Run dialog box works just fine:
Process process = new Process();
process.StartInfo.UseShellExecute = true;
process.StartInfo.FileName = "c:\\splitter.lnk";
process.Start();
EDIT 2: I figured that instead of trying to launch the LNK file directly, I would get the target for it, and then run the target. I tried using How to resolve a .lnk in c# and How to follow a .lnk file programmatically, but both methods return the full path as C:\Program Files (x86)\Splitter.exe instead of the actual path of C:\Program Files\Splitter.exe.
Perhaps I can use one of the above methods to get the target of the LNK file. Then I can see if the target contains Program Files (x86). If it does, replace it with Program Files and check if the file exists. If it exists in Program Files, run that. If not, run the file from the Program Files (x86) location. This would be a messy workaround, but I don't know what else to try at this point. Any suggestions would be appreciated.
I was able to provide a workaround for this issue by using Sam Saffron's example script at How to resolve a .lnk in c#. I modified the ResolveShortcut function to the following:
public static string ResolveShortcut(string filename)
{
// this gets the full path from a shortcut (.lnk file).
ShellLink link = new ShellLink();
((IPersistFile)link).Load(filename, STGM_READ);
StringBuilder sb = new StringBuilder(MAX_PATH);
WIN32_FIND_DATAW data = new WIN32_FIND_DATAW();
((IShellLinkW)link).GetPath(sb, sb.Capacity, out data, 0);
string final_string = sb.ToString();
if (final_string.Length == 0)
final_string = filename;
// If the the shortcut's target resolves to the Program Files or System32 directory, and the user is on a
// 64-bit machine, the final string may actually point to C:\Program Files (x86) or C:\Windows\SYSWOW64.
// This is due to File System Redirection in Windows -- http://msdn.microsoft.com/en-us/library/aa365743%28VS.85%29.aspx.
// Unfortunately the solution there doesn't appear to work for 32-bit apps on 64-bit machines.
// We will provide a workaround here:
string new_path = Validate_Shortcut_Path(final_string, "SysWOW64", "System32");
if (File.Exists(new_path) == true && File.Exists(final_string) == false)
{
// the file is actually stored in System32 instead of SysWOW64. Let's update it.
final_string = new_path;
}
new_path = Validate_Shortcut_Path(final_string, "Program Files (x86)", "Program Files");
if (File.Exists(new_path) == true && File.Exists(final_string) == false)
{
// the file is actually stored in Program Files instead of Program Files (x86). Let's update it.
final_string = new_path;
}
// the lnk may incorrectly resolve to the C:\Windows\Installer directory. Check for this.
if (final_string.ToLower().IndexOf("windows\\installer") > -1)
final_string = filename;
if (File.Exists(final_string))
return final_string;
else
return filename;
}
public static string Validate_Shortcut_Path(string final_string, string find_what, string replace_with)
{
string final_string_lower = final_string.ToLower();
string find_what_lower = find_what.ToLower();
int find_value = final_string_lower.IndexOf(find_what_lower);
if (find_value > -1)
{
// the shortcut resolved to the find_what directory, which can be SysWOW64 or Program Files (x86),
// but this may not be correct. Let's check by replacing it with another value.
string new_string = final_string.Substring(0, find_value) + replace_with + final_string.Substring(find_value + find_what.Length);
if (File.Exists(new_string) == true && File.Exists(final_string) == false)
{
// the file is actually stored at a different location. Let's update it.
final_string = new_string;
}
}
return final_string;
}
If anyone is aware of a better way to do this, I am open to ideas. Otherwise I will use this method and accept this workaround as the answer.

Running an app using process.start returns different value than running it manually

I'm running a tool that samples the HW PCI for a specific value (I didn't write it).
When I run it from the command prompt, it returns one exit code (the correct one) but when I run it from another application using Process.Start, it returns another exit code.
Is there a difference between running an application directly or via Process.Start?
Do you know of a simple workaround for this issue?
As stated in Hassan's answer (which solved my similar issue), the exit code returned from Process.Start() is affected by the location of the executable, in particular which directory it is located in. Here's the code I used:
string yourExe = "C\\Program Files\\Your Directory\\YourExe.exe";
string currentDir = Directory.GetCurrentDirectory();
string yourExeDir = "C\\Program Files\\Your Directory";
try
{
Directory.SetCurrentDirectory(yourExeDir);
}
catch (DirectoryNotFoundExeption dnfe)
{
MessageBox.Show("The specified directory does not exist. " + dnfe.Message);
}
if (!File.Exists(yourExe))
{
MessageBox.Show("Can't find yourExe");
}
else
{
Process.Start(yourExe);
}
try
{
//Set the current directory.
Directory.SetCurrentDirectory(currentDir);
}
catch (DirectoryNotFoundException dnfe)
{
MessageBox.Show("The specified directory does not exist. " + dnfe.Message);
}
This switches the current working directory to the directory where the .exe is located, runs it, and then switches back to whatever your previous working directory was.
If you want the same result from Process.Start(), you have to execute your application on
the same working directory as your command line.

Categories