Link to home
Start Free TrialLog in
Avatar of bswiftly
bswiftly

asked on

.NET Remoting with Windows Forms

I have looked through various tutorials and figured out how to get one of them working.. made a little sample with the following setup:

1) Server (console app, with console.readline()  to keep the listener open)
2) Client (console app that calls the remote code)
3) Class library which is the remote vb.net  code.  


What I don't understand is how am I going to use Remoting to send a signal from my Windows Service to the system tray icon..(Windows form) ?    When the Service becomes active I want it to send a message to the desktop (System Tray) so that the System Tray can change its icon into an 'active' mode, or I will have alittle msn like window pop up.  

What I know:    put the server code in the system tray project,  client code in the Service.

What I need to know:   When the Client calls the Remote Code, how can I get this code (which is just a class library)  to pop up a window or do something as mentioned above?   I couldn't get the remote code to work any other way than just a class library, is there a way to have my entire form the remote code?  (I tried that but then couldn't reference it in the server because i can only reference .dll , and the .exe wasn't a proper format I guess).

Any help would be appreciated.  Thanks.



This is the code:


Class library ###########################################
Public Class SampleRemoter : Inherits MarshalByRefObject

    Public Function getUser() As String
        Return Environment.MachineName
    End Function

End Class
###################################################

Server###############################################

Class RemotingServer

    Shared Sub Main(ByVal args() As String)

        Console.WriteLine("Registering our remote object...")
        RemotingConfiguration.RegisterWellKnownServiceType(GetType(SampleRemoter), "SampleRemoter", WellKnownObjectMode.SingleCall)
        Console.WriteLine("Create and register a channel...")
        Dim ch As New TcpServerChannel(8080)
        ChannelServices.RegisterChannel(ch)
        Console.Read()

    End Sub

End Class

###################################################

Client ###############################################

    Shared Sub Main(ByVal args() As String)
        Try
            Console.WriteLine("Registering Channel...")
            Dim ch As New TcpClientChannel
            ChannelServices.RegisterChannel(ch)
            Console.WriteLine("Registering remote object in local domain...")
            RemotingConfiguration.RegisterWellKnownClientType(GetType(SampleRemoter), "tcp://localhost:8080/SampleRemoter")
            Console.WriteLine(" Create remote object and execute method")
            Dim sr As New SampleRemoter
            Dim username As String = sr.getUser()
            Console.WriteLine("Machine name of remote machine = " & username)
        Catch ex As Exception
            Console.WriteLine(ex.ToString())
        Finally
            Console.Read()
        End Try


###################################################

Avatar of Jigit
Jigit
Flag of Israel image

bswiftly, you can use System.Windows.Forms.Timer object from your System Tray app and perform some checks in your internal file, or registry setting which are set by the Windows Service.

HTH,
Jigit
Avatar of bswiftly
bswiftly

ASKER

there must be a better way.. I'm looking to be able to send events directly to it without having to poll a local file or registry.  
Avatar of Bob Learned
Are these going to be on the same machine or separate machines?

Bob
they are both on the same machine.  I have had some help outside this site and what I have been able to do is create a wrapper for my form class.  The wrapper inherits the MarshalByRefObject class and then calls the pop up to display.  The Remoting Server is the forms project with the NotifyIcon object.  

So in other words I have resolved this issue, but will still give points for someone who can give me a way to change the notifyIcon.   Is there a way to return a variable from the remote code to the remoting server and have the remoting server do something accordingly? (i see how this may not be possible due to potential security risks..)

Why are you using Remoting if the 2 applications are on the same machine?  That sounds like a lot of overhead to me.

Bob
I have a Windows Service.  It can't have visual elements.  So I created a System tray app.. (NotifyIcon class)  that will be the controller for the Service.  This all works without remoting.

However,  when I want to have the service notify the desktop of its state, there is no way to do this other than remoting, or my own TCP/IP connectivity, or something that would incur more overhead is to have the system tray query a file every 5 seconds, as Jigit suggested.  

Do you have a  more efficient implementation of this that I could use?
Does your System Tray application have a form?

Bob
http://www.thinktecture.com/Resources/RemotingFAQ/RemotingUseCases.html

this is a good article to read on .NET Remoting.  The author, Ingo Rammer, has a few books published on .NET Remoting, and suggests that this is a good use of Remoting.
Yes, my systray app does have a form.  It is currently invisible (I just created a windows forms app, not a console app, because originally to test the pop up window I used the form).   How do you suggest the windows service sends events to the systray app to have the form slide up?

I am thinking of a way to send a message, not through MSMQ, but through the normal Windows message (WndProc), as long as you can get a handle to the Window.

Bob
But then how do I ensure where the message is coming from?  

I think that way would have just as much overhead as using Remoting?  After all, all remoting does is TCP commands.  The server just holds a reference to the memory allocation for the remote code..  In my trials the code is very quick and works well.

Ingo Rammer got back to me and said I should be able to use most Windows.Forms classes directly as Remote Hosts because most of them derive from MarshalByRefObject, however I haven't been able to import forms projects into my client, so this is not a possibility yet.

All very good points, so let's get back to the matter at hand.  

Would something like this work:

http://www.dotnet247.com/247reference/msgs/12/60499.aspx

Why would you use the "Invoke" method instead of the Remote
Asynchronous Programming Paradigm? What is the difference between the
two? Does the RemoteAsyncDelegate give you anything?

Their example in the MSDN is something like..

Dim RemoteCallback As New AsyncCallback(AddressOf Me.OurCallBack)
Dim RemoteDel As New RemoteAsyncDelegate(AddressOf obj.RemoteMethod)
Dim RemAr As IAsyncResult = RemoteDel.BeginInvoke(RemoteCallback,
Nothing)
RemAr.AsyncWaitHandle.WaitOne()

Bob
I'm not really sure.. I'm new to Remoting.. even found out about it while researching what I wanted to do here.

I found this quote at teh botton of the page where you got that code snippet off msdn:
"Note that if the client is a context-bound class that requires a synchronized context, the callback function is dispatched through the .NET remoting context infrastructure. "

So that offers some insight, although I'm not sure how much sense I can make of it... not sure what a context bound class is or if what I have IS one?


I'm not sure how to implement this remotecallback .. how would that interact ?  Have you actually used this before?
Do you have Ingo Rammer's .NET Remoting book?

Bob
No I don't, there is a new version that we might pick up, but isn't available yet.
I have it on the shelf, so I thought a good reference in the book might help.  I don't do remoting very much, so the specifics escape me.

Bob
Maybe I will email Ingo the link to this article and see if he has time to reply :)
Now that would be the best solution.  Get it right from the real Remoting expert :)

Bob
If you find some specifics on this, I would be very interested.

Bob
I have emailed him and he will either reply here or I will post his email reply for him.   I think its bedtime in Austria so we may have to wait till tomorrow.
ASKER CERTIFIED SOLUTION
Avatar of rammeri
rammeri

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
well i've been hacking away at this all day.. can't seem to get it working.  Ingo, I haven't actually looked at your code above, I'm still trying to just get the popup window working from my windows service.  I had it working from a test application, but when i  put the client in the windows service I can't see the remote code being run.. I have a method that writes to the eventlog and I am using that to place checkpoints as I run it.. (debugging is a pain!)

I'm wondering if there is an easy way of debugging the remote code file?

Or is this realising its being called from a windows service and therefore not allowing a visual element to be displayed?
For debugging, you have to first simply start your Windows Service using Service Control Manager. In VS.NET you can then select Debug->Attach and select the EXE of your windows service. This attaches the VS.NET debugger to the running instance of your Windows Service. Afterwards, you can use breakpoints, etc. just as if it were a "regular" application.

(BTW: I'll be travelling for the next few days an won't be able to respond to this thread. If you didn't find a solution by Saturday, just email me your code and I'll have a look at it)

In general: I would design the Windows Service as your Remoting server and the systray app as a client.

Cheers,
-Ingo
I know how to debug windows services.. its the remote code library that I am having probs with.

Why would you have the remoting server on the windows service?  That kind of defeats the purpose of what I'm trying to do doesn't it?  The whole issue with this is to create a form pop up when something happens with the windows service.. I can't have visual elements in the windows service so i thought of using remoting to notify the systray application and have it (or now the remote code) display the popup.

Sorry - I routinely meet developers who don't know how to debug a Svc ... ;-)

Re client/server: Simply because more than one listener could be active. For example with Windows XP "Fast User Switching" or Terminal Services. In this case, it's better if you allow multiple clients to receive notifications from one server. (i.e. subscribe the client to an event of the Windows Service)

-Ingo
Hey, thanks for your help.. haven't tried implementing your "shim layer" to get notifications directly to  the systray Icon, but I do now have a little popup working.  Systray as the remoting host and Win. Service as the client... it works great!  I might be able to find some time to try that shim layer thing soon, so I'll keep this question open for now in case I have other questions.  

ps. If you have an email list for notifications of when your new book is printed and shipped, feel free to put me on it :)
Me too :)

Bob
Well I have a working sample, converted it to VB (unfortunately we use VB), and implemented it in windows forms.  Didn't use the Client listener app or code to implement that in the server because I will not need this for the systray Icon.

One interesting thing, is that the config file is case sensitive even in VB!  A second is that VB automatically gives you a namespace in the properties window that doesn't appear in the code.. so when you specify Namespace Server.. in your code.. it is redundant..  (dumb VB!)

One problem that I did have however, which will be the last problem hopefully.. in order to get this Systray working with the features I want, is that I want to be able to manipulate the context menu and displayed image of the icon, when specific events are received from the Windows service.  Because I can't inherit more than one interface, my remote code has to be in a separate class from the Form code (Systray icon).  I have tested to see if I could make a shared (static) label, and set that label from the other class but this didn't work.  

How would I do this... anyone?

nevermind.. i'm guessing that is what the EventListener part of the code is for..   will have to implement that now to test it!