I've run into this several times. My app receives some event (e.g. WndProc) and needs to return quickly. The code that needs to run takes some time, and does not need to be executed before returning from the event handler.
My current solution is to start a timer for a short time, and in the Tick event - run that code.
But that seems like the wrong tool for this case, and is prone to some errors (like running the code more than once, for example).
So, is there any ExecuteWhenThisThreadIsIdle scheme?
EDIT
A C#/.NET solution would be best, but a framework specific solution would be welcome too. Mainly Winforms. (But also WPF, UWP, Xamarin.Forms, ...)
The code needs to run on the same thread as the event handler. (Usually the UI thread.)
In WinForms, you can subscribe to Application.Idle.
For example ...
private bool _wndProcEventHooked = false;
protected override void WndProc(ref Message m) {
// Run your code here
}
private void Application_Idle(Object sender, EventArgs e) {
if (!this._wndProcEventHooked) {
// Hook your wndProc event here.
this._wndProcEventHooked = true;
}
}
In WPF this can be done using DispatcherTimer:
var timer = new DispatcherTimer
(
TimeSpan.FromMinutes(10),
DispatcherPriority.ApplicationIdle,// Or DispatcherPriority.SystemIdle
(s, e) => MessageBox.Show("Timer"),
Application.Current.Dispatcher
);
Related
I have code in c# Microsoft.CognitiveServices.Speech.SpeechSynthesize to output some text to speech. synthesizer.SpeakTextAsync(text) This is done asyncronlesly. In another thread I would like to either check the state of synthesizer for example "isSpeaking" or have an event fire when SpeakTextAsync has completed. I see there is this SpeechSynthesizer.SynthesisCompleted but I can't figure out how to implement and have not found samples.
I figured it out
synthesizer.SynthesisCompleted += synth_SpeechOver;
Than handle the event
public void synth_SpeechOver(object sender, EventArgs e)
{
//code
}
I'm starting out in C#, coded a lot in Java but having some trouble here. I'm trying to learn how to use MouseKeyHook for an application I'm developing. I cannot get the actual listener to fire off an event. Here's my listener code:
using System;
using System.Windows.Forms;
using Gma.System.MouseKeyHook;
namespace ChromaHeatmap
{
class keyListener
{
private IKeyboardMouseEvents m_GlobalHook;
public void Subscribe()
{
// Note: for the application hook, use the Hook.AppEvents() instead
m_GlobalHook = Hook.GlobalEvents();
m_GlobalHook.KeyPress += GlobalHookKeyPress;
}
private void GlobalHookKeyPress(object sender, KeyPressEventArgs e)
{
Console.WriteLine("blah");
}
public void Unsubscribe()
{
m_GlobalHook.KeyPress -= GlobalHookKeyPress;
//It is recommened to dispose it
m_GlobalHook.Dispose();
}
}
}
And here's the part of my application code where I attempt to do something with the listener. If anyone can let me know what the best way is to loop here and wait for events, I'd appreciate it.
//Listen for key presses
keyListener heyListen = new keyListener();
heyListen.Subscribe();
while(true)
{
}
while(true) {}
This is a hold-and-catch-fire statement, the thread will burn 100% core and cannot execute the hook callback. You'll notice that the machine goes dead for 5 seconds when you press a key, the operating system is waiting for an opportunity to invoke the callback. But it won't wait forever and unceremoniously will destroy the hook so you regain control over the machine. Also the kind of mishap that will occur when you try to debug your event handler.
Windows needs an opportunity to safely call the hook callback. That requires your program to be "idle", not executing any code. The technical term for this is "pumping the message loop", your program must wait for a notification from the operating system that something interesting happened.
A very simple way is to use the Winforms project template as-is, you'll also get a window. Note how the Main() method in the project makes the call that you need instead of the while() loop. You must call Application.Run().
Check this post for code that avoids displaying a window.
i got code inglobal.asax page:
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
System.Timers.Timer timScheduledTask = new System.Timers.Timer();
// Timer interval is set in miliseconds,
// In this case, we'll run a task every minute
timScheduledTask.Interval = 60 * 1000;
timScheduledTask.Enabled = true;
// Add handler for Elapsed event
timScheduledTask.Elapsed += new System.Timers.ElapsedEventHandler(timScheduledTask_Elapsed);
}
void timScheduledTask_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
// Over here i want to call function that i have in asp.net page
}
my problem here that in timScheduledTask_Elapsed function, i want to call to function that i have in asp.net page (Contacts.aspx.cs).
Any idea how to call this function??
It would need to be a static method of the page as the application class has no way of knowing the current page instance.
A rough example can be seen below.
somepage.aspx:
public class SomePage : Page
{
public static void DoSomething()
{
...
}
}
global.asax:
void Application_Start(object sender, EventArgs e)
{
...
// Add handler for Elapsed event
timScheduledTask.Elapsed += new System.Timers.ElapsedEventHandler(timScheduledTask_Elapsed);
}
void timScheduledTask_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
SomePage.DoSomething();
}
However, this does mean that anything in DoSomething() must not rely on anything instance specific in the page.
I would rethink your approach.
Also note that timers are a bad idea here IMHO, if the application process unloads your timers will never get called so you cannot really rely on them.
If you need to call a PAGE method from outside, you have a very bad design. Before doing anything else I'd advise you to do some research on separating your code into multiple layers, so such situation can never happen.
If you however still insist you want to do it your way (you'll understand sooner or later anyway), simplest solution is to make the requested page method static. That way you can call it anytime without a reference to an actual instance.
I'd make the code into a non-visual class and call it.
If you need it to be visual, make it a visual user control and include it on a master page that you use.
I agree that trying to call something on a different page when that's not the page running is just asking for problems.
Part of my program uses an event handler for the receive data of my serial port. The idea is when data is received that the text received is then added to the textbox (rx). I did not used to have this problem but something has changed and I can't figure out what. So now I am re-examining the way this is handled.
During the form load of my winform the last thing I do is
if (!serialPort1.IsOpen)
{
serialPort1.Open();
serialPort1.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
}
Then I have the event handler
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
string indata1 = serialPort1.ReadExisting();
// rx.Text = " "; accidentally posted this. it was from trial and error.
rx.AppendText(Environment.NewLine + indata1);
}
When I run the program it stops at the rx.AppendText(Environment.NewLine + indata1); and gives the error
invalidoperationexception was unhandled: Control "accessed from a
thread other than the thread it was created on.
From what I have been able to read suggests that I need to use invoke or BeginInvoke.
I have never had problems appending the text before so now I can't understand why it's a problem. Also from what I have been reading on invoking i just don't understand it.
Can someone help me understand how to use the invoke instance for my situation? or perhaps show me another way of appending the text box?
Usually the exception you're seeing occurs when you run in debug mode, and if you run your application in release mode, you're unlikely to see the exception.
However, it is best to use invoke, as you have read. Something like this:
private delegate void RefreshTextBox();
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e) {
//this event is raised in an event separate from UI thread,
//so InvokeRequired must be checked and Invoke called to update UI controls.
if (this.InvokeRequired) {
RefreshTextBox d = new RefreshTextBox(RefreshTextBoxResults);
Invoke(d);
} else {
RefreshTextBoxResults();
}
}
private void RefreshTextBoxResults() {
string indata1 = serialPort1.ReadExisting();
rx.Text = " ";
rx.AppendText(Environment.NewLine + indata1);
}
The first time you see this invoke stuff, it's nearly impossible to follow, but take a close look and give it some time and it will make sense. Promise. :)
Updates in GUI applications should only be done on the GUI thread. Another thread attempting to update GUI components directly will result in either the error you described or in seemingly random behavior.
The role of Invoke & friends is to enable a secondary thread to safely forward GUI updates to the GUI thread, which will then process them from a queue.
In your case (assuming WinForms here):
rx.BeginInvoke(
(Action)(() =>
{
rx.AppendText(Environment.NewLine + indata1);
}));
BeginInvoke is asynchronous, so the thread calling it will not wait for the actual updates to be processed before moving on, while Invoke is synchronous.
This has been asked before here, but the answer there was simply "use BackgroundWorker", and I'm asking if there is a complete code sample available.
I'd like to create a standard AutocompleteTextBox that works with a timer, such that there is only one BackgroundWorker working on searching - if the user entered a few more keystrokes, but the old search is still running - that search shall be canceled gracefuly (via CancelAsync), and as soon as its canceled the new search will begin.
This is not so trivial to implement - are there any code samples of this?
I doubt you'll find a code sample that helps you with the specific issues you're talking about here. Here's how I'd do this. None of this code is tested, so beware of stupid bugs.
First, subclass TextBoxBase and add two basic methods to implement the search logic, with the following signatures:
private IEnumerable<string> PerformSearch(string text)
private DisplayResults(IEnumerable<string> results)
Add a private BackgroundWorker field named Worker to the class and set its DoWork and RunWorkerCompleted events to event handlers named Worker_DoWork and Worker.RunWorkerCompleted.
Override OnTextChanged:
public override void OnTextChanged(TextChangedEventArgs e)
{
base.OnTextChanged(e);
// if we're already cancelling a search, there's nothing more to do until
// the cancellation is complete.
if (Worker.CancellationPending)
{
return;
}
// if there's a search in progress, cancel it.
if (Worker.IsBusy)
{
Worker.CancelAsync();
return;
}
// there's no search in progress, so begin one using the current value
// of the Text property.
Worker.RunWorkerAsync(Text);
}
The Worker_DoWork event handler is pretty simple:
private void Worker_DoWork(object sender,
RunWorkerCompletedEventArgs e)
{
e.Result = PerformSearch((string) e.Argument);
}
The Worker_RunWorkerCompleted event handler looks something like this:
private void Worker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
// always check e.Error first, in case PerformSearch threw an exception.
if (e.Error != null)
{
// in your version, you want to do real exception handling, not this.
throw e.Error.InnerException;
}
// if the worker was cancelled, it's because the user typed some more text, and
// we want to launch a new search using what's currently in the Text property.
if (e.Cancelled)
{
Worker.RunWorkerAsync(Text);
return;
}
// if the worker wasn't cancelled, e.Result contains the results of the search.
DisplayResults((IEnumerable<string> e.Result);
}
Note that DisplayResults should test any assumption it makes about the state of the text box. The text box may have been visible or enabled when the user launched the search and not be visible or enabled now, for instance. What happens if you use this text box in a modal dialog and the user cancels the dialog while the search is running?
Note also that if you have multiple instances of this control in your application, each one will have a different BackgroundWorker, so it's important that the PerformSearch method be thread-safe. If it's not, it will have to implement locking, so that if you launch a search in one text box it blocks and waits if another text box is currently using the shared resource.
I suggest using the AutoComplete feature in System.Windows.Forms.TextBox. You can customize it and build your completion stuff around this.
NOTE: AutoComplete feature is only available from .NET 2.0