Sunny
6/4/2004 3:03:00 PM
Hi Shone,
please read inline:
In article <38885f3a.0406031335.69c0a285@posting.google.com>,
shonend@yahoo.com says...
> Hi,
> my idea is to establish a simple communication between appA, app1 and
> app2, or better to say CONTROL the app1 and app2 from applicationA.
> All of those are WinForm apps.
> AppA should be able to query app1 and app2 for some simple info, like
> the status of those applications, and also to shut them down if
> needed, i.e. issue the command to applications to shutdown themselves.
>
> The research on how to accomplish this brought me to .NET Remoting, so
> I''ve decided to try it out.
>
> First thing I learned about Remoting is that initiating application
> should be the client, while the application that "listens" for remote
> calls, and responds to them is the server. In my case that would make
> the appA the client and app1 and app2 the servers, since the appA will
> be the one always initiating the remote contact. And that''s what I
> did, allthough already here I think I made mistake, because it''s
> unusual to have 1 client and 2 servers (and even more, because
> eventually there will be app3, app4...). So what''s the better solution
> for the problem I need to solve?
Actually, client is the app, which makes a request, and server is this,
which serves (executes) the request. Even if you dedicate some app for a
server, it will be so only while it servers the requests. And if this
server rises an event (i.e. invokes a method on the client) the roles
for that call are switched.
For your solution, it will depend on what is the exact scenario. You can
create a so called server (appA) to which all other apps 1, 2, etc will
register. And in that moment the roles are switched, because as appA
already knows which are connected apps, will make calls to them to get
the data or to execute something else.
>
> Second problem I had was how to retreive the desired info from the
> server, since the server application doesn''t instantiate the remoting
> class? Namely, the remoting class is defined and registered on the
> server, but there is no reference from the class to the Form''s
> variables, and vice-versa. What I mean, I have the (single) Form code,
> and separate remoting-class code, but there is no obvious connection
> in executing these two, so that I can for example feed the desired
> data from Form to the remoting class and return that data to the
> remote-calling client.
> I found a solution for this problem by sharing some data on the server
> through AppDomain properties, and it''s working (amazingly!). I''m sure
> that this ad-hoc solution is pretty much unusual, and that there must
> be some more common method and right way to do it, so I would like to
> hear your expert oppinion about it, please.
What you can do, is to create and instance of the remoting object in the
main form, passing the same form as parameter. Then the remote object
will know who is its parent and to get data and/or execute some methods.
>
> The third and most important problem, I still didn''t manage to solve
> is how to issue a command from appA to app1 (or app2) to shutdown? I
> tried with defining and raising the events, but it''s not working for
> some strange reason. Here''s what I did:
> 1) server side - defined a Delegate and an Event in the remoting
> class:
>
> Public Delegate Sub ShutDownEventHandler(ByVal sender As Object, ByVal
> e As EventArgs)
>
> Public Class clsRemotingListener
> Inherits System.MarshalByRefObject
>
> ...
>
> Public Shared Event ShutDown As ShutDownEventHandler
>
> ...
>
> I declared Event as Public Shared, because I need to catch this event
> on the server, and server doesnt instantiate the remoting object.
>
> 2) defined the public method that will raise the event. This method
> will be called from the client (appA). This is still in the
> clsRemotingListener code:
>
> Public Sub ShutDownServer(ByVal e As EventArgs)
>
> RaiseEvent ShutDown(Me, e)
>
> End Sub
>
> 3) in the server code (Form''s constructor), I declare the handler for
> the event:
>
> AddHandler fRemoting.clsRemotingListener.ShutDown, AddressOf
> Me.RemoteShutDown
>
> Here you can see why the Event needed to be "Shared".
> (fRemoting is just a name of the separate dll where the
> clsRemotingListener is)
>
> 4) Finnaly, I define the Form''s local RemoteShutDown method:
>
> Private Sub RemoteShutDown(ByVal sender As Object, ByVal e As
> EventArgs)
> Me.TextBox1.Text = "I AM BEING SHUTDOWN!!!"
> Me.Close()
> End Sub
>
>
> So when I try to invoke the "ShutDownServer" method from the client -
> it doesn''t work. Everything freezes, both client and server, and the
> third window pops-up, obviously some error report, but it doesn''t even
> get to paint itself (remains blank). All that stays like that until I
> kill the app1 (server), then the "third window" closes too, and appA
> (client) continues to work normally.
>
> I would like to point out that only the server shutdown attempt causes
> the problem, not the remoting or event raising/receiving. At least
> that''s what I think, since everything works great when I comment-out
> the "Me.Close()" statement in step 4). Then the server successfully
> receives and processes the event, writes out the message, and
> everyone''s happy (both appA and app1 continue to do their jobs
> uninterrupted). But the closing attempt messes everybody up!?!
The forms are not thread safe. Every remoting call is executed on a
different thread from the thread pool. In your remote method you have to
use Form.Invoke to transfer the execution in the main form thread. Thats
why your app freezes.
>
>
> So if you had enough patience to read so far, I would like to kindly
> ask you to give me a couple of guidelines here.
> Please don''t respond with "it''s obvious that you don''t understand the
> basic principles of .NET Remoting...". First of all that statement
> couldn''t be more TRUE, second of all - I already know that :)))
>
> All I need is a couple of lines of example code with or without a
> short explanation. I believe that I was clear in explaining what my
> problem is and that is pretty much basic, so many of you experts in
> .NET remoting are already doing that as a routine.
This is a sample pseudo code for such a scenario (take a look at
interface or abstract class approach, and SAO/CAO factory pattern on
internet):
Shared.dll (must be referenced by both clients and servers):
public interface IRegistered
{
int GetSomeFormData();
void ExecuteSomething();
void CloseApp();
}
public interface IRegistrator
{
void RegisterClient(IRegistered client)
}
// declare some delegates which you want to use to make event calls
// declare simple event wrapper to transfer the event calls (search the
// newsgroup, I have posted examples)
// you can also declare here some custom exceptions, if you wish
-----------------------------
appA (I''ll put only the remote object, you have to expose it sa SAO
Singleton, as it will contain all registered clients. You can use
Marshal method as well, so your server app can create the object and
have access to it as well):
public ClientRegistrator : MarshalByReferenceObject, IRegistrator
{
private ArrayList clients; /or some other way to store the client
references
public void RegisterClient(IRegistered client)
{
this.clients.Add(client);
// TODO whatever you want more
}
public void CloseAllClients()
{
// this is only example of a public method for which the
//clients does have no idea. But your appA, which have created
//a ClientRegistrator object, can invoke it (thats where Marshal
//approache becomes handy.
foreach (IRegistered client in this.clients)
{
client.CloseApp();
//also, you can make this call async or
//[OneWay], as it will never return (client will be
//closed
//remove the client from the list
}
}
}
----------------------------
app1,2..N
public class RemoteComunicator : MBR, IRegistered
{
//holds a ref to the main form
private Form myhostform;
//ctor
public RemoteComunicator(Form myhosthorm)
{
this.myhostform = myhostform;
}
public int GetSomeFormData()
{
return this.myhostform.Field1;
}
public void ExecuteSomething()
{
//manipulate the host form as you wish
//use Invoke !!!!
if (this.myhostform.InvokeRequired)
this.myhostform.Invoke .....
else
this.myhostform.Method1();
}
public void CloseApp()
{
//something like prev method, but call form.Close
}
}
class MainForm : Form
{
....
private RemoteComunicator comunicator;
....
....
//now in Onload event or wherever you need
//create the remoting channels, and get refference to
//the remote server IRegistrator
void RegisterWithServer()
{
//create channels
/get rem reference
IRegistrator server = Activator....
//now create our comunicator and register it to the server
this.comunicator = new RemoteComunicator(this);
server.RegisterClient(this.comunicator);
}
}
So, thats the idea, go for it :)
Hope that helps
Sunny