I managed to came up with function for my windows form program that will copy files from one directory to another and doesn't require exact path to directory as long as my program is in the correct directory (that part was really important).
I need to upgrade CopyFile function that I use in my program to make it do two more things:
Overwrite files in dir2 using files from dir1.
Let user know that function did it's job (either by attaching progess bar to it or something way simpler like windows messagebox with custom text on it).
So anyway thats my code:
private static void CopyFiles(string source, string destiny, string pattern = "*.*")
{
DirectoryInfo dir1 = new DirectoryInfo(source);
DirectoryInfo dir2 = new DirectoryInfo(destiny);
if (!dir1.Exists)
throw new ArgumentException(source);
else
{
if (!dir2.Exists)
dir2.Create();
FileInfo[] files = dir1.GetFiles(pattern);
foreach (var item in files)
{
item.CopyTo(Path.Combine(dir2.FullName, item.Name));
}
}
}
static void Main(string[] args)
{
CopyFiles(#".\FolderI\FolderII\", #".\FolderA\FolderB","a.*");
}
CopyTo has an override that takes in a boolean stating whether or not to overwrite a file
item.CopyTo(Path.Combine(dir2.FullName, item.Name), True);
And change the return type of the method to a boolean which can say whether it was successful or not
private static bool CopyFiles(string source, string destiny, string pattern = "*.*")
... return True
You will need to update your code to return false if any errors (Hint: Try-Catch) or if anything else goes wrong
Related
I have a click once application which is deployed on server (network share), is there a way programmatically that extract the .dlls from the .dll.deploy files. Once we launch the application, it converts into dlls and places in user/appdata/local/... folder, but I need a way to extract the dlls without launching the application.
Thanks for the help.
Simply rename the files by omitting the .deploy suffix and you're done.
Since this task could get a little tedious - depending on the number of files in your ClickOnce package - you might want to use a file renaming tool.
Update:
I just noticed that you were asking for a way to do this programmatically. That makes it even easier since you don't have to worry about finding a file renaming tool:
public class Program
{
private const string SourcePath = "\\\\ClickOnceDeployDir";
private const string TargetPath = "C:\\Users\\UserName\\Documents\\ClickOnceTargetDir";
public static void Main(string[] args)
{
var sourceDirectory = new DirectoryInfo(SourcePath);
var targetDirectory = new DirectoryInfo(TargetPath);
// you can omit this step if you would like to do the renaming in-place
Copy(sourceDirectory, targetDirectory);
foreach (var file in targetDirectory.GetFiles("*.deploy", SearchOption.AllDirectories))
{
var directoryName = file.DirectoryName;
if (directoryName != null)
{
// here it is: rename the file
file.MoveTo(Path.Combine(directoryName, Path.GetFileNameWithoutExtension(file.Name)));
}
}
}
private static void Copy(DirectoryInfo sourceDirectory, DirectoryInfo targetDirectory)
{
foreach (var file in sourceDirectory.GetFiles())
{
file.CopyTo(Path.Combine(targetDirectory.FullName, file.Name));
}
foreach (var directory in sourceDirectory.GetDirectories())
{
Copy(directory, targetDirectory.CreateSubdirectory(directory.Name));
}
}
}
I know that Directory.CreateDirectory actually creates parents, so how can I STOP this from happening? i.e. is there a mode that I can utilise like a stricter way of doing so, the reason is that I have a watch program watching the parent top tree dir and it goes beserk if Directory.CreateDirectory makes more than one dir at a time.
Is there an equivalent to Directory.CreateDirectory which will NOT make parents?
Do you understand what for you need such method? It seems like you don't want to create all folders needed to create your target folder, like: C:\this\is\your\path\TargetFolder
In this case you can just do the following:
const string path = #"C:\this\is\your\path";
if (Directory.Exists(path))
{
Directory.CreateDirectory(Path.Combine(path, "TargetDirectory"));
}
If you have other purpose for that method, please help us to understand which one
List<string> missingDirectories = null;
private void MakeParents(string path)
{
missingDirectories = new List<string>();
missingDirectories.Add(path);
parentDir(path);
missingDirectories = missingDirectories.OrderBy(x => x.Length).ToList<string>();
foreach (string directory in missingDirectories)
{
Directory.CreateDirectory(directory);
}
}
private void parentDir(string path)
{
string newPath = path.Substring(0, path.LastIndexOf(Path.DirectorySeparatorChar));
if (!Directory.Exists(newPath))
{
missingDirectories.Add(newPath);
parentDir(newPath);
}
}
this does it, the issue is that if you want to "gently" roll up the paths one dir at a time making them, something like this is the only way you can do it :/
I want to read 100s of textfiles. Theyre located in a directory which has about 15 folders and in those folders there are a number of textfiles. So the program should Loop through each folder and then loop through each file. Some nice users helped me come up with the code to do this, but I'm getting a number of errors and they all seem to be related.
The errors are numbered and marked as comments. I need help with applying the fix to each of them (new programmer so there are probably very obvious fixes to these which I probably overlooked).
private static void ReadAllFilesStartingFromDirectory(string // 1) Identifyer expected "#\\camis01srfs04\DATA\Stats Analysis Project\Sobeys Stats\Atlantic")
{
const string searchPattern = "*.txt";
string subDirectories = Directory.EnumerateDirectories( #"\camis01srfs04\DATA\Stats Analysis Project\Sobeys Stats\Atlantic");
// 2) cannot implicity convert systems.collections.generic.ieenumerable<string> to 'string'
string filesInDirectory = Directory.EnumerateFiles(//topLevelDirectory, searchPattern);
foreach (string subDirectory in subDirectories) // 3) windowsformapplication.form1.subdirectories is a 'field' but is used like a 'type';
{
ReadAllFilesStartingFromDirectory (subDirectory);
// 4) method must have return type
}
IterateFiles(filesInDirectory, topLevelDirectory);
// 5) A namespace cannot directly contain members such as fields or methods
}
private static void IterateFiles(IEnumerable<string> files, string directory)
{
int counter = 0;
foreach (var file in files)
{
// string[] filelines = File.ReadAllLines(file) and execute code;
}
}
Ok, lets try to tackle your issues one by one, before proposing a slightly different solution to your problem.
1) Identifyer expected "#\camis01srfs04\DATA\Stats Analysis Project\Sobeystats\Atlantic")
static void ReadAllFilesStartingFromDirectory(string )
You will need to provide a name for the arguments to a method, as in:
static void ReadAllFilesStartingFromDirectory(string topLevelDirectory)
2) cannot implicity convert systems.collections.generic.ieenumerable to 'string'
string subDirectories = Directory.EnumerateDirectories( #"\camis01srfs04\DATA\Stats Analysis Project\Sobeys Stats\Atlantic");
The return value of Directory.EnumerateDirectories is IEnumerable<string>, not string. You can change this code to one of the two lines below:
IEnumerable<string> subDirectories = Directory.EnumerateDirectories(topLevelDirectory);
var subDirectories = Directory.EnumerateDirectories(topLevelDirectory);
The same is true for this line (it returns an IEnumerable<string>):
string filesInDirectory = Directory.EnumerateFiles(//topLevelDirectory, searchPattern);
We can change it to
var filesInDirectory = Directory.EnumerateFiles(topLevelDirectory, searchPattern);
3) windowsformapplication.form1.subdirectories is a 'field' but is used like a 'type';
This error message should be gone now with the changes we have made in 2)
4) method must have return type
This error message should also be gone now with the changes we have made in 2)
5) A namespace cannot directly contain members such as fields or methods
This error message should also be gone now with the changes we have made in 2)
So this would make your program, with the corrections we just made look like this:
private static void ReadAllFilesStartingFromDirectory(string topLevelDirectory)
{
const string searchPattern = "*.txt";
var subDirectories = Directory.EnumerateDirectories(topLevelDirectory);
foreach (string subDirectory in subDirectories)
ReadAllFilesStartingFromDirectory(subDirectory);
var filesInDirectory = Directory.EnumerateFiles(topLevelDirectory, searchPattern);
IterateFiles(filesInDirectory, topLevelDirectory);
}
private static void IterateFiles(IEnumerable<string> files, string directory)
{
int counter = 0;
foreach (var file in files)
{
var filelines = File.ReadAllLines(file)
//and execute code;
}
}
The method Directory.EnumerateFiles already supports searching subdirectories recursively, so you could also do this:
public static void ProcessAllFilesUnderDirectory(string topLevelDirectory, string searchMask, Action<string> processFile)
{
var files = Directory.EnumerateFiles(topLevelDirectory, searchMask, SearchOption.AllDirectories);
foreach (var file in files)
processFile(file);
}
private static void ProcessAFile(string fileName)
{
var lines = File.ReadAllLines(fileName);
// perform processing.
}
public static void Main(params string[] args)
{
ProcessAllFilesUnderDirectory(#"\camis01srfs04\DATA\Stats Analysis Project\Sobeys Stats\Atlantic", "*.txt", ProcessAFile);
}
Given a command-line style path to a command such as bin/server.exe or ping, how can I get the full path to this executable (as cmd or Process.Start would resolve it)?
I tried Path.GetFullPath, but it always expands relative to the working directory. It expands bin/server.exe correctly, however given ping it returns c:\users\matt\ping (non-existent). I want c:\Windows\system32\ping.exe.
Edit: I would like the same behaviour as cmd. Some considerations:
When there is a local executable with the same name as one in the path, cmd prefers the local one
cmd can expand the command server to server.bat or server.exe (adding the file extension)
I also tried Windows' command-line tool called where . It does almost I want:
Displays the location of files that match the search pattern. By default, the search is done along the current directory and in the paths specified by the PATH environment variable.
>where ping
C:\Windows\System32\PING.EXE
>where bin\server
INFO: Could not find files for the given pattern(s).
(This question is hard to search around because of the two different meanings of the word 'path')
Considering PATHEXT too, stealing from Serj-Tm's answer (sorry! +1 to him):
public static string WhereSearch(string filename)
{
var paths = new[]{ Environment.CurrentDirectory }
.Concat(Environment.GetEnvironmentVariable("PATH").Split(';'));
var extensions = new[]{ String.Empty }
.Concat(Environment.GetEnvironmentVariable("PATHEXT").Split(';')
.Where(e => e.StartsWith(".")));
var combinations = paths.SelectMany(x => extensions,
(path, extension) => Path.Combine(path, filename + extension));
return combinations.FirstOrDefault(File.Exists);
}
Sorry the indentation's a bit all-over-the-place - I was trying to make it not scroll. I don't know if the StartsWith check is really necessary - I'm not sure how CMD copes with pathext entries without a leading dot.
public static string GetFullPath(string filename)
{
return new[]{Environment.CurrentDirectory}
.Concat(Environment.GetEnvironmentVariable("PATH").Split(';'))
.Select(dir => Path.Combine(dir, filename))
.FirstOrDefault(path => File.Exists(path));
}
If you're only interested in searching the current directory and the paths specified in the PATH environment variable, you can use this snippet:
public static string GetFullPath(string fileName)
{
if (File.Exists(fileName))
return Path.GetFullPath(fileName);
var values = Environment.GetEnvironmentVariable("PATH");
foreach (var path in values.Split(';'))
{
var fullPath = Path.Combine(path, fileName);
if (File.Exists(fullPath))
return fullPath;
}
return null;
}
You have to search the entire disk.
Windows can respond to things like, iexplore, ping, cmd, etc, because they are in the registry under this key:
HKEY_LOCAL_MACHINE
SOFTWARE
Microsoft
Windows
CurrentVersion
App Paths
The only other way is to search the entire disk for the application.
EDIT: My understanding was, that you want to search for any random executable name, not the ones that are already known to Windows..
internal class Program
{
static void Main(string[] args)
{
string fullPath = GetExactPathFromEnvironmentVar("ping.exe");
if (!string.IsNullOrWhiteSpace(fullPath))
Console.WriteLine(fullPath);
else
Console.WriteLine("Not found");
}
static string GetExactPathFromEnvironmentVar(string program)
{
var pathVar = System.Environment.GetEnvironmentVariable("PATH");
string[] folders = pathVar.Split(';');
foreach (var folder in folders)
{
string path = Path.Combine(folder, program);
if (File.Exists(path))
{
return path;
}
}
return null;
}
}
HTH
I am just learning C# (have been fiddling with it for about 2 days now) and I've decided that, for leaning purposes, I will rebuild an old app I made in VB6 for syncing files (generally across a network).
When I wrote the code in VB 6, it worked approximately like this:
Create a Scripting.FileSystemObject
Create directory objects for the source and destination
Create file listing objects for the source and destination
Iterate through the source object, and check to see if it exists in the destination
if not, create it
if so, check to see if the source version is newer/larger, and if so, overwrite the other
So far, this is what I have:
private bool syncFiles(string sourcePath, string destPath) {
DirectoryInfo source = new DirectoryInfo(sourcePath);
DirectoryInfo dest = new DirectoryInfo(destPath);
if (!source.Exists) {
LogLine("Source Folder Not Found!");
return false;
}
if (!dest.Exists) {
LogLine("Destination Folder Not Found!");
return false;
}
FileInfo[] sourceFiles = source.GetFiles();
FileInfo[] destFiles = dest.GetFiles();
foreach (FileInfo file in sourceFiles) {
// check exists on file
}
if (optRecursive.Checked) {
foreach (DirectoryInfo subDir in source.GetDirectories()) {
// create-if-not-exists destination subdirectory
syncFiles(sourcePath + subDir.Name, destPath + subDir.Name);
}
}
return true;
}
I have read examples that seem to advocate using the FileInfo or DirectoryInfo objects to do checks with the "Exists" property, but I am specifically looking for a way to search an existing collection/list of files, and not live checks to the file system for each file, since I will be doing so across the network and constantly going back to a multi-thousand-file directory is slow slow slow.
Thanks in Advance.
The GetFiles() method will only get you files that does exist. It doesn't make up random files that doesn't exist. So all you have to do is to check if it exists in the other list.
Something in the lines of this could work:
var sourceFiles = source.GetFiles();
var destFiles = dest.GetFiles();
foreach (var file in sourceFiles)
{
if(!destFiles.Any(x => x.Name == file.Name))
{
// Do whatever
}
}
Note: You have of course no guarantee that something hasn't changed after you have done the calls to GetFiles(). For example, a file could have been deleted or renamed if you try to copy it later.
Could perhaps be done nicer somehow by using the Except method or something similar. For example something like this:
var sourceFiles = source.GetFiles();
var destFiles = dest.GetFiles();
var sourceFilesMissingInDestination = sourceFiles.Except(destFiles, new FileNameComparer());
foreach (var file in sourceFilesMissingInDestination)
{
// Do whatever
}
Where the FileNameComparer is implemented like so:
public class FileNameComparer : IEqualityComparer<FileInfo>
{
public bool Equals(FileInfo x, FileInfo y)
{
return Equals(x.Name, y.Name);
}
public int GetHashCode(FileInfo obj)
{
return obj.Name.GetHashCode();
}
}
Untested though :p
One little detail, instead of
sourcePath + subDir.Name
I would use
System.IO.Path.Combine(sourcePath, subDir.Name)
Path does reliable, OS independent operations on file- and foldernames.
Also I notice optRecursive.Checked popping out of nowhere. As a matter of good design, make that a parameter:
bool syncFiles(string sourcePath, string destPath, bool checkRecursive)
And since you mention it may be used for large numbers of files, keep an eye out for .NET 4, it has an IEnumerable replacement for GetFiles() that will let you process this in a streaming fashion.