Sunday, March 20, 2011

[C#] - Debugging C# Applications - 3 : Tracing


1. Introduction


To Read the other topic below check the C# Label.
Part 1: Debugging Basic
Part 2: Debugging Windows
Part 3: Tracing [You are Here]

In this third and last part of the debugging, I will show you how do you get debugging information in the form trace. This will be helpful when the application deployed in the customer place. It is not possible to do the debugging, as we usually do not keep the source code in the deployed environment.

Dot net framework provided a facility to do the tracing on the client end and you can the information from them in the form of a flat text file or as an "Event Log" file. In this article, we will change the existing sample used in the previous two parts to enable the logging in the flat text file as well as in the windows event logger.

Below is the Screenshot of changed application. I will explain the code change when the article progresses.





2. Trace source, Trace switch, and Trace Listener


To understand this easily have a look at the below diagram:




The "Trace Source" is the place from where the information is pushed out. Usually, this is your application, which pumps the trace data for recording purpose. The framework has a class for this trace source and its member function will actually push the trace data.

"Trace Listeners" are recording media, which takes the pumped trace information and stores it. The listener can be a flat text file, an XML file, Console output window or a windows event logger. These listeners are actually attached to the Trace source. Also, note that multiple listeners can be attached to a single trace source.

"Trace Switch" is the intermediate common filter between all the listeners attached to single Trace source. So trace switch can perform the trace filter. A trace source can push a variety of trace data say Information messages, Critical messages, Error Message etc., So here you can set the switch to skip all the trace type except Critical messages.

There is one more filtering available in the listener itself. In our demo, we are going to use the filter in the trace switch level to reduce the length of this article. But, I will give a brief idea on how do you put second level filter also. This filter is specific for the individual listeners.

OK. Let us start to enables these traces in our sample that we used in the previous two parts of this debugging article.

3. Add required controls


Add two radio button and check boxes. In the above screenshot, it looks like a toggle button. To set the required properties for the controls open the attached sample and set the properties that show in bold in the property window.

OK. Let me start explaining the code that enables the tracing for our sample.  If you do not want the explanation you can directly go to the attached sample and start exploring the code.

Search Tag: //Trace 0

Before you move to the next section, have a brief look at the search result, which will give a high-level idea of what we are going to do.

4. Default Trace Listener


The default tracing takes the debugger output window as the trace listener. First, let me show you the trace output on the debugger output window. Then we will move to the text Log file and Window event logger.

1) First provide the required name space for using the Tracing.

//Trace 007: Include diagnosis Namespace
using System.Diagnostics;

2) Introduce member variables for Trace Source and Trace Switch

//Trace 008 : Declare the Trace Source, Trace Switch
TraceSource TrcSource;
SourceSwitch TrcSwitch;

3) Create the Trace Source and Trace Switch objects. Set no filtering in the Switch object using the property Level. Then attach the switch to the Trace Source. That’s all. What have we done? As already stated the default listener is debugger output window. The switch does not to any filtering is attached to the Trace Source which has the only listener as Output window. So all the Trace Message pumped will be displayed on the output window. Below is the code:

//Trace 009: Create the Trace Source,Switch,
TrcSource = new TraceSource("Trace Source");
TrcSwitch = new SourceSwitch("Trace Switch");

//Trace 010: Set the Switch Levels
TrcSwitch.Level = SourceLevels.All;

//Trace 011: Set the above-created switch to Trace Source
TrcSource.Switch = TrcSwitch;

4) Let us start pumping a debug message. The TraceInformation function will pump the messages to all listener attached to the source (Now it is only the output window). The sample pumps the Information message to output window when a prime number is detected. Below is the code:

//Trace 012: Write the Trace to the Output Window
String info = string.Format("Prime Number Detected: {0}", i);
TrcSource.TraceInformation(info);
TrcSource.Flush();

5) Now run the sample and provide 24 as the input, right click on the output window and clear all the existing texts. Then click Get prime sum button. Look at the output window to see the results. The screen shot is given below:



5. Text File Listener



When the toggle button File Log is clicked the text trace logging will be enabled. The text log enabled form is shown below:


When you click the same button again the log will be disabled. So we will create the text log listener and attach it to the Trace source when the check box is in the checked state (Here, toggled on) and remove the text log listener when the check box is in the un-checked state (Here, toggled off).

When the check box is checked, create a "TextWriterTraceListener" object by specifying the name (Useful when you are removing it) and the filename with path in which the pumped messages are written. Using the "TraceOutputOptions Property" set the time and thread are as required. Refer MSDN for more output options. Once the listener is ready add it to the trace source. At this stage, we have two listeners for the trace source. So the message pumped in the search tag Trace 012 will now go to C:\Trace.log in addition to the debugger output window.

if (chkLogFileTrace.CheckState  == CheckState.Checked)
{
    //Trace 013: Create text file Listener and set the output options
    TextWriterTraceListener txtListen;
    txtListen = new TextWriterTraceListener(@"C:\Trace.log", "Text Trace Listener");
    txtListen.TraceOutputOptions = TraceOptions.Timestamp | TraceOptions.ThreadId ;

    //Trace 014: Add the Listener to the TraceSource
    TrcSource.Listeners.Add(txtListen);
}

And remove the listener when the check box is in the unchecked state. Note that we referred the Listener name that we gave in the constructor when we are creating it. Below is the code:

else
{
    //Trace 015: When the Toggle is off, remove the listener
    TrcSource.Listeners.Remove("Text Trace Listener");
}

Run the Sample now to check you are getting the pumped message in both the listener when the File Log is toggled on.

6. Event log Listener


Event logger is the windows tool that hides the underlying file and shows you the log information in a UI. However, you can copy the content to a new file. Right, click on your “My computer” icon and select Manage. In the left pane expand the Event Viewer and click on the System node.  The right side pane now shows all the events logged. Double-clicking an event display the detailed information about the event. Below is the screen shot:




Now we will make our sample to write the debugging content here.

The code logic is similar to the one explained in the previous section. In place of creating TextWriterTraceListener we will create the EventLogListener. Below is the code:

if (chkEventLog.CheckState == CheckState.Checked)
{
    //Trace 016: Create the event log listener and add it to the Listener collection
    //of Trace Source.
    EventLogTraceListener evtListener;
    evtListener = new EventLogTraceListener("Event Log Listener");
    TrcSource.Listeners.Add(evtListener);
}
else
{
    //Trace 017: Remove the listener
    TrcSource.Listeners.Remove("Event Log Listener");
}

Test the sample by enabling the Event Log toggle now. Look at the application event log, as the log will not enter into system event log.

7. Set the Filters at the Source Switch


Do you remember? In the constructor, we asked the Trace Switch to allow all the information to the listeners attached to the Trace Source. Now by the radio button, we will do the filtering of the log data. The code is straight forward and below is the code:

private void RadError_CheckedChanged(object sender, EventArgs e)
{
    //Trace 018: Filter the Trace
    TrcSwitch.Level = SourceLevels.Error;
}

private void RadInfo_CheckedChanged(object sender, EventArgs e)
{
    //Trace 019: Filter the Trace
    TrcSwitch.Level = SourceLevels.Information;
}


Below is the screen shot was taken from the MSDN about the trace levels and what it filters.



All right. At present, we are writing only the information messages. How do I check the filters are working fine? Well. Add the below piece of code:

//Trace 016: Log the Trace Errors
TrcSource.TraceEvent(TraceEventType.Error, 3, "Invalid Number Specified");
TrcSource.Flush();

Note that this time we are not making a call to the Trace Source function TraceInformation. We are making a call to the TraceEvent function and setting the TraceEventType as Error. Once again refer the MSDN for other enumeration values. To test provide the text in the input and click the Get Prime Sum button.

To perform the Secondary level ( that is level2 ) filtering at the individual listener make use the filter property of the individual listener. The property takes same enumeration values shown above.

See you in the next article. Bye.

Note: The sample is created using the VS2005 IDE. If you have the latest one say yes to the conversion dialog.

Source Code : Download


[C#] - Debugging C# Applications - 2 : Debugging Windows


1. Introduction


In this second part, we will still use the same sample that we used in the previous part. Here, we will cover some other important windows that help the process of debugging.

You can read the series from the C# Label.

Part 1: Debugging Basic
Part 2: Debugging Windows [You are here]
Part 3: Trace Logging

The screen shot of the application is given below (Yes! I am using the Same sample from the previous part of debugging):



2. Call Stack


The call stack window helps to see the order in which function call occurred from the application startup to the place where you break the application. Before I explain more about it please follow the steps to bring the "Call Stack window".

1) Open the Attached application and put a breakpoint in the function IsPrime as shown below.



2) Run the Sample and enter the input 24 then press the button Get Prime Sum. This will get you to the break point.
3) Now bring the Call Stack Window by navigating Debug=>Window=>Call Stack. The call stack window is shown below.



In the above call stack window, the first line in the top shows that the application is paused in the function IsPrime by taking the input parameter theNumber with the value 3. Now, look at the second line. The function SumIfPrime receives the parameter theNumber with value 24 from the function btnGetResult_Click (Event handler right?) and it called the function IsPrime where we kept the break point to temporarily halt the application. You can also note the beginning of the application that is; program entry point Main().

You can easily move to the function shown in the call stack by double clicking the particular line. It will be useful when you are working on a big project where function call, as shown above, may happen between dll or from a different assembly.

3. Auto and Local Window


Before I explain about these windows, we will add a function so that you can know the difference between them. The function added to the form is shown below:

//006: Auto Locals
void AutoLocals()
{
    int a, b, c, d, e;
    a = 10;
    b = 20;
    c = 30;
    d = 40;
    e = 50;
}

And this function is called in the constructor:

//001 : Declarations
int PrimeSum = 0;

public frmDebug()
{
    InitializeComponent();
    AutoLocals();
}

Now put a breakpoint on the function in the statement b=20. Run the sample with the input number 24. Once the breakpoint is hit, open the "Auto and Local Windows". You can reach these windows by navigating Debug=>Windows. If it is not available to bring it from the tools=>customize option. It is explained in the first article to bring the step-in, step-out, step-over and the link is provided in the introduction section. The auto window is shown below:

 The breakpoint is at b=20, and it displays the variable that changed or involved in the previous statement plus the variable that involved in the execution of the current statement. Also, the changed variable value is shown in red color, which is not recognized in the first statement as I highlighted it (Oh! Man, my mistake). Now, step over the statement to move the control to the next statement say C=30 and see how the Autos window changes.

The local window shows the variable, which is local to the execution scope. Here, in our case, if the execution point is at d=40 for example, all the variables are displayed as they are local between the curly braces start and end. Below is screen shot of the locals window:



4. Output Window


The output window is useful to display the content written by the code. We should use System.diagnostic namespace to access the debug utility method. Write the following piece of code in the function SumIfPrime as shown below:

//004: When a passed in number is prime, sum it.
void SumIfPrime(int theNumber)
{
    for (int i = 1; i <= theNumber; i++)
    {
        if (IsPrime(i) == true)
        {
            txtOutput.AppendText(i.ToString() + ", ");
            PrimeSum = PrimeSum + i;
         System.Diagnostics.Debug.WriteLine( string.Format("Prime Detected {0}", i));
        }
    }
}

Now, the above code will write the string specified in the output window. Just, image how do you write something on your console output window. It is similar to that but what you write is displayed on the output window. Remove all the breakpoints and run the sample with the input 24 again and look at the output window. Before you click the button on the form, right-click the output window and select clear all from the context menu. This will help you to see what you wrote in a clean way. If output window is not displayed bring it from Debug=>Windows or from the customize option. The output window content after running the application is shown below:




5. Immediate window


When output window allows you to print something on it through the utility function supplied by the dot net framework, the immediate window allows examining the execute expressions. Put a break point in the statement e=50 in the autolocals function. Run the sample attached with the input value 24. Once the breakpoint hit, bring the output window.

Now write the following in the immediate window and hit enter: ?c
The above command retrieves the value of c and displays it on the next line. Looks simple and you are wondering what is there in it? I can simply hover my mouse on the variable c to see what value it has. To know the real use, type the following and hit enter and I think justification on why the immediate window is useful not required. I used it often when framing a complex string manipulation statements. It is painful to run the application again and again to make perfect string manipulation expressions. Use it if you are new to it and you will become addicted to this window.

?(a*a) + (2*a*b) + (b*b)
900

The below screen shot shows immediate window in action:



Note: The sample is created in VS2005. If you have the latest IDE, Say yes to the conversion UI displayed.

Source Code : DownLoad

Sunday, March 06, 2011

[ Dot Net Remoting ] - Singleton Generic Interface, Server activated

1. Introduction


In the previous articles, you saw Singlecall remote object and Singleton remote object. In this article I will show you the usage of Generics in the remote object and how the server will register it and how the client will consume it. For the previous articles, select the remoting section from the sidebar and navigate. You will the good articles on this topic from other authors also.

Let us first begin with a server. I suggest, you can first read the basic article from the remoting label. It will be easy to understand this article once you know the basics.

Search Tags: Search the below tags in the downloaded application to know the Sequence of code changes.

//Server 0
//Client 0

2. The Generic Interface


Start a Visual C# console project called GenRemSrv. Once the project is started, add a generic interface. Our remote object will implement this interface. Below is the code:

//Server 001: Generic Interface which has only one method takes and
//generic type and return same kind of generic type
public interface IGenericIface<T>
{
    void AddData(T Data);
    string GetData();
}

Note the usage of the Letter T. It indicates that the function accepts any data type. In our example, we are going to use this interface for int as well as string data types.

3. Remote Class using Generic Interface


Add a new class to the GenRemSrv Project and Name it InputKeeper<T>. Here, once again the T stands for some data type.  Also, note how the generic interface is inherited here by specifying the T substitution. Below is the code:

//Server 002: Public class that implements MarshalbyRef and the Generic interface
public class InputKeeper<T> : MarshalByRefObject, IGenericIface<int> ,
    IGenericIface<string>
{

Next the constructor and the variables required for this coded. Below is the code for that:

//Server 003: Variable declaration
int CollectedInt;
string CollectedString;

//Server 004: Constructor
public InputKeeper()
{
    CollectedInt = 0;
    CollectedString = "";
    System.Console.WriteLine("Input Keeper Constructoed");
}

Finally, the interface functions are implemented as shown below:

//Server 005: Implement the Interface Function
public void AddData(int Data)
{
    CollectedInt = CollectedInt + Data;
}

public void AddData(string Data)
{
    CollectedString = CollectedString + ", " + Data;
}

public string GetData()
{
    string str = String.Format("Collected integer sum {0},"  +
    "Collected String : {1}", CollectedInt, CollectedString);
    return str;           
}

In the above code, not that the Adddata function is implemented twice one using the int data type and another one is using the string data type. As we derived the class from the generic interface that supports both the data types int and string (IGenericIface<int>IGenericIface<string>) it becomes necessary to implement the interface generic function void AddData(T Data); twice by substituting the required data types.

4. Hosting the remote objects


I hope you read my first article. I am not going to explain everything, which I already explained in the first article on remoting. As our remote object itself a generic object (InputKeeper<T>), we need to register the object resolving the type T. In our case, we are using two different types integer and string and so we need two registrations. The below code register the InputKeeper for both the data types on the TCP channel identified by the port 14750.

//Server 007: Register the TCP channel with port number 14750
TcpServerChannel port = new TcpServerChannel(14750);
ChannelServices.RegisterChannel(port, false);

//Server 008: Register the remote objects. Note the generic is split based
//            on the type usage
RemotingConfiguration.RegisterWellKnownServiceType(typeof(InputKeeper<int>),
    "IntKeeper", WellKnownObjectMode.Singleton );
RemotingConfiguration.RegisterWellKnownServiceType(typeof(InputKeeper<string>),
    "StringKeeper", WellKnownObjectMode.Singleton);

//Server 009: Some indication that server is started
Console.WriteLine("Server Started. Int Keeper and String Keepers are ready..");
Console.WriteLine("Press Any key to Halt the Server");
Console.ReadLine();

5. The client application


Add a new visual C# windows application and Name the project as Generic User. Use the File, add new project without closing the server project so that both the project is available for a solution. The form design is shown below:

Generic Interface - Example
Fig 1. Generic Interface - Example


The first send button will contact the generic remote object for integer and second send button will contact the generic remote object for the string. The data collected so for will be displayed on the multi-select list box. Also, note that we have two generic objects on the server’s remote pool and they are independent. Enter some integer on the left group box and click the send button and type some other integer click the send button again. Do the same for the string also. This is just for the test and data is persisted on each object in their contexts we registered the object as a singleton.

Provide the reference for Remoting runtime and the server project as we already did in our first dot net remoting project. The reference to that article is given in the introduction section.

1) Include the following Namespace for the form code:

//Client 001: Namespace inclution
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using GenRemSrv;

2) Client declared generic interfaces. One interface is for integer and the other one is a string. The integer interface is used by the left send button click and right send button will use string interface.

//Client 002: Object declaration using the generic types
private IGenericIface<int> intObject;
private IGenericIface<string> stringObject;

3) In the form load event handler, after registering the communication channel, the remote objects are retrieved (Proxy) and stored in the interface class members declared in the previous step.

//Client 003: Register the Communication channel
ChannelServices.RegisterChannel(new TcpClientChannel(), false);

//Client 004: Get the generic remote object of int type and string type
intObject = (IGenericIface<int>) Activator.GetObject(typeof(IGenericIface<int>),
    "Tcp://localhost:14750/IntKeeper");
stringObject = (IGenericIface<string>) Activator.GetObject(typeof(IGenericIface<string>),
    "Tcp://localhost:14750/StringKeeper");

4) The left and right send button will make a call to the relevant remote object (Generic object). Remember, the function exposed by our remote generic interface is AddData and GetData. The AddData for integer simply performs the summation of supplied integer and AddData for the string will simply append the given string input. Note that the proper simplicity I collected the data in string and integer variables in the server. Collecting the input in a variable of Type T should do the proper implementation.  For simplicity, I used CollectedInt and CollectedString. However, I hope this will explain how to use generic remote objects in remoting environment. Below is the code for the Send button click handlers:

//Client 005: Parse the data entered in the integer input and send it to the remote object
private void btnSend1_Click(object sender, EventArgs e)
{
    int input;
    if (int.TryParse(txtInt.Text, out input) == true)
    {
        intObject.AddData(input);
    }

    txtOutput.Text = intObject.GetData();
}

//Client 006: send the string data to the remote object
private void btnSend2_Click(object sender, EventArgs e)
{
    stringObject.AddData(txtString.Text);
    txtOutput.Text = stringObject.GetData();
}

Running the application is shown below:

 Running the application
Fig 2. Running the application


Source Code : Download

Note: The attached solution is created in VS2005. If you have the latest version say yes to the conversion UI displayed.

Like this site? Tell it to your Firend :)