Using a form as a remote object

G

Guest

Can a form be a remote object?

If not, is there any work around... basically I want a central computer to
be controlling forms on several computers on a network?

When I try to add Inherits MarshallByRefObject to a Windows Form class, I
get the error: Base class 'System.MarshalByRefObject' specified for class
'MyForm' cannot be different from the base class 'System.Windows.Forms.Form'
of
one of its other partial types

Thanks for any pointers
 
M

Marc Gravell

I cringe when writing the following, as I don't like it as a solution, but:

You don't necessarily need to marshal the actual Form object to control the
form. If you created a custom class (MarshalByRefObject) which contains a
reference to the local form, and provides helper functions to do the actions
you need to do, it might just work (perhaps also using an external interface
assembly to define the methods on the class). Your class is marshalled, and
when the methods are fired they are transferred (PRC or whatever) to the
local machine, which then interacts with the local form.

Any use?

Marc
 
G

Guest

Thanks Marc! I've tried this, and I can open and show the form that way on
the remote machine but then it immediately becomes unresponsive. Maybe
there's a threading issue or something I'm not accounting for?
 
M

Marc Gravell

This could be thread affinity? THe form will only want to talk to the thread
that created and opened it (the UI thread); if you have received an RPC
message, it is likely to be running on a different thread - so you show the
form (and your RPC message completes), but it could be that this thread
isn't interested in maintaining the new form.

What I would do is to sync the messages back to a thread that you know is a:
owned by yourself (i.e. not a thread-pool thread), and b: performing UI. One
easy way is to (at app startup) create a master form somewhere that can be
seen by all - a static field would suffice. Make this form handle the
messages, handling InvokeRequired suitably to ensure that your new form is
created on the same thread as the master form.

An example (cut down):

{
/// ...blah blah blah - your code
masterForm.DoSomething(id, obj); // this contains the code to interact
with the user forms

}


private delegate void DoSomethingDelegate(int id, object obj);
[System.Runtime.Remoting.Messaging.OneWayAttribute]// make sure that
this is fire-and-forget for BeginInvoke usage
internal void DoSomething(int id, object obj) {
if (InvokeRequired) {
// we're not on the right thread; switch back to the thread
that owns the master-form
BeginInvoke(new DoSomethingDelegate(DoSomething), new
object[] { id, obj});
return;
}
// if we get here we are back onto the primary UI thread
// actual code which shows a form etc
}


Note: if you need to pass results back up the chain you should remove
OneWayAttribute and use Invoke instead of BeginInvoke - however, if you do
this you should definitely avoid blocking calls like ShowDialog, otherwise
your remote call will be stuck waiting on the local user closing a form.

Does this make any sense?

Marc
 
M

Marc Gravell

Aside: if you don't want to use a static field for the master form, then a
better option is to add (to your new class) a field that is declared as
ISynchronizeInvoke any Control or Form can satisfy this, and it provides
the necessary InvokeRequired, Invoke, BeginInvoke etc to throw the message
between threads, without requiring a static form anywhere. Obviously you'd
need to tell your object which form to use to get the UI thread (maybe in
the ctor for your new class), so you would still need some form running
somewhere - but it is perhaps preferable to using a static property to hold
a form.

Marc
 
G

Guest

Great, thanks Marc! That sounds like exactly what's going on.

I'm a novice at threading so I'll have to read up a bit to understand the
approach you're suggesting -- thanks so much!
 
G

Guest

This works! Thanks so much for the help.

Going back to your initial post, what part made you "cringe"? Is there a
ditch I'm headed for on this?
 
M

Marc Gravell

I don't know really. Maybe it was premature and prejudiced.

Initially, I didn't like the idea of a server interacting directly with a
client's forms - but with this approach, this isn't what we are doing
(hoorah). We have wrapped the UI in a facade that exposes a limited
interface, and remoted the facade. So maybe all is well. It just isn't an
approach I would have leapt to myself. But if it works, the very best of
luck to you.

Incidentally (and apologies if you already know this) - on the "facade"
front - you should endeavour to not make this interface "chatty" - i.e.
since you are remoting, avoid setting 17 properties in turn (instead
choosing perhaps a method that accepts 17 parameters and sets them all at
the end hosting the objects). This will decrease the round trips and make
the system far more responsive - particularly if you separate the client and
the server by any appreciable distance.

Anybody else with an opinion on this?

Marc
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top