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.
Related
I have managed to store data, but I can't retrieve it and i would be so grateful if someone could just help me get at least 1 example working.
First I am storing data when the user signs up:
public void SetupNewParseMember(ParseUser user)
{
ParseObject gameScore = new ParseObject("GameScore");
gameScore["cash"] = 500;
gameScore["playerName"] = user.Username;
gameScore["HighestCash"] = 500;
gameScore["GamesPlayed"] = 0;
Task saveTask = gameScore.SaveAsync();
}
This works fine, I can see the data in parse and all seems ok..
The problem is when i try to retrieve the objects.
public void SetupMainScreen(ParseUser user)
{
var query = ParseObject.GetQuery("GameScore").WhereEqualTo("playerName", user.Username);
query.FindAsync().ContinueWith(t =>
{
IEnumerable<ParseObject> results = t.Result;
List<ParseObject> resultsList = results.ToList();
DealWithResults(resultsList, user);
});
}
public void DealWithResults(List<ParseObject> resultsList, ParseUser me)
{
userGamesPlayed = resultsList[1].Get<int>("GamesPlayed");
userHighestCash = resultsList[2].Get<int>("HighestCash");
userCash = resultsList[3].Get<int>("Cash");
WelcomeText.text = "Welcome, " + me.Username + "\n" +
"Cash: $" + userCash + "\n" +
"Highest Cash: $" + userHighestCash + "\n" +
"Games Played: " + userGamesPlayed;
}
First I tried just making changes to the unity ui from inside the Query but that did not work, So i made an outside function and passed the results to it that way, and that still does not work?
I tried to debug what i was getting in the list with this:
foreach (var res in resultsList)
{
Debug.Log("Class Name = " + res.ClassName + "| Keys are: " + res.Keys);
}
But all it returned was:
Class Name = GameScore| Keys are: System.Collections.Generic.Dictionary`2+KeyCollection[System.String,System.Object]
Can anyone offer any insights?
EDIT2:
ok so first i found results list and its contents
http://i.imgur.com/IKcBbey.png
Then if i open it, it seems to be null ref?
http://i.imgur.com/VmSpi9c.png
But if i go digging, i found the info i need all the way down here
http://i.imgur.com/1Wwu5uc.png
Now just need to work out how to get it?
As there is only one set of data it is always accessible through resultsList[0]. What you want is:
double cash = (double)resultsList[0]["cash"];
string playerName = (string)resultsList[0]["playerName"];
double highestCash = (double)resultsList[0]["HighestCash"];
int gamesPlayed = (int)resultsList[0]["GamesPlayed"];
Though you probably want to check that resultsList is not null and contains one element before you try to dereference it.
Also as your ParseObject appears to be a Dictionary you might find this MSDN page useful.
Ended up solving it.. Much different to the examples...
I had to make a coroutine that called a function on callback to access the variables outside of the query.
I called it with
StartCoroutine(SetupMainScreen(me, DealWithResults));
then called this.
public IEnumerator SetupMainScreen(ParseUser user, Action<GameScore> callback)
{
var query = ParseObject.GetQuery("GameScore").WhereEqualTo("playerName", user.Username).FirstOrDefaultAsync();
while (!query.IsCompleted)
{
yield return null;
}
if (query.IsFaulted || query.IsCanceled)
{
Debug.Log("Getting of GameScores faulted or cancelled...");
}
else
{
var obj = query.Result;
if (obj != null)
callback(new GameScore(obj.Get<int>("cash"),obj.Get<string>("playerName"),obj.Get<int>("HighestCash"),obj.Get<int>("GamesPlayed")));
}
}
public void DealWithResults(GameScore gs)
{
WelcomeText.text = "Welcome, " + gs.Username + "\n" +
"Cash: $" + gs.Cash + "\n" +
"Highest Cash: $" + gs.HighestCash + "\n" +
"Games Played: " + gs.GamesPlayed;
}
And i just made a class to hold the objects.. Hopefully this helps someone else.
i have a cloud database server like application on my computer that i'm hosting my game on. However, every time an user tries to save data i get an UnauthorizedAccessException.
Im running it by admin and i dont have any specias right in my folder so i have no idea what's the problem.
Here's my code:
public const string root = "D:/DATABASE/";
public static void WriteData(string playername, string type, string data)
{
if (!Directory.Exists("D:/DATABASE/" + playername))
{
Directory.CreateDirectory("D:/DATABASE/" + playername);
Directory.CreateDirectory("D:/DATABASE/" + playername + "/weapons");
}
if (type != "Weapon")
{
using (StreamWriter sw = new StreamWriter("D:/DATABASE/" + playername + "/" + type + ".sav"))
{
sw.WriteLine(data);
}
}
else
{
string[] dat = data.Split('%');
using (StreamWriter sw = new StreamWriter("D:/DATABASE/" + playername + "/weapons/" + dat[0] + ".gfa"))
{
string[] lines = dat[1].Split('#');
foreach (string cline in lines)
{
sw.WriteLine(cline);
}
}
}
}
public static string ReadLoadout(string playername)
{
string output = "";
string[] items = new string[2];
using (StreamReader sr = new StreamReader(root + playername + "/loadout.gfl"))
{
items[0] = sr.ReadLine();
items[1] = sr.ReadLine();
}
int c = 0;
foreach (string citem in items)
{
if (c > 0) output += "$";
output += citem + "%" + GetCompressedWeaponFile(playername, citem);
c++;
}
return output;
}
public static string GetCompressedWeaponFile(string playerName, string weaponName)
{
string output = "";
using (StreamReader sr = new StreamReader(root + playerName + "/weapons/" + weaponName))
{
string line = " ";
int c = 0;
while (line != null)
{
line = sr.ReadLine();
if (line != null)
{
if (c > 0) output += "#";
output += line;
}
c++;
}
}
return output;
}
public static void RegisterNewUser(string username, string password, string email)
{
string udir = root + username;
Directory.CreateDirectory(udir);
Directory.CreateDirectory(udir + "/weapons");
Directory.CreateDirectory(udir + "/loadouts");
File.WriteAllText(udir + "/password.sav", password);
File.WriteAllText(udir + "/level.sav", "1");
File.WriteAllText(udir + "/money.sav", "1000");
File.WriteAllText(udir + "/email.sav", email);
File.WriteAllText(udir + "/loadout.gfl", "");
using (StreamWriter sw = new StreamWriter(root + "emails.txt", true))
{
sw.WriteLine(email);
}
Email.Send(email, "New Account Registration", string.Format(mailTemplate, username, password));
}
public static void EditLoadout(string username, string items)
{
File.WriteAllLines(root + username + "/loadout.gfl",items.Split('#'));
}
It is difficult to provide specific help without more information. Here are a few of troubleshooting suggestions:
1) Try running your code on a different machine. Specifically your development computer. Do you still have the same error? If not, then there is indeed a permission problem.
2) Have you tried checking the stack trace of the exception?
When you run the application on your own computer, try using the IDE to display the exception. Yes, the problem may ultimately be in a low-level class, but you should be able to break on the error and go back in the call stack to see which method in your code is actually throwing the error.
3) Check the actual exception, even for a system-level exception.
Chances are, if you are able to debug this in the IDE, that you will see property information that will give you a hint. Is it in a directory method or a file write method? Check additional properties. Somewhere it might give you the text of the path (assuming it's a file issue) that it failed on that that could help narrow things down too.
4) Add Exception handling to your code
This is a good rule of thumb, and you should really do this anyway to make a stronger program. Regardless of who's method you are calling (yours, someone else's, or a system method) you need to determine where it should be handled.
For example, in your code, in the RegisterNewUser() method, consider something like:
public static void RegisterNewUser(string username, string password, string email)
{
try
{
string udir = root + username;
Directory.CreateDirectory(udir);
Directory.CreateDirectory(udir + "/weapons");
Directory.CreateDirectory(udir + "/loadouts");
File.WriteAllText(udir + "/password.sav", password);
File.WriteAllText(udir + "/level.sav", "1");
File.WriteAllText(udir + "/money.sav", "1000");
File.WriteAllText(udir + "/email.sav", email);
File.WriteAllText(udir + "/loadout.gfl", "");
using (StreamWriter sw = new StreamWriter(root + "emails.txt", true))
{
sw.WriteLine(email);
}
Email.Send(email, "New Account Registration", string.Format(mailTemplate, username, password));
}
catch (Exception ex)
{
// Create a method to display or log the exception, with it's own error handler
LogAndDisplayExceptions(ex);
// Send the user a message that we failed to add them. Put this in it's own try-catch block
// ideally, for readability, in it's own method.
try
{
Email.Send(email, "Failed to register", "An error occurred while trying to add your account.");
}
catch (Exception exNested)
{
LogAndDisplayExceptions(exNested);
}
}
}
5) Add a "crash-and-burn" exception handler to "main"
In the method that is your "top method" (it's hard to tell in the snippet you provided since there are few methods that would attempt to write to the disk) you could wrap your code in a try - catch block and print the exception or write it to disk.
If you have having trouble writing the exception to disk, I would suggest creating an error file first, make sure that the user account that is running the program can write to it, and then in the catch block open the file for APPEND. This should make it easier to get to the error text.
6) When all else fails, use the Debug class or Console class to write the traditional "I made it to line x."
While this will not solve your problem, it should help you get more information that will provide more insight into where your code is causing an error.
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);
}
}
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;
I have a C# app with a button for dragging and dropping files. I am able to take 6 files from my desktop and drop it onto the button and have it process those 6 files.
However, when I start a thread from the DragDrop event and pass the file path to a new thread started from within the DragDrop event, the file path is incorrect once the thread receives the FilePath parameter.
If I execute my code by dragging 6 text files onto my button (I had to strip a lot of code out of it for this example), I will see the following in my console:
++ Calling testthread with these params: false, TestButton,test.txt,c:\test.txt
++ Calling testthread with these params: false, TestButton,test2.txt,c:\test2.txt
++ Calling testthread with these params: false, TestButton,test3.txt,c:\test3.txt
++ Calling testthread with these params: false, TestButton,test4.txt,c:\test4.txt
++ Calling testthread with these params: false, TestButton,test5.txt,c:\test5.txt
++ Calling testthread with these params: false, TestButton,test6.txt,c:\test6.txt
The above output is correct
The following output is incorrect, notice the FilePath does not match the CleanFileName like it does in the above console output.
++ testthread Thread - CallingfromPendingUploads == false ButtonName == TestButton CleanFileName == test.txt FilePath = c:\test2.txt
++ testthread Thread - CallingfromPendingUploads == false ButtonName == TestButton CleanFileName == test1.txt FilePath = c:\test3.txt
++ testthread Thread - CallingfromPendingUploads == false ButtonName == TestButton CleanFileName == test3.txt FilePath = c:\test4.txt
++ testthread Thread - CallingfromPendingUploads == false ButtonName == TestButton CleanFileName == test4.txt FilePath = c:\test5.txt
++ testthread Thread - CallingfromPendingUploads == false ButtonName == TestButton CleanFileName == test5.txt FilePath = c:\test5.txt
++ testthread Thread - CallingfromPendingUploads == false ButtonName == TestButton CleanFileName == test6.txt FilePath = c:\test5.txt
As you can see, the FilePath from thread does not match the FilePath that is being passed to the Thread before it starts. All of the FilePaths are off when compared to the filename that is passed to the Thread. And some of the FilePaths are duplicates such as text5.txt.
I have been struggling with this for hours. Can someone please tell me what I am doing wrong?
private void btnClick_DragDrop(object sender, DragEventArgs e)
{
string[] file = (string[])e.Data.GetData(DataFormats.FileDrop);
string ButtonName = "TestButton"
string[] files = new string[10];
files = (string[])e.Data.GetData(DataFormats.FileDrop);
foreach (string file in files)
{
FileInfo fileInfo = new FileInfo(file);
Console.WriteLine("++ Filename: " + fileInfo.Name + " Date of file: " + fileInfo.CreationTime + " Type of file: " + fileInfo.Extension + " Size of file: " + fileInfo.Length.ToString());
string CleanFileName = System.Web.HttpUtility.UrlEncode(fileInfo.Name.ToString());
//Start thread
try
{
Console.WriteLine("++ Calling testthread with these params: false, " + ButtonName + "," + CleanFileName + "," + file);
new Thread(() => testthread(false, ButtonName, CleanFileName, file)).Start();
Console.WriteLine("++ testthead thread started # " + DateTime.Now);
}
catch (Exception ipwse)
{
logger.Debug(ipwse.Message + " " + ipwse.StackTrace);
}
}
}
public void testthread(bool CalledfromPendingUploads, string ButtonName, string CleanFileName, string FilePath)
{
Console.WriteLine("++ testthread Thread - CallingfromPendingUploads == " + CalledfromPendingUploads.ToString() + " ButtonName == " + ButtonName + " CleanFileName == " + CleanFileName + " FilePath = " + FilePath);
}
All of your threads are sharing the same file variable.
If one of the threads only starts running after the UI thread has started the next iteration, it will use the next value of the file variable.
You need to declare a separate variable inside the loop, so that each thread will get its own variable.
For example:
foreach (string dontUse in files)
{
string file = dontUse;
...
}
Since the file variable is now scoped within the loop, each iteration gets a separate variable.
this will fix it:
string tempFile = file;
new Thread(() => testthread(false, ButtonName, CleanFileName, tempFile)).Start();
You've fallen into the common trap of lambdas and loop variables, the threading just made it obvious.
When you create a lambda, any variables you use will be closed over by reference not by value as you may have assumed.
What this means is that when you do:
foreach (var outer in collection)
{
var state = 42;
Grok(() => frob(outer, state));
}
The lambda you've created closed over outer, whose reference remains the same at each loop iteration even though it's value may change!
// Conceptual look at the previous code
Bar outer; // outside the loop-scope
foreach (outer in collection)
{
var state = 42; // inside the loop-scope
Grok(() => frob(outer, state));
}
Therefore when you introduce threads into the mix, you've included a fixed reference to a variable whose value is being changed on a different thread. Hence file appeared to jump to the last value when your threads slowed down.
In the case of CleanFileName, it was declared inside the loop and thus it was closed over locally at each loop iteration. You need to follow a similar strategy to correct your usage of file:
foreach (var outer in collection)
{
var inner = outer; // make a closure safe copy of the loop variable
var state = 42;
Grok(() => frob(inner, state));
}
I suspect that the value of file may get overwritten in this line: -- ( at least I was correct here )
new Thread(() => testthread(false, ButtonName, CleanFileName, file)).Start();
Edit -- I concur that #SLaks answer is correct and I understand why. I also understand why my answer is incorrect. Instead of deleting it, I believe it has value in demonstrating why a lock won't work in this case. And, for that reason, I am making it CW.
Maybe a lock is NOT necessary with a modification in the line of code above.
I DON'T think you would need something close to this:
object key = new object();
private void btnClick_DragDrop(object sender, DragEventArgs e)
{
// your code ...
//Start thread
try
{
Console.WriteLine("++ Calling testthread with these params: false, " + ButtonName + "," + CleanFileName + "," + file);
lock (key)
{
string[] fileCopy;
file.CopyTo(fileCopy);
new Thread(() => testthread(false, ButtonName, CleanFileName, fileCopy)).Start();
}
Console.WriteLine("++ testthead thread started # " + DateTime.Now);
}
catch (Exception ipwse)
{
logger.Debug(ipwse.Message + " " + ipwse.StackTrace);
}
}