Thread Safe, prevent variables from updating - c#

I have a delegate that is being executed in a threadpool. A count gets passed in correctly as a variable, however, when the program goes to return the output, The initial value passed in is now the updated version. How can I modify ths so the variable stays the correct value?
private void SetControlText(TextBox TB, string txt)
{
if (TB.InvokeRequired)
{
Invoke((MethodInvoker)delegate
{
TB.AppendText(txt + "\n");
TB.Update();
});
return;
}
TB.Text = txt;
}
private void DoWork(OCAdapter.OCAdapter Adapter, OutputForm output, int c, object ThreadContext = null)
{
int count = c;
//output.AppendToOutput("Initializing Adapter: " + count + " Test\n");
SetControlText(output.OutputBx, "Initializing Adapter: " + count + " Test\n");
try
{
var Test = Adapter.GetBookmarks();
if (Test != null)
//output.AppendToOutput("Adapter: " + count + " is valid\n");
SetControlText(output.OutputBx, "Adapter: " + count + " is valid\n");
}
catch (Exception ex)
{
//output.AppendToOutput("Exception occured on adapter: " + count + " Exception: " + ex.Message);
SetControlText(output.OutputBx, "Exception occured on adapter: " + count + " Exception: " + ex.Message);
}
}

Hey I actually found out the answer, the threads were using shared memory so they were accessing the variable after it was incremented.
The way I fixed this is by passing in a temporary variable with the count.

Your SetControlText() isn't quite right. It's doing BOTH an Invoke() and also setting the Text anyways, from the wrong thread, directly below that; every time.
Try something like this instead and see if the problem goes away:
private delegate void SetControlTextDelegate(TextBox TB, string txt);
private void SetControlText(TextBox TB, string txt)
{
if (TB.InvokeRequired)
{
TB.Invoke(new SetControlTextDelegate(SetControlText), new object[] { TB, txt });
}
else
{
TB.AppendText(txt + Environment.NewLine);
}
}

Related

I want to run the process in background in my .net project

I have a piece of code which requires me to use a process, but i want that to run in background and not open console window.
public uint LaunchProcess(string sIPAddress, string sPort)
{
uint iPid = 0;
try
{
logger.AddLog("LaunchProcess : " + sIPAddress + " " + sPort);
object[] PlugInRunnerInfo = { StaticUtils.GetLocation(AgilentPluginCommonConstants.PlugInRunnerPath) + "\\" + "PlugInRunner.exe" + " " + sIPAddress + " " + sPort, null, null, 0 };
//ManagementClass is a part of Windows Management Intrumentation,namespaces. One of its use is to provides access to manage applications.
//Here this class is used to launch PlugInRunner as detached process.By setting the ManagementClass object's property 'CreateFlags' to value 0x00000008
//we can start the PlugInRunner as detached one.
using (var mgmtObject = new ManagementClass("Win32_Process"))
{
var processStartupInfo = new ManagementClass("Win32_ProcessStartup");
processStartupInfo.Properties["CreateFlags"].Value = 0x00000008;//DETACHED_PROCESS.
var result = mgmtObject.InvokeMethod("Create", PlugInRunnerInfo);
if (result != null)
{
logger.AddLog("Process id " + Convert.ToUInt32(PlugInRunnerInfo[3]));
iPid = Convert.ToUInt32(PlugInRunnerInfo[3]);
}
}
}
catch (Exception ex)
{
logger.AddLog("Exception " + ex.Message);
}
return iPid;
}
Above is my code, can anyone help me run the process in background?

Can't call playerprefs on other scene

so I'm here want to retrieve the string to text in a different scene with, but after I've tried by my self isn't working, am I do it wrong?
here the google sign script in that script I want to save the string task.result.displayName with Playerprefs
internal void OnAuthenticationFinished(Task<GoogleSignInUser> task)
{
if (task.IsFaulted)
{
using (IEnumerator<Exception> enumerator = task.Exception.InnerExceptions.GetEnumerator())
{
if (enumerator.MoveNext())
{
GoogleSignIn.SignInException error = (GoogleSignIn.SignInException)enumerator.Current;
AddToInformation("Got Error: " + error.Status + " " + error.Message);
}
else
{
AddToInformation("Got Unexpected Exception?!?" + task.Exception);
}
}
}
else if (task.IsCanceled)
{
AddToInformation("Canceled");
}
else
{
AddToInformation("Welcome: " + task.Result.DisplayName + "!");
AddToInformation("Email = " + task.Result.Email);
AddToInformation("Google ID Token = " + task.Result.IdToken);
AddToInformation("Email = " + task.Result.Email);
SignInWithGoogleOnFirebase(task.Result.IdToken);
currEmail = task.Result.DisplayName;
PlayerPrefs.SetString("Username " + currEmail, currEmail);
Debug.Log(currEmail);
}
private void AddToInformation(string str) { infoText.text += "\n" + str; }
after that, I want to call the Playerprefs.GetString to another scene with attaching on text
void Start()
{
displayName();
}
public void displayName()
{
userName.text = PlayerPrefs.GetString("Username " + googleSign.currEmail + "Username ");
}
but why that doesn't work, for your info I don't use DontDestroyOnload on google sign script so I attach on a sign in the scene and main menu scene
Call
PlayerPrefs.Save();
after your SetString method to save data in prefs
Check if googleSign.currEmail has value in debug, also you get your string wrong, it should be
PlayerPrefs.GetString("Username " + googleSign.currEmail)
Without
+ "Username "
at the end

C# How to check a Listbox for a string + object?

I'm trying to search for a certain number(object) in a listbox which comes together with a string in order to highlight it. In the following bit of code i override a ToString() method to contain all my objects.
public override string ToString()
{
string reservatiestring;
reservatiestring = "Kamer: " + roomNumber + "" + " Op datum: " + datum + " Aantal personen: " + personen.Count + " Naam: " + reservatienaam;
return reservatiestring;
}
Following this I add it to my listbox in the following bit of code:
listBox1.Items.Add(reservatie.ToString());
I now want to search for all the items in my listbox containing the same roomNumber object. To do this i tried the Contains() method with the text before it: "Kamer: " and the object which I'm looking for +comboBox1.SelectedItem. This however always fails and my code goes to the else option giving me the error message.
private void buttonSearch_Click(object sender, EventArgs e)
{
listBox1.SelectionMode = SelectionMode.MultiExtended;
Reservations reservaties = new Reservations();
reservaties.roomnumberstring = "Kamer: " + comboBox1.SelectedValue;
for (int i = listBox1.Items.Count - 1; i >= 0; i--)
{
if (listBox1.Items[i].ToString().ToLower().Contains(("Kamer: " + comboBox1.SelectedValue)))
{
listBox1.SetSelected(i, true);
}
else
{
MessageBox.Show("error");
}
Please note: All my roomNumber objects are stored in the combobox, so whenever i select for example roomNumber 3 in my combobox and hit search all the items in the listbox containing "Kamer: 3" should be selected.
The roomnumberstring is a option I tried which did not work unfortunately.
reservaties.roomnumberstring = "Kamer: " + comboBox1.SelectedValue;
Your override of the ToString method is wrong and won't modify anything. Try this :
public override string ToString(this string reservatiestring)
{
reservatiestring = "Kamer: " + roomNumber + "" + " Op datum: " + datum + " Aantal personen: " + personen.Count + " Naam: " + reservatienaam;
return reservatiestring;
}
I can see one thing that might make your code fail. you are comparing
.ToLower()
with "Kamer", where the "K" isnĀ“t in lowercase

c# "Cannot write to a closed TextWriter." on a string object?

this error has appeared to many of the users, but in my case Visual studio seems to be pointing on a string object.
My code is the following:
protected delegate void DPrint_To_LogScreen(string Text, bool NewLine);
protected void Print_To_LogScreen(string Text, bool NewLine)
{
if (InvokeRequired)
Invoke(new DPrint_To_LogScreen(Print_To_LogScreen), new object[] { Text, NewLine }); // exception thrown here from the Text string
else
{
LogScreen.AppendText(Convert.ToString(DateTime.Now) + " -> " + Text + (NewLine ? System.Environment.NewLine : ""));
if (Log_Screen_File == null)
{
Log_Screen_File = new StreamWriter(#"Global.log", true);
Log_Screen_File.WriteLine(Convert.ToString(DateTime.Now) + " -> " + Text);
Log_Screen_File.Close();
}
else
{
lock (Log_Screen_File)
Log_Screen_File.WriteLine(Convert.ToString(DateTime.Now) + " -> " + Text);
}
}
}
I generally want to call function Print_To_LogScreen from different places and threads.
i expected that the "if (Log_Screen_File == null)" statement would do the job (and in the general case it works) but now the exception is thrown by the Text object on the invoke command!!
Is this even possible or Visual Studio means the output file? And if so why the "if (Log_Screen_File == null)" does not work?
thank you
Calling Close does not set it to null. Also, you should be using using here. Change your code to:
if (Log_Screen_File == null)
{
using (Log_Screen_File = new StreamWriter(#"Global.log", true))
{
Log_Screen_File.WriteLine(Convert.ToString(DateTime.Now) + " -> " + Text);
}
Log_Screen_File = null;
}
That has the same functionality as your code above, except that it won't throw the exception you're currently getting.
It's hard to say what you really want to happen, though. And it looks to me as though you have a potential problem. Imagine that thread A and thread B are executing. Thread A sees that Log_Screen_File == null, and creates it. Then Thread B gets a timeslice and sees that the file exists. Then Thread A gets another timeslice, writes the file and closes it. Thread B will then will try to write to a non-existent file.
If this code will be used by multiple threads, you have to make sure that the entire operation is atomic. I would suggest:
private readonly object logLock = new object();
protected void Print_To_LogScreen(string Text, bool NewLine)
{
if (InvokeRequired)
Invoke(new DPrint_To_LogScreen(Print_To_LogScreen), new object[] { Text, NewLine }); // exception thrown here from the Text string
else
{
lock (logLock)
{
LogScreen.AppendText(Convert.ToString(DateTime.Now) + " -> " + Text + (NewLine ? System.Environment.NewLine : ""));
if (Log_Screen_File == null)
{
using (Log_Screen_File = new StreamWriter(#"Global.log", true))
{
Log_Screen_File.WriteLine(Convert.ToString(DateTime.Now) + " -> " + Text);
}
Log_Screen_File = null;
}
else
{
Log_Screen_File.WriteLine(Convert.ToString(DateTime.Now) + " -> " + Text);
}
}
}
}
But do you really want to be opening and closing the file every time? Wouldn't you instead do this:
if (Log_Screen_File == null)
{
Log_Screen_File = new StreamWriter(#"Global.log", true);
}
Log_Screen_File.WriteLine(Convert.ToString(DateTime.Now) + " -> " + Text);
Assuming, of course, that you close the log file when the program exits.
Come to think of it, you probably don't need the lock at all because that method is executing on the UI thread. The lock doesn't hurt anything, though, and doesn't affect performance.

How to read a textbox from a thread other than the one it was created on?

I am fairly new to C# and I have written several functioning programs, but all of them have been single thread applications. This is my first multi-threaded application and I am struggling to resolve this "Cross-thread operation not valid: Control 'cbLogType' accessed from a thread other than the one it was created on" error. My application searches Windows Event viewer for a user defined Event ID in a user defined Event Log Source(cbLogType). I am using a backgroundworker process to do all the work and I am using the worker.reportprogress to update a label, however, I receive the above error when debugging. I have tried several Invoke methods, but none seem to resolve my error. I have also tried removing the combobox and setting the Log Source directly in the code, which works to an extent, but still fails. I have included my code and any help would be greatly appreciated. I suspect that I might not be using the Invoke method correctly. Thanks in advance!
CODE:
private void bgWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
{
if (File.Exists(#"C:\Events.log"))
MessageBox.Show("File 'Events.log' already exists. All new data will be appended to the log file!", "Warning!");
string message = string.Empty;
string eventID = (tbEventID.Text);
string text;
EventLog eLog = new EventLog();
Invoke((MethodInvoker)delegate() { text = cbLogType.Text; });
eLog.Source = (this.cbLogType.Text); // I am receiving the error here
eLog.MachineName = ".";
int EventID = 0;
string strValue = string.Empty;
strValue = tbEventID.Text.Trim();
//string message = string.Empty;
EventID = Convert.ToInt32(strValue); // Convert string to integer
foreach (EventLogEntry entry in eLog.Entries)
{
int entryCount = 1;
if (cbDateFilter.Checked == true)
{
if (entry.TimeWritten > dtPicker1.Value && entry.TimeWritten < dtPicker2.Value)
if (entry.InstanceId == EventID)
message = "Event entry matching " + (tbEventID.Text) + " was found in " + (cbLogType.Text);
using (StreamWriter writer = new StreamWriter(#"C:\Events.log", true))
writer.WriteLine("EventID: " + entry.InstanceId +
"\r\nDate Created: " + entry.TimeWritten +
"\r\nEntry Type: " + entry.EntryType +
"\r\nMachinename: " + entry.MachineName +
"\r\n" +
"\r\nMessage: " + entry.Message +
"\r\n");
if (entry.InstanceId != EventID)
message = "No event ids matching " + (tbEventID.Text) + " was found in " + (cbLogType.Text);
}
else
{
if (cbDateFilter.Checked == false)
{
if (entry.InstanceId == EventID)
using (StreamWriter writer = new StreamWriter(#"C:\Events.log", true))
writer.WriteLine("EventID: " + entry.InstanceId +
"\r\nDate Created: " + entry.TimeWritten +
"\r\nEntry Type: " + entry.EntryType +
"\r\nMachinename: " + entry.MachineName +
"\r\n" +
"\r\nMessage: " + entry.Message +
"\r\n");
else if (entry.InstanceId != EventID)
message = "No event ids matching " + (tbEventID.Text) + " was found in " + (cbLogType.Text);
}
bgWorker1.ReportProgress((entryCount) * 10, message);
entryCount++;
}
}
}
}
private void bgWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
lblStat.Text = e.UserState.ToString();
}
You're accessing cbLogType in a non-UI thread.
Change to
eLog.Source = text;

Categories

Resources