Monday, August 28, 2006

Dns.GetHostEntry: No such host is known

We had a really weird scenario debugging a MacroView ADO.NET based package today. The package was running on a Windows Server 2003 machine connecting to a local MacroView Linux server. Typing in the IP address of the Linux server kept coming up with a “No such host is known” error when initiating the MacroView ADO.NET connection. The weird part was that other machines on the local network connected fine and running telnet with the relevant port number on the problem machine also connected successfully. My first thought was some sort of program specific firewall setting, but that turned out not to be the case.

To cut a long story short, the problem was related to a combination of migrating the MacroView ADO.NET code to Visual Studio 2005 and the behaviour of Dns.GetHostEntry when compared to Dns.Resolve. The ADO.NET provider was first developed using Visual Studio 2003 and .NET 1.1. At the time the Socket.Connect methods didn’t have an option to pass a host name as string to the connect call. The TcpClient class had this functionality, while the Socket class provided more control in other areas but would only take IPAddress objects in the Connect calls. The Dns.Resolve method was used to get an IPHostEntry which was then used to create an IPAddress to pass to the connect calls. This all worked fine.

When it came time to upgrade to Visual Studio 2005, the compiler complained that Dns.Resolve was obsolete and that Dns.GetHostEntry should be used. I made this change along with others to get the unit tests passing on .NET 2.0. The problem that was lurking under the covers was that when a numeric IP address was specified in the connection string, the Dns.GetHostEntry somehow figured out the actual destination machine name even though the machine in question could not resolve the host name if typed in explicitly. The Socket used this destination machine name and then failed the name to address resolution. I don’t know the exact details of what was going on under the covers, but the solution was simply to use the new .NET 2.0 Socket.Connect methods that take a string host name as a parameter.

 

Saturday, August 26, 2006

MacroView Studio VNC

I’m currently working on a VNC extension to the MacroView Studio functionality and its nearing completion – at least for this version anyway. Its implemented in C# from scratch which allows for a bit more control on the client side both from both a functionality and a commercial perspective. Here’s a few screenshots of the connection settings screen. It can connect to a running VNC server using the typical VNC ports (5901…) but its expected that most of the time users will leverage the MacroView metaserver functionality for ease of deployment and authentication. As long as the Xvnc executable is in the path when the metaserver process is started up, then metaserver will handle all of the VNC server settings requests and Xvnc startup. It leverages the Xvnc -inetd and other command line arguments to achieve this.

The ability to VNC into a server via the Studio environment is handy in that you can flip between graphics, scripts, reports and the remote system very easily – but the main purpose will be in a viewer that integrates the MacroView Unix/Linux graphics with a .NET client side reporting system plus programmable extensions. I’ll post more on the viewer as its developed.

VncConnectionGeneral VncConnectionDisplay VncConnectionPrograms

Here’s an example VNC session running in one tab with a MacroView report in another tab.

VncInStudio

The following links are to sites which make an Xvnc server available for the Solaris 8 and SCO Openserver platforms. Typically Linux distributions have a VNC server as one of the install options.

 

Monday, August 14, 2006

MVS Reports Script Tips

MacroView Studio is one of the software products developed and maintained by Torq Software. It includes the ability to edit MacroView graphic, metascript and report template files. This blog post relates to the reporting template functionality. The reporting functionality leverages the Active Reports developer product from Data Dynamics as its core reporting engine. As such this post also partially applies to other ActiveReports based applications in addition to MacroView Studio in particular. The reporting engine includes the ability to define a report class in script code. The report class methods will be called when particular report events occur and can be used to tailor reports in more ways than available using the standard GUI interface reporting engine functionality.

Here are the hints and tips:

  • MacroView Studio automatically adds all of the report sections and controls as fields to the report script class. This allows the script writer to easily get or set section and report control properties without a verbose syntax. The field name is take directly from the name given to the report section or control. The following report script sample illustrates the use of these automatically defined fields. Detail is a field that references the report Detail section whilst Rsh, Rsl, RshLabel, RslLabel, ScanFactor and ScanFactorLabel are all report controls whose property values and display state is modified by the script logic.

    public void Detail_BeforePrint()

    {

        string format = Format.Text;

        bool isImageFormat = false;

        bool needsRawScales = ( format != "INTEGER" && format != "FLOAT" );

     

        if ( format == "IMAGE" )

        {

            isImageFormat = true;

            Detail.BackColor = System.Drawing.Color.Bisque;

        }

        else if ( format == "INTEGER" )

            Detail.BackColor = System.Drawing.Color.SeaShell;

        else if ( format == "SCALE" )

            Detail.BackColor = System.Drawing.Color.LemonChiffon;

        else if ( format == "INTEGER" )

            Detail.BackColor = System.Drawing.Color.Honeydew;

        else if ( format == "DIGITAL" )

            Detail.BackColor = System.Drawing.Color.AliceBlue;

        else if ( format == "FLOAT" )

            Detail.BackColor = System.Drawing.Color.Khaki;

        else

            Detail.BackColor = System.Drawing.Color.White;

     

        Rsh.Visible = needsRawScales;

        Rsl.Visible = needsRawScales;

        RshLabel.Visible = needsRawScales;

        RslLabel.Visible = needsRawScales;

        ScanFactor.Visible = isImageFormat;

        ScanFactorLabel.Visible = isImageFormat;

    }       

  • There is currently no means of specifying a “using namespace;” statement within the report script. This is a limitation of the ActiveReports functionality. It means that arbitrary .NET class names or static members have to be referenced by their full name e.g. System.Drawing.Color.White.
  • MacroView Studio report script classes automatically have the Write and WriteLine methods defined. These take an object as an argument. The object is converted to a string via its ToString() method and the result is written to the report output log. The report output tab can be selected to view both server side metascript output and client side report script Write/WriteLine output. It provides a basic debug/tracing feature for MacroView reports.
  • The report field and control Value property provides access to the underlying value associated with the field or control. MacroView field values will be converted to string, decimal, DateTime or TimeSpan values in the reporting engine depending on the MacroView attribue value type. Decimal values can be cast to any of the numeric value types such as doubles for use in the report scripts.
  • The Format section event should be used to perform the majority of display format actions within a report section. The BeforePrint section event should only be used for control sizing actions as the section size has been fixed in the previous Format event processing. The AfterPrint section event is provided for ActiveReports backward compatibility and should not be used for MacroView reports.
  • Report scripts can easily get verbose and hence difficult to read and edit. Defining small report script functions to perform repetitive coding tasks can make your report script much more readable and maintainable. The following sample script functions provide examples of the types of repetitive tasks that can be done within a function rather than repetitively through your script code.

    // Gets a double precision numeric value for the specified field name.

    // Any data extraction exceptions are caught and a zero value returned.

    public double ToDouble( string fieldName )

    {

        try

        {

            Field field = rpt.Fields [fieldName];

            return (double) field.Value;

        }

        catch

        {

            return 0.0F;

        }

    }

     

    // Sets the value of the specified field.

    public void Set( string fieldName, object value )

    {

        Field field = rpt.Fields [fieldName];

        if ( field != null )

            field.Value = value;

        else

            WriteLine( "Failed to set field " + fieldName + " to " + value );

    }

     

    // Gets a double precision numeric value from the specified TextBox.

    private double ToDouble( TextBox textBox )

    {

        try

        {

            return (double) textBox.Value;

        }

        catch

        {

            return 0.0F;

        }

    }

  • It is often easiest to implement a report using calculated fields as a storage point for the results of script based calculations. To achieve this in the report script add the calculated fields within the DataInitialize event handler and  perform the calculations in the FetchData event handler.

    // Add any calculated fields not provided by the server query data in the DataInitialize

    // event handler function.

    public void ActiveReport_DataInitialize()

    {

        rpt.Fields.Add( "SourceType" );

    }

     

    // Perform the calculated field script logic in the FetchData event handler function.

    // Returning the eof parameter without modification at the end of this function

    // allows the server query result to be traversed as is whilst setting the

    // calculated field values.

    public bool ActiveReport_FetchData( bool eof )

    {

        string type = "PLC";

        if ( GetFieldString( "TYPE" ) == "IMAGE" )

            type = "IMAGE";

     

        SetFieldValue( "SourceType", type );

        return eof;

    }

     

    // Leverage small private functions to perform any repetive code actions and thus

    // keep the other code sections readable and maintainable.

    private string GetFieldString( string name )

    {

        Field field = rpt.Fields [name];

        if ( field != null )

            return field.Value.ToString();

        else

            return string.Empty;

    }

     

    private void SetFieldValue( string name, object value )

    {

        Field field = rpt.Fields [name];

        if ( field != null )

            field.Value = value;

        else

            WriteLine( "Field not defined: " + name );

    }

  • Field objects should only be referred to within the DataInitialize and FetchData event handlers to match the reporting model used by ActiveReports. Other event handlers can access the report control properties for use in script logic.
  • An alternative to using calculated fields on the client side is to create the calculated fields on the MacroView server side. Metascript table variables can be created in memory with the additional calculated fields which are then sent over to the client side for reporting. Scenario specific considerations affect whether a client side or server side approach should be taken e.g. metascript performance, report developers familiarity with C# and metascript, network throughput available between the server and client.

 

Saturday, August 05, 2006

Delegators mount up!

Following on from a previous post on event subscription related memory leaks, I’ve implemented a set of classes that provide Weak Delegate support. They worked quite well in practice and have the following features:

  • A WeakDelegate class that allows an object to subscribe to an event but not have the object publishing the event from having to be tied to subscriber objects from a garbage collector point of view.
  • A Delegator structure that is used to to define an event field in a publisher object. The Delegator structure provides a means for the developer to choose whether to use the weak delegate or standard event/delegate functionality at run time. The choice is made by setting the Delegator.UseWeakDelegatesByDefault static property to true instead of the default of false. The reason a choice between delegate approaches is useful is that the WeakDelegate implementation does impose a performance hit when compared to the standard event implementation. The performance hit is manageable for some scenarios, so it is quite reasonable to just run with the the WeakDelegate approach for deployment in selected applications.
  • The WeakDelegate class provides the ability for an application to be informed whenever an event subscriber has been garbage collected via the WeakDelegate.SubscriberGarbaged event. This provides a means for the developer to identify what parts of their code is missing an event unsubscription call.
  • The SubscriberGarbagedArgs information identifies the type of the target object that was garbage collected and which method would have been called on an event occurrence. It can also optionally provide the stack trace current at the time the target object subscribed to the event. The stack trace functionality is activated by setting WeakDelegate.TakeStackTraces to true. The ability to identify the original event subscription code is very useful in helping to work out where the unsubscription code should be.
  • The WeakDelegate class has static counter properties (TotalAddCount, TotalRemoveCount, TotalRaiseEventCount and TotalMethodCallEventCount) which assist in identifying how many event add/remove/invoke calls happen when running an application through a typical user scenario. This can help identify whether the WeakDelegate functionality can practically be used at runtime without degrading the effective application performance. The add/remove performance difference varies depending on the number of subscribers per event. Add/remove tests with an average of 10 subscribers have resulted in a 12x decrease in add/remove performance. Invoke tests have resulted in a 600x decrease in invoke performance. Reflection is used to perform the invoke actions because there is no way I have found of  replacing the target of a delegate once it has been created. These may seem to be such large factors that an initial conclusion is that its not worth using the WeakDelegate functionality because of the performance issues. In practice it really depends on the number of add/remove/invoke occurrences per unit of time in your application. It may be worth sacrificing some processor cycles for the WeakDelegate implementation if the end user doesn’t have to end up dealing with a memory leak. Its important to note that even if you aren’t willing to take the WeakDelegate performance hit, it is still very useful for identifying where event unscubscription is missing from your code or third party libraries.
  • Even if a target object within an event subscription is garbage collected, there will still be the “residue” of a WeakDelegate instance left in the event subscription list. These residue objects are automatically cleaned up the next time the event’s add, remove or invoke methods are called. This residue clean up is the main reason for the decrease in add/remove performance when comparing the standard event/delegate approach to the WeakDelegate implementation.

The sample source code for WeakDelegate functionality has been made available here.

It’s very easy to make an application event leverage the WeakDelegate functionality. An example implementation is shown below. Note that the standard delegate approach will be used by default. To turn on the WeakDelegate functionality, set WeakDelegate.UseWeakDelegatesByDefault  = true in your mainline. A useful approach is to only set UseWeakDelegatesByDefault  to true in Debug mode for development and then throw an exception if the WeakDelegate.SubscriberGarbaged event is fired. This will help identify scenarios where event unsubscriptions didn’t occur but they should have during development and unit tests.

private Delegator statusChanged;
 
public event EventHandler StatusChanged
{
    add { Delegator.Add( ref statusChanged, value ); }
    remove { Delegator.Remove( ref statusChanged, value ); }
}

The WeakDelegate functionality has helped resolve some memory leak issues with a Windows Forms application I am working on. The exercise has lead me to the opinion that I would actually prefer the default event behaviour to weakly reference target objects. If Microsoft could implement a better performing implementation based on access to the CLR innards and compiler modifications, it would allow application programmers to not have to worry about event unsubcription. The reasoning behind this is similar to the reasoning for why we use garbage collectors in general i.e. to let the developer have less to worry about and leverage the available processing power where it’s suitable. Some examples of the potential syntax for such functionality follows: 

// Made up syntax example 1.
public weakref event EventHandler StatusChanged;
 
// Made up syntax example 2.
[ WeakEventReference ]
public event EventHandler StatusChanged;