Delegates with out parameters not marshalling right

  • Thread starter Thread starter Jerry Houston
  • Start date Start date
J

Jerry Houston

In a winform application, I use worker threads to perform asynchronous tasks
(e.g., mail merge with Word). The thread procs need to gather some user
options from controls in the UI, and I know that must be done using a
delegate that's running on the main UI thread. (Can't safely access a
window from a thread different than the one that created the window.)

Rather than write and execute 7 separate methods to get things like checkbox
and radio button values, I wrote two methods, one of which accepts 3 'out'
parameters and the other accepts 4 'out' parameters. Thus, I can pass the
parameters via the delegate invocation, and upon return from it, they should
contain my values from the UI. Trouble is, they don't.

My first indication that something was wrong was when the compiler insisted
that my parameter variables be initialized before invoking the delegate.
Since both the delegate and the method itself adorn those parameters with
the 'out' keyword, the compiler should not have required that. In fact,
that's the only real difference between an 'out' parameter and a 'ref'
parameter.

My second clue was when, upon return from the invocation, the initial values
were still in those variables, despite my having seen them reassigned when I
traced through the method call. It appears that marshalling is being done
only in the IN direction, but not the OUT direction.

Has anyone else here come up against this issue? If so, what did you end up
doing? Thanks!
 
Jerry said:
In a winform application, I use worker threads to perform asynchronous tasks
(e.g., mail merge with Word). The thread procs need to gather some user
options from controls in the UI, and I know that must be done using a
delegate that's running on the main UI thread. (Can't safely access a
window from a thread different than the one that created the window.)

A piece of code would help.
Does cross-thread call cause marshalling at all???
In old dark times of MFC I used messages in need of cross-thread data
exchange.

Vadim Chekan.
 
Jerry Houston said:
Has anyone else here come up against this issue? If so, what did you end up
doing? Thanks!

Jerry,

I haven't tried to use out parameters in a delegate but I can offer an
alternative suggestion, you could use a class to store the parameters
similiar to the CancelEventArgs class.

hth
andrew
 
Jerry said:
In a winform application, I use worker threads to perform
asynchronous tasks (e.g., mail merge with Word). The thread procs
need to gather some user options from controls in the UI, and I know
that must be done using a delegate that's running on the main UI
thread. (Can't safely access a window from a thread different than
the one that created the window.)

Right. You have to call Form.Invoke and pass a delegate to the method that
you want the form to run on the UI thread.
Rather than write and execute 7 separate methods to get things like
checkbox and radio button values, I wrote two methods, one of which
accepts 3 'out' parameters and the other accepts 4 'out' parameters.
Thus, I can pass the parameters via the delegate invocation, and upon
return from it, they should contain my values from the UI. Trouble
is, they don't.

This is where I lose you. Invoke looks like one of these two

public object Invoke(Delegate method);

the docs says that method should be an instance of MethodInvoker or *any
delegate thattakes a void parameter*

the other version is:

public virtual object Invoke(Delegate method, object[] args);

the docs say: args is an array to pass as arguments to the specified method.

Note that both use a mehod called MarshaledInvoke, which calls
InvokeMarshalledCallback. The details of the delegate and parameters are
passed through a Control.ThreadMethodEntry field which is accessed by
InvokeMarshalledCallback. In effect the code looks like this (for Invoke
declared above)

if (method is MethodInvoker)
{
MethodInvoker i = method as MethodInvoker;
i.Invoke();
}
else
{
retVal = method.DynamicInvoke(args);
}

Note that DynamicInvoke takes an array of object, and this is *not* out or
ref. The only thing that is returned is the return value of the delegate
(retVal) which is returned from MarshaledInvoke to Invoke.
My second clue was when, upon return from the invocation, the initial
values were still in those variables, despite my having seen them
reassigned when I traced through the method call. It appears that
marshalling is being done only in the IN direction, but not the OUT
direction.
Yes

Has anyone else here come up against this issue? If so, what did you
end up doing? Thanks!

Use a delegate that returns a value. If you want multiple values, create a
class with multiple fields:

class Data
{
public string one;
public string two;
}

object GetData()
{
Data data = new Data();
data.one = this.txtOne.Text;
data.two = this.txtTwo.Text;
return data;
}

void ClickMyButton(object sender, EventArgs e)
{
Data data = this.Invoke(new MethodInvoker(GetData));
// access the values here.
}

Richard
 
Back
Top