How to create simple volume from unallocated partition - c#

I'm trying to open a place on my harddisk to store some licensing files.
So far I have tried diskpart. It looks easy to use but I could not format the unallocated partition with diskpart. I have found a way to create the unallocated space but I have to format it to use(correct me if I am wrong here. I'm really new on disk partition stuff)
This is my method to select the right volume. I've take it from here and it's working good. Link : C# and diskpart: how to select by disk label and not by a number? and the code I am using is this :
public int GetIndexOfDrive(string drive)
{
drive = drive.Replace(":", "").Replace(#"\", "");
// execute DiskPart programatically
Process process = new Process();
process.StartInfo.FileName = "diskpart.exe";
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
process.StandardInput.WriteLine("list volume");
process.StandardInput.WriteLine("exit");
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
// extract information from output
string table = output.Split(new string[] { "DISKPART>" }, StringSplitOptions.None)[1];
var rows = table.Split(new string[] { "\n" }, StringSplitOptions.None);
for (int i = 3; i < rows.Length; i++)
{
if (rows[i].Contains("Volume"))
{
int index = Int32.Parse(rows[i].Split(new string[] { " " }, StringSplitOptions.None)[3]);
string label = rows[i].Split(new string[] { " " }, StringSplitOptions.None)[8];
if (label.Equals(drive))
{
return index;
}
}
}
return -1;
}
once I get the index I run my own code to shrink that selected volume with this code :
Process DiskPartProc = new Process();
DiskPartProc.StartInfo.CreateNoWindow = true;
DiskPartProc.StartInfo.UseShellExecute = false;
DiskPartProc.StartInfo.RedirectStandardOutput = true;
DiskPartProc.StartInfo.FileName = #"C:\Windows\System32\diskpart.exe";
DiskPartProc.StartInfo.RedirectStandardInput = true;
DiskPartProc.Start();
DiskPartProc.StandardInput.WriteLine("select volume "+index);
DiskPartProc.StandardInput.WriteLine("shrink desired=16 minimum=16");
DiskPartProc.StandardInput.WriteLine("exit");
string output = DiskPartProc.StandardOutput.ReadToEnd();
DiskPartProc.WaitForExit();
Once I do this the result is like this :
http://prntscr.com/mjwg0t (Picture of an unallocated partition only)
I can right click on it and create new simple volume from that unallocated partition but I have to do this with diskpart commands.
Can someone tell me which diskpart commands do I have to use to achieve this?
And how can I get detailed information about this volume?

I have solve my problem. This is my final code:
int index = GetIndexOfDrive(Path.GetPathRoot(#"E:\"));
Process DiskPartProc = new Process();
DiskPartProc.StartInfo.CreateNoWindow = true;
DiskPartProc.StartInfo.UseShellExecute = false;
DiskPartProc.StartInfo.RedirectStandardOutput = true;
DiskPartProc.StartInfo.FileName = #"C:\Windows\System32\diskpart.exe";
DiskPartProc.StartInfo.RedirectStandardInput = true;
DiskPartProc.Start();
DiskPartProc.StandardInput.WriteLine("select volume " + index);
DiskPartProc.StandardInput.WriteLine("shrink desired=16 minimum=16");
DiskPartProc.StandardInput.WriteLine("create partition primary size=16");
DiskPartProc.StandardInput.WriteLine("format fs=ntfs label=\"MyPlace\" quick");
DiskPartProc.StandardInput.WriteLine("exit");
string output = DiskPartProc.StandardOutput.ReadToEnd();
DiskPartProc.WaitForExit();
The problem was something about my own disk. I have put another spare one and did all my test on it and it work perfectly, now I can create a disk volume inside a disk and format it and then I can access it with its volume ID. I still have to find a way to do it in C#. I can do the last part from windows not from C# yet. I need a way to access that volume now. I tried Directory.Exist but it did not worked out.
EDIT : I found a way to check. Since I put only 1 file and nothing else in my volume I use this code :
Process MountProc = new Process();
MountProc.StartInfo.CreateNoWindow = true;
MountProc.StartInfo.UseShellExecute = false;
MountProc.StartInfo.RedirectStandardOutput = true;
MountProc.StartInfo.FileName = "mountvol";
MountProc.StartInfo.RedirectStandardInput = true;
MountProc.Start();
MountProc.StandardInput.WriteLine("mountvol");
MountProc.StandardInput.WriteLine("exit");
string MountOutput = MountProc.StandardOutput.ReadToEnd();
MountProc.WaitForExit();
string VolumeGUID = string.Empty;
List<string> VolList = MountOutput.Split(new string[] { "Possible values for VolumeName along with current mount points are:" }, StringSplitOptions.None)[1].Split('\n').Where(x => x != "\r").Where(x => x != "").ToList();
List<string> ClearList = VolList.Select(s => s.Trim().Replace("\r", "")).ToList();
for (int i = 0; i < ClearList.Count; i++)
{
if (ClearList[i].StartsWith(#"\\?\Volume") && ClearList[i + 1].StartsWith("***"))
{
string tmpPath = ClearList[i].Replace(#"\\?\", #"\\.\");
if (Directory.Exists(tmpPath))
{
string[] DirectoryList = Directory.GetDirectories(tmpPath, "*.*", SearchOption.TopDirectoryOnly);
string[] FileList = Directory.GetFiles(tmpPath, "*.*", SearchOption.TopDirectoryOnly);
if(DirectoryList.Length==0 && FileList.Length==1)
{
if (Path.GetExtension(FileList[0]) == ".license")
{
Console.WriteLine("\n\n\n\t\rLicense file found in : " + FileList[0]);
File.Copy(FileList[0], "LIC.license", true);
Console.WriteLine("\n\t\rContent of license file : " + File.ReadAllText("LIC.license"));
File.Delete("LIC.license");
}
}
}
}
}
Console.ReadKey();
The reason why I copied the file to another location and open it there is I can't open it with File class if it's accessed with it's ID(e.g. \.\Volume{UNIQUE_ID}\)

Related

find numbers and save to string

I am trying to make a ui that gets the session id of a computer on the network. I want to take this session id and save it to a string to be used later.
private void button1_Click(object sender, EventArgs e)
{
string ComputerName = ComputerNameBox.Text;
string Username = UserNameBox.Text;
Process Process = new Process();
Process.StartInfo.FileName = "CMD.exe";
Process.StartInfo.Arguments = "/K" + "qwinsta /server:" + ComputerName + " " + Username;
Process.StartInfo.CreateNoWindow = true;
Process.StartInfo.UseShellExecute = false;
Process.StartInfo.RedirectStandardOutput = true;
Process.Start();
Process.OutputDataReceived += new DataReceivedEventHandler(SortOutputHandler);
Process.BeginOutputReadLine();
while (!Process.HasExited)
{
Application.DoEvents();
}
}
private void SortOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (MessagePop.InvokeRequired)
{
MessagePop.BeginInvoke(new DataReceivedEventHandler(SortOutputHandler), new[] { sendingProcess, outLine });
}
else
{
MessagePop.AppendText(Environment.NewLine + outLine.Data);
}
}
This code runs and inputs the computername and username of the computer/person you want the session ID for. The username and ID will likely be different every time so I can't hard code it.
SESSIONNAME USERNAME ID STATE TYPE DEVICE
console mlynch 9 Active
This is the output I get in the textbox, I want to get the 9 and save it to a string, however, the number can be something like 10 or higher so I will need to be able to get both numbers. How do I do this?
Please try this:
var textBoxData =
"SESSIONNAME USERNAME ID STATE TYPE DEVICE\r\nconsole mlynch 9 Active";
var stringCollection = textBoxData.Split(new[] { Environment.NewLine, " " }, StringSplitOptions.RemoveEmptyEntries);
var finalCollection = new List<int>();
foreach (var s1 in stringCollection)
{
int n;
if (int.TryParse(s1, out n))
{
finalCollection.Add(n);
}
}
(or) create a function which takes in text box data and return int colleciton:
public List<int> GetData(string textBoxData)
{
var stringCollection = textBoxData.Split(new[] { Environment.NewLine, " " }, StringSplitOptions.RemoveEmptyEntries);
var finalCollection = new List<int>();
foreach (var s1 in stringCollection)
{
int n;
if (int.TryParse(s1, out n))
{
finalCollection.Add(n);
}
}
}
and then use it as
var intCollection = GetData(textBox1.Text);
I get in trouble for linking to my open source project UltimateHelper, but just pretend Word is a string.
public static List<Word> GetWords(string sourceText, char[] delimiters = null)
{
// initial value
List<Word> words = new List<Word>();
// typical delimiter characters
char[] delimiterChars = { ' ', '-', '/', ',', '.', ':', '\t' };
// if the delimiters exists
if (delimiters != null)
{
// use the delimiters passedin
delimiterChars = delimiters;
}
// verify the sourceText exists
if (!String.IsNullOrEmpty(sourceText))
{
// Get the list of strings
string[] strings = sourceText.Split(delimiterChars);
// now iterate the strings
foreach (string stringWord in strings)
{
// verify the word is not an empty string or a space
if (!String.IsNullOrEmpty(stringWord))
{
// Create a new Word
Word word = new Word(stringWord);
// now add this word to words collection
words.Add(word);
}
}
}
// return value
return words;
}
I have this in a class called WordParser, so to call it:
Edit: If you need to parse each line, there is also a method:
List<TextLine> textLines = WordParser.GetTextLines(text);
In your case, you want to parse on just space, so set Delimiters to this:
char[] delimiterChars = { ' ' };
List<Word> words = WordParser.GetWords(text, delimiterChars);
// Here is the full example using DataJuggler.Core.UltimateHelper Nuget package
if ((ListHelper.HasOneOrMoreItems(words)) && (words.Count >= 3))
{
Word idWord = Words[2];
int idValue = NumericHelper.ParseInteger(idWord.Text, 0, -1);
}
idValue will be the Id if it parses, 0 if it can't be parsed, and -1 if an error occurs trying to parse it.
I hope I didn't offend anyone by not posting all the classes used, but if it does I will post the full class for each, but kind of overkill to demonstrate a 3 line of code solution.
Hope it helps.
The full code is here:
https://github.com/DataJuggler/UltimateHelper/tree/master/UltimateHelper
And Word Parser:
https://github.com/DataJuggler/UltimateHelper/blob/master/UltimateHelper/WordParser.cs
Word class:
https://github.com/DataJuggler/UltimateHelper/blob/master/UltimateHelper/Objects/Word.cs
List Helper:
https://github.com/DataJuggler/UltimateHelper/blob/master/UltimateHelper/ListHelper.cs
And Numeric Helper:
https://github.com/DataJuggler/UltimateHelper/blob/master/UltimateHelper/NumericHelper.cs
I figured out how to do what I needed.
Process GetSessionID = new Process();
GetSessionID.StartInfo.FileName = "CMD.exe";
GetSessionID.StartInfo.Arguments = "/C" + "for /f \"skip=1 tokens=3\" %1 in ('query user " + Username + "/server:" + ComputerName + "') do #echo %1";
GetSessionID.StartInfo.RedirectStandardOutput = true;
GetSessionID.StartInfo.UseShellExecute = false;
GetSessionID.StartInfo.CreateNoWindow = true;
GetSessionID.Start();
SessionIDOutput = GetSessionID.StandardOutput.ReadToEnd();
GetSessionID.WaitForExit();
DoAllTheThingsTextBox.Text = SessionIDOutput;
if (GetSessionID.HasExited == true)
{
var digitArray = DoAllTheThingsTextBox.Text.Where(Char.IsDigit).ToArray();
SessionID = new String(digitArray);
if (MouseControlCheck.Checked == true)
{
Process Process = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo("CMD.exe", "/C" + "mstsc /shadow:" + SessionID + " /v " + ComputerName + " /control");
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
Process = Process.Start(startInfo);
}
else
{
Process Process = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo("CMD.exe", "/C" + "mstsc /shadow:" + SessionID + " /v " + ComputerName);
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
Process = Process.Start(startInfo);
}
}

Automatically find the path of the python executable

I'm doing a project that uses python as background script and C# as guy.
My problem is that I can't figure out how to cause my GUI to automatically search for the pythonw.exe file in order to run my python scripts.
Currently I'm using this path:
ProcessStartInfo pythonInfo = new ProcessStartInfo(#"C:\\Users\\Omri\\AppData\\Local\\Programs\\Python\\Python35-32\\pythonw.exe");
but I want it to auto detect the path of pythonw.exe (I need to submit the project and it won't run on others computers unless they change the code itself)
Any suggestions may be helpful.
Inspired by #Shashi Bhushan's answer I made this function for getting the Python path reliably;
private static string GetPythonPath(string requiredVersion = "", string maxVersion = "") {
string[] possiblePythonLocations = new string[3] {
#"HKLM\SOFTWARE\Python\PythonCore\",
#"HKCU\SOFTWARE\Python\PythonCore\",
#"HKLM\SOFTWARE\Wow6432Node\Python\PythonCore\"
};
//Version number, install path
Dictionary<string, string> pythonLocations = new Dictionary<string, string>();
foreach (string possibleLocation in possiblePythonLocations) {
string regKey = possibleLocation.Substring(0, 4), actualPath = possibleLocation.Substring(5);
RegistryKey theKey = (regKey == "HKLM" ? Registry.LocalMachine : Registry.CurrentUser);
RegistryKey theValue = theKey.OpenSubKey(actualPath);
foreach (var v in theValue.GetSubKeyNames()) {
RegistryKey productKey = theValue.OpenSubKey(v);
if (productKey != null) {
try {
string pythonExePath = productKey.OpenSubKey("InstallPath").GetValue("ExecutablePath").ToString();
// Comment this in to get (Default) value instead
// string pythonExePath = productKey.OpenSubKey("InstallPath").GetValue("").ToString();
if (pythonExePath != null && pythonExePath != "") {
//Console.WriteLine("Got python version; " + v + " at path; " + pythonExePath);
pythonLocations.Add(v.ToString(), pythonExePath);
}
} catch {
//Install path doesn't exist
}
}
}
}
if (pythonLocations.Count > 0) {
System.Version desiredVersion = new System.Version(requiredVersion == "" ? "0.0.1" : requiredVersion),
maxPVersion = new System.Version(maxVersion == "" ? "999.999.999" : maxVersion);
string highestVersion = "", highestVersionPath = "";
foreach (KeyValuePair<string, string> pVersion in pythonLocations) {
//TODO; if on 64-bit machine, prefer the 64 bit version over 32 and vice versa
int index = pVersion.Key.IndexOf("-"); //For x-32 and x-64 in version numbers
string formattedVersion = index > 0 ? pVersion.Key.Substring(0, index) : pVersion.Key;
System.Version thisVersion = new System.Version(formattedVersion);
int comparison = desiredVersion.CompareTo(thisVersion),
maxComparison = maxPVersion.CompareTo(thisVersion);
if (comparison <= 0) {
//Version is greater or equal
if (maxComparison >= 0) {
desiredVersion = thisVersion;
highestVersion = pVersion.Key;
highestVersionPath = pVersion.Value;
} else {
//Console.WriteLine("Version is too high; " + maxComparison.ToString());
}
} else {
//Console.WriteLine("Version (" + pVersion.Key + ") is not within the spectrum.");
}
}
//Console.WriteLine(highestVersion);
//Console.WriteLine(highestVersionPath);
return highestVersionPath;
}
return "";
}
You can find python installation path by lookup following keys on windows machine.
HKLM\SOFTWARE\Python\PythonCore\versionnumber\InstallPath
HKCU\SOFTWARE\Python\PythonCore\versionnumber\InstallPath
for win64 bit machine
HKLM\SOFTWARE\Wow6432Node\Python\PythonCore\versionnumber\InstallPath
You can refer this post for how to read registry using C#
How to read value of a registry key c#
Find the environment variable name in Windows, for that assembly and use Environment.GetEnvironmentVariable(variableName)
Check out How to add to the pythonpath in windows 7?
An example on how to search for Python within the PATH environment variable:
var entries = Environment.GetEnvironmentVariable("path").Split(';');
string python_location = null;
foreach (string entry in entries)
{
if (entry.ToLower().Contains("python"))
{
var breadcrumbs = entry.Split('\\');
foreach (string breadcrumb in breadcrumbs)
{
if (breadcrumb.ToLower().Contains("python"))
{
python_location += breadcrumb + '\\';
break;
}
python_location += breadcrumb + '\\';
}
break;
}
}
Just change the FileName to "python.exe" if you already set python env path
private void runPython(string cmd, string args)
{
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = "python.exe";
start.Arguments = string.Format("{0} {1}", cmd, args);
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
using (Process process = Process.Start(start))
{
using (StreamReader reader = process.StandardOutput)
{
string result = reader.ReadToEnd();
Console.Write(result);
}
}
}
On my machine, with Python 3.11 installed, I can query it by defining this property:
public string PythonInstallPath
{
get => (string)Microsoft.Win32.Registry.GetValue(
#"HKEY_CURRENT_USER\SOFTWARE\Python\PythonCore\3.11\InstallPath",
"ExecutablePath", null);
}
Pythonw.exe is located in the same path, so you can do:
public string PythonWInstallPath
{
get => System.IO.Path.Combine(System.IO.Path.GetDirectoryName(PythonInstallPath),
"pythonw.exe");
}
There is also a way to look it up in the environment, check this out as an alternative.

how to handle winrar diagnostic messages from code

I am developing an windows application in that application i use winrar command line utility to make rar files.
Code
public static string RarFiles(string rarPackagePath,
Dictionary<int, string> accFiles)
{
string error = "";
try
{
string[] files = new string[accFiles.Count];
int i = 0;
foreach (var fList_item in accFiles)
{
files[i] = "\"" + fList_item.Value;
i++;
}
string fileList = string.Join("\" ", files);
fileList += "\"";
System.Diagnostics.ProcessStartInfo sdp = new System.Diagnostics.ProcessStartInfo();
string cmdArgs = string.Format("A {0} {1} -ep1 -r",
String.Format("\"{0}\"", rarPackagePath),
fileList);
sdp.ErrorDialog = true;
sdp.UseShellExecute = true;
sdp.Arguments = cmdArgs;
sdp.FileName = winrarPath;//Winrar.exe path
sdp.CreateNoWindow = false;
sdp.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
System.Diagnostics.Process process = System.Diagnostics.Process.Start(sdp);
process.WaitForExit();
//string s = process.StandardOutput.ReadToEnd();
error = "OK";
}
catch (Exception ex)
{
error = ex.Message;
}
return error;
}
Can any one tell me how can i handle winrar diagnostic messages.
i think u missed some parts :
try adding these :
sdp.StartInfo.RedirectStandardOutput = true;
after Start, add a string to get the output. After that, call WaitForExit()
sdp.Start();
string output = stillc.StandardOutput.ReadToEnd();
sdp.WaitForExit();
*Note : This works only if an output is shown in the console window.
Hope it helps :)

command line output validation in c#

My output in the actual command prompt looks like this:
Name: My Software
Version: 1.0.1
Installed location: c:\my folder
I am trying to get this output via c# code
System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo("cmd", "/c " + "my command to execute");
// The following commands are needed to redirect the standard output.
// This means that it will be redirected to the Process.StandardOutput StreamReader.
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
// Do not create the black window.
procStartInfo.CreateNoWindow = true;
// Now we create a process, assign its ProcessStartInfo and start it
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo = procStartInfo;
proc.Start();
// Get the output into a string
string result = proc.StandardOutput.ReadToEnd();
string[] lines = result.Split(new string[] { System.Environment.NewLine, }, System.StringSplitOptions.None);
foreach (string tmp in lines)
{
if (tmp.Contains("Version"))
{
isAvailable= true;
}
}
I don't want to just check a version tag, I am trying to get the version value and do a compare, for example if the value is 1.0.1, i would want that value and do a comparison with 2.0.0.
I can use indexof(like result.IndexOf("Version:");) - But that doesn't get me the value of the version
Any thoughts will be helpful.
You should use the .NET Version class and it's CompareTo(Object) method to do your comparison.
var input = new Regex(#"(?<=Version:)\s*(.*)").Matches(#"Name: My Software
Version: 1.0.1
Installed location: c:\my folder")[0].Value.Trim();
var a = new Version(input);
var b = new Version("2.0.0");
int comparison = a.CompareTo(b);
if(comparison > 0)
{
Console.WriteLine(a + " is a greater version.");
}
else if(comparison == 0)
{
Console.WriteLine(a + " and " + b +" are the same version.");
}
else
{
Console.WriteLine(b + " is a greater version.");
}
Try like below... it will help you...
Instead of Contains check the word by using IndexOf...
if (tmp.IndexOf("Version") != -1)
{
isAvailable = true;
string[] info = tmp.Split(':');
string version = info[1].Trim();
Console.WriteLine(version);
}
string versionText;
var stuff = tmp.Split(":");
if(stuff[0].Trim() == "Version")
{
isAvailable = true;
versionText = stuff[1].Trim();
}
if(versionText == expectedVersionText) // Do something specfic.
You might want to use Regular Expressions:
^Version:\s*(.*)$
should match the version number inside the parentheses.
string sought = "Version:";
foreach (string tmp in lines)
{
if (tmp.Contains(sought))
{
int position = tmp.IndexOf(sought) + sought.Length;
string version = tmp.Substring(tmp.IndexOf(sought) + sought.Length);
string[] versionParts = version.Split('.');
VersionCompare(versionParts, new string[]{"2", "0", "0"});
}
}
/// <summary>Returns 0 if the two versions are equal, else a negative number if the first is smaller, or a positive value if the first is larder and the second is smaller.</summary>
private int VersionCompare(string[] left, string[] right)
{
for(int i = 0; i < Math.Min(left.Length, right.Length); ++i)
{
int leftValue = int.Parse(left[i]), rightValue = int.Parse(right[i]);
if(leftValue != rightValue) return leftValue - rightValue;
}
return left.Length - right.Length;
}

how to get the oldest file in a directory fast using .NET?

I have a directory with around 15-30 thousand files. I need to just pull the oldest one. In other words the one that was created first. Is there a quick way to do this using C#, other than loading them into a collection then sorting?
You will have to load the FileInfo objects into a collection & sort, but it's a one-liner:
FileSystemInfo fileInfo = new DirectoryInfo(directoryPath).GetFileSystemInfos()
.OrderBy(fi => fi.CreationTime).First();
Ok, two lines because it's a long statement.
The short answer is no. Windows file systems don't index files by date so there is no native way to do this, let alone a .net way without enumerating all of them.
You can't do it without sorting but what you can do is make it fast.
Sorting by CreationTime can be slow because first accessing this property for each file involves interrogation of the file system.
Use A Faster Directory Enumerator that preserves more information about files while enumerating and allows to do sorting faster.
Code to compare performance:
static void Main(string[] args)
{
var timer = Stopwatch.StartNew();
var oldestFile = FastDirectoryEnumerator.EnumerateFiles(#"c:\windows\system32")
.OrderBy(f => f.CreationTime).First();
timer.Stop();
Console.WriteLine(oldestFile);
Console.WriteLine("FastDirectoryEnumerator - {0}ms", timer.ElapsedMilliseconds);
Console.WriteLine();
timer.Reset();
timer.Start();
var oldestFile2 = new DirectoryInfo(#"c:\windows\system32").GetFiles()
.OrderBy(f => f.CreationTime).First();
timer.Stop();
Console.WriteLine(oldestFile2);
Console.WriteLine("DirectoryInfo - {0}ms", timer.ElapsedMilliseconds);
Console.WriteLine("Press ENTER to finish");
Console.ReadLine();
}
For me it gives this:
VEN2232.OLB
FastDirectoryEnumerator - 27ms
VEN2232.OLB
DirectoryInfo - 559ms
Edit: Removed the sort and made it a function.
public static FileInfo GetOldestFile(string directory)
{
if (!Directory.Exists(directory))
throw new ArgumentException();
DirectoryInfo parent = new DirectoryInfo(directory);
FileInfo[] children = parent.GetFiles();
if (children.Length == 0)
return null;
FileInfo oldest = children[0];
foreach (var child in children.Skip(1))
{
if (child.CreationTime < oldest.CreationTime)
oldest = child;
}
return oldest;
}
Sorting is O(n log n). Instead, why don't you just enumerate the directory? I'm not sure what the C# equivalent of FindFirstFile()/FindNextFile() is, but you want to do is:
Keep the current lowest date and filename in a local variable.
Enumerate the directory.
If the date on a given file is less than the local variable, set the local variable to the new date and filename.
Oddly enough, this worked perfectly on a directory of mine with 3000+ jpg files:
DirectoryInfo di = new DirectoryInfo(dpath);
FileInfo[] rgFiles = di.GetFiles("*.jpg");
FileInfo firstfile = rgFiles[0];
FileInfo lastfile = rgFiles[rgFiles.Length - 1];
DateTime oldestfiletime = firstfile.CreationTime;
DateTime newestfiletime = lastfile.CreationTime;
Here's a C# routine that may do what you want by spawning a cmd shell execute a dir /o:D on the specified directory and returning the name of the first file found.
static string GetOldestFile(string dirName)
{
ProcessStartInfo si = new ProcessStartInfo("cmd.exe");
si.RedirectStandardInput = true;
si.RedirectStandardOutput = true;
si.UseShellExecute = false;
Process p = Process.Start(si);
p.StandardInput.WriteLine(#"dir " + dirName + " /o:D");
p.StandardInput.WriteLine(#"exit");
string output = p.StandardOutput.ReadToEnd();
string[] splitters = { Environment.NewLine };
string[] lines = output.Split(splitters, StringSplitOptions.RemoveEmptyEntries);
// find first line with a valid date that does not have a <DIR> in it
DateTime result;
int i = 0;
while (i < lines.Length)
{
string[] tokens = lines[i].Split(' ');
if (DateTime.TryParse(tokens[0], out result))
{
if (!lines[i].Contains("<DIR>"))
{
return tokens[tokens.Length - 1];
}
}
i++;
}
return "";
}
Look, would it not be easier to shell out to a hidden process and redirect the output stream to the input and use the dir /o-d which sorts by the date/time, using the dash reverses the operation....
Edit: here's a sample code to do this...quick and dirty...
public class TestDir
{
private StringBuilder sbRedirectedOutput = new StringBuilder();
public string OutputData
{
get { return this.sbRedirectedOutput.ToString(); }
}
public void Run()
{
System.Diagnostics.ProcessStartInfo ps = new System.Diagnostics.ProcessStartInfo();
ps.FileName = "cmd";
ps.ErrorDialog = false;
ps.Arguments = string.Format("dir {0} /o-d", path_name);
ps.CreateNoWindow = true;
ps.UseShellExecute = false;
ps.RedirectStandardOutput = true;
ps.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
using (System.Diagnostics.Process proc = new System.Diagnostics.Process())
{
proc.StartInfo = ps;
proc.Exited += new EventHandler(proc_Exited);
proc.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(proc_OutputDataReceived);
proc.Start();
proc.WaitForExit();
proc.BeginOutputReadLine();
while (!proc.HasExited) ;
}
}
void proc_Exited(object sender, EventArgs e)
{
System.Diagnostics.Debug.WriteLine("proc_Exited: Process Ended");
}
void proc_OutputDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
{
if (e.Data != null) this.sbRedirectedOutput.Append(e.Data + Environment.NewLine);
//System.Diagnostics.Debug.WriteLine("proc_OutputDataReceived: Data: " + e.Data);
}
}
The very first 4 or 5 lines of the StringBuilder object sbRedirectedOutput can be chopped out,then after that line would contain the oldest filename and would be quite easy to parse out....
I also have thousands of files. To retrieve them in sorted order by the date modified, use either of these C# statements.
files = di.GetFiles("*.*").OrderByDescending(f => f.LastWriteTime).ToArray();
files = di.GetFiles("*.*").OrderBy(f => f.LastWriteTime).ToArray();
To make it easier for the user to access the appropriate file, I display either of the following two lines. I have two windows open. One lists the files in descending order. The other list the files in ascending order. The descending order list is updated by Windows. The ascending order is not updated so the Hm key must be used to put the oldest file at the top of the list.
Console.WriteLine( "DateMod v (latest)");
Console.WriteLine( "DateMod ^ (oldest) Sel Hm");

Categories

Resources