Reading only a certain part of the Filename in C# - c#

I'm new to this Page just now but already got a Question to ask.
Ok, I'm right now on a bigger Project (for me atleast) for a Server Interface of round about 3 Minecraft Servers on one machine.
Now I got to the point where I no longer want to call the startfiles manually, so i created a function that creates a Savefile with the location of the "servers" startfile (it's a simple batchfile) called "(name_of_server)_SAVEFILE(number_of_server).txt".
And now i want the Program to show me (best on startup) how many Servers actually have been saved by asking for the number talked about earlier.
I also want to implement a lock so the filenumber already created can't be saved with another name, but that's a different story there.
I did it like this:
private void checkForServer_button_Click(object sender, EventArgs e)
{
if (File.Exists(#"C:\Users\" + system_user + #"\Desktop\savedServers\*1.txt") == true)
{
string server1_location = File.ReadAllText(#"C:\Users\" + system_user + #"\Desktop\savedServers\*_SAVEFILE1.txt");
checkForServer_response.Text = "There are Servers!";
onlyInfo.Clear();
onlyInfo.Text = "[CONSOLE] Found existing Server! Found at: " + server1_location;
}
}
onlyInfo is a RichTextBox used as a dummy output atm, might stay in the final version to show what the saved batchfiles look like.
Yes, so basically my code does nothing when I click the button.
And another Question, how do I set the "*" properly for this type of usage.
Thanks in advance!

File.Exists does not support wild characters in the file name. And neither does File.ReadAllText.
string[] files = System.IO.Directory.GetFiles(#"C:\Users\" + system_user +
#"\Desktop\savedServers\", "*1.txt");
if (files.Length > 0)
{
string server1_location = File.ReadAllText(files[0]);
...
}

Related

How not to allow running some parts of a script by different users at the exact moment of time?

everyone!
I do a small project for my company and I use C#. I have a script for my project. But before this day, my colleagues and I had an idea that the script would be used by users one by one. For example, if there are a user A and user B, there can be the order where the user B runs the script and only then the user A can run the script.
Today the decision was made to give the users the possibility to run the script freely without the predetermined order. And now I have some thoughts. Here the part of the script:
if (Directory.Exists(#"H:\" + doc_number + #"\detached") == false)
{
Directory.CreateDirectory(#"H:\" + doc_number + #"\detached");
File.WriteAllBytes(#"H:\" + doc_number + #"\detached\1.cms", signature_bytes);
}
else
{
string[] files = Directory.GetFiles(#"H:\" + doc_number + #"\detached"); int files_number = files.Length;
File.WriteAllBytes(#"H:\" + doc_number + #"\detached\" + Convert.ToString(files_number + 1) + ".cms", signature_bytes);
}
Firstly, there is a check of the existence of a directory. If it doesn't exist, the directory will be created and the first file will be added there. Otherwise, we just count the number of files in the directory and then create a new file with a name which is the number of the files in the folder plus one.
However, I'm thinking about the situation when the user A and the user B were at the beginning of this part of the script at the same time and the condition for both would be positive so it wouldn't be executed correctly. Or if one of them started running this part earlier but his or her PC was less powerful so while creating the directory another user would go through the condition, counting files and start creating a file before the first user which would be also incorrect.
I don't know how likely one of these situations are. if so, how can I solve it?
Indeed, you can run into concurrency issues. And you are correct that you can't rely on the existence of a directory to decide what branch to take in your if statement because you might have operations execute in this order:
User A: Checks for directory. Does not exist.
User B: Checks for directory. Does not exist.
User A: Creates directory, enters if branch.
User B: Creates directory, enters if branch.
If the code was running in one process on one machine but in multiple threads, you could use a lock statement.
If the code was running on different processes on the same machine, you could use a cross-process coordination method such as a Mutex.
The question implies that the code runs on different computers but accesses the same file system. In this case, a lock file is a common mechanism to coordinate access to a shared resource. In this approach, you would attempt to create a file and lock it. If that file already exists and is locked by another process, you know someone else got there first. Depending on your needs, a common scenario is to wait for the lock on the file to go away then acquire the lock yourself and continue.
This strategy also works for the other 2 cases above, though is less efficient.
For information about how to create a file with a lock, see
How to lock a file with C#?
There are some issues with your code. For example, what would happen if a file is deleted? The number of files in the directory would be different than the number of the last file, and you can end up trying to write a file that already exists. Also, please use Path.Combine to create paths, it is safer. You also don't need to check if the directory exists, since Directory.Create will do nothing if it already exists.
Common for all solutions bellow:
string baseDir = Path.Combine("H:",doc_number, "detached");
Directory.Create(baseDir);
If you just want any number of users to create files in the same directory, some solutions that are more safe:
Use a GUID:
var guid = Guid.NewGuid();
var file = Path.Combine(baseDir, $"{guid}.cms");
File.WriteAllBytes(file, signature_bytes);
Iterate, trying to create a new file:
bool created = false;
int index = 1;
while(!created)
{
//Check first if the file exists, and gets the next available index
var file = Path.Combine(baseDir, $"{index}.cms");
while(File.Exists(file))
{
file = Path.Combine(baseDir, $"{++index}.cms");
}
//Handle race conditions, if the file was created after we checked
try
{
//Try create the file, not allowing others to acess it while open
using var stream = File.Open(file,FileMode.CreateNew,FileAccess.Write,FileShare.None);
stream.Write(signature_bytes);
created = true;
}
catch (IOException) //If the file already exists, try the next index
{
++index;
}
}

Fastest way to search specific file in subdirectories

I'm trying to search files in a folder that has a lot of folders, with a name that contain specific string.
I'm able to do it but it's taking me about 2 minutes and this is too much time for me.
This is the function:
private void Search()
{
foreach (var file in Directory.EnumerateFiles(#"P:\system\mail\", "*" + textBox1.Text + "*.pdf*", SearchOption.AllDirectories))
{
this.Invoke(new MethodInvoker(delegate ()
{
listBoxControl1.Items.Add(file);
}));
if (XtraMessageBox.Show("Open the file: " + file + " ?", "Information", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.OK)
{
Process.Start(file);
}
}
}
And this is where I'm using the function:
private async void simpleButton1_Click(object sender, EventArgs e)
{
labelControl1.Text = "Status: Please wait . . .";
Stopwatch watch = new Stopwatch();
watch.Start();
await Task.Run(() => Search());
watch.Stop();
labelControl1.Text = "The process done in " + watch.Elapsed.TotalMinutes.ToString() + " minutes.";
}
The goal is to do it like the search in windows that takes me 4-7 seconds.
To compare your search to Windows own search functionality is somewhat invalid because Windows search takes advantage of indexing the filesystem, while in your current implementation, you do not.
But there's good news: You can do it, too.
There are several ways to achieve similar response times, some are faster, some are more precise.
For example you could:
Perform searches in fixed intervals and use the results. Drawback: List may be outdated. OR...
Have an initial search on App start, then use FileSystemWatcher to get notified about FileSystem events (File new, File deleted, File moved ...) to update your internal index. Use that index as your source of information. Drawback: FSW can be a pain to deal with.
Find a way to take advantage of windows own indexes. See:
SO Answer https://stackoverflow.com/a/34340288/982149 utilizes OLE DB Api and may be outdated!
Windows Search Developer's Guide
I don't know if 3. is working for you. 1. may be out of the race because you probably don't want potentially outdated data. So I'd go with 2, but give 3. a shot, too.
Try to use Directory.GetFiles:
string[] files = Directory.GetFiles(filePath, "*.pdf", SearchOption.AllDirectories);

Write into a log file with C#

I am using the JitBit Macro Recorder to create "bots" that save me a lot of time at work. This program can use the mouse and the keyboard and perform tasks by checking different if-options like "if image found on screen".
My newest "bot" is about 900 lines of commands long and I would like to make a log-file to find an error somewhere in there. Sadly, this program doesn't offer such an option, but it let's me use c# as a task. I have NO experience with c# but I thought, that this is easy to do for someone who has some experience.
If I click execute c# code, I get the following input field:
Important: This code MUST contain a class named "Program" with a static method "Main"!
public class Program
{
public static void Main()
{
System.Windows.Forms.MessageBox.Show("test");
}
}
Now I need two code templates:
1. Write a message to a "bot_log.txt" located on my desktop.
[19.05.2016 - 12:21:09] "Checking if item with number 3 exists..."
The number "3" changes with every run and is an exact paste of the clipboard.
2. Add an empty line to the same file
(Everything should be added to a new line at the end of this file.)
If you have no idea how to program in C#, then you should learn it,
if you want to use code provided from answers.
And if you want to generate timestamps and stuff then it's not done within minutes and I don't think someone writes the whole code just for your fitting. Normally questions should have at least a bit of general interest.
Anyway:
This works, if you have a RichTextTbox in your program.
Just do a new event (like clicking a button) and do this inside it.
(This was posted somewhere here too or on another site, with sligh changes)
public static void SaveMyFile(RichTextBox rtb)
{
// Create a SaveFileDialog to request a path and file name to save to.
SaveFileDialog saveLog = new SaveFileDialog();
// Initialize the SaveFileDialog to specify the RTF extention for the file.
saveLog.DefaultExt = "*.rtf";
saveLog.Filter = "RTF Files|*.rtf"; //You can do other extensions here.
// Determine whether the user selected a file name from the saveFileDialog.
if (saveLog.ShowDialog() == System.Windows.Forms.DialogResult.OK &&
saveLog.FileName.Length > 0)
{
// Save the contents of the RichTextBox into the file.
try
{
rtb.SaveFile(saveLog.FileName);
}
catch
{
MessageBox.Show("Error creating the file.\n Is the name correct and is enough free space on your disk\n ?");
}
MessageBox.Show("Logfile was saved successful.");
}
}

Getting the current Directory

I have been working with a simple program essentially designed to be digital flash cards. Ideally, I want the program to be portable. I am trying to get the current directory. My setup is this:
I have a FileIO.cs method which reads currentDir/Data. I then have a winform which calls the method and retrieves a string[] array of the list of folders. Both of these methods are public static. I then have a separate button to create controls on the form.
The problem:
I have cleaned/rebuilt the solution and been using the debugger; The program is running from C:\Users\user\a\b\c\solution\bin\debug. The control (radioButton) I created to verify this data is telling me the 'currentDir' is C:\Users\user and not the programs current directory.
Notes:
I have manually created Debug\Data and 4 folders within the data folder so I am 100% sure they exist. No warnings or compile or run time errors are thrown. Card_Base.GetGrades is automatically called on form load successfully.
I have also confirmed the Release folder is empty and not being used. Also I have a backup program which shows an icon when a file is being uploaded to my backup every time the file(s) change. So I am 100% sure bin\debug is the proper working folder in this scenario.
Perhaps what is puzzling me most is that I have a totally separate program written on the same PC using the same IDE and it properly retrieves the path using the same setup.
In FileIO.cs
//public static string pathPortable = System.IO.Directory.GetCurrentDirectory();
public static string pathPortable = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
static string[] grade1;
public static string[] GetGrade()
{
string fullPath = FileIO.pathPortable + #"\Data";
grade1 = Directory.GetDirectories(fullPath);
return grade1;
}
in Card_Base.cs
public static List<RadioButton> buttonGrade = new List<RadioButton>(10);
public static void GetGrades()
{
string[] grade2 = FileIO.GetGrade();
//Proper and accurate names of the folders I manually added
//C:\Users\user\a\b\c\solution\bin\debug\Data\K
//C:\Users\user\a\b\c\solution\bin\debug\Data\1
//C:\Users\user\a\b\c\solution\bin\debug\Data\2
//C:\Users\user\a\b\c\solution\bin\debug\Data\3
MessageBox.Show("" + grade2[0]); //Information (entire path) is accurate as of this line
int x = 5;
int y = 0;
for (int i = 0; i < grade2.Count(); i++)
{
y = i * 21;
Card_Base.buttonGrade.Add(new RadioButton());
Card_Base.buttonGrade[i].Text = grade2[i];
MessageBox.Show("" + buttonGrade[i].Text); //Proper entire path is shown here!
Card_Base.buttonGrade[i].Location = new System.Drawing.Point(x, y);
}
}
The control whose .text property which shows C:\Users\User, not the Bin\Debug folder.
private void buttonTest_Click(object sender, EventArgs e)
{
MessageBox.Show("abc:" + buttonGrade[0].Text);
for (int i = 0; i < buttonGrade.Count(); i++)
{
panelGrade.Controls.Add(buttonGrade[i]);
}
MessageBox.Show("def:" + buttonGrade[0].Text); //Proper string path is displayed in the popup box here!
}
Result: ???
Four vertically lined radioButtons on a form panel which all have text reading "C:\Users\user\"
Update1:
The text also appears slightly misaligned, slightly higher than the radioButton bubble itself, very strange. However, I've gone back into the editor and confirmed the panelGrade is initially empty. Click/Drag grabs nothing and right clicking the panel does not reveal any underlying objects in the panel's space.
After modifying the target directory to its parent (1 level higher), each messageBox checkpoint reveals the proper string/path is being sent in. The visible radioButton when the program is launcher, after the "test" button is pushed is the only occurrence of this unusual text/string appearing anywhere.
Placing messageBoxes before/after the loop within the test button itself shows that the control (radioButton in buttonGrade[0]) DOES contain the proper string / text / path. Thus the change must occur at some point after the buttonTest code is finished executing.
Update2:
I just opened a brand new solution and copy/pasted the relevant code eliminating anything extraneous. Named all the items/controls with the same names. Completely bare bones. Exact same problem.
HOWEVER, when I change radioButtons to TextBoxs ... the program displays the proper information. Um. What???
Update 3:
Looking through the MSDN radioButtons are derived from buttonBase. The only relevant event I see at a glance is the textChanged event. While it does note that the way the text property of derived classes like radioButton varies, it fails to specify exactly how or what limits it has. Control.Text is simply a System.String , thus I see no reason why a radioButton would not be able to contain that information.
I tried creating a 'test2' button to change 'buttonGrade[0].Text = FileIO.pathPortable;' . Oddly enough, it does not change the text all. Nor does it throw an error.
AH HA! I noticed that the folder after user was my google drive, which is "Google Drive" and has a space in it. I then copied my trash program to C:\and named it TrashMe2 and ensured no folder names contained spaces. The result was "C:\TrashMe2\bi". I then tried "1234567890123456789". The result was that it showed up to the second 3.
The radioButton wasn't receiving a different string and the string/path/data was never changed. It simply showed a 'different folder' because that, by luck of the draw' was the exact number of visible characters it showed. Because I created the radioButtons programmatically, AutoSize did NOT default to true. The proper string was part of the radioButton, it simply was not all visible.
And for security reasons (and a bit of humor), I actually call my user account "user". So it wasn't a scenario where I would see C:\Users\JoeNomidBlow was cut off.
I feel... rather stupid at this particular moment. Thanks for the help!
Programmatically adding a control, particularly a radioButton, does not automatically set the autoSize property to true. When creating a radioButton programatically, one must be sure to add
radioButton.Autosize = true;
or as this case is/was
buttonGrade[i].Autosize = true;
Otherwise the visible space of the text is shortened. In this case, leaving behind a partial path. Which coincidentally happens to be identical to a different valid path.

Can't Access a xml file at random by C# console application

I have a C# console application which creates, parses and deletes multiple xml files at runtime. The application used to run fine in Windows 2003 server with .Net 2.0.
Recently, the Application framework was upgraded to >net 4.0 and the Windows Server OS to Windows 2008 64-bit.
Since then, the application encounters the following exception at random:
Access to the path 'D:\Content\iSDC\GDCOasis\GATE_DATA\LOG\635125008068192773\635125008074911566\SOD\AllRespId.xml' is denied.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.File.Delete(String path)
at ProcessGateFile.SOD.saveFile(String psFile, String psXMLString, Boolean isNonAscii)
The code for the creation, parsing and deletion is as follows:
saveFile(tmpPath + "\\SOD\\AllRespId.xml", "<?xml version= \"1.0\" ?><XML>" + sbldDistinctResp.ToString() + "</XML>", isChinese);
//Save list of Distinct responsibilities for User
sbldDistinctResp.Remove(0, sbldDistinctResp.Length);
xmlCase.Load(tmpPath + "\\SOD\\AllRespId.xml");
arrResps.Clear();
//Start preparing Responsibility selection criteria
RespNodes = xmlCase.SelectNodes("//row");
sRespCriteria = "";
if (RespNodes.Count > 0)
{
foreach (XmlNode RespNode in RespNodes)
{
string RespName = RespNode.Attributes.GetNamedItem("RespId").Value.ToString();
if (!arrResps.Contains(RespName))
{
arrResps.Add(RespName);
}
}
for (int i = 0; i < arrResps.Count; i++)
{
sbldDistinctResp.Append("(#RespId = '" + arrResps[i].ToString() + "') or ");
}
sbldDistinctResp.Remove(sbldDistinctResp.Length - 4, 4);
sRespCriteria = sbldDistinctResp.ToString();
if (!sRespCriteria.Equals(""))
{
sRespCriteria = "(" + sRespCriteria + ")";
}
}
File.Delete(tmpPath + "\\SOD\\AllRespId.xml");
I repeat, the error is happening at random, i.e. it works at times and does not at other times during the same process.
Any idea what might be causing this and how to resolve?
Just a couple of observations:
Why are you saving and then immediately loading the file again? In fact, why do you even need to save this file - you already have all the information you need in the sbldDistinctResp variable to generate the XML you need to work with (as evidenced by the saveFile call at the start of the code) - couldn't you just make a copy of it, surround it with the same XML as you did during saveFile, and work with that?
"It happens randomly" is a very subjective observation :). You should profile this (run it 10,000 times in a loop for example) and record the pattern of errors. You may well be surprised that what seems random at first actually shows a clear pattern over a large number of runs. This may help you to make a connection between the problem and some other apparently unrelated event on the server; or it may confirm that it truly is random and therefore outside of your control.
If you really can't find the problem and you go with the idea of anti-virus, etc, then you could wrap the loading code in a try/catch and re-try a couple of times if you get the error. It's hacky but it would work, assuming you have accepted that the initial error is beyond your control.

Categories