Save file from Richtextbox using Invoke - c#

I am having a RichTextbox and I am trying to save file using
public bool SaveNote(string path)
{
try
{
_rtbContent.SaveFile(path, RichTextBoxStreamType.RichText);
return true;
}
catch
{
return false;
}
}
It was working fine until I started working with background worker thread. Now this method is being called from background worker and now I am receiving an error as
Cross-thread operation not valid: Control 'rtbContent' accessed from a thread other than the thread it was created on.
I think we have to invoke it using _rtbContent.Invoke but failing to get the syntax correct. What I tried was
if(_rtbContent.InvokeRequired)
_rtbContent.Invoke(new MethodInvoker(_rtbContent.SaveFile(path, RichTextBoxStreamType.RichText)));
Here I am getting Method name expected compilation error on _rtbContent.SaveFile(path, RichTextBoxStreamType.RichText).
I am not very comfortable in using these threads but has recently started working on them. Can anyone help me on this issue?

Use a callback:
delegate void SaveNoteCallback(string path);
public void SaveNote(string path)
{
if(_rtbContent.InvokeRequired)
{
SaveNoteCallback d = new SaveNoteCallback(SaveNote);
this.Invoke(d, new object[] {path});
}
else
{
_rtbContent.SaveFile(path, RichTextBoxStreamType.RichText);
}
}

I got another interesting solution and would like to post that.
public void SaveNote(string path)
{
if(_rtbContent.InvokeRequired)
{
_rtbContent.Invoke(new MethodInvoker(delegate{_rtbContent.SaveFile(path, RichTextBoxStreamType.RichText}));
//Below is also same as above
//_rtbContent.Invoke(new MethodInvoker(()=>_rtbContent.SaveFile(path, RichTextBoxStreamType.RichText)));
}
else
{
_rtbContent.SaveFile(path, RichTextBoxStreamType.RichText);
}
}
I think it is much clean solution. Hope it helps.

Related

C# Process.Start Causing AccessViolationException Randomly

I have been tackling this issue for 3 months now.
Error I am Getting in Native Debugging:
"Exception thrown at 0x5A222FC2 (comct123.dll) in FileReader.exe:
0xC0000005: Access violation reading location 0x0000000C."
Normal Debug:
'System.AccessVioliationException' in System.Windows.Forms.dll
My setup is really simple:
public static Form_Interface Interface;
public static void Initialize()
{
Application.SetCompatibleTextRenderingDefault(false);
Interface = new Form_Interface();
Interface.Filesdgv.DataSource = File.SortableBindingList;
Application.Run(Interface);
}
Seems simple enough, right? No.
So basically I have a simple Event that simply opens the file using Process.Start() and no matter what I do it will randomly crash with 'System.AccessVioliationException' in System.Windows.Forms.dll here:
private void Filesdgv_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
Filesdgv.Invoke((Action)(() =>
{
try
{
int rowIndex = e.RowIndex;
if (rowIndex >= 0)
{
int columnIndex = e.ColumnIndex;
File file = (File)((DataGridView)sender).Rows[rowIndex].DataBoundItem;
switch (columnIndex)
{
case 0:
{
Process.Start(file.Location);
}
break;
}
}
}
catch
{
// This fking catch never works anyway.
}
}));
}
private void FileInterface_Load(object sender, EventArgs e)
{
foreach (string oCurrent in Directory.GetFiles(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "Files To Check")))
if (oCurrent.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase))
new File(oCurrent.Split('\\').Last(), oCurrent);
}
It doesn't matter if I am opening files/links or anything else, it still behaves in the same way.
The link and file location is a readonly field as well.
I have many other uses for reading row data and it never crashes, even if i spam click 10000 times, It will only crash randomly with Process.Start()
Things I tried:
Using BeginInvoke
Using Invoke
Not Using Invoke/BeginInvoke
Putting File Link into a string before reading it.
Using multiple Try Catch
Recoded on another machine... same results there aswell.
I tried using File.Open (either doesn't open the file or throws same error lmao)
Tried using [HandleProcessCorruptedStateExceptions], still won't catch the exception.
Dosen't matter if i click slow or fast still 1/30 chance it happens.
Tried Putting Task.Run(() => Process.Start()); you'd think that a thread will protect you from an exception? no still crashes...
File Class looks like this:
public class File
{
public static SortableBindingList<File> SortableBindingList = new SortableBindingList<File>(new List<File>());
public readonly string fileName;
public readonly string filePath;
public void AddRow()
{
Client.Interface.Invoke((Action)(() =>
{
lock (SortableBindingList)
if (!SortableBindingList.Contains(this))
SortableBindingList.Add(this);
}));
}
public string FileName
{
get
{
return fileName;
}
}
public string Location
{
get
{
return filePath;
}
}
public File(string fileName, string filePath)
{
this.fileName = fileName;
this.filePath = filePath;
AddRow();
}
}
Initalize() is called in static void Main(string[] args) btw.
There are no other threads running editing stuff or anything like that, the only thread running is the form thread. which waits for user input.
Solutions I am looking for:
Alternative Method to launch files/hyperlinks.
A way to avoid form crashing (try catch style)
Crashes even with static data!:
Other threads running although these were not started by me.
Task.Run(() =>
{
Thread.Sleep(100);
Process.Start("https://www.youtube.com");
});
This has fixed my issues, it seems that when trying to immediately run "process.start" during a click event, the GUI unfocusing + starting a new process the exact same moment causes an Exception. (Microsoft pls fix.)

Can a C# Multithreaded Application use separate WorkingDirectories per thread?

In C# (.NET), can two threads running in the same application have DIFFERENT "WorkingFolders"??
As best I can tell, the answer would be "NO". I think the WORKING DIR is set by the PROCESS in Win32.. Am I wrong here?
According to the following test code, (as well the Win32 SetCurrentDirectory API call), this is NOT possible, but has anyone figured out a way to MAKE it possible?
using System;
using System.Threading;
public class TestClass {
public ManualResetEvent _ThreadDone = new ManualResetEvent(false);
public static void Main() {
Console.WriteLine(Environment.CurrentDirectory);
Thread _Thread = new Thread(new ParameterizedThreadStart(Go));
TestClass test = new TestClass();
_Thread.Start(test);
if(test._ThreadDone.WaitOne()) {
Console.WriteLine("Thread done. Checking Working Dir...");
Console.WriteLine(Environment.CurrentDirectory);
}
}
public static void Go(object instance) {
TestClass m_Test = instance as TestClass;
Console.WriteLine(Environment.CurrentDirectory);
System.IO.Directory.SetCurrentDirectory("L:\\Projects\\");
Console.WriteLine(Environment.CurrentDirectory);
m_Test._ThreadDone.Set();
}
}
I know SOMEONE out there has to have ran across this before!
I'm going to guess what you're trying to do is to make code such as File.Open("Foo.txt") behave differently on different threads. Can you do this? The short answer is No - nor should you be trying to do this. On Windows, the current working directory is set at the process level. The .NET framework does not violate that rule.
A better approach would be to create an abstraction on top of Environment.CurrentDirectory that is thread specific. Something like:
public static class ThreadEnvironment
{
[ThreadStatic]
static string _currentDir;
public static string CurrentDirectory
{
get
{
if (_currentDir == null) // If Current Directory has not been set on this thread yet, set it to the process default
{
_currentDir = Environment.CurrentDirectory;
}
return _currentDir;
}
set
{
if (value == null)
throw new ArgumentException("Cannot set Current Directory to null.");
_currentDir = value;
}
}
}
You can then refer to ThreadEnvironment.CurrentDirectory to get that thread's current directory, which will default to the process directory if it has not been set on that thread. For example:
static void Main(string[] args)
{
(new Thread(Thread1)).Start();
(new Thread(Thread2)).Start();
}
static void Thread1()
{
Console.WriteLine("Thread1 Working Dir is: {0}", ThreadEnvironment.CurrentDirectory);
ThreadEnvironment.CurrentDirectory = #"C:\";
Console.WriteLine("Thread1 Working Dir is: {0}", ThreadEnvironment.CurrentDirectory);
}
static void Thread2()
{
Console.WriteLine("Thread2 Working Dir is: {0}", ThreadEnvironment.CurrentDirectory);
ThreadEnvironment.CurrentDirectory = #"C:\Windows";
Console.WriteLine("Thread2 Working Dir is: {0}", ThreadEnvironment.CurrentDirectory);
}
You would, of course, then need to qualify that path whenever dealing with file IO, however this is arguably a safer design anyway.
has anyone figured out a way to MAKE it possible?
It's simply not possible. You can't even have different working directories per App Domain.
The windows rule is: one Environment set per Process. Running in .NET won't change the basic rules.
Instead of that, if you experienced problem in loading assemblies, consider adding the corresponding folder to the PATH environment variable.

Shared Variable between Watin ApartmentState.STA Thread and parent Thread?

I'm trying to get Watin working in my SSIS script Task to do some automation by opening IE in a new thread, do something, find the final value and basically return/set that value in the parent thread.
So I have this for now:
public partial class TestWatin{
public void Main()
{
String finalValueFromWeb = "";
Thread runnerThread = new Thread(delegate() { getDAFValue(ref finalValueFromWeb ); });
runnerThread.ApartmentState = ApartmentState.STA;
runnerThread.Start();
runnerThread.Join();
MessageBox.Show(finalValueFromWeb);
//here i want to use the value of finalValueFromWeb to download a file
//but if i try to access finalValueFromWeb the process would fail.
}
//do the Watin stuff here
public void findHiddenURL(String refObject)
{
//setup page controls, press search, grab the value of "hiddenURL"
IE ie = new IE("some_webadress_to_go_to");
ie.Visible = false;
ie.SelectList("testID1").Option("Car").Select();
ie.SelectList("testID2").Option("JAP").Select();
ie.SelectList("testID3").Option("2012").Select();
ie.Button("testSearch").Click();
Link link = ie.Link("hiddenURL");
ie.Close();
//fails here?
refObject = link.Url;
}
}
What I basically want to is for findHiddenURL() to find me a value which is a string containing some CSV url. I then want to use that string to download the CSV and process it.
The problem is when I try to set the value of finalValueFromWeb inside findHiddenURL() where the process fails. The Exception message says The Object Reference is not set to an instance of an object
Can someone please tell me how I should be going about this problem? What is the proper way of doing this type of thing? Thanks
Make the variable a member of the class and try to lock it. You can use c# lock :
http://msdn.microsoft.com/en-us/library/c5kehkcz%28v=vs.71%29.aspx
protected string finalValueFromWeb ;
....
public void Main()
{
...
lock(finalValueFromWeb)
{
MessageBox.Show(finalValueFromWeb);
}
}
public void findHiddenURL(String refObject)
{
...
lock(finalValueFromWeb)
{
finalValueFromWeb = link.Url;
}
}

How can I implement asynchronous pattern for save method in C#?

I am working on a project where I am implementing a SaveSettings method which saves lot of settings to a xml file.
Problem is it takes time to do that, that's why when I click Save button on my form my UI just hangs/stops for a while.
The method looks like below
public void SaveSettings(SettingsType settingsType)
{
if (!File.Exists(_settingsFile))
{
File.Create(_settingsFile);
}
var xmlDoc = XDocument.Load(_settingsFile);
switch (settingsType)
{
case SettingsType.Measurement:
SaveMeasurementSettings(ref xmlDoc);
break;
case SettingsType.Display:
SaveDisplaySettings(ref xmlDoc);
break;
case SettingsType.Common:
SaveCommonSettings(ref xmlDoc);
break;
case SettingsType.View:
SaveViewSettings(ref xmlDoc);
break;
case SettingsType.InputChannel:
SaveInputChannelSettings(ref xmlDoc);
break;
default:
break;
}
xmlDoc.Save(_settingsFile);
}
I want to make SaveSettings method asynchronous something BeginSave/EndSave so that when I call BeginSave my UI should go smooth. I have no BackgroundWorker as I am using .Net Compact Framework.
Any guidance on implementing Asynchronous pattern please...
The Save() of XDocument can be implemented as:
public void Save(string xmlFilePath)
{
Thread thread = new Thread(new ParameterizedThreadStart(SaveSettings));
thread.Start(xmlFilePath);
}
private void SaveSettings(object data)
{
string xmlFilePath;
if ((xmlFilePath = data as string) != null)
{
this.SaveSettingsFile(xmlFilePath);
}
}
private void SaveSettingsFile(string xmlFilePath)
{
// Save the file contents
}
The simplest way is using .Net Thread
Take a look at the accepted answer on this post. You could also use reflector and grab the code for the BackgroundWorker class if you wanted. Here is an implementation of it to get you started.
There is also an article on MSDN about this: Microsoft .NET Compact Framework Background Processing Techniques
If you're on .Net 4 (or newer), consider using Tasks. They're an easier way to deal with asynchronous behavior that you're spinning up.
I have tried to put together a simplistic implementation. It is untested. Also instead of using a Thread see if you can use a ThreadPool thread in the compact framework. Hope it helps.
public class SettingsType {}
public class Settings
{
private Thread _worker;
public void SaveSettings(SettingsType type)
{
// save logic
}
public void BeginSaveSettings(SettingsType type)
{
_worker = new Thread(() => SaveSettings(type)) {IsBackground = true};
_worker.Start();
}
public bool EndSaveSettings(TimeSpan timeOut)
{
return _worker.Join(timeOut);
}
}

What's wrong with my application ---- Size was 0, but I expected 46806 !

I'm a C# programmer.
Now, I'm using the ICSharpCode.SharpZipLib.dll to create a zip file in my current project. But it occurs to me that when I click the button at the SECOND TIME to execute a function to create a zip file, the application will throw an exception, friendly and seriously told me that "Size was zero, but I expected 46086".
I'm so confused that I want to know why? When I click the button at the first time, I can do it successfully without any error.
My related codes are as follows:
internal void ThreadProc()
{
try
{
ZipHelper.CreateZip(backupZipFile, Constants.HomeConstant, true);
// do other things
}
}
The CreateZip() function's realization is as follows:
public static void CreateZip(string zipFileName, string sourceDirectory, bool recurse)
{
FastZip zip = new FastZip();
if (File.Exists(zipFileName))
{
File.Delete(zipFileName);
}
zip.CreateZip(zipFileName, sourceDirectory, true, "");
}
Now, I will show you the recursive calling process:
Call method "UpdateAppAsync" in "ActiveCheckManager" class
public void UpdateAppAsync(string masterConfig)
{
this.masterConf = masterConfig;
Thread actualThread = new Thread(new ThreadStart(UpdateApp));
actualThread.IsBackground = true;
actualThread.CurrentCulture = Thread.CurrentThread.CurrentCulture;
actualThread.CurrentUICulture = Thread.CurrentThread.CurrentUICulture;
actualThread.Start();
}
Call the UpdateApp function asynchronously, in the UpdateApp method, it will only call the UpdateDetail function simply.
private void UpdateDetail(string masterConfig, string category)
{
IUpdate worker = new HP.ActiveCheckLocalMode.UpdateEngine.UpdateManager();
worker.UpdateApp(masterConf);
}
The worker.UpdateApp will call UpdateDetail(string, UpdateCategory) only.
private void UpdateDetail(string masterConfig, UpdateCategory cat)
{
UpdateThread updateThread = new UpdateThread(this, cat);
updateThread.MasterConfig = masterConfig;
updateThread.ThreadProc();
}
That is the calling process. When I click the update button second time, it will throw an exception, can you help me? Thank you very much.
Has the first task thread finished before you start the second time?
I would imagine that File.Delete() and some items in the SharpZipLib to not respond nicelly to multithreadingly zip the same folder simultaneously to the same file.
Promote that " UpdateThread updateThread " as a private member of the "ActiveCheckManager" class, then check if it is already running from a previous click before creating a new thread.

Categories