C# - I'm building a console app to read the OneDrive folder status.
I can read so many attributes of file/folder but don't know how to get the Status column value here.
UPDATED: This approach is different that the one that describe here (using Shell32, registry...)
How can I check local OneDrive folder is in sync?
So it is not duplicated question
Add reference to Shell32 e.g C:\Windows\SysWOW64\shell32.dll
( I did try the WindowsAPICodePack ShellPropertyCollection but that did not work )
public static class OneDriveExtensions
{
private static int GetAvailabilityStatusIndex(Folder folder)
{
var index = 0;
while (true)
{
var details = folder.GetDetailsOf(folder, index);
if (details == "Availability status")
{
return index;
}
index++;
}
}
public static string OneDriveAvailability(this FileInfo file)
{
int availabilityStatusIndex;
return OneDriveAvailability(file, out availabilityStatusIndex);
}
public static string OneDriveAvailability(this FileInfo file,out int availabilityStatusIndex)
{
Shell shell = new Shell();
Folder objFolder = shell.NameSpace(file.DirectoryName);
availabilityStatusIndex = GetAvailabilityStatusIndex(objFolder);
return objFolder.GetDetailsOf(objFolder.ParseName(file.Name), availabilityStatusIndex);
}
public static string OneDriveAvailability(this FileInfo file, int availabilityStatusIndex)
{
Shell shell = new Shell();
Folder objFolder = shell.NameSpace(file.DirectoryName);
FolderItem objFolderItem = objFolder.ParseName(file.Name);
return objFolder.GetDetailsOf(objFolderItem, availabilityStatusIndex);
}
public static IEnumerable<OneDriveFileInfo> OneDriveAvailability(this DirectoryInfo directory,Func<DirectoryInfo,IEnumerable<FileInfo>> files)
{
var requireIndex = true;
int availabilityStatusIndex = 0;
return files(directory).Select(f =>
{
string oneDriveAvailability;
if (requireIndex)
{
requireIndex = false;
oneDriveAvailability= f.OneDriveAvailability(out availabilityStatusIndex);
}
else
{
oneDriveAvailability= f.OneDriveAvailability(availabilityStatusIndex);
}
return new OneDriveFileInfo(oneDriveAvailability, f);
});
}
public static IEnumerable<OneDriveFileInfo> OneDriveAvailability(this IEnumerable<FileInfo> files,int availabilityIndex)
{
return files.Select(f => new OneDriveFileInfo(f.OneDriveAvailability(availabilityIndex), f));
}
}
public class OneDriveFileInfo
{
public OneDriveFileInfo(string availabilityStatus, FileInfo file)
{
this.AvailabilityStatus = availabilityStatus;
this.File = file;
}
public string AvailabilityStatus { get; private set; }
public FileInfo File { get; private set; }
}
Related
I want to clean usb using diskpart from C#. I have written below code in C# to get all connect USB. And I iterate thru all usb and clean each usb using diskpart below cmd command.
diskpart
list disk
select disk <0/1/2>
clean
I want to get disk number <0/1/2> from drive name so that I can clean each usb one after another.
foreach (DriveInfo drive in DriveInfo.GetDrives())
{
if (drive.IsReady == true)
{
if (drive.DriveType == DriveType.Removable)
{
string usbName = drive.Name;
}
}
}
The following shows how to use ManagementObjectSearcher, ManagementObject to retrieve a list of removable USB drives
Create a Windows Forms App (.NET Framework) project
Add Reference (System.Management)
VS 2022:
Click Project
Select Add Reference...
Click Assemblies
Check System.Management
Click OK
Add using directives
using System.IO;
using System.Management;
using System.Diagnostics;
Create a class (name: LogicalDiskInfo)
public class LogicalDiskInfo : IComparable<LogicalDiskInfo>
{
public string Description { get; set; }
public string DeviceID { get; set; }
public uint DiskIndex { get; set; }
public uint DriveType { get; set; }
public string FileSystem { get; set; }
public bool IsRemovable { get; set; } = false;
public string Name { get; set; }
public uint PartitionIndex { get; set; }
public uint PartitionNumber { get; set; }
public UInt64 Size { get; set; }
public int CompareTo(LogicalDiskInfo other)
{
if (String.Compare(this.Name, other.Name) == 0)
return 0;
else if (String.Compare(this.Name, other.Name) < 0)
return -1;
else
return 1;
}
}
Create a class (name: LogicalDisk)
public class LogicalDisk
{
public List<LogicalDiskInfo> LogicalDiskInfos = new List<LogicalDiskInfo>();
}
Create a class (name: DiskDriveInfo)
public class DiskDriveInfo : IComparable<DiskDriveInfo>
{
public string Caption { get; set; } = string.Empty;
public string DeviceID { get; set; } = string.Empty;
public List<LogicalDiskInfo> LogicalDisks { get; set; } = new List<LogicalDiskInfo>();
public UInt32 DiskIndex { get; set; } = 0;
public string InterfaceType { get; set; } = string.Empty;
public bool IsRemovable { get; set; } = false;
public string MediaType { get; set; }
public string Model { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public UInt32 Partitions { get; set; } = 0;
public string PnpDeviceID { get; set; } = string.Empty;
public UInt64 Size { get; set; } = 0;
public string Status { get; set; } = string.Empty;
public int CompareTo(DiskDriveInfo other)
{
if (this.DiskIndex == other.DiskIndex)
return 0;
else if (this.DiskIndex < other.DiskIndex)
return -1;
else
return 1;
}
}
GetUSBRemovableDiskDriveInfo:
Note: In Windows 10, it's possible to create multiple partitions on a USB flash drive. See here for more info. Therefore, it's possible that more than one drive letter may exist on the same physical disk drive. The code below works with USB drives having either a single partition or multiple partitions.
private List<DiskDriveInfo> GetUSBRemovableDiskDriveInfo()
{
SortedDictionary<uint, DiskDriveInfo> diskDict = new SortedDictionary<uint, DiskDriveInfo>();
List<DiskDriveInfo> driveInfos = new List<DiskDriveInfo>();
//MediaType: 'Removable Media'
using (ManagementObjectSearcher searcherDiskDrive = new ManagementObjectSearcher("SELECT Caption, DeviceID, Index, InterfaceType, MediaType, Model, Name, Partitions, PNPDeviceID, Size, Status FROM Win32_DiskDrive WHERE InterfaceType='USB' and MediaType='Removable Media'"))
{
foreach (ManagementObject objDiskDrive in searcherDiskDrive.Get())
{
if (objDiskDrive == null)
continue;
//create new instance
DiskDriveInfo ddInfo = new DiskDriveInfo();
//set value
uint diskIndex = Convert.ToUInt32(objDiskDrive["Index"]);
ddInfo.Caption = objDiskDrive["Caption"]?.ToString();
ddInfo.DeviceID = objDiskDrive["DeviceID"]?.ToString();
ddInfo.DiskIndex = diskIndex;
ddInfo.InterfaceType = objDiskDrive["InterfaceType"]?.ToString();
ddInfo.MediaType = objDiskDrive["MediaType"]?.ToString();
ddInfo.Model = objDiskDrive["Model"]?.ToString();
ddInfo.Name = objDiskDrive["Name"]?.ToString();
ddInfo.Partitions = Convert.ToUInt32(objDiskDrive["Partitions"]);
ddInfo.PnpDeviceID = objDiskDrive["PnpDeviceID"]?.ToString();
ddInfo.Size = Convert.ToUInt64(objDiskDrive["Size"]);
ddInfo.Status = objDiskDrive["Status"]?.ToString();
if (ddInfo.MediaType == "Removable Media")
ddInfo.IsRemovable = true;
else
ddInfo.IsRemovable = false;
if (!diskDict.ContainsKey(diskIndex))
{
//Debug.WriteLine($"Adding DiskIndex {ddInfo.DiskIndex} Partitions: {ddInfo.Partitions}");
//add
diskDict.Add(diskIndex, ddInfo);
}
}
}
//create new instance
SortedDictionary<string, LogicalDisk> logicalDiskToPartitionDict = new SortedDictionary<string, LogicalDisk>();
//get info from Win32_LogicalDiskToPartition
//this is used to associate a DiskIndex and PartitionIndex with a drive letter
using (ManagementObjectSearcher searcherLogicalDiskToPartition = new ManagementObjectSearcher($#"SELECT * FROM Win32_LogicalDiskToPartition"))
{
foreach (ManagementObject objLogicalDiskToPartition in searcherLogicalDiskToPartition.Get())
{
if (objLogicalDiskToPartition == null)
continue;
string antecedent = objLogicalDiskToPartition["Antecedent"]?.ToString();
string dependent = objLogicalDiskToPartition["Dependent"]?.ToString();
string antecedentValue = antecedent.Substring(antecedent.IndexOf('=') + 1).Replace("\"", "");
uint diskIndex = 0;
uint partitionIndex = 0;
//get disk index and convert to uint
UInt32.TryParse(antecedentValue.Substring(antecedentValue.IndexOf("#") + 1, antecedentValue.IndexOf(",") - (antecedentValue.IndexOf("#") + 1)), out diskIndex);
//get partition index and convert to uint
UInt32.TryParse(antecedentValue.Substring(antecedentValue.LastIndexOf("#") + 1), out partitionIndex);
string driveLetter = dependent.Substring(dependent.IndexOf("=") + 1).Replace("\"", "");
if (diskDict.ContainsKey(diskIndex))
{
if (!logicalDiskToPartitionDict.ContainsKey(driveLetter))
{
//add
logicalDiskToPartitionDict.Add(driveLetter, new LogicalDisk());
}
//get info from Win32_LogicalDisk
using (ManagementObjectSearcher searcherLogicalDisk = new ManagementObjectSearcher($"SELECT Description, DeviceID, DriveType, FileSystem, Name, Size FROM Win32_LogicalDisk WHERE Name = '{driveLetter}'"))
{
foreach (ManagementObject objLogicalDisk in searcherLogicalDisk.Get())
{
if (objLogicalDisk == null)
continue;
//create new instance
LogicalDiskInfo logicalDiskInfo = new LogicalDiskInfo();
//set value
logicalDiskInfo.Description = objLogicalDisk["Description"]?.ToString();
logicalDiskInfo.DeviceID = objLogicalDisk["DeviceID"]?.ToString();
logicalDiskInfo.DriveType = Convert.ToUInt32(objLogicalDisk["DriveType"]);
logicalDiskInfo.DiskIndex = diskIndex;
logicalDiskInfo.FileSystem = objLogicalDisk["FileSystem"]?.ToString();
logicalDiskInfo.Name = objLogicalDisk["Name"]?.ToString();
logicalDiskInfo.PartitionIndex = partitionIndex;
logicalDiskInfo.PartitionNumber = partitionIndex + 1; //diskpart partitions start at 1
logicalDiskInfo.Size = Convert.ToUInt64(objLogicalDisk["Size"]);
//DriveType: 2=Removable; 3=Local Disk; 4=Network Drive; 5=CD
if (logicalDiskInfo.DriveType == 2)
logicalDiskInfo.IsRemovable = true;
else
logicalDiskInfo.IsRemovable = false;
Debug.WriteLine($"adding logicalDiskInfo for DiskIndex: '{diskIndex}' PartitionIndex: '{partitionIndex}' PartitionNumber: '{logicalDiskInfo.PartitionNumber}'");
//add
logicalDiskToPartitionDict[driveLetter].LogicalDiskInfos.Add(logicalDiskInfo);
}
}
}
}
}
//add logical disk info to disk dictionary
foreach(KeyValuePair<string, LogicalDisk> kvp in logicalDiskToPartitionDict)
{
List<LogicalDiskInfo> logicalDiskInfoList = kvp.Value.LogicalDiskInfos;
//sort
logicalDiskInfoList.Sort();
foreach (LogicalDiskInfo ldInfo in logicalDiskInfoList)
{
//add
diskDict[ldInfo.DiskIndex].LogicalDisks.Add(ldInfo);
}
}
//only add disks that are listed as 'Removable'
foreach(KeyValuePair<uint, DiskDriveInfo> kvp in diskDict)
{
if (kvp.Value.IsRemovable)
{
//add
driveInfos.Add(kvp.Value);
}
}
return driveInfos;
}
Usage:
System.Diagnostics.Debug.WriteLine("--------GetUSBRemovableDiskDriveInfo----------");
foreach (DiskDriveInfo ddInfo in GetUSBRemovableDiskDriveInfo())
{
string driveLetters = string.Empty;
for (int i = 0; i < ddInfo.LogicalDisks.Count; i++)
{
if (i > 0)
driveLetters += ", ";
driveLetters += ddInfo.LogicalDisks[i].Name;
}
System.Diagnostics.Debug.WriteLine($"Caption: '{ddInfo.Caption}' Name: '{ddInfo.Name}' DiskIndex: '{ddInfo.DiskIndex}' DriveLetters: [{driveLetters}] Partitions: '{ddInfo.Partitions}' Size: '{ddInfo.Size}'");
}
One can use System.Diagnostics.Process to execute a diskpart script to clean one or more disks. See this post for more info.
Resources:
System.Management.ManagementObjectSearcher
System.Management.ManagementObject
System.Diagnostics.Process
diskpart
diskpart scripts and examples
Win32_DiskDrive class
Win32_LogicalDisk class
Win32_LogicalDiskToPartition class
It may be the dirty way, but you could just use diskpart interactively and send commands / parse output. Since we are invoking diskpart anyway, a 100% managed codebase seems to not be the target as much as a way of getting it done.
So emphasis on "Dirty" way, but very simple functional way as well :-)
Sometimes when there is a perfectly good tool for the job such as ghostscript or ffmpeg, I automate them this way. Just as if I were typing at the CMD prompt.
Wrap a CMD instance in a wrapper to read/write to, and power it in event driven logic from there.
using System;
using System.Diagnostics;
namespace yourapp
{
public class cmdShell
{
private Process shellProcess;
public delegate void onDataHandler(cmdShell sender, string e);
public event onDataHandler onData;
public cmdShell()
{
try
{
shellProcess = new Process();
ProcessStartInfo si = new ProcessStartInfo("cmd.exe");
si.Arguments = "/k";
si.RedirectStandardInput = true;
si.RedirectStandardOutput = true;
si.RedirectStandardError = true;
si.UseShellExecute = false;
si.CreateNoWindow = true;
si.WorkingDirectory = Environment.GetEnvironmentVariable("windir");
shellProcess.StartInfo = si;
shellProcess.OutputDataReceived += shellProcess_OutputDataReceived;
shellProcess.ErrorDataReceived += shellProcess_ErrorDataReceived;
shellProcess.Start();
shellProcess.BeginErrorReadLine();
shellProcess.BeginOutputReadLine();
}
catch (Exception ex)
{
Trace.WriteLine(ex.Message);
}
}
void shellProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
doOnData(e.Data);
}
void shellProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
doOnData(e.Data);
}
private void doOnData(string data)
{
if (onData != null) onData(this, data);
}
public void write(string data)
{
try
{
shellProcess.StandardInput.WriteLine(data);
}
catch (Exception ex)
{
Trace.WriteLine(ex.Message);
}
}
}
}
I am a beginner in programming,It's really difficult for me to analyze and debug how to skip reading the first line of the csv file. I need some help.
I need my id to fill my combobox in my form that contains all
Id's.In order to not include the header in browsing and
displaying.I need to skip the first line.
public bool ReadEntrie(int id, ref string name, ref string lastname, ref
string phone, ref string mail, ref string website)
{
int count = 0;
CreateConfigFile();
try
{
fs = new FileStream(data_path, FileMode.Open);
sr = new StreamReader(fs);
string temp = "";
bool cond = true;
while (cond == true)
{
if ((temp = sr.ReadLine()) == null)
{
sr.Close();
fs.Close();
cond = false;
if (count == 0)
return false;
}
if (count == id)
{
string[] stringSplit = temp.Split(',');
int _maxIndex = stringSplit.Length;
name = stringSplit[0].Trim('"');
lastname = stringSplit[1].Trim('"');
phone = stringSplit[2].Trim('"');
mail = stringSplit[3].Trim('"');
website = stringSplit[4].Trim('"');
}
count++;
}
sr.Close();
fs.Close();
return true;
}
catch
{
return false;
}
}
#Somadina's answer is correct, but I would suggest a better alternative. You could use a CSV file parser library such as CSV Helpers.
You can get the library from Nuget or Git. Nuget command would be:
Install-Package CsvHelper
Declare the following namespaces:
using CsvHelper;
using CsvHelper.Configuration;
Here's how simple your code looks when you use such a library:
class Program
{
static void Main(string[] args)
{
var csv = new CsvReader(File.OpenText("Path_to_your_csv_file"));
csv.Configuration.IgnoreHeaderWhiteSpace = true;
csv.Configuration.RegisterClassMap<MyCustomObjectMap>();
var myCustomObjects = csv.GetRecords<MyCustomObject>();
foreach (var item in myCustomObjects.ToList())
{
// Apply your application logic here.
Console.WriteLine(item.Name);
}
}
}
public class MyCustomObject
{
// Note: You may want to use a type converter to convert the ID to an integer.
public string ID { get; set; }
public string Name { get; set; }
public string Lastname { get; set; }
public string Phone { get; set; }
public string Mail { get; set; }
public string Website { get; set; }
public override string ToString()
{
return Name.ToString();
}
}
public sealed class MyCustomObjectMap : CsvClassMap<MyCustomObject>
{
public MyCustomObjectMap()
{
// In the name method, you provide the header text - i.e. the header value set in the first line of the CSV file.
Map(m => m.ID).Name("id");
Map(m => m.Name).Name("name");
Map(m => m.Lastname).Name("lastname");
Map(m => m.Phone).Name("phone");
Map(m => m.Mail).Name("mail");
Map(m => m.Website).Name("website");
}
}
Some more details in an answer here.
To skip the first line, just replace the line:
if (count == id)
with
if (count > 0 && count == id)
MORE THOUGHTS ON YOUR APPROACH
Because you used the ref keyword, each line you read will override the previous values you stored in the parameters. A better way to do this is to create a class to hold all the properties of interest. Then, for each line you read, package an instance of the class and add it to a list. You method signature (even the return type) will change eventually.
From your code, the class will look like this:
public class DataModel
{
public string Name { get; set; }
public string LastName { get; set; }
public string Phone{ get; set; }
public string Mail { get; set; }
public string Website{ get; set; }
}
Then your method will be like this:
public IList<DataModel> ReadEntrie(int id, string data_path)
{
int count = 0;
CreateConfigFile();
var fs = new FileStream(data_path, FileMode.Open);
var sr = new StreamReader(fs);
try
{
var list = new List<DataModel>();
string temp = "";
bool cond = true;
while (cond == true)
{
if ((temp = sr.ReadLine()) == null)
{
cond = false;
if (count == 0)
throw new Exception("Failed");
}
if (count > 0 && count == id)
{
string[] stringSplit = temp.Split(',');
var item = new DataModel();
item.Name = stringSplit[0].Trim('"');
item.LastName = stringSplit[1].Trim('"');
item.Phone = stringSplit[2].Trim('"');
item.Mail = stringSplit[3].Trim('"');
item.Website = stringSplit[4].Trim('"');
// add item to list
list.Add(item);
}
count++;
}
return list;
}
catch
{
throw; // or do whatever you wish
}
finally
{
sr.Close();
fs.Close();
}
}
I'm trying to upload files to a folder using SharePoint and C#.
I managed to create folders with my code and it works fine.
This is my Document class:
[DataContractAttribute]
public class Document
{
[DataMemberAttribute]
public string Name { get; set; }
[DataMemberAttribute]
public byte[] Content { get; set; }
[DataMemberAttribute]
public bool ReplaceExisting { get; set; }
[DataMemberAttribute]
public string Folder { get; set; }
[DataMemberAttribute]
public Dictionary<string, object> Properties { get; set; }
public Document()
{
Properties = new Dictionary<string, object>();
}
public Document(string name, byte[] content)
{
Name = name;
ReplaceExisting = false;
Content = content;
Properties = new Dictionary<string, object>();
}
public Document(string name, byte[] content, bool replace)
{
Name = name;
Content = content;
ReplaceExisting = replace;
Properties = new Dictionary<string, object>();
}
}
And this is the class where I use it to upload files (Document) to an existing share point folder:
public class SharePointHandler : IDisposable
{
private static string sharePointSite = "My Site";
private static string documentLibraryName = "Root folder";
public SharePointHandler() { }
public void Upload(List<Document> documents, string company, int year, string quarter)
{
string Year = year.ToString();
try
{
using (ClientContext context = new ClientContext(sharePointSite))
{
var list = context.Web.Lists.GetByTitle(documentLibraryName);
context.Load(list);
var root = list.RootFolder;
context.Load(root);
context.ExecuteQuery();
.
.
.
foreach (var document in documents)
{
var fileCreationInformation = new FileCreationInformation();
fileCreationInformation.Content = document.Content;
fileCreationInformation.Overwrite = true;
fileCreationInformation.Url = list.RootFolder.ServerRelativeUrl + "/" + company + "/" + Year + "/" + quarter + "/" + document.Name;
Microsoft.SharePoint.Client.File uploadFile = quarterFolder.Files.Add(fileCreationInformation);
foreach (KeyValuePair<string, object> property in document.Properties)
uploadFile.ListItemAllFields[property.Key] = property.Value;
try
{
uploadFile.CheckOut();
context.ExecuteQuery();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
uploadFile.ListItemAllFields.Update();
context.ExecuteQuery();
uploadFile.CheckIn("", CheckinType.MajorCheckIn);
context.ExecuteQuery();
}
}
}
catch (Exception ex)
{
EventLog.WriteEntry(ex.Message, EventLogEntryType.Error);
return;
}
}
public void Dispose() { }
}
When I run the code I have one document with:
Content: {byte[11430]}
Folder: null
Name: Testing.docx
Properties: Count = 0
ReplaceExisting: false
Everything works fine and I get the URL needed.
But when I get to these commands:
try
{
uploadFile.CheckOut();
context.ExecuteQuery();
}
The program falls and I get error that says: File not found.
What am I doing wrong?
Thank you for your help!
Here is a working example of an upload to SharePoint via CSOM:
using (ClientContext conext = new ClientContext(site.url))
{
List projectFiles = projects.Web.Lists.GetByTitle("Project Files");
context.Load(projectFiles.RootFolder, w => w.ServerRelativeUrl);
context.ExecuteQuery();
FileStream documentStream = System.IO.File.OpenRead(filePath);
byte[] info = new byte[documentStream.Length];
documentStream.Read(info, 0, (int)documentStream.Length);
string fileURL = projectFiles.RootFolder.ServerRelativeUrl + "/Folder/FileName.ext";
FileCreationInformation fileCreationInformation = new FileCreationInformation();
fileCreationInformation.Overwrite = true;
fileCreationInformation.Content = info;
fileCreationInformation.Url = fileURL;
Microsoft.SharePoint.Client.File uploadFile = projectFiles.RootFolder.Files.Add(fileCreationInformation);
context.Load(uploadFile, w => w.MajorVersion, w => w.MinorVersion);
context.ExecuteQuery();
}
In your case, I would upload the file and ExecuteQuery() and then add your metadata and execute a second query, make sure you add a context.Load after your files.Add(). You are trying to do too many things at once, so just use this code to get the file uploaded and work your way through your other needs after. Also, the file add will not create folders for you so make sure you are specifying a valid path.
I'm currently working on a .NET 2.0 application and I got stuck while trying to loop through all files in a specific folder and determine its file pairs. (Files with the same name but different extensions)
Like:
MyFile0001.jpg
MyFile0001.mp3
MyFile0001.txt
I'm interested in the filename extension, if they are available or not, that's why I created a class called "FileClass". At the end the file pairs will be added to a list list.AddRange().
Her is my code example:
class FileClass
{
public string FileName { get; set; }
public string filePath { get; set; }
public bool mp3 { get; set; }
public bool txt { get; set; }
public bool jpg { get; set; }
}
static void Main()
{
var list = new List<FileClass>();
var dirConfig = new DirectoryInfo(#"New Folder");
var allFiles = dirConfig.GetFiles("*");
foreach (var fileInfo in allFiles)
{
// Some code for finding file pairs...
// set properties, if filename extension is available
// and add them to a list.
}
}
Any suggestion how to handle this?
if possible without LINQ
I would use a Dictionary<String, List<String>> (for filename -> list of extensions)
The first step could look like this:
Dictionary<String, List<String>> d = new Dictionary<string, List<string>>();
var dirConfig = new DirectoryInfo(#"SomeFolder");
var allFiles = dirConfig.GetFiles("*");
foreach (var fileInfo in allFiles)
{
String coreName = Path.GetFileNameWithoutExtension(fileInfo.Name);
if (!d.ContainsKey(coreName)) d.Add(coreName, new List<String>());
d[coreName].Add(fileInfo.Extension);
}
Something like this should work:
class FileClass
{
public string FileName { get; set; }
public string filePath { get; set; }
public bool mp3 { get; set; }
public bool txt { get; set; }
public bool jpg { get; set; }
}
static void Main()
{
var list = new List<FileClass>();
var dirConfig = new DirectoryInfo(#"New Folder");
var allFiles = dirConfig.GetFiles("*");
foreach (var fileInfo in allFiles)
{
// for purposes of matching files with different extensions,
// the "fileName" variable below is the file name minus the extension
var fileName = fileInfo.Name.Substring(0, fileInfo.Name.Length
- fileInfo.Extension.Length);
var fileClass = list.Where(fc => fc.FileName == fileName).FirstOrDefault();
if (fileClass == null)
{
// this is the first version of this file name,
// so create a new FileClass instance and add it to the list
fileClass = new FileClass({
FileName = fileName,
filePath = Path.Combine(fileInfo.DirectoryName, fileName),
mp3 = (fileInfo.Extension.ToLower() == ".mp3"),
txt = (fileInfo.Extension.ToLower() == ".txt"),
jpg = (fileInfo.Extension.ToLower() == ".jpg")
});
list.Add(fileClass);
}
else
{
// a different version of this file was already found,
// so just set the boolean flag for this extension
switch (fileInfo.Extension.ToLower()) {
case ".mp3": fileClass.mp3 = true; break;
case ".txt": fileClass.txt = true; break;
case ".jpg": fileClass.jpg = true; break;
}
}
}
}
You can do something like this.
string[] arrayOfFiles= Directory.GetFiles(folderPath, "Filename.*");
First, I did check this post but it is in Python, first, and second it appears to be actually making the directories, which I cannot do in this scenario.
Second, these are not directories that exist, nor can I create them.
I have an input in C# like this:
List<string> filePaths = new List<string>();
filePaths.Add(#"ProgramDir\InstallDir\Module1\mod1pack1.exe");
filePaths.Add(#"ProgramDir\InstallDir\Module1\mod1pack2.exe");
filePaths.Add(#"ProgramDir\InstallDir\Module2\mod2pack1.exe");
filePaths.Add(#"ProgramDir\InstallDir\Module1\SubModule1\report1.rpt");
filePaths.Add(#"SystemDir\DependencyDir\dependency1.dll");
filePaths.Add(#"SystemDir\DependencyDir\dependency2.dll");
What I have been trying to do is create an object that represents this structure, such that it could be visualized like this:
-ProgramDir
Installdir
Module1
mod1pack1.exe
mod1pack2.exe
-SubModule1
report1.rpt
Module2
mod2pack1.exe
-SystemDir
-DependencyDir
dependency1.dll
dependency2.dll
What I have tried is various versions of the following, and I could really use some help to figure out where I've got it wrong.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
SetFilePathList();
DTree forest = new DTree();
List<DTreeBranch> branches = new List<DTreeBranch>();
foreach (string path in filePaths)
{
forest.GrowTree(path.Split('\\'), branches);
}
forest.SubBranches.AddRange(branches);
}
private static List<string> filePaths { get; set; }
private static void SetFilePathList()
{
filePaths = new List<string>();
filePaths.Add(#"ProgramDir\InstallDir\Module1\mod1pack1.exe");
filePaths.Add(#"ProgramDir\InstallDir\Module1\mod1pack2.exe");
filePaths.Add(#"ProgramDir\InstallDir\Module2\mod2pack1.exe");
filePaths.Add(#"ProgramDir\InstallDir\Module1\SubModule1\report1.rpt");
filePaths.Add(#"SystemDir\DependencyDir\dependency1.dll");
filePaths.Add(#"SystemDir\DependencyDir\dependency2.dll");
}
}
public class DTree
{
public List<DTreeBranch> SubBranches { get; set; }
public string BranchName { get; set; }
public DTree() { SubBranches = new List<DTreeBranch>(); }
public DTreeBranch AddChildren(string[] childElements, DTreeBranch branch)
{
DTreeBranch childBranch;
foreach (string element in childElements)
{
childBranch = new DTreeBranch();
childBranch.BranchName = element;
branch.SubBranches.Add(childBranch);
var query = from q in childElements
where q != childBranch.BranchName
select q;
AddChildren(query.ToArray<string>(), childBranch);
}
return branch;
}
public void GrowTree(string[] pathElements, List<DTreeBranch> Branches)
{
DTreeBranch result = Branches.Find(delegate(DTreeBranch b)
{
return b.BranchName == pathElements[0];
});
if (result == null)
{
DTreeBranch newRootBranch = new DTreeBranch();
newRootBranch.BranchName = pathElements[0];
Branches.Add(newRootBranch);
GrowTree(pathElements, Branches);
}
else
{
var query = from q in pathElements
where q != result.BranchName
select q;
DTreeBranch childBranch = AddChildren(query.ToArray<string>(), result);
Branches.Add(childBranch);
}
}
}
public class DTreeBranch
{
public List<DTreeBranch> SubBranches { get; set; }
public string BranchName { get; set; }
public DTreeBranch()
{
SubBranches = new List<DTreeBranch>();
}
}
}
The main thing is that the output is only two layers deep. I guess what I'm saying is that the new elements are added to the depth, not the breadth, and I'm at a loss as to how to effectively work through this. I also think that I have way more code than I need.
Thanks in advance.
I'm not sure exactly what our goals are, but a simple recursive parse will do it quite easily. Wrote this up, and hope it helps. You can make it significantly more fancy if you want, with DTrees and sub branches, or separate collections for Files and Directories, etc. I don't really understand what all that code in there is for. If it has something to do with WIX, I'm sorry ;) And you could always use something like this to parse it out into the tree, and then convert that sanely to a different format.
this assumes no duplicate leaf nodes (file names).
if that isn't the case, just add a sanity check like for directories.
The main "Node" class -
public class Node
{
public string Name { get; set; }
public bool IsDirectory { get; set; }
public List<Node> Children = new List<Node>();
internal void AddChildren(string f)
{
var dirs = Path.GetDirectoryName(f);
if (string.IsNullOrEmpty(dirs))
{
// we are adding a file
var file = Path.GetFileName(f);
Children.Add(new Node {Name = file, IsDirectory = false});
}
else
{
// we are adding a directory
var firstDir = dirs.Split(Path.DirectorySeparatorChar)[0];
var childNode = Children.FirstOrDefault(d => d.Name == firstDir);
if (childNode == null)
{
childNode = new Node {Name = firstDir, IsDirectory = true};
Children.Add(childNode);
}
var subPath = f.Substring(firstDir.Length + 1);
childNode.AddChildren(subPath);
}
}
}
Calling it is simple, like this:
var filePaths = new List<string> {
#"ProgramDir\InstallDir\Module1\mod1pack1.exe",
#"ProgramDir\InstallDir\Module1\mod1pack2.exe",
#"ProgramDir\InstallDir\Module2\mod2pack1.exe",
#"ProgramDir\InstallDir\Module1\SubModule1\report1.rpt",
#"SystemDir\DependencyDir\dependency1.dll",
#"SystemDir\DependencyDir\dependency2.dll",
};
var node = new Node { Name = "Root", IsDirectory = true };
foreach (var f in filePaths )
{
node.AddChildren(f);
}
Printing it out (with indent per level, gives me this)
public static void PrintNode(Node node, int indent)
{
if (indent > 0) // don't print out root directory (level 1).
{
var ending = node.IsDirectory ? Path.DirectorySeparatorChar.ToString() : "*";
Console.WriteLine("{0}{1}{2}", new string('\t', indent - 1), node.Name, ending);
}
node.Children.ForEach(n => PrintNode(n, indent + 1));
}
ProgramDir\
InstallDir\
Module1\
mod1pack1.exe*
mod1pack2.exe*
SubModule1\
report1.rpt*
Module2\
mod2pack1.exe*
SystemDir\
DependencyDir\
dependency1.dll*
dependency2.dll*
I got about the same as Andrew:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
namespace ConsoleApplication1
{
class Program
{
public static void Main(String[] args)
{
var filePaths = new List<string> {#"ProgramDir\InstallDir\Module1\mod1pack1.exe", #"ProgramDir\InstallDir\Module1\mod1pack2.exe", #"ProgramDir\InstallDir\Module2\mod2pack1.exe", #"ProgramDir\InstallDir\Module1\SubModule1\report1.rpt", #"SystemDir\DependencyDir\dependency1.dll", #"SystemDir\DependencyDir\dependency2.dll"};
var nodes = Parse(filePaths.ToArray());
foreach (var node in nodes)
Console.Out.WriteLine(node.ToString());
Console.ReadLine();
}
public static IEnumerable<Node> Parse(params String[] paths)
{
var roots = new NodeSet();
foreach (var path in paths)
{
var pathSplit = path.Split('\\');
Node current = null;
foreach (var pathElement in pathSplit)
{
var currentRoots = (current == null) ? roots : current.Children;
if (currentRoots.Contains(pathElement))
current = currentRoots[pathElement];
else
currentRoots.Add(current = new Node(pathElement));
}
}
return roots;
}
public class Node
{
public String Name { get; private set; }
public NodeSet Children { get; private set; }
public Node(String name)
{
if (String.IsNullOrWhiteSpace(name)) throw new ArgumentNullException("name");
Name = name;
Children = new NodeSet();
}
public override string ToString() { return ToString(1); }
private String ToString(Int32 indent)
{
var indentStr = Environment.NewLine + new string('\t', indent);
return Name + (Children.Count == 0 ? "" : indentStr + String.Join(indentStr, Children.Select(c => c.ToString(indent + 1)).ToArray()));
}
}
public class NodeSet : KeyedCollection<String, Node> {
protected override string GetKeyForItem(Node item) { return item.Name; }
}
}
}