C# COMException: Cannot call a <MethodName> from within an event - c#

I have a winform (MyFrm) which contains a winforms user control (myWinformUserControl). I call a public method (MyMethod) from this user control:
using (var frm = new MyFrm())
{
frm.myWinformUserControl.MyMethod();
}
public class myWinformUserControl: System.Windows.Forms.UserControl
{
public void MyMethod()
{
// Do some stuff
TryToDisconnect();
}
private void TryToDisconnect()
{
myComObj.Disconnect(); // This throws COMException
}
}
This user control communicates with a COM object. When I call to disconnect on the COM Object an exception is thrown:
COMException: Cannot call a 'Disconnect()' from within an event
so in order to solve this I use a thread instead of calling it directly:
public void MyMethod()
{
System.Threading.Thread th = new System.Threading.Thread(new
System.Threading.ThreadStart(TryToDisconnect));
th.SetApartmentState(System.Threading.ApartmentState.STA);
th.IsBackground = true;
th.Priority = System.Threading.ThreadPriority.Highest;
th.Start();
}
The below block of code indicated at the beginning of this question:
using (var frm = new MyFrm())
{
frm.myWinformUserControl.MyMethod();
}
... is called from a WPF MVVM view model class.
The problem I have here is that I need MyMethod to be done immediatelly so I set highest priority for the thread, and I want the call in view model class (frm.myWinformUserControl.MyMethod()) to stop and do not continue until this thread is completed. I have observed that the thread is not immediatelly executed, so how can I achieve this?
I have tried an asynchronous call and wait until it is completed instead of using a thread:
public void MyMethod()
{
Action action = Foo;
IAsyncResult result = action.BeginInvoke(ar => action.EndInvoke(ar), null);
result.AsyncWaitHandle.WaitOne();
}
public delegate void Action();
private void Foo()
{
TryToDisconnect();
}
but again the same COMException is thrown:
COMException: Cannot call a 'Disconnect()' from within an event
Also, in case of using the thread, If I immediatelly do th.Join() just after doing th.start() it does not work.

Related

How to invoke UI thread in Winform application without a form or control?

I have created a tray application for controlling some hardware components. How can I invoke the UI thread without a main form or control?
The tray app is started with Application.Run(new MyTrayApp()):
class MyTrayApp : ApplicationContext
{
private NotifyIcon trayIcon;
public MyTrayApp()
{
trayIcon = new NotifyIcon()
{
Icon = Resources.app_icon,
ContextMenu = new ContextMenu(new MenuItem[] {
new MenuItem("Exit", Exit)
}),
Visible = true
};
// context is still null here
var context = SynchronizationContext.Current;
// but I want to invoke UI thread in hardware events
MyHardWareController controller= new MyHardWareController(context);
}
void Exit(object sender, EventArgs e)
{
// context is accessible here because this is a UI event
// too late tho
var context = SynchronizationContext.Current;
trayIcon.Visible = false;
Application.Exit();
}
}
Control.Invoke() is not available as there are no controls
Searching suggests that SynchronizationContext.Current should be saved for later invoke but there is no ApplicationContext.Load() event...?
I've noticed that MainForm is null in the whole cycle. I wonder how does SynchronizationContext initialized in this case?
Edit:
Just to add some background info on why I would like to invoke UI thread. It is because System.Threading.ThreadStateException will be thrown when attempt to access Windows resources such as Clipboard or SendKeys in another thread:
HResult=0x80131520
Message=Current thread must be set to single thread apartment (STA) mode before OLE calls can be made. Ensure that your Main function has STAThreadAttribute marked on it.
Source=System.Windows.Forms
StackTrace:
...
It's another can of worms but just for information:
[STAThreadAttribute] is already set for Main function (no effect)
Creating a new STA thread would result in anti-virus deleting my application upon compile
Thus Form.Invoke() or the equivalent to invoke main thread should be the easiest.
Edit 2:
Add a gist for reproducing the error:
https://gist.github.com/jki21/eb950df7b88c06cc5c6d46f105335bbf
Solved it with Application.Idle as mentioned by Loathing! Thanks everyone for your advice!
TrayApp:
class MyTrayApp: ApplicationContext {
private MyHardwareController controller = null;
public MyTrayApp() {
Application.Idle += new EventHandler(this.OnApplicationIdle);
// ...
}
private void OnApplicationIdle(object sender, EventArgs e) {
// prevent duplicate initialization on each Idle event
if (controller == null) {
var context = TaskScheduler.FromCurrentSynchronizationContext();
controller = new MyHardwareController((f) => {
Task.Factory.StartNew(
() => {
f();
},
CancellationToken.None,
TaskCreationOptions.None,
context);
});
}
}
// ...
}
MyHardwareController:
class MyHardwareController {
private Action < Action > UIInvoke;
public MyHardwareController(Action < Action > UIInvokeRef) {
UIInvoke = UIInvokeRef;
}
void hardware_Event(object sender, EventArgs e) {
// Invoke UI thread
UIInvoke(() => Clipboard.SetText("I am in UI thread!"));
}
}
An alternative solution would be to create a dummy form (which will never be shown, but should be stored somewhere. You just have to access the Handle property of the Form to be able to invoke it from now on.
public static DummyForm Form { get; private set; }
static void Main(string[] args)
{
Form = new DummyForm();
_ = Form.Handle;
Application.Run();
}
Now it is possible to invoke into the UI thread:
Form.Invoke((Action)(() => ...);

Communication between threads via delegates?

I am looking for a solution for interthread communication.
Thread A is the main thread of a windows app. I starts a Thread B that is working independant of thread a, they do not share code. But thread A has to get some feedback about status of thread b. I try to solve this with a delegate.
I am very sorry, I forgot to add that I have to work on .net 3.5, c#, WEC7
It is important that the code
public void OnMyEvent(string foo)
{
MessageBox.Show(foo);
}
is executed in context of thread a, how can I achieve this
public partial class Form1 : Form
{
//...
public void StartThread(Object obj)
{
new ClassForSecondThread(obj as Parameters);
}
private void button1_Click(object sender, EventArgs e)
{
//ParameterizedThreadStart threadstart = new ParameterizedThreadStart(startThread);
ParameterizedThreadStart threadstart = new ParameterizedThreadStart(StartThread);
Thread thread = new Thread(threadstart);
Parameters parameters = new Parameters(){MyEventHandler = OnMyEvent};
thread.Start(parameters);
}
public void OnMyEvent(string foo)
{
MessageBox.Show(foo);
}
}
//This code is executed in Thread B
public class ClassForSecondThread
{
public ClassForSecondThread(Parameters parameters)
{
if (parameters == null)
return;
MyEventhandler += parameters.MyEventHandler;
DoWork();
}
private void DoWork()
{
//DoSomething
if (MyEventhandler != null)
MyEventhandler.DynamicInvoke("Hello World");// I think this should be executed async, in Thread A
Thread.Sleep(10000);
if (MyEventhandler != null)
MyEventhandler.DynamicInvoke("Hello World again"); // I think this should be executed async, in Thread A
}
public event MyEventHandler MyEventhandler;
}
public class Parameters
{
public MyEventHandler MyEventHandler;
}
public delegate void MyEventHandler(string foo);
As you want to call the MessageBox on the main UI thread, you can achieve what you want using Control.Invoke.
Invoke((MethodInvoker)(() => MessageBox.Show(foo)));
The Invoke method can be called directly on the Form and you won't be in the context of Thread B within the delegate - the code will run on the same thread as the Form.
EDIT:
OP question: if I understood Control.Invoke correctly, it always acts in the context of a control?
Although the Invoke method uses a Control (in this case the form) to get a handle to the UI thread it is running on, the code within the delegate is not specific to the UI. If you want to add more statements and expand it to include more stuff, just do this:
string t = "hello"; //declared in the form
//Thread B context - Invoke called
Invoke((MethodInvoker)(() =>
{
//Back to the UI thread of the Form here == thread A
MessageBox.Show(foo);
t = "dd";
}));
Also, if you are updating things in a multi threaded environment where the data is accessible to more than one thread, then you will need to investigate sychronization - applying locks to data etc.
For what it is worth you can simplify your code considerably by using the new async and await keywords in C# 5.0.
public class Form1 : Form
{
private async void button1_Click(object sender, EventArgs args)
{
OnMyEvent("Hello World");
await Task.Run(
() =>
{
// This stuff runs on a worker thread.
Thread.Sleep(10000);
});
OnMyEvent("Hello World again");
}
private void OnMyEvent(string foo)
{
Message.Show(foo);
}
}
In the code above OnMyEvent is executed on the UI thread in both cases. The first call be executed before the task starts and the second call will be executed after the task completes.

C# / Communication between Async networking and GUI

I'm doing async network in C#.NET with the TcpClient and TcpListener classes.
I use WinForms for the GUI.
Whenever I receive data from a remote computer, the operation is done on a different underlying thread.
What I need to do is to update the GUI of my application whenever I receive a network response.
// this method is called whenever data is received
// it's async so it runs on a different thread
private void OnRead(IAsyncResult result)
{
// update the GUI here, which runs on the main thread
// (a direct modification of the GUI would throw a cross-thread GUI exception)
}
How can I achieve that?
In Winforms you need to use Control.Invoke Method (Delegate) to make sure that control is updated in the UI thread.
Example:
public static void PerformInvoke(Control ctrl, Action action)
{
if (ctrl.InvokeRequired)
ctrl.Invoke(action);
else
action();
}
Usage:
PerformInvoke(textBox1, () => { textBox1.Text = "test"; });
in GUI write function like this:
public void f() {
MethodInvoker method = () => {
// body your function
};
if ( InvokeRequired ) {
Invoke( method ); // or BeginInvoke(method) if you want to do this asynchrous
} else {
method();
}
}
if you in other thread call this function it will be calling in GUI thread
I added an extension method to the code suggested by Alex. It gets even better!
// Extension method
public static class GuiHelpers
{
public static void PerformInvoke(this Control control, Action action)
{
if (control.InvokeRequired)
control.Invoke(action);
else
action();
}
}
// Example of usage
private void EnableControls()
{
panelMain.PerformInvoke(delegate { panelMain.Enabled = true; });
linkRegister.PerformInvoke(delegate { linkRegister.Visible = true; });
}

How do I invoke a method in another thread?

I have the following classes:
using System;
using System.Windows.Forms;
namespace FastEyeControl
{
public partial class ConnectView : Form, IConnectView
{
private IConnectPresenter m_Presenter;
public ConnectView()
{
InitializeComponent();
m_Presenter = new ConnectPresenter(this);
}
public string Hostname
{
get
{
return m_Hostname.Text;
}
}
public int Port
{
get
{
return Convert.ToInt32(m_Port.Text);
}
}
public void ShowMessage(string message)
{
MessageBox.Show(message,
"Success",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
public void ShowError(string message)
{
MessageBox.Show(message,
"ERROR!",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
private void m_ConnectButton_Click(object sender, EventArgs e)
{
m_Presenter.ConnectButtonPressed();
}
}
}
The presenter class:
using System;
using System.Collections.Generic;
namespace FastEyeControl
{
public class ConnectPresenter : IConnectPresenter
{
private IConnectView m_View;
private IConnectModel m_Model;
public ConnectPresenter(IConnectView view)
{
m_View = view;
m_Model = FastEyeClient.Instance;
}
public void ConnectButtonPressed()
{
m_Model.Connect(m_View.Hostname, m_View.Port);
}
private void ConnectionComplete(object sender, ConnectionEventArgs e)
{
// Code here indicating whether connection was successful and informing the view.
// i.e...
if (e.IsConnected)
{
m_View.ShowMessage("Successfully connected.");
}
else
{
m_View.ShowError("Unable to connect.");
}
}
}
}
The model code runs in another thread. The problem is that when I call m_Model.Connect(), I'm calling code that's usually running in another thread within the main thread still (the UI thread). This is not a database connection. This is a TCP/IP connection to a server. If I set a variable within the model, then I am doing this from the UI thread which is not thread safe.
I know that with user controls, they have InvokeRequired and Invoke/BeginInvoke operations that will handle this situation. But that is for user controls only. I know you can't just interrupt another thread in the middle of its execution and tell it to call another method instead. I basically want the non-UI thread to call the Connect code somehow.
Just as a test, I tried using a delegate (fire off an event whenever I want to connect) and when I look in the debugger, the Connect code is still running in the UI thread.
I need a multi-threaded event queue essentially. What's the best way to achieve what I want to do here? Thanks!
public void ConnectButtonPressed()
{
var threadedTask = () => m_Model.Connect(m_View.Hostname, m_View.Port);
threadedTask.BeginInvoke(null,null);
}
This will, no question, use a background thread from the ThreadPool to do the work. Maybe you had tried to call the delegate directly, or called Invoke() on it; that will execute the delegate synchronously.
Now, BeginInvoke is simple to set up, but it has its limitations; you cannot cancel execution of the background thread, and if it throws an exception you cannot catch it in the invoking thread.
You can use BackgroundWorker.

Ensuring that all callbacks were completed before sending a new request through a DuplexChannel using WCF

I am experiencing some issues when using a Callback in a WCF project.
First, the server invokes some function Foo on the client which then forwards the request to a Windows Forms GUI:
GUI CLASS
delegate void DoForward();
public void ForwardToGui() {
if (this.cmdSomeButton.InvokeRequired) {
DoForward d = new DoForward(ForwardToGui);
this.Invoke(d);
}
else {
Process(); // sets result variable in callback class as soon as done
}
}
}
CALLBACK CLASS
object _m = new object();
private int _result;
public int result {
get { return _result; }
set {
_result = value;
lock(_m) {
Monitor.PulseAll(_m);
}
}
}
[OperationContract]
public int Foo() {
result = 0;
Program.Gui.ForwardToGui();
lock(_m) {
Monitor.Wait(_m, 30000);
}
return result;
}
The problem now is that the user should be able to cancel the process, which doesn't work properly:
SERVER INTERFACE
[OperationContract]
void Cleanup();
GUI CLASS
private void Gui_FormClosed(object sender, EventArgs e) {
Program.callbackclass.nextAction = -1;
// so that the monitor pulses and Foo() returns
Program.server.Cleanup();
}
The problem with this is that Cleanup() hangs. However, when I close the form when Process() is not running, it works properly.
The source seems to be that the Cleanup() is called before the monitor pulses etc and therefore a new request is sent to the server before the last request from the server has not yet been responded.
How can I solve this problem? How can I ensure before calling Cleanup() that no Foo() is currently being executed?
The first warning I'm seeing is that you're calling Invoke instead of
BeginInvoke
Invoke waits until the action has been completed on the other thread before returning, which can result in a deadlock in some situations.

Categories