9.6 Marshal by Value (MBV)

only for RuBoard

Example 9-11 contains yet another version of ServerInfo . There are only two differences between this version and the last:

  • The class is marked <Serializable> instead of derived from MarshalByRefObj , which is required to marshal an object by value.

  • All data that is provided by the class is obtained in the constructor instead of at call time.

When ServerInfoFactory.CreateServerInfo is called, all remote machine properties are ascertained immediately. This is necessary because next , the entire class is marshaled by value back to the client; that is, a complete copy is transferred to the client. If the machine values were not obtained in the constructor, the class would return values relative to the client machine (because the object will be local to the client).

Example 9-11 is not meant to be presented as the optimal method for the ServerInfo case. The purpose is to demonstrate the flexibility of the remoting framework and to present some of the issues that are faced in real-world development. Remember, when remoting, use what works and is really fast.

Example 9-11. MBV version of ServerInfo
 <Serializable( )> _ Public Class ServerInfo   Implements IServerInfo       Private machineTime As DateTime   Private processorUsed As Single   Private availableMemory As Long   Private machine As String   Private ip As IPAddress       Public Sub New( )         'Get machine info when object is created     machine = Dns.GetHostName( )     Dim ipHost As IPHostEntry = Dns.GetHostByName(machine)     ip = ipHost.AddressList(0)         'Machine date     machineTime = DateTime.Now         'Processor used     If PerformanceCounterCategory.Exists("Processor") Then       Dim pc As New PerformanceCounter("Processor", _           "% Processor Time", "_Total", True)       Dim sampleA As CounterSample       Dim sampleB As CounterSample       sampleA = pc.NextSample( )       Thread.Sleep(1000)       sampleB = pc.NextSample( )       processorUsed = CounterSample.Calculate(sampleA, sampleB)     End If         'Available memory     If PerformanceCounterCategory.Exists("Memory") Then       Dim pc As New PerformanceCounter("Memory", "Available MBytes")       availableMemory = pc.RawValue( )     End If       End Sub       'Shared method   Public Function GetMachineTime( ) As DateTime _     Implements IServerInfo.GetMachineTime     Return machineTime   End Function       'Get % of process currently in use   Public Function GetProcessorUsed( ) As Single _     Implements IServerInfo.GetProcessorUsed     Return processorUsed   End Function       'Get MBytes of free memory   Public Function GetAvailableMemory( ) As Long _     Implements IServerInfo.GetAvailableMemory     Return availableMemory   End Function       Public ReadOnly Property MachineName( ) As String _     Implements IServerInfo.MachineName     Get       Return machine     End Get   End Property       Public ReadOnly Property IPAddress( ) As IPAddress _     Implements IServerInfo.IPAddress     Get       Return ip     End Get   End Property 

Other than these two changes, everything is the same on the server sideeven the configuration file (which contains one well-known entry that exposes the object factory). Just stop the service, copy the assembly to the appropriate location, and restart.

Some interesting architectural changes must be made to accommodate the fact that ServerInfo is now an MBV object. The client must have access to the ServerInfo assembly to use the object, which raises several questions.

Can the client get the type information from the ServerInfo assembly without linking to it? Thankfully, yes. The client does not need to reference the object directly. Otherwise, the object factory concept would be rendered useless. ServerInfo just needs to be in the same directory as the client executable.

This still presents some problems, though. The client is not linked to the assembly, but what about distribution? If changes are made to the remote assembly, how is the client going to get it? Also, the source to the remote assembly will be on the client machine, where it can be disassembled by money-hungry corporate spies! IL is not difficult to pick up, and reverse engineering has never been easier.

One solution is to move the entire Windows Service into a virtual directory on the web server. The service can still provide the objects over TCP, and the client can now use a few lines of code to download the ServerInfo assembly to its location at runtime. This way, the client always has the latest version of the object. The assembly can be loaded into the client AppDomain , making the ServerInfo type available. Once this has occurred, the downloaded file can be deleted from the client directory, leaving no trace of its existence.

9.6.1 Configuring IIS

To make the ServerInfo assembly available from IIS, set up a directory called ServerInfo for the component under wwwroot (it can be set up anywhere local to the web server if a different location is desired):

 Inetpub   wwwroot     ServerInfo       bin           <- server, assemblies, service .config file 

In IIS, configure ServerInfo as a virtual directory. Make sure that the ServerInfo and bin directories use the following settings:

  • Execute Permissions = "Scripts Only"

  • Application Protection = "Low (IIS Process)"

  • Read Permission

  • Anonymous and Integrated Windows authentication (IWA)

If Execute Permissions are not set to "Scripts Only," the ServerInfo assembly will not download.

All server files should be placed in the bin directory, while the upper-level ServerInfo directory contains nothing. The server should be started with everything configured as before. Remember that this configuration also applies when using IIS (versus a server) to remote objects.

9.6.2 Custom Application Settings

On the client side, the location of the virtual directory and the object name can be stored in the client configuration file, which is shown in Example 9-12. For the .NET configuration class to find the file, it must have the same name (with file extension) as the client assembly, followed by .config (i.e., remoteclient.exe.config ). Custom settings can be added in <appSettings> , as illustrated in Example 9-12.

Example 9-12. Client-side configuration settings
 <configuration>   <appSettings>  <add key="location" value="" />   <add key="assembly" value="ServerInfo" />  </appSettings>   <system.runtime.remoting>     <!--     client side remoting settings are here     -->   </system.runtime.remoting> </configuration> 

At runtime, these values can be retrieved (rather than hardcoded in the source) by using the System.Configuration.ConfigurationSettings class:

 Dim location As String Dim remoteAssembly As String Dim fileName As String     location = ConfigurationSettings.AppSettings("location") remoteAssembly = ConfigurationSettings.AppSettings("assembly") fileName = remoteAssembly + ".dll" 

Example 9-13 contains the listing for the revised client that uses the MBV ServerInfo object. The ServerInfo assembly is downloaded to the client by calling WebClient.DownloadFile , which conveniently provides just what is needed here. The first parameter to the method is the remote file's URL, while the second is the name it should be saved to locally.

Once the file is downloaded, a call is made to AppDomain.SetShadowCopyFiles (for the current domain). This call is necessary to prevent ServerInfo.dll from being locked when the assembly is loaded into the AppDomain. After calling AppDomain.Load to inject the assembly into the current AppDomain, the file is deleted using File.Delete . However, the ServerInfo type is now available to the client. The rest of the client code behaves as it did previously, using Activator.GetObject to get the object factory interface. This time, when CreateServerInfo is called, the factory returns an MBV instance of the remote object.

Example 9-13. Revised client
 Imports System Imports System.Configuration Imports System.IO Imports System.Net Imports System.Reflection Imports System.Runtime.Remoting     Imports ServerInterfaces     Public Class Client       Public Shared Sub Main( )         Try          '----- Download ServerInfo.dll  Dim location As String   Dim remoteAssembly As String   Dim fileName As String   location = ConfigurationSettings.AppSettings("location")   remoteAssembly = ConfigurationSettings.AppSettings("assembly")   fileName = remoteAssembly + ".dll  "  Dim wc As New WebClient( )   wc.DownloadFile(location + fileName, fileName)   AppDomain.CurrentDomain.SetShadowCopyFiles( )   AppDomain.CurrentDomain.Load(remoteAssembly)   File.Delete(fileName)  '----------------------------           Dim factoryObj As Object = _       Activator.GetObject(GetType(IServerInfoFactory), _         "tcp://")           Dim factory As IServerInfoFactory = _         CType(factoryObj, IServerInfoFactory)       Dim si As IServerInfo = factory.CreateServerInfo( )           If Not (si Is Nothing) Then         Console.WriteLine(si.MachineName)         Console.WriteLine(si.IPAddress)         Console.WriteLine(si.GetMachineTime)         Console.WriteLine("{0}MB Free", si.GetAvailableMemory)         Console.WriteLine("Processor Used: {0}", si.GetProcessorUsed)       End If             Catch e As Exception       Console.WriteLine(e.GetType( ).FullName)       Console.WriteLine(e.Message)     End Try         Console.WriteLine("Hit ENTER to continue...")     Console.ReadLine( )       End Sub     End Class 
only for RuBoard

Object-Oriented Programming with Visual Basic. Net
Object-Oriented Programming with Visual Basic .NET
ISBN: 0596001460
EAN: 2147483647
Year: 2001
Pages: 112
Authors: J.P. Hamilton

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net