C# parameter count mismatch when trying to add AsyncCallback into BeginInvoke() - c#

I have main form (PrenosForm) and I am trying to run Form2 asynchronously.
It works without callback delegate:
this.BeginInvoke(cp, new object[] { datoteke, this.treeView1.SelectedNode.FullPath.ToString(), this, efekt }, null); //works 1.
Doesn't work with callback delegate (parameter count mismatch):
this.BeginInvoke(cp, new object[] { datoteke, this.treeView1.SelectedNode.FullPath.ToString(), this, efekt }, new AsyncCallback(callBackDelegate), null); //doesn't work parameter count mismatch 2.
Works with callback delegate if I do it like this:
cp.BeginInvoke(datoteke, this.treeView1.SelectedNode.FullPath.ToString(), this, efekt, new AsyncCallback(callBackDelegate), null); //works 3.
My question is why does one way work and the other doesn't? I'm new at this. Would anyone be so kind as to answer my question and point out my mistakes?
private delegate void copyDelegat(List<ListViewItem> datoteke, string path, PrenosForm forma, DragDropEffects efekt);
private delegate void callBackDelegat(IAsyncResult a);
public void doCopy(List<ListViewItem> datoteke, string path, PrenosForm forma, DragDropEffects efekt)
{
new Form2(datoteke, path, forma, efekt);
}
public void callBackFunc(IAsyncResult a)
{
AsyncResult res = a.AsyncState as AsyncResult;
copyDelegat delegat = res.AsyncDelegate as copyDelegat;
delegat.EndInvoke(a);
}
public void kopiraj(List<ListViewItem> datoteke, DragDropEffects efekt)
{
copyDelegat cp = new copyDelegat(doCopy);
callBackDelegat callBackDelegate = new callBackDelegat(callBackFunc);
this.BeginInvoke(cp, new object[] { datoteke, this.treeView1.SelectedNode.FullPath.ToString(), this, efekt }, new AsyncCallback(callBackDelegate), null); //doesn't work parameter count missmatch 2.
this.BeginInvoke(cp, new object[] { datoteke, this.treeView1.SelectedNode.FullPath.ToString(), this, efekt }, null); //works 1.
cp.BeginInvoke(datoteke, this.treeView1.SelectedNode.FullPath.ToString(), this, efekt, new AsyncCallback(callBackDelegate), null); //works 3.
}

It is because Control.BeginInvoke() has a completely different signature from SomeDelegate.BeginInvoke(). While their method names are the same, they are fundamentally different methods. And fundamentally work differently at runtime, there is no comparison.
Control.BeginInvoke() takes a delegate and an object[]. Cast in stone.
private delegate SomeDelegate(mumble, foo, bar) automatically creates a SomeDelegate.BeginInvoke() method. Whose signature takes those three arguments, plus two extra arguments, a callback and a state object.
A significant runtime difference is that Control.BeginInvoke() can call a delegate and if it bombs with an exception then the exception is raised on the UI thread. A delegate's BeginInvoke() method doesn't do this, it re-raises the exception in the callback that calls EndInvoke().
Very confuzzling, I know, maybe they shouldn't have used the same name.

Don't do this at all.
Showing multiple forms on different threads is an extremely bad idea and will end up causing lots of trouble.
Your second example doesn't work because Control.BeginInvoke doesn't support a callback parameter.
Your code is interpreted as calling a delegate with three parameters; an array, and AsyncCallback, and null.
Since your method doesn't take such parameters, an exception is thrown.
Also, calling Control.BeginInvoke won't run a function in the background; it will run it on the UI thread when it next reaches the message loop.

Related

control.invoke with an out parameter

Winforms, C#, VS2010.
I have a polling thread that runs for the lifetime of my app.
Occasionally it calls an event on my main form. I've not touched the code for years and it's run successfully but now I need to add an "out" parameter to the list of parameters. I've searched online but all the threads I've found have been regarding reflection and been complex to attempt to convert to my context. Mine doesn't use reflection.
Can somebody help over how to fix this pls? On the reflection threads I read people seem to check some object array for the out parameter result, which I don't use in my code, and I wouldn't know where to get it anyway.
private bool OnNeedUpdateCreateEvent(string title, string message,
bool creatingNew, out string newPlanName)
{
newPlanName = "";
// 1st pass through this function.
// Check to see if this is being called from another thread rather
// than the main thread. If so then invoke is required
if (InvokeRequired)
{
// Invoke and recall this method.
return (bool)Invoke(new onNeedToUpdatePlanEvent(OnNeedUpdateCreateEvent),
title, message, creatingNew, out newPlanName); <- wrong out param
}
else
{
// 2nd pass through this function due to invoke, or invoke not required
return InputDlg(this, title, message, creatingNew, out newPlanName);
}
}
It is much like you already know, you just haven't found the array yet. It is automatically created by the compiler. The signature of the Invoke method is:
public object Invoke(
Delegate method,
params object[] args
)
It is the params keyword that gets the compiler to auto-create the array. Nice syntax sugar, but it doesn't help you here. You just have to do it yourself, like this:
if (!creatingNew) {
// Invoke and recall this method.
object[] args = new object[] { title, message, creatingNew, null };
var retval = (bool)Invoke(new onNeedToUpdatePlanEvent(OnNeedUpdateCreateEvent), args);
newPlanName = (string)args[3];
return retval;
}
// etc..

TargetException on Invoke

I have a problem with my Invoke() throwing a TargetException.
public Controller(SystemUI ui, System system)
{
UI = ui;
System = system;
UI.CommandEntered += ParseCommand;
Commands = new Dictionary<string, Delegate>();
Commands.Add(":q", new Action(UI.Close));
}
I then call Commands[input[0]].Method.Invoke(this, input.ToArray<object>());, but it throws a TargetException with the message
Object does not match target type.
Do I need a cast?
I'm quite lost, and I'd appreciate any help!
Based on comments above, you are trying to invoke an Action (UI.Close), but you are passing an array of objects as parameters to this action, which has no parameters therefore incurring this exception.
Change...
input.toArray<object>()
to...
new object[0], or new object[] {} // or perhaps even just null may do the trick.

WPF C# Threading

I am writing a code in c# to do image processing. I want to use threading and i suppose threading in WPF application is little different. I tried to run thread but it only works when the function is void(), i.e does not take any arguments.
However, my function is taking 3 arguemnts like this
frame_extract.Frame_Processing(_colorFrame_, widht, height);
So therefore the following does not work
depth_Threads = new System.Threading.Thread(**) since ** takes on void() type.
perhaps i am missing something, but my question is how can i work with threading for functions that take arguments.
Maybe you could use the TPL.
It should then be something like:
Task.Factory.StartNew(() => frame_extract.Frame_Processing(_colorFrame_, widht, height));
But be aware that you might have to marshal to the ui-thread.
If you want to create the thread in the ui thread and want the new thread to interact with mentioned ui thread, something like the following should work:
var task = new Task(() => frame_extract.Frame_Processing(_colorFrame_, widht, height));
task.Start(TaskScheduler.FromCurrentSynchronizationContext());
That should work.
I'm not 100% sure if that's what you want, but I think you need to do this :
depth_Threads = new System.Threading.Thread(()=>frame_extract.Frame_Processing(_colorFrame_, widht, height));
It depends on what values you are passing in. Sometimes if you are using objects, they are locked to a given thread in which case you need to create duplicates prior and then pass the duplicates into the new thread.
Do the following. Your method should receive single object argument like this void SomeVoid(object obj). Make an object array with all variables that you want to pass to the method SomeVoid like this object[] objArr = { arg1, arg2, arg3 }; and then call the Start method of thread object with objArr argument since the Start() method receives one object parameter. Now back to your method, cast and obj received from Start method to an object array like this object arr = obj as object[]; and then you can access those 3 arguments like this arr[0] arr[1] and arr[2]
You can use the ParameterizedThreadStart class.
1) Create a class who will holds your three arguments
public class FrameProcessingArguments
{
public object ColorFrame { get; set; }
public int Width { get; set; }
public int Height { get; set; }
}
2) Modify your Frame_Processing method to take as parameter an instance of Object and inside it, cast that instance as a FrameProcessingArguments
if (arguments == null) throw new NullArgumentException();
if(arguments.GetType() != typeof(FrameProcessingArguments)) throw new InvalidTypeException();
FrameProcessingArguments _arguments = (FrameProcessingArguments) arguments;
3) Create and start your thread
FrameProcessingArguments arguments = new FrameProcessingArguments()
{
ColorFrame = null,
Width = 800,
Height = 600
}
Thread thread = new Thread (new ParameterizedThreadStart(frame_extract.Frame_Processing));
// You can also let the compiler infers the appropriate delegate creation syntax:
// and use the short form : Thread thread = new Thread(frame_extract.Frame_Processing);
thread.Start (arguments);

Delegate in method using variables in scope of method but outside delegate scope

I wrote some sample code where I have an Action delegate declared in a method body where two params are passed and then consumed by the delegate code without those params being passed into the delagte. It seems cleaner to me to explictely pass in these params to the delegate too, but in this case I am not, and this code would work fine.
I am wondering how .NET keeps these references available in the export delegate that is now running on a new thread.
public void MyMethod(string name, ComplexObject myObj)
{
Action export = () => {
//do something with name
name = name + name;
//do something with complex reference object
myObj.SomeMethod(name);
};
// do more work
// example launch export on a new thread
System.Threading.Thread newThread = new System.Threading.Thread(new System.Threading.ThreadStart(export));
newThread.Start();
}
The compiler creates a special type that keeps those variables. Then, instead of storing those variables on the stack, it instantiates an instance of that type every time you call that method. Then, the anonymous delegates use a reference to that new instance to get access to those variables.

How do I delegate an AsyncCallback method for Control.BeginInvoke? (.NET)

Is it possible to use Control.BeginInvoke in anything other than a "fire & forget" manner?
I want to change the following request to delegate a callback method so that i can do something when each of my asynchronous calls complete.
this.BeginInvoke(new RefreshRulesDelegate(RefreshRules), new object[] { ctrl, ctrl.DsRules, ctrl.CptyId });
I would be able to do this with a normal delegate.BeginInvoke e.g.
RefreshRulesDelegate del = new RefreshRulesDelegate(RefreshRules);
del.BeginInvoke(ctrl, ctrl.DsRules, ctrl.CptyId, new AsyncCallback(RefreshCompleted), del);
But because I'm calling Control.BeginInvoke I can't do this as I get the "cross-thread operation not valid" error.
Anyone help?
Further to some of the answers received, I will clarify the "why". I need to load/refresh a Control on my GUI without locking up the rest of the app. The control contains numerous controls (ruleListCtls) which all require a dataset to be retrieved and passed to them. i.e.
public void RefreshAll()
{
foreach (LTRFundingRuleListControl ctrl in ruleListCtls)
{
this.BeginInvoke(new RefreshRulesDelegate(RefreshRules), new object[]{ctrl,ctrl.DsRules, ctrl.CptyId });
}
}
I have found that I can do this if I provide a delegate callback method and move any code which amends the controls back onto the main GUI thread on which they were created (to avoid the cross-thread error)
public void RefreshAll()
{
IntPtr handle;
foreach (LTRFundingRuleListControl ctrl in ruleListCtls)
{
handle = ctrl.Handle;
RefreshRulesDsDelegate del = new RefreshRulesDsDelegate(RefreshRulesDs);
del.BeginInvoke(ctrl.DsRules, ctrl.CptyId, handle, out handle, new AsyncCallback(RefreshCompleted), del);
}
}
private void RefreshCompleted(IAsyncResult result)
{
CptyCatRuleDataSet dsRules;
string cptyId;
IntPtr handle;
AsyncResult res = (AsyncResult) result;
// Get the handle of the control to update, and the dataset to update it with
RefreshRulesDsDelegate del = (RefreshRulesDsDelegate) res.AsyncDelegate;
dsRules = del.EndInvoke(out handle,res);
// Update the control on the thread it was created on
this.BeginInvoke(new UpdateControlDatasetDelegate(UpdateControlDataset), new object[] {dsRules, handle});
}
public delegate CptyCatRuleDataSet RefreshRulesDsDelegate(CptyCatRuleDataSet dsRules, string cptyId, IntPtr ctrlId, out IntPtr handle);
private CptyCatRuleDataSet RefreshRulesDs(CptyCatRuleDataSet dsRules, string ruleCptyId, IntPtr ctrlId, out IntPtr handle)
{
try
{
handle = ctrlId;
int catId = ((CptyCatRuleDataSet.PSLTR_RULE_CAT_CPTY_SelRow)dsRules.PSLTR_RULE_CAT_CPTY_Sel.Rows[0]).RULE_CAT_ID;
return ltrCptyRulesService.GetCptyRules(ruleCptyId, catId);
}
catch (Exception ex)
{
throw ex;
}
}
Here's what we delgate to the main thread having received the callback:
private delegate void UpdateControlDatasetDelegate(CptyCatRuleDataSet dsRules, IntPtr ctrlId);
private void UpdateControlDataset(CptyCatRuleDataSet dsRules, IntPtr ctrlId)
{
IEnumerator en = ruleListCtls.GetEnumerator();
while (en.MoveNext())
{
LTRFundingRuleListControl ctrl = en.Current as LTRFundingRuleListControl;
if (ctrl.Handle == ctrlId)
{
ctrl.DsRules = dsRules;
}
}
}
This now works fine. However, the main problem, apart from that I don't think this is particularly elegant, is exception handling. Maybe this is another question, but if RefreshRulesDs throws an exception then my app crashes as the error is not bubbled back up the GUI thread (obviously) but as an unhandled exception. Until I can catch these then I will have to do this whole operation synchronously. How do I successfully catch an error and load up the rest of my controls? Or how do I do achieve this asynchronous operation another way, with proper exception handling?
Regarding the "Is it possible" part: No, Control.BeginInvoke uses Windows' PostMessage() and that means there is no answer. It also means that the RefreshRulesDelegate is executed on the main thread, not on a background thread.
So, use delegate.BeginInvoke or the ThreadPool and when they are completed use Control.[Begin]Invoke() to update the UI.
You could do this:
this.BeginInvoke(delegate
{
RefreshRules(ctrl, ctrl.DsRules, ctrl.CptyId);
RefreshCompleted();
});
EDIT:
I would consider removing the IAsyncResult argument from the method RefreshCompleted and use the solution above.
If for some reason you really need to keep the IAsyncResult argument. You could implement an extension method for Control:
public static IAsyncResult BeginInvoke(this Control control, Delegate del, object[] args, AsyncCallback callback, object state)
{
CustomAsyncResult asyncResult = new CustomAsyncResult(callback, state);
control.BeginInvoke(delegate
{
del.DynamicInvoke(args);
asyncResult.Complete();
}, args);
return asyncResult;
}
public static void EndInvoke(this Control control, IAsyncResult asyncResult)
{
asyncResult.EndInvoke();
}
You would need to define your CustomAsyncResult class, you can get documentation on how to do this here
So you want the "extra thing" to happen on a worker thread? (else you'd just run it in th RefreshRules method). Perhaps just use ThreadPool.QueueUserItem:
ThreadPool.QueueUserWorkItem(delegate { /* your extra stuff */ });
at the end of (or after) your RefreshRules method?
For info, you may find it easier/tidier to call BeginInvoke with an anonymous method too:
this.BeginInvoke((MethodInvoker) delegate {
RefreshRules(ctrl, ctrl.DsRules, ctrl.CptyId);
ThreadPool.QueueUserWorkItem(delegate { /* your extra stuff */ });
});
this avoids creating a delegate type, and provides type-checking on your call to RefreshRules - note that it captures ctrl, though - so if you are in a loop you'll need a copy:
var tmp = ctrl;
this.BeginInvoke((MethodInvoker) delegate {
RefreshRules(tmp, tmp.DsRules, tmp.CptyId);
ThreadPool.QueueUserWorkItem(delegate { /* your extra stuff */ });
});

Categories

Resources