Microsoft Agent; Providing a Custom Popup Menu

هذه المقالة متوفرة أيضا باللغة العربية، اقرأها هنا.

A second honeymoon with Microsoft Agent. Do you remember our article Programming Microsoft Agent in WinForms and our sample application PartIt? After you have included your desired agent in your application, are you feeling bad with the default popup menu? If so, then you are in the right place (welcome :).)

Enough talking, let’s get to work! First, prepare your code that loads the Microsoft Agent and brings it to the screen.

After that, create your System.Windows.Forms.ContextMenuStrip and add your required items (well, including ‘Hide’ maybe) and finish the item event handlers.

Now, let’s complete it. Get to the code that loads the agent character (e.g. calls the Characters.Load() method of the agent control object, AxAgentObjects.AxAgent) and just disable the AutoPopupMenu flag/property of the character object, AgentObjects.IAgentCtlCharacterEx. This flag/property determines whether to allow the default popup menu or not.

For example, the following code disables this property:

    AxAgentObjects.AxAgent agentCtl;
    AgentObjects.IAgentCtlCharacterEx agentChar;

    // initializing 'agentCtl'
    // . . .

    agentCtl.Characters.Load("Merlin", "merlin.acs");
    agentChar = agentCtl.Characters.Character("Merlin");
    agentChar.AutoPopupMenu = false;

Next comes the interesting point. When the character is clicked, the ClickEvent event of the agent control (AxAgent) fires. So the next step is to handle this event and to provide your code that brings up your custom context menu. Consider the following code:

// agentCtl.ClickEvent += agent_ClickEvent;

public void agentCtl_ClickEvent(object sender, AxAgentObjects._AgentEvents_ClickEvent e)
{
    if (e.characterID == "Merlin")  // check for this if you have many characters
    {
        if (e.button == 2) // 1 = left, 2 = right
        {
            myContextMenu.Show(e.x, e.y);
        }
    }
}

Well done!

Have a nice Sunday!

9 Rules about Constructors, Destructors, and Finalizers

هذه المقالة متوفرة أيضا باللغة العربية، اقرأها هنا.

Overview

First, this writing concentrates of and compares between three programming languages, C#, C++/CLI, and ISO/ANSI C++. It discusses 9 rules that every developer should keep in mind while working with constructors, destructors, and finalizers and class hierarchies:

  • Rule #1: Contrsuctors are called in descending order
  • Rule #2: In C# lexicology, a destructor and a finalizer refer to the same thing
  • Rule #3: Destructors are called in ascending order
  • Rule #4: Finalizers are a feature of GC-managed objects only
  • Rule #5: You cannot determine when finalizers would be called
  • Rule #6: C++/CLI differs between destructors and finalizers
  • Rule #7: In C++/CLI and classic C++, you can determine when destructors are called
  • Rule #8: In C++/CLI, destructors and finalizers are not called together
  • Rule #9: Beware of virtual functions in constructors

Rule #1: Constructors are called in descending order

Rule #1: Constructors are called in descending order; starting from the root class and stepping down through the tree to reach the last leaf object that you need to instantiate. Applies to C#, C++/CLI, and ANSI C++.

Let’s consider a simple class hierarchy like this:

    class BaseClass
    {
        public BaseClass()
        {
            Console.WriteLine("ctor of BaseClass");
        }
    }

    class DerivedClass : BaseClass
    {
        public DerivedClass()
        {
            Console.WriteLine("ctor of DerivedClass");
        }
    }

    class ChildClass : DerivedClass
    {
        public ChildClass()
        {
            Console.WriteLine("ctor of ChildClass");
        }
    }

ChildClass inherits from DerivedClass, and DerivedClass, in turn, inherits from BaseClass.

When we create a new instance of ChildClass using a simple line like this:

    static void Main()
    {
        ChildClass cls = new ChildClass();
    }

the code outputs the following results:

ctor of BaseClass
ctor of DerivedClass
ctor of ChildClass

Rule #2: In C# lexicology, a destructor and a finalizer refer to the same thing

Rule #2: In C# lexicology, a destructor and a finalizer refer to the same thing; the function called before the object is fully-removed from the memory (i.e. GC-collected). Applies to C# only.

Let’s consider the same class hierarchy but with destructors:

    class BaseClass
    {
        public ~BaseClass()
        {
            Console.WriteLine("dtor of BaseClass");
        }
    }

    class DerivedClass : BaseClass
    {
        public ~DerivedClass()
        {
            Console.WriteLine("dtor of DerivedClass");
        }
    }

    class ChildClass : DerivedClass
    {
        public ~ChildClass()
        {
            Console.WriteLine("dtor of ChildClass");
        }
    }

When you define a class destructor with that C++-alike syntax (preceding the function name with a ~) the compiler actually replaces your destructor with an override of the virtual Object.Finalize() function. That is, before the object is removed (i.e. GC-collected) from the memory, the finalizer (i.e. destructor) is called first. This finalizer first executes your code. After that it calls the finalizer of the base type of your object. If we could decompile our assembly, we would see that our destructor in the ChildClass (so other classes) has been replaced with this function:

        protected virtual void Finalize()
        {
            try
            {
                Console.WriteLine("dtor of ChildClass");
            }
            finally
            {
                base.Finalize();
            }
        }

Rule #3: Destructors are called in ascending order

Rule #3: Destructors are called in ascending order, starting from the leaf object that you need to instantiate and moving up through the tree to reach the very first base class of your object. In reverse of constructors calling order. Applies to C#, C++/CLI, and ANSI C++.

Now, instantiate your class:

    static void Main()
    {
        ChildClass cls = new ChildClass();

        // 'cls' is removed from memory here
    }

the code should outputs the following results:

dtor of ChildClass
dtor of DerivedClass
dtor of BaseClass

Rule #4: Finalizers are a feature of GC-managed objects

Rule #4: Finalizers are a feature of GC managed objects (i.e. managed classes). That’s because the finalizer is called only when the GC removes the object from the memory (i.e. frees memory associated with).

Now, try to create a simple structure with a destructor:

    struct MyStruct
    {
        ~MyStruct()
        {
            Console.WriteLine("dtor of MyStruct");
        }
    }

The code won’t compile. That’s because that GC doesn’t handle structures.

Rule #5: You can’t determine when finalizers would be called

That’s because you don’t know when the next garbage collection would occur, even if you performed a manual garbage collection (using System.GC.Collect() function) you won’t know exactly when memory would be released. In addition, GC always delay releasing of finalizable object, it puts them in a special GC queue called freachable (pronounced ef-reachable, F stands for Finalize) queue. Applies to C# and C++/CLI (.NET.)

Rule #6: C++/CLI differs between destructors and finalizers

Rule #6: C++/CLI differs between destructors and finalizers. That is, finalizers are called by GC, and destructors are called when you manually delete the object.

Let’s consider the same example but in C++/CLI:

ref class BaseClass
{
public:
	BaseClass()
	{
		Console::WriteLine("ctor of BaseClass");
	}

	~BaseClass()
	{
		Console::WriteLine("dtor of BaseClass");
		GC::ReRegisterForFinalize(this);
	}
};

ref class DerivedClass : BaseClass
{
public:
	DerivedClass()
	{
		Console::WriteLine("ctor of DerivedClass");
	}

	~DerivedClass()
	{
		Console::WriteLine("dtor of DerivedClass");
		GC::ReRegisterForFinalize(this);
	}
};

ref class ChildClass : DerivedClass
{
public:
	ChildClass()
	{
		Console::WriteLine("ctor of ChildClass");
	}

	~ChildClass()
	{
		Console::WriteLine("dtor of ChildClass");
		GC::ReRegisterForFinalize(this);
	}
};

When we run the code:

int main()
{
	ChildClass^ cls = gcnew ChildClass();
}

it outputs the following results:

ctor of BaseClass
ctor of DerivedClass
ctor of ChildClass

The destructors are not called. Why? Unlike C#, in C++/CLI there is a big difference between destructors and finalizers. As you know, the finalizer is called when the GC removes the object from the memory. Destructors, on the other hand, are called when you destroy the object yourself (e.g. use the delete keyword.)

Now, try to change the test code to the following:

int main()
{
	ChildClass^ cls = gcnew ChildClass();
	delete cls;
}

Run the code. Now, destructors are called.

Next, let’s add finalizers to our objects. The code should be like the following:

ref class BaseClass
{
public:
	BaseClass()
	{
		Console::WriteLine("ctor of BaseClass");
	}

	~BaseClass()
	{
		Console::WriteLine("dtor of BaseClass");
		GC::ReRegisterForFinalize(this);
	}
	!BaseClass()
	{
		Console::WriteLine("finz of BaseClass");
	}
};

ref class DerivedClass : BaseClass
{
public:
	DerivedClass()
	{
		Console::WriteLine("ctor of DerivedClass");
	}

	~DerivedClass()
	{
		Console::WriteLine("dtor of DerivedClass");
		GC::ReRegisterForFinalize(this);
	}
	!DerivedClass()
	{
		Console::WriteLine("finz of DerivedClass");
	}
};

ref class ChildClass : DerivedClass
{
public:
	ChildClass()
	{
		Console::WriteLine("ctor of ChildClass");
	}

	~ChildClass()
	{
		Console::WriteLine("dtor of ChildClass");
		GC::ReRegisterForFinalize(this);
	}
	!ChildClass()
	{
		Console::WriteLine("finz of ChildClass");

	}
};

As you see, the syntax of constructors, destructors, and finalizers are very similar.

Now, let’s try the code:

int main()
{
	ChildClass^ cls = gcnew ChildClass();
}

GC would call finalizers and the code would outputs the following:

ctor of BaseClass
ctor of DerivedClass
ctor of ChildClass
finz of ChildClass
finz of DerivedClass
finz of BaseClass

Rule #7: In C++/CLI and C++, you can determine when destructors are called

Now, try to destroy the object yourself:

int main()
{
	ChildClass^ cls = gcnew ChildClass();
	delete cls;
}

The delete statement calls object destructors and removes the object from memory.

Or else, declare the object with stack-semantics:

int main()
{
	ChildClass cls;
}

Now, destructors are called when the scope of the object ends.

Rule #8: In C++/CLI, destructors and finalizers are not called together

Rule #8: In C++/CLI, destructors and finalizers are not called together. Only destructors or finalizers are called. If you manually delete the object or you declare it with stack-semantics, destructors are called. If you leaved the object for GC to handle, finalizers are called.

Now try to run the code. The code should outputs the following results:

ctor of BaseClass
ctor of DerivedClass
ctor of ChildClass
dtor of ChildClass
dtor of DerivedClass
dtor of BaseClass

Rule #9: Beware of virtual functions in constructors

Rule #9: Beware of virtual (overridable) functions in constructors. In .NET (C# and C++/CLI,) the overload of the most derived object (the object to be instantiated) is called. In traditional C++ (ISO/ANSI C++,) the overload of the current object constructed is called.

Let’s update our C# example:

class BaseClass
{
    public BaseClass()
    {
        Foo();
    }

    public virtual void Foo()
    {
        Console.WriteLine("Foo() of BaseClass");
    }
}

class DerivedClass : BaseClass
{
    public DerivedClass()
    {
    }

    public override void Foo()
    {
        Console.WriteLine("Foo() of DerivedClass");
    }
}

class ChildClass : DerivedClass
{
    public ChildClass()
    {
    }

    public override void Foo()
    {
        Console.WriteLine("Foo() of ChildClass");
    }
}

When you execute the code:

    static void Main()
    {
        ChildClass cls = new ChildClass();
    }

you would get the following results:

Foo() of ChildClass

The same code in C++/CLI:

ref class BaseClass
{
public:
	BaseClass()
	{
		Foo();
	}

	virtual void Foo()
	{
		Console::WriteLine("Foo() of BaseClass");
	}
};

ref class DerivedClass : BaseClass
{
public:
	DerivedClass()
	{
	}

	virtual void Foo() override
	{
		Console::WriteLine("Foo() of DerivedClass");
	}
};

ref class ChildClass : DerivedClass
{
public:
	ChildClass()
	{
	}

	virtual void Foo() override
	{
		Console::WriteLine("Foo() of ChildClass");
	}
};

The code outputs the same results.

But what if you need to call the virtual function of the BaseClass? Just change the code to the following:

ref class BaseClass
{
public:
	BaseClass()
	{
		BaseClass::Foo();
	}

	virtual void Foo()
	{
		Console::WriteLine("Foo() of BaseClass");
	}
};

Now, the code outputs:

Foo() of BaseClass

Let’s consider the same example but in classic ISO/ANSI C++:

class CBaseClass
{
public:
	CBaseClass()
	{
		Foo();
	}
	virtual void Foo()
	{
		cout << "Foo() of CBaseClass" << endl;
	}
};

class CDerivedClass : CBaseClass
{
public:
	CDerivedClass()
	{
	}

	virtual void Foo() override
	{
		cout << "Foo() of CDerivedClass" << endl;
	}
};

class CChildClass : CDerivedClass
{
public:
	CChildClass()
	{
	}

	virtual void Foo() override
	{
		cout << "Foo() of CChildClass" << endl;
	}
};

Now, run the code. It should outputs:

Foo() of BaseClass

In classic C++, the overload of the function of the class being constructed is called unlike C# and C++/CLI (.NET in general.)

Video: What’s New in C# 2/3/4

هذه المقالة متوفرة أيضا باللغة العربية، اقرأها هنا.

Here’s the complete “What’s New in C#” webcast series of Bruce Kyle (ISV Architect Evangelist of Microsoft) from Channel 9 blog:

What’s new in C# 2:

We will try to update this list as soon as new items release.

Creating a Simple Twitter Client Application

هذه المقالة متوفرة أيضا باللغة العربية، اقرأها هنا.

Code: Geming.twitoo.msi

Contents

Contents of this writing:

  • Contents
  • Overview
  • Introduction
  • Twitter API
    • What Twitter API is
    • API Documentation
    • Things to be Kept into mind
  • API Methods
    • Method Address
    • Methods with Arguments
    • Methods Require Authentication
  • Twitter and .NET
    • Accessing the API
    • Authentication
    • Encoding URIs
    • Business Objects
    • Retrieving Data
  • twittoo; Sample Application
    • Snapshots
    • Description
    • Interface
    • URL Shortening
    • Download

Overview

This writing discusses the Twitter API and how you can utilize it in your managed application. It begins by a brief discussion of the API and the methods used. After that, it digs into the discussion of how you can utilize the API into your application with help of code samples and examples. At the end of this writing, there’s a nice open-source Twitter client application available for download. Worth mentioning that this article focuses on the REST API set of Twitter, specifically XML endpoints.)

Introduction

Today, we are talking about Twitter as it is one of the most famous social networking services. This article discusses how you can create Twitter clients using .NET Framework. Oh, before we start, I’d like to introduce you my twitter account first; it’s @elsheimy.

Twitter API

What Twitter API is

Before we dig into the discussion of Twitter API we need to know first what the Twitter API is.

Twitter API is a set of endpoints (or methods) that allow applications to work with Twitter data.

Twitter API consists of three parts:

  • Two REST APIs:
    • REST API:
      This is the core API set; it allows developers to access core Twitter data, it contains most (if not all) of the methods and functions you would use to utilize Twitter data in your application, and it supports (mostly) three formats (or endpoints) for each method: XML, Atom, and JSON formats.
    • Search API:
      Fully integrating search methods allows you to search for tweets based on specific criteria and to search for daily/weekly hot trends. It supports only the JSON format. This set of API was originally developed by Summize, Inc. and integrated into the Twitter API. (Twitter is looking forward to unify the REST API.)
  • Streaming API:
    A set of API methods that allow near-realtime access to various subsets of Twitter public statuses. All methods are in JSON format, no XML or ATOM endpoints available.

Actually, the REST API would be very sufficient for you unless you find it easier to handle the JSON format.

It is worth mentioning that the REST API provides very handful and sufficient search methods that your application can use. If you need more searching capabilities (like searching for tweets that confirm to a set of criteria,) you can dig into the REST Search API.

You are not required to stick to a specific set of API methods. You can mix the methods you like from the three APIs.

Wikipedia: REST (Representational State Transfer) is a style of software architecture for distributed hypermedia systems such as the World Wide Web.

Wikipedia: JSON (JavaScript Object Notation) is a lightweight text-based open standard designed for human-readable data interchange based on the JavaScript programming language.

Wikipedia: Atom Publishing Protocol (AtomPub or APP) is a simple HTTP-based protocol for creating and updating web resources.

This writing focuses on the XML endpoints of the REST API.

API Documentation

Twitter has with a very nice API documentation that documents each and all methods and endpoints of the three parts of the API. This documentation can be found here: http://apiwiki.twitter.com.

To avoid duplication, we are not going to discuss the Twitter API again in this writing; it is already documented and available for all users. Instead, we will take a brief look at the API and discuss how we can utilize it in our .NET application. For simplicity, we will focus on XML endpoints. Therefore, all of our code would rely on the XML features of .NET Framework 2.0 (i.e. System.Xml.dll library.) Oh, you are free to write the code the way you like (e.g. integrating LINQ for XML into the code.)

Now, we are going to look inside the Twitter API.

Things to be Kept into Mind

There are some basics of the Twitter API that a developer should keep into his mind.

  • Calls to the Twitter API are limited:
    Don’t expect to post unlimited updates or to follow thousands of users in just an hour! Many methods have a call limit (hourly/daily.) For example, at the time of this writing, you have only 150 API requests per hour, further requests would return an exception. In addition, you can send only 1,000 updates and 250 direct messages per day (can we remove this word ‘only€?) You can check Twitter limits here.
  • Every endpoint has its HTTP methods that need to be set (e.g. GET, POST, and DELETE) in the request. The documentation of each endpoint lists the HTTP methods required for that endpoint. For more information about HTTP and HTTP methods check the RFC 2616 Section 9 document.
  • Some methods (like methods send updates) require user authentication and others not. Authentication is discussed soon.

API Methods

The following is a discussion about Twitter API methods and how you can call them.

Method Address

Every method has an address (i.e. URI). The address of a method is something like that: http://api.twitter.com/1/statuses/public_timeline.format (for the statuses/public timeline method.) We replace the format field with the format we like to work with (e.g. xml, atom, json.)

Now, try to copy the method address http://api.twitter.com/1/statuses/public_timeline.xml and paste it into your browser. You should get something like that:


    
        Wed Apr 14 19:32:07 +0000 2010
        12179999634
        35 Ingenious Examples of Footwear - http://su.pr/1JxMAr
        <a href="http://apiwiki.twitter.com/" rel="nofollow">API</a>
        false
        
        
        false
        
        
            15736190
            Smashing Magazine
            smashingmag
            Freiburg, Germany
            Vitaly Friedman, editor-in-chief of SmashingMagazine.com ...
            http://a3.twimg.com/profile_images/572829723/...
            http://www.smashingmagazine.com
            false
            166019
            B2DFDA
            333333
            f51616
            ffffff
            eeeeee
            394
            Tue Aug 05 14:00:40 +0000 2008
            4
            -10800
            <time>Greenland</time>
            http://s.twimg.com/a/...
            true
            false
            true
            false
            false
            8703
            en
            false
        
        
        
        
        
    
    . . .

This method returns the 20 most recent statuses from arbitrarily-selected non-protected Twitter users; this list is called the Public Timeline. Because we have selected the XML format (or endpoint) we end up with XML data.

From the data returned we can extract the structure of statuses and users. Twitter API is good enough; it uses the same structure for all data returned. In other words, all methods (in the REST API, remember?) use the same structure (i.e. schema) to represent status and user objects. Those structures are covered soon.

Methods with Arguments

Most of methods accept arguments, some are optional and others are required. An example of a function requires a single argument is the users/show method that returns profile information about a specific user.

This function accepts one of three arguments:

  • id:
    The ID or screen name of the user.
  • user_id:
    The ID of the user.
  • screen_name:
    The screen name of the user.

We set arguments the same way we set web pages query strings. Considering my twitter account @elsheimy as an example, we could query user profile using one of four ways:

All of the four calls return the same results that should be like this:


    19645411
    Mohammad Elsheimy
    Elsheimy
    KB, Egypt
    Technology evangelist from Egypt born in 1991
    http://a1.twimg.com/profile_images/833061500/...
    http://justlikeamagic.com
    false
    278
    DBE9ED
    333333
    CC3366
    E6F6F9
    DBE9ED
    179
    Wed Jan 28 10:47:36 +0000 2009
    7
    7200
    <time>Cairo</time>
    http://s.twimg.com/a/1271811071/...if
    false
    
    true
    false
    
    1877
    en
    false
    
        Wed Apr 21 16:21:46 +0000 2010
        12585240751
        I'm reading this, it's really hot: #RFC 2616 - Hypertext Transfer Protocol ...
        <a href="http://bit.ly" rel="nofollow">bit.ly</a>
        false
        
        
        false
        
        
        
        
        
    

As you can see, it returns user information and the last update of that user. Notice that the structure of the data is the same in all methods.

Methods Require Authentication

Some methods require user authentication. Examples are functions update status, send direct messages, retrieve friends’ timeline, etc.

Let’s take an example. Bring up your browser and browse to the address of statuses/friends timeline method that returns user’s friends’ timeline, http://api.twitter.com/1/statuses/friends_timeline.xml. A small window like this should appear that asks you your Twitter username and password.

Figure 1. IE Authentication

Provide your Twitter username and password to pass to the results. If authentication was OK, you should receive the most recent 20 statuses of your friend timeline in the same structure (schema) as the Public Timeline method. On the other hand, if the authentication process failed, you should receive an error:


    /1/statuses/friends_timeline.xml
    Could not authenticate you.

Let’s consider another example, the direct_messages/new method. This function sends a new direct message to a specific user from the current user authenticated.

This function accepts two required arguments:

  • user:
    The ID or screen name of the recipient user. You can use one of two arguments in place of this argument:

    • screen_name:
      Screen name of the user.
    • user_id:
      The ID of the user.
  • text:
    Message body, not longer than 140 UTF-8-encoded characters.

The following two calls send the same message (‘hi, how is it going€) to the same user:

Notice that we need to encode arguments before we pass them to the server.

Because this function updates data, it requires HTTP POST method. Therefore, you won’t be able to try it from your browser unless your browser allows you to set HTTP methods in the request.

If the function succeeded, it should return the message object that has been sent, and it should be like this:


    88619848
    1401881
    all your bases are belong to us.
    7004322
    Wed Apr 08 20:30:04 +0000 2009
    dougw
    igudo
    
        1401881
        Doug Williams
        dougw
        San Francisco, CA
        Twitter API Support. Internet, greed, users, dougw ...
        http://s3.amazonaws.com/twitter_production/...
        http://www.igudo.com
        false
        1036
        9ae4e8
        000000
        0000ff
        e0ff92
        87bc44
        290
        Sun Mar 18 06:42:26 +0000 2007
        0
        -18000
        <time>Eastern Time (US &amp; Canada)</time>
        http://s3.amazonaws.com/...
        false
        3394
        false
        false
        true
    
    
        7004322
        Doug Williams
        igudo
        North Carolina
        A character.
        http://s3.amazonaws.com/...
        http://www.igudo.com
        false
        19
        69A1AA
        000000
        F00
        ACBEC1
        8A8F85
        3
        Thu Jun 21 21:16:21 +0000 2007
        0
        -18000
        <time>Eastern Time (US &amp; Canada)</time>
        http://static.twitter.com/...
        false
        382
        false
        true
        true
    

Notice that the sender and recipient are just user objects. It is worth mentioning that the direct_messages function return a list of direct_message objects (like the timeline functions that return list of status objects.)

Twitter and .NET

Now, you have a solid understanding of the Twitter API and how you can access it. Now, let’s utilize this API into our .NET applications.

Accessing the API

To access the API from your .NET application you need to create a HTTP request and send it to the server and wait for server response. Let’s consider an example. The following code snippet connects to the server and retrieves a System.Xml.XmlDocument that contains the returned data.

// C# Code

public static Main()
{
    GetResponse("http://api.twitter.com/1/statuses/public_timeline.xml");
}

public static XmlDocument GetResponse(string uri)
{
    WebRequest req = WebRequest.Create(new Uri(uri));

    XmlDocument doc = new XmlDocument();
    doc.Load(req.GetResponse().GetResponseStream());
    return doc;
}
' VB.NET Code

Sub Main()
    GetResponse("http://api.twitter.com/1/statuses/public_timeline.xml")
End Sub

Public Function GetResponse(ByVal uri As String) As XmlDocument

    Dim req As WebRequest = WebRequest.Create(New Uri(uri))

    Dim doc As New XmlDocument()
    doc.Load(req.GetResponse().GetResponseStream())

    Return doc
End Function

We have used the System.Xml.WebRequest class to create the request and to get the response (as an instance of System.Xml.WebResponse class) from the server. Once we get the XmlDocument, we can walk through the data and retrieve it.

Authentication

You can take one of two approaches to authenticate Twitter users:

  • OAuth Authentication:
    An authentication protocol that allows users to approve application to act on their behalf without sharing their password. As this function requires more overhead and requires your application to be registered in the Twitter clients’ directory, we would better use the second approach in our examples.
  • Basic Authentication:
    To provide authentication information in each request you make to the server. Unfortunately, Twitter announced that this method will not be available later.

Considering a method like statuses/update method that updates the status information of the user (i.e. sends a tweet) we would develop our previous code to be like this:

// C# Code

public static void Main()
{
    GetResponse("http://api.twitter.com/1/statuses/update.xml?status=hello%20from%20the%20API",
        "elsheimy", "b@zzword", true);
}

public static XmlDocument GetResponse(string uri, string username, string password, bool post)
{
    WebRequest req = WebRequest.Create(new Uri(uri));
    if (post)
        req.Method = "POST";
    if ((username != null) && (username.Trim() != String.Empty) && (!String.IsNullOrEmpty(password)))
        req.Credentials = new NetworkCredential(username.Trim(), password);

    XmlDocument doc = new XmlDocument();
    doc.Load(req.GetResponse().GetResponseStream());
    return doc;
}
' VB.NET Code

Sub Main()
    GetResponse("http://api.twitter.com/1/statuses/update.xml?status=hello%20from%20the%20API", "elsheimy", "b@zzword", True)
End Sub

Public Function GetResponse(ByVal uri As String, ByVal username As String, ByVal password As String, ByVal post As Boolean) As XmlDocument
    Dim req As WebRequest = WebRequest.Create(New Uri(uri))
    If (post) Then
        req.Method = "POST"
    End If
    If ((username  Nothing) And (username.Trim()  String.Empty) And (Not String.IsNullOrEmpty(password))) Then
        req.Credentials = New NetworkCredential(username.Trim(), password)
    End If

    Dim doc As XmlDocument = New XmlDocument()
    doc.Load(req.GetResponse().GetResponseStream())

    Return doc
End Function

Notice how we set the HTTP method based on the function requirements. It is worth mentioning that a status should not exceed 140 UTF-8-encoded characters.

Encoding URIs

Have you noticed the previous code? It tries to post the update ‘hello from the API€. Because the text is included with the URI, special handling to the text should be carried. This special handling for text included in URIs is usually called Percent-encoding or URL Encoding. This encoding replaces unsafe characters with their hexadecimal values preceded by percentage (%) signs. Unsafe characters are those somewhat conflicted with URI special characters. For example, if we would encode the text ‘hello from the API€ we would end up with ‘hello%20from%20the%20API€.

There are many unsafe characters that should be percent-encoded including $, +, &, :, and =. A nice discussion of URL Encoding can be found in the article URL Encoding by Brian Wilson.

Once we get the idea, we can create our percent-encoding class that encodes/decodes strings:

// C# Code

private static string[,] _chars = new string[,]
        {
        { "%", "%25" },     // this is the first one
        { "$" , "%24" },
        { "&", "%26" },
        { "+", "%2B" },
        { ",", "%2C" },
        { "/", "%2F" },
        { ":", "%3A" },
        { ";", "%3B" },
        { "=", "%3D" },
        { "?", "%3F" },
        { "@", "%40" },
        { " ", "%20" },
        { """ , "%22" },
        { "", "%3E" },
        { "#", "%23" },
        { "{", "%7B" },
        { "}", "%7D" },
        { "|", "%7C" },
        { "\", "%5C" },
        { "^", "%5E" },
        { "~", "%7E" },
        { "[", "%5B" },
        { "]", "%5D" },
        { "`", "%60" } };

public static string EncodeUrl(string url)
{
    for (int i = 0; i < _chars.GetUpperBound(0); i++)
        url = url.Replace(_chars[i, 0], _chars[i, 1]);

    return url;
}

public static string DecodeUrl(string url)
{
    for (int i = 0; i < _chars.GetUpperBound(0); i++)
        url = url.Replace(_chars[i, 1], _chars[i, 0]);

    return url;
}

' VB.NET Code
Public Module UrlEncoder

Private _chars(,) As String = _
{ _
{"%", "%25"}, _
{"$", "%24"}, _
{"&", "%26"}, _
{"+", "%2B"}, _
{",", "%2C"}, _
{"/", "%2F"}, _
{":", "%3A"}, _
{";", "%3B"}, _
{"=", "%3D"}, _
{"?", "%3F"}, _
{"@", "%40"}, _
{" ", "%20"}, _
{"", "%22"}, _
{"”, “%3E”}, _
{“#”, “%23”}, _
{“{“, “%7B”}, _
{“}”, “%7D”}, _
{“|”, “%7C”}, _
{“”, “%5C”}, _
{“^”, “%5E”}, _
{“~”, “%7E”}, _
{“[“, “%5B”}, _
{“]”, “%5D”}, _
{“‘”, “%60”}}

Public Function EncodeUrl(ByVal url As String) As String
For i As Integer = 0 To _chars.GetUpperBound(0) – 1
url = url.Replace(_chars(i, 0), _chars(i, 1))
Next

Return url
End Function

Public Function DecodeUrl(ByVal url As String) As String
For i As Integer = 0 To _chars.GetUpperBound(0) – 1
url = url.Replace(_chars(i, 1), _chars(i, 0))
Next

Return url
End Function

End Module

For clearness, we have included the encoded string of each character along with the character itself. You don’t have to do this. You can convert the character to a number and just output the number in hex format.

Now we could change the code that updates the status to the following:

// C# Code

public static void Main()
{
    string uri;
    string text = UrlEncoder.EncodeUrl("hello from the API");
    uri = "http://api.twitter.com/1/statuses/update.xml?status=" + text;
    GetResponse(uri, "elsheimy", "b@zzwrd", true);
}
' VB.NET Code

Public Sub Main()

    Dim uri As String
    Dim text As String = UrlEncoder.EncodeUrl("hello from the API")
    uri = "http://api.twitter.com/1/statuses/update.xml?status=" & text
    GetResponse(uri, "elsheimy", "b@zzwrd", True)
End Sub

.NET includes a nice function that escapes (encodes) a URI, System.Uri.EscapeUriString() function. However, this function does not encode all unsafe characters.

Business Objects

Once you know the structure of the XML data returned, you can create your business objects that would encapsulate this data. The following are the three classes that would represent our three core objects, the user, the status, and the message.

// C# Code

public structure TwitterUser
{
    public long ID;
    public string Name;
    public string ScreenName;
    public string Location;
    public string Description;
    public string ProfileImage;
    public string Url;
    public bool IsProtected;
    public long FollowersCount;
    public long FriendsCount;
    public string CreatedAt;
    public long FavoritesCount;
    public bool Verified;
    public bool Following;
    public long StatusCount;
}

public structure TwitterStatus
{
    public string CreatedAt;
    public long ID;
    public string Text;
    public string Source;
    public bool Truncated;
    public long InReplyToStatusID;
    public long InReplyToUserID;
    public bool Favorited;
    public string InReplyToScreenName;
    public TwitterUser User;
}

public structure TwitterMessage
{
    public long ID;
    public long SenderID;
    public long SenderScreenName;
    public long RecipientID;
    public long RecipientScreenName;
    public string Text;
    public string CreatedAt;
    public TwitterUser Sender;
    public TwitterUser Recipient;
}
' VB.NET Code

Public Structure TwitterUser
    Public ID As Long
    Public Name As String
    Public ScreenName As String
    Public Location As String
    Public Description As String
    Public ProfileImage As String
    Public Url As String
    Public IsProtected As Boolean
    Public FollowersCount As Long
    Public FriendsCount As Long
    Public CreatedAt As String
    Public FavoritesCount As Long
    Public Verified As Boolean
    Public Following As Boolean
    Public StatusCount As Long
End Structure

Public Structure TwitterStatus
    Public CreatedAt As String
    Public ID As Long
    Public Text As String
    Public Source As String
    Public Truncated As Boolean
    Public InReplyToStatusID As Long
    Public InReplyToUserID As Long
    Public Favorited As Boolean
    Public InReplyToScreenName As String
    Public User As TwitterUser
End Structure

Public Structure TwitterMessage

    Public ID As Long
    Public SenderID As Long
    Public SenderScreenName As Long
    Public RecipientID As Long
    Public RecipientScreenName As Long
    Public Text As String
    Public CreatedAt As String
    Public Sender As TwitterUser
    Public Recipient As TwitterUser
End Structure

Retrieving Data

Now you can walk through the XML data and get that data inside your objects. The following code returns a list of statuses from your friends’ timeline.

// C# Code

public static void Main()
{
    GetStatuses("elsheimy", "b@zzword");
}
public static List GetStatuses(string username, string password)
{
    XmlNode node = GetResponse("http://api.twitter.com/1/statuses/friends_timeline.xml",
        username, password, true);

    List lst = new List(node.ChildNodes.Count);

    foreach (XmlNode nd in node.ChildNodes)   // for each status
        lst.Add(HandleStatus(nd));

    return lst;
}

public static TwitterStatus HandleStatus(XmlNode nd)
{
    // HandleNumber, FormatText, HandleBool
    // are just functions that converts strings
    // to numbers, decoded strings, and bool
    TwitterStatus status = new TwitterStatus(
        nd["created_at"].InnerText,
        HandleNumber(nd["id"]),
        FormatText(nd["text"]),
        FormatText(nd["source"]),
        HandleBool(nd["truncated"]),
        HandleNumber(nd["in_reply_to_status_id"]),
        HandleNumber(nd["in_reply_to_user_id"]),
        HandleBool(nd["favorited"]),
        FormatText(nd["in_reply_to_screen_name"]),
        HandleUser(nd["user"]));
    return status;
}

public static TwitterUser HandleUser(XmlNode nd)
{
    // HandleNumber, FormatText, HandleBool
    // are just functions that converts strings
    // to numbers, decoded strings, and bool
    long id = HandleNumber(nd["id"]);
    TwitterUser user;

    user = new TwitterUser(
        id,
        FormatText(nd["name"]),
        FormatText(nd["screen_name"]),
        FormatText(nd["location"]),
        FormatText(nd["description"]),
        nd["profile_image_url"].InnerText,
        nd["url"].InnerText,
        HandleBool(nd["protected"]),
        HandleNumber(nd["followers_count"]),
        HandleNumber(nd["friends_count"]),
        nd["created_at"].InnerText,
        HandleNumber(nd["favourites_count"]),
        HandleBool(nd["verified"]),
        HandleBool(nd["following"]),
        HandleNumber(nd["statuses_count"]));

    return user;
}
' VB.NET Code

Sub Main()
    GetStatuses("elsheimy", "b@zzword")
End Sub

Public Function GetStatuses(ByVal username As String, ByVal password As String) As List(Of TwitterStatus)
    Dim node As XmlNode = GetResponse("http://api.twitter.com/1/statuses/friends_timeline.xml", username, password, True)

    Dim lst As New List(Of TwitterStatus)(node.ChildNodes.Count)

    For Each nd As XmlNode In node.ChildNodes
        lst.Add(HandleStatus(nd))
    Next

    Return lst
End Function

Public Function HandleStatus(ByVal nd As XmlNode) As TwitterStatus
    ' HandleNumber, FormatText, HandleBool
    ' are just functions that converts strings
    ' to numbers, decoded strings, and bool

    Dim status As New TwitterStatus( _
            nd("created_at").InnerText, _
            HandleNumber(nd("id")), _
            FormatText(nd("text")), _
            FormatText(nd("source")), _
            HandleBool(nd("truncated")), _
            HandleNumber(nd("in_reply_to_status_id")), _
            HandleNumber(nd("in_reply_to_user_id")), _
            HandleBool(nd("favorited")), _
            FormatText(nd("in_reply_to_screen_name")), _
            HandleUser(nd("user")))

    Return status
End Function

Public Function HandleUser(ByVal nd As XmlNode) As TwitterUser
    ' HandleNumber, FormatText, HandleBool
    ' are just functions that converts strings
    ' to numbers, decoded strings, and bool
    Dim id As Long = HandleNumber(nd("id"))

    Dim user As New TwitterUser( _
        id, _
        FormatText(nd("name")), _
        FormatText(nd("screen_name")), _
        FormatText(nd("location")), _
        FormatText(nd("description")), _
        nd("profile_image_url").InnerText, _
        nd("url").InnerText, _
        HandleBool(nd("protected")), _
        HandleNumber(nd("followers_count")), _
        HandleNumber(nd("friends_count")), _
        nd("created_at").InnerText, _
        HandleNumber(nd("favourites_count")), _
        HandleBool(nd("verified")), _
        HandleBool(nd("following")), _
        HandleNumber(nd("statuses_count")))
    Return user
End Function

twittoo; Sample Application

twitto, is our WinForms sample application that utilizes the Twitter API. This is just a very simple application with basic functionalities.

Snapshots

The following are snapshots of the application:

Figure 2 - twitto 0
Figure 3 - twitto 1
Figure 4 - twitto 2
Figure 5 - twitto 3
Figure 6 - twitto 4
Figure 7 - twitto 5

Description

This application was created using C# and WinForms 2.0 technology; it allows the user to navigate through his friends’ timeline, mentions, direct messages, retweets, and friends, and to update his status, reply to tweets, retweets, and to direct messages. Data is not refreshed automatically, the user have to click the ‘refresh€ button. (You can create your own routine that creates a timer that updates the data automatically.)

Interface

As you see, the application uses just the Windows Common Controls all around the application; no 3rd party controls were used.

To represent a status, message, or a user, the application overuses the System.Windows.Forms.TableLayoutPanel control to represent each status, message, or user. It consists of four columns and two rows. The following figure shows how the control is laid-out.

Figure 8 - TableLayoutPanel Status Template

URL Shortening

twittoo, has a very nice feature, it allows the user to insert a shortened URL into his tweets. For this to work, the application makes use of http://is.gd URL shortening service. The following is the function that utilizes the http://is.gd API:

// C# Code

public static string Shorten(string url)
{
    if (!System.Text.RegularExpressions.Regex.IsMatch
        (url, @"(http|ftp|https)://[w-_]+(.[w-_]+)+([w-.,@?^=%&:/~+#]*[w-@?^=%&/~+#])?"))
        throw new FormatException("The URL you specificed is not in the current format.");

    url = Uri.EscapeUriString(url);
    string reqUri = String.Format(@"http://is.gd/api.php?longurl={0}", url);

    WebRequest req = WebRequest.Create(reqUri);
    req.Timeout = 5000;
    using (System.IO.StreamReader reader =
        new System.IO.StreamReader(req.GetResponse().GetResponseStream()))
    {
        return reader.ReadLine();
    }
}
' VB.NET

Public Function Shorten(ByVal url As String) As String
    If (Not System.Text.RegularExpressions.Regex.IsMatch _
        (url, "(http|ftp|https)://[w-_]+(.[w-_]+)+([w-.,@?^=%&;:/~+#]*[w-@?^=%&;/~+#])?")) Then
        Throw New FormatException("The URL you specificed is not in the current format.")
    End If

    url = Uri.EscapeUriString(url)
    Dim reqUri As String = String.Format("http://is.gd/api.php?longurl={0}", url)

    Dim req As WebRequest = WebRequest.Create(reqUri)
    req.Timeout = 5000

    Dim reader As New System.IO.StreamReader(req.GetResponse().GetResponseStream())
    url = reader.ReadLine()
    reader.Close()

    Return url
End Function

Download

Download twittoo; our sample application

Bad Practices: Locking on Non-shared Objects in Multi-threaded Applications

Actually, I was having a problem synchronizing threads calling a function. If we could regenerate the bug, we would end up with code like this:

static void Main()
{
    Thread[] arr = new Thread[10];

    for (int i = 0; i < 10; i++)
    {
        arr[i] = new Thread(ThreadProc);
        arr[i].IsBackground = true;
        arr[i].Start(new object());
    }

    foreach (Thread t in arr)
        t.Join();
}

private static void ThreadProc(object obj)
{
    lock (obj)
    {
        int i = 0;
        while (i < 10)
        {
            Thread.Sleep(1);
            Console.WriteLine("Thread #{0},t{1}",
                Thread.CurrentThread.ManagedThreadId, i++);
        }
    }
}

And when we execute this code the results would be like this:

Thread #4,      2
Thread #3,      3
Thread #5,      1
Thread #7,      0
Thread #5,      2
Thread #6,      1
Thread #3,      4
Thread #4,      3
Thread #8,      1
Thread #9,      0

What is the problem with this code? It starts multiple threads and passes them a locking object and the object is locked successfully using the lock statement, so why threads overlap? Why the synchronization doesn’t take effect?

Well, after a long period and after pushing a new thread in the MSDN forums (we are all developers do make silly mistakes, aih? :P), I come up with the solution and discovered the drawback of the code.

The problem was that each time we start off a thread we pass it a new locking object different from the others:

        arr[i].Start(new object());

Therefore, every thread is locking on its own objects, so no thread synchronization take effect.

The solution is very easy, you should lock on a shared object; an object that is shared between all your threads accessing this block of code. Read more about thread synchronization here.

For example, we should change our code to the following:

private static object _lockThis = new object();
static void Main()
{
    Thread[] arr = new Thread[10];

    for (int i = 0; i < 10; i++)
    {
        arr[i] = new Thread(ThreadProc);
        arr[i].IsBackground = true;
        arr[i].Start();
    }

    foreach (Thread t in arr)
        t.Join();
}

private static void ThreadProc(object obj)
{
    lock (_lockThis)
    {
        int i = 0;
        while (i < 10)
        {
            Thread.Sleep(1);
            Console.WriteLine("Thread #{0},t{1}",
                Thread.CurrentThread.ManagedThreadId, i++);
        }
    }
}

Finally, this was one of the bad practices and mistakes me (and many others too) fall in. Honestly, I would like to start my Bad Practices series :”>. I would write about problems I came across and solutions I found for them. In addition, I’m thinking of starting the Questions series. I got a huge number of questions every week, if we could publish them I think it would be great for all.

Have a nice day!

Launched Visual Studio 2010!

Actually, I’m about 50 hours (I won’t say 2 days :P) late from the launch of Microsoft Visual Studio 2010. I was attending there in CodeProject Tech Summit 2010 for the launch when Bob Muglia gave the keynote speech (Life Runs on Code.)

A bunch of updates has been made to Visual Studio 2010 and .NET 4.0 including new visual designers, new capabilities when running on multi-core processors, less time debugging and code interpretion, integrated version control and bug tracking, and more (read more here.)

Five editions of Microsoft Visual Studio 2010 are available:

Professional

The least in features and the least expensive edition. For individuals to perform basic development tasks.

Download Details (Trial Version):

Premium

For individuals and teams to deliver scalable, high-quality applications.

Download Details (Trial Version):

Ultimate

ALM (Application Lifecycle Management formerly called Team System) tools for teams to ensure quality results, from design to deployment.

Download Details (Trial Version):

Test Professional

Specialized toolset for QA teams to simplify test planning and manual test execution.

Download Details (Trial Version):

Team Foundation Server

Automates and streamlines the software delivery process for all team members.

Download Details (Trial Version):

Compare Visual Studio 2010 Editions

Programmatically Enumerating, Attaching, and Detaching SQL Server Databases

هذه المقالة متوفرة أيضا باللغة العربية، اقرأها هنا.

Contents

Contents of this article:

  • Contents
  • Overview
  • Introduction
  • Enumerating Databases on a Server
    • INFORMATION_SCHEMA.SCHEMATA System View
    • sys.sysdatabases System Table/View
    • sys.databases System View
    • sys.sp_databases Stored Procedure
  • Attaching Databases to the Server
    • CREATE DATABASE Statement
    • sys.sp_attach_db Stored Procedure
    • sys.sp_attach_single_file_db Stored Procedure
  • Detaching Databases from the Server
    • CREATE DATABASE Statement
    • sys.sp_detach_db Stored Procedure
  • Security Considerations

Overview

This writing is like an encyclopedia for the SQL statements and stored procedures used to enumerate, attach, and detach databases on a SQL Server instance. It lists the statements and stored procedures that you can use and discusses them in simple examples.

Introduction

Today we are going to talk about how to programmatically enumerate databases on a SQL Server instance and how you can attach and detach SQL Server databases.

Enumerating Databases on a Server

You can get a list of databases on a server using one of many ways:

  • INFORMATION_SCHEMA.SCHEMATA system view (SQL Server 2000 only)
  • sys.sysdatabases system table (a view in SQL Server 2005 and higher versions)
  • sys.databases system view (SQL Server 2005 and higher versions)
  • sys.sp_databases stored procedure

INFORMATION_SCHEMA.SCHEMATA System View

If you are using SQL Server 2000, you can query the system view INFORMATION_SCHEMA.SCHEMATA to get information about current databases on the server.

The following is the table diagram for INFORMATION_SCHEMA.SCHEMATA system view:

Actually, you don’t need to worry about any of the view columns, just the first column, CATALOG_NAME, that you need to worry about, it is the database (i.e. catalog) name.

The following code simply prints out the databases currently found on the default SQL Server instance in the current machine:

    // C# Code

    SqlConnection conn = new SqlConnection("Server=(local);Data Source=;Integrated Security=SSPI");
    SqlCommand cmd = new SqlCommand("", conn);
    SqlDataReader rdr;

    cmd.CommandText = "SELECT DISTINCT    CATALOG_NAME    FROM    INFORMATION_SCHEMA.SCHEMATA";

    conn.Open();

    rdr = cmd.ExecuteReader();
    while (rdr.Read())
    {
        Console.WriteLine(rdr.GetString(0));
    }

    rdr.Dispose();
    cmd.Dispose();
    conn.Dispose();
' VB.NET

Dim conn As New SqlConnection("Server=(local);Data Source=;Integrated Security=SSPI")
Dim cmd As New SqlCommand("", conn)
Dim rdr As SqlDataReader

cmd.CommandText = "SELECT DISTINCT    CATALOG_NAME    FROM    INFORMATION_SCHEMA.SCHEMATA"

conn.Open()

rdr = cmd.ExecuteReader()
While (rdr.Read())
    Console.WriteLine(rdr.GetString(0))
End While

rdr.Dispose()
cmd.Dispose()
conn.Dispose()

Again, this is for SQL Server 2000 only.

Check the MSDN documentation for INFORMATION_SCHEMA.SCHEMATA system view here.

sys.sysdatabases System Table/View

This is a system table specific to SQL Server 2000. In SQL Server 2005 it is provided for backward compatibility as a system view. Therefore, do not rely on this system view (or table) because it is expected to be removed in a future version of SQL Server.

The definition of this table/view is as following:

Only the first column, name, is the most important to us, it contains the database name. Other columns of importance (not for the subject of this topic) are:

  • dbid:
    Database ID.
  • sid:
    Security ID for the database.
  • crdate:
    Creation date of the database.
  • filename:
    Database filename.

You can change the line that sets the command text in the previous code to this line:

    // C# Code

    cmd.CommandText = "SELECT [name]    FROM    sys.sysdatabases";
    ' VB.NET Code

    cmd.CommandText = "SELECT [name]    FROM    sys.sysdatabases";

Again, using the sys.sysdatabases system table/view is not recommended because it would be removed in a future version of SQL Server.

Check the MSDN documentation for sys.sysdatabases system view/table here.

Check this MSDN article out: Mapping System Tables to System Views for more information about SQL Server 2000 tables mapped to SQL Server 2005 views or stored procedure.

sys.databases System View

This is the new version included in SQL Server 2005 (and higher versions) replaces the SQL Server 2000 sys.sysdatabases table.

This is a very lengthy system view, it includes tenths of columns, we are interested only on the first column, name, that contains the database name.

You can change the line that sets the command text in the first code to this line:

    // C# Code

cmd.CommandText = "SELECT [name]    FROM    sys. databases";
    ' VB.NET Code

cmd.CommandText = "SELECT [name]    FROM    sys. databases"

Check the MSDN documentation for sys.sysdatabases system view/table here.

sys.sp_databases Stored Procedure

This way is different from all others because it is not a system view or a system table, it is a system stored procedure.

This stored procedure accepts no parameters and returns a result set of three columns:

  • DATABASE_NAME:
    The name of the database.
  • DATABASE_SIZE:
    The size of the database (in kilobytes.)
  • REMARKS:
    Always NULL. For the Database Engine.

The following code demonstrates this stored procedure:

// C# Code

cmd.CommandText = "exec sys.sp_databases";
' VB.NET Code

cmd.CommandText = "exec sys.sp_databases"

Check the MSDN documentation for sys.sysdatabases system view/table here.

Attaching Databases to the Server

You can programmatically attach a database to the server in two ways:

  1. Using the CREATE DATABASE statement
  2. Using the sys.sp_attach_db system stored procedure
  3. Using the sys.sp_attach_single_file_db system stored procedure

CREATE DATABASE Statement

The CREATE DATABASE statement can be used to create databases into the server or to attach existing database files.

If you are going to attach a database, this statement should be formed as the following:

CREATE DATABASE database_name
    ON  [ ,...n ]
    FOR ATTACH [;]

The database_name is the name that you wish to give to the database. In addition, this statement takes filegroups of the database files.

Keep in mind that the database name should not be exist in the server or the function would fail.

The following example shows how you can attach the database database.mdf to the server and give it the name MyDatabase:

    // C# Code

    SqlConnection conn = new SqlConnection("Server=(local);Data Source=;Integrated Security=SSPI");
    SqlCommand cmd = new SqlCommand("", conn);

    cmd.CommandText =
        "CREATE DATABASE 'MyDatabase' ON " +
        "PRIMARY ( FILENAME =  'database.mdf' ) " +
        "FOR ATTACH";

    conn.Open();

    cmd.ExecuteNonQuery();

    cmd.Dispose();
    conn.Dispose();
' VB.NET Code

Dim conn As New SqlConnection("Server=(local);Data Source=;Integrated Security=SSPI")
Dim cmd As New SqlCommand("", conn)

cmd.CommandText = "CREATE DATABASE 'MyDatabase' ON " & _
    "PRIMARY ( FILENAME =  'database.mdf' ) " & _
    "FOR ATTACH"

conn.Open()

cmd.ExecuteNonQuery()

cmd.Dispose()
conn.Dispose()

If no log file (.LDF) can be found, SQL Server creates one for you.

The following code attaches the same database along with its log file. Just change the third line of the previous example that sets the command text with this line:

    // C# Code

    cmd.CommandText = "CREATE DATABASE 'MyDatabase' ON " +
        "PRIMARY ( FILENAME =  'database.mdf' ), " +
        "FILEGROUP MyDatabase_Log ( FILENAME = 'database.ldf')" +
        "FOR ATTACH";
' VB.NET Code

cmd.CommandText = "CREATE DATABASE 'MyDatabase' ON " & _
    "PRIMARY ( FILENAME =  'database.mdf' ), " & _
    "FILEGROUP MyDatabase_Log ( FILENAME = 'database.ldf')" & _
    "FOR ATTACH"

Check the MSDN documentation for the CREATE DATABASE statement here.

More about database files and filegroups can be found in the MSDN documentation here.

sys.sp_attach_db Stored Procedure

Another way that allows to attach a database to the server is the sys.sp_attach_db stored procedure. The definition of this stored procedure is as following:

sp_attach_db [ @dbname= ] 'dbname'
    , [ @filename1= ] 'filename_n' [ ,...16 ]

This function takes the database name as the first argument. In addition it accepts another 16 arguments (only the first is required) represent database files. The following code attaches the same database to the server. Again, just change the third line of the previous code to the following line:

    // C# Code

    cmd.CommandText = "exec sys.sp_attach_db    MyDatabase,    'database.mdf'";
' VB.NET Code

cmd.CommandText = "exec sys.sp_attach_db    MyDatabase,    'database.mdf'"

Check the MSDN documentation for the sys.sp_attach_db statement here.

sys.sp_attach_single_file_db Stored Procedure

This statement is the same as sys.sp_attach_db stored procedure. However, this statement accepts only one file, the database file.

Check the following code out:

    // C# Code

    cmd.CommandText = "exec sys.sp_attach_single_file_db    MyDatabase,    'database.mdf'";
' VB.NET Code

cmd.CommandText = "exec sys.sp_attach_single_file_db    MyDatabase,    'database.mdf'"

Check the MSDN documentation for the sys.sp_attach_single_file_db statement here.

Detaching Databases from the Server

Unlike attaching databases, you can detach a database from a server in only two ways:

  1. DROP DATABASE statement
  2. sys.sp_detach_db system stored procedure

DROP DATABASE Statement

This statement is used to remove one or more databases from SQL Server. It has the following syntax:

DROP DATABASE database_name [ ,...n ] [;]

The following code simply executes this statement against our database MyDatabase:

    // C# Code

    SqlConnection conn = new SqlConnection("Server=(local);Data Source=;Integrated Security=SSPI");
    SqlCommand cmd = new SqlCommand("", conn);

    cmd.CommandText = "DROP DATABASE MyDatabase";

    conn.Open();

    cmd.ExecuteNonQuery();

    cmd.Dispose();
    conn.Dispose();
' VB.NET Code

Dim conn As New SqlConnection("Server=(local);Data Source=;Integrated Security=SSPI")
Dim cmd As New SqlCommand("", conn)

cmd.CommandText = "DROP DATABASE MyDatabase"

conn.Open()

cmd.ExecuteNonQuery()

cmd.Dispose()
conn.Dispose()

Check the MSDN documentation for the sys.sp_attach_single_file_db statement here.

sys.sp_detach_db Stored Procedure

Huh, the last one. This stored procedure is used to detach a database from the server. It has the following syntax:

sp_detach_db [ @dbname= ] 'database_name'

It accepts a single argument, the database name. The following code removes our database, MyDatabase, from the server:

    // C# Code

    cmd.CommandText = "sys.sp_detach_db MyDatabase";
' VB.NET Code

cmd.CommandText = "sys.sp_detach_db MyDatabase"

Check the MSDN documentation for the sys.sp_attach_single_file_db statement here.

Security Considerations

Every statement and stored procedure we discussed in this writing requires specific permissions. Check the MSDN documentation to get more information.

Creating a Simple Sheet Designer in C#

هذه المقالة متوفرة أيضا باللغة العربية، اقرأها هنا.

Code: Geming.Sisc.msi

Contents

Contents of this article:

  • Contents
  • Introduction
  • Problem
  • Requirements
  • Solution
  • Snapshots
  • Component Design
  • Class Diagrams
  • Database Diagram
  • Characteristics
  • Background
  • Code Highlights
  • Download

Introduction

Today, we are going to create a simple application, actually a simple sheet designing tool. This tool gives the user the flexibility to design his sheets, reports, bills, invoices, and receipts (whatever.)

In this writing, we will refer to reports, bills, invoices, receipts, etc. with just the name sheets. For this, we will need to give a sheet a definition.

A sheet is just like an empty page (i.e. template) that contains sheet items. For example, a resume template is a sheet that has many items (name, birth date, address, etc.) A cash receipt is a sheet that has a few items (date, amount, charged to, received by, etc.)

We will go through this tool in a nice way. We will begin by a very simple system analysis and application design. After that, we will get into coding.

Actually, I€™m not an architect, and I think I will never be. So please, DO NOT blame me for this bad analysis. It is just an illustration to get you have a solid understanding of what our application is designed for.

Problem

The user is tied into the sheet designs that the developer has created for him. The user should have the ability to design his own sheets and/or to edit application defined sheets.

Requirements

User Requirements

The following is list of common user requirements:

  • The application should allow the user to create, edit, and delete sheets, and to group those sheets into categories he creates.
  • For the sake of simplicity, those categories would be only one level, no nested categories are allowed.
  • The application should be persistent. The data should be stored in a database and loaded when the user needs it.
  • The sheet should be loaded with right data and printed whenever the user asks.
  • Like Visual Studio, the user should have a Toolbox that has all types of sheet items. The user could insert a sheet item into his sheet by drawing it into the screen, dragging it from the toolbox, or just double-clicking its icon in the toolbox.
  • The user should have a ruler to allow him measuring sheet item dimensions. In addition, he should have a design-time-only grid.
  • The user should be allowed to set attributes of the sheet like the page setup, grid settings, background color, etc.
  • Not all sheet attributes are printed out; some are for design-mode only (like background color, and grid.)
  • Each type of sheet items should have its own attributes and the user should be allowed to change them too.
  • The user should be allowed to drag sheet items around the screen and position them.
  • The user should be allowed to cut, copy, paste, and delete sheet items.
  • Of course, the user should be allowed to print out the sheet, and to preview it too.
  • The application should be generic to be used in any system and in any application.
  • Other pieces of the system should have the possibility to interact with the sheet.

Functional Requirements

Common functional (developer) requirements are:

  • Abstraction. Component/Class abstraction should be considered into the design. The system should consist of several components each of which groups a related features together (e.g. interface objects, business objects, and data management.)
  • Extensibility. The system should be extensible. A good class hierarchy should be created, and derivation (inheritance) should be taken into consideration. In addition, every sheet item should be represented by a class in this hierarchy.
  • Technically, sheet items would be called shapes. Because they are just drawings into the sketch (or sheet.)
  • To allow the user to work with sheet items (shapes) and the application to populate the sheet with data, sheet items should not be drawn directly into the sheet page. Instead every item should represented by a class that has its own drawing routine.
  • The sheet should be a container for the shapes. All shapes are children of the sheet.
  • The sheet and rulers should be Windows controls to allow them to be hosted by a Windows Form.
  • Sheet items should be Windows controls too to allow them to be hosted by the sheet.
  • The sheet items should be owner-drawn controls and they should not be derived from existing Windows controls to help integrating them easily into the sheet.
  • For the sake of simplicity, the sheet and shapes should be serialized into XML and stored in the database.
  • The database should be a SQL Server database to allow faster communication and easier manipulation of XML data.
  • The database should have three tables for the three core system components, the category, the sheet, and the shape.
  • Every sheet item in a given sheet should have a unique name (or a tag) to allow other system components to interact with it (e.g. populate it with the right data.)
  • The developer should have the ability to control the quality of the painting process for each control or item independently.

Solution

After going through project requirements and considering a sophisticated system design, we got a nice plan for our project that would be called Geming SISC (Sheet Infrastructure Components.)

This project would be created using C# and .NET 2.0 (or future versions of course.)

Snapshots

The following are final snapshots of the application. Figure 1 shows a cash receipt sheet designed by labels, boxes, and lines. Figure 2 shows a simple resume template designed by only a few labels and a picture. The left pane of the application lists current sheets grouped by their categories.

Figure 01 - Geming SISC (Cash Receipt)
Figure 01 - Geming SISC (Cash Receipt)
Figure 2 - Geming SISC (Resume Template)
Figure 2 - Geming SISC (Resume Template)

As you know other system components could fill those fields up with correct information.

Component Design

Figure 3 shows the three components of our system.

Figure 3 - Geming SISC Component Design
Figure 3 - Geming SISC Component Design

The three core components of our system, Geming SISC are:

  • Geming.Sisc.Infrastructure:
    Contains the sheet control and shapes controls (label, text box, etc.) The sheet, rulers, and sheet items controls are all derived from System.Windows.Forms.Control.
  • Geming.Sisc.Data:
    Business objects that would be sent to the data among database manager objects. This component references the Geming.Sisc.Infrastructure component.
  • Geming.Sisc.Driver:
    The application interface that would be used to design sheets. It references the other two components.

Class Diagrams

Extensibility is one of the main goals of the system. A flexible class hierarchy should be considered as well as derivation (i.e. inheritance) of controls for other external objects.

The following is the class hierarchy for the sheet and the ruler controls.

Figure 4 - Sheet and Rulers Class Hierarchy
Figure 4 - Sheet and Rulers Class Hierarchy

As we can see, the base class is the System.Windows.Forms.Control class. This allows the sheet and rulers controls to be inserted into a Windows form or a Windows control.

The following figure, figure 5, shows the class hierarchy of sheet items (technically called shapes.)

Figure 5 - Sheet Items (Shapes) Class Hierarchy
Figure 5 - Sheet Items (Shapes) Class Hierarchy

As we can see, the base class for all shapes is the abstract Geming.Sisc.Infrastructure.ShapeBase class which inherits from System.Windows.Forms.Control class. All other shapes are derived from ShapeBase.

Two edit shapes were created, TextBoxShape that has a look like a Windows Text Box, and LabelShape that has a look like a normal Windows Label. Both are derived from the abstract EditableShapeBase.

Other shapes are BoxShape, LineShape, ImageShape, and CheckBoxShape.

All classes are serializable (implement System.Runtime.Serialization.ISerializable interface,) so they can easily converted into XML and pushed to the database.

For simplicity, we have developed just the box, line, image, check box, and two edit shapes (or sheet items.) You can go further and create any other shapes you like. In addition, you can create more specific items like CurrencyField, DateField, etc.

For more detailed class diagrams like those shows class members, check the application code.

Database Diagram

In its simplicity, database is defined as the following diagram illustrates:

Figure 6 - Geming SISC Database Diagram
Figure 6 - Geming SISC Database Diagram

Notice that, all data operations are carried out through stored procedures in the database.

The Shape.Value column is of the type xml to allow easier manipulation of XML data in the future.

Characteristics

Here are some of the characteristics (i.e. attributes) of the sheet (some are represented by properties):

  • Background color:
    The user should be able change the background color. Notice that the background color is not printed.
  • Non-printable grid:
    To help the user position sheet items. The user should be able to display them or not. In addition, the user can change the grid color.
  • Margin:
    The application should set the page margin based on print settings.
  • Title:
    Every sheet has a title, a description, and a category.

In addition, next is a list of some the characteristics of a shape (sheet item):

  • Selected:
    Is the shape currently selected or not. The user could select a shape by the mouse. A selection frame is drawn around the shape when selected.
  • Non-printable size grip:
    Each shape should have a size grip to allow the user to resize the shape.
  • Cloning:
    The shape should be able to be cloned; that is copied, to allow the copy and paste feature.

Background

Here are some refreshers of techniques we use in this system:

  • Painting

We will rely on custom painting in most situations. Therefore, well request help from System.Drawing classes specially the System.Drawing.Graphics class.

In some shapes like those mimic existing Windows controls (like the text box and the check box,) we will get help from classes in System.Windows.Forms.VisualStyles namespace to reflect the visual styles of Windows in our controls. In addition, System.Windows.Forms.ControlPaint class is used for drawing borders and selection rectangles.

  • Painting Quality

Every control in our project (sheet, rulers, and shapes) has a paining quality property that determines the quality of the drawing (low, medium, and high.) For this to work we will make use of some properties of the System.Drawing.Graphics object like those related to smoothing and anti-aliasing feature.

  • Serialization

All serialized objects should be marked with System.SerializableAttribute attribute. With help from System.Runtime.Serialization namespace we could customize the serialization process.

The core interface that would allow us to customize the serialization process is the System.Runtime.Serialization.ISerializable interface (implemented in all serializable classes.) Notice that, we should add the deserialization constructor to get correct deserialization.

  • Database

The database is a SQL Server client database with all operations included as stored procedures.

The system uses two-tier architecture; means that it accesses the database directly using just three objects, a connection, a command, and a data reader.

  • Design-Mode Support

For extending design-mode support we have created custom designer service for the Sheet class that inherits the System.Windows.Forms.Design.ParentControlDesigner class to allow nested controls in the Sheet object in the design-mode.

Code Highlights

In this section we will talk about significant blocks of code that would be of interest.

  • Painting Routines

The following is the code for OnPaint() routine overridden by only the ShapeBase class.

Listing 1 – ShapeBase.OnPaint() Method Code Listing
protected override void OnPaint(PaintEventArgs e)
{
    GraphicsManager.SetQuality(e.Graphics, PaintingQuality);

    ControlPaint.DrawBorder(e.Graphics, this.ClientRectangle, this.ForeColor, ButtonBorderStyle.Dotted);

    PaintShape(e.Graphics);

    if (Selected)
        DrawSelectionFrame(e.Graphics);

    ControlPaint.DrawSizeGrip(e.Graphics, this.BackColor, GetResizeGripRect());
}

This function first calls the GraphicsManager.SetQuality() function that sets the quality attributes of the Graphics object. We will get back to this function in about a moment.

After that the function paints the control border using the System.Windows.Forms.ControlPaint class.

Next comes the interesting point. The function calls the virtual function PaintShape() that a derived class overrides to provide its own drawing routines.

For example, the TextBoxShape class has this PaintShape() override:

Listing 2 – TextBoxShape.PaintShape() Method Code Listing
public override void PaintShape(Graphics dc)
{
    base.PaintShape(dc);

    using (Brush b = new SolidBrush(this.BackColor))
    dc.FillRectangle(b, this.ClientRectangle);

    using (Pen p = new Pen(SystemColors.ActiveCaption, 1))
    {
        p.Alignment = System.Drawing.Drawing2D.PenAlignment.Inset;
        Rectangle r = this.ClientRectangle;
        dc.DrawRectangle(p, r);
    }

    Rectangle rect = this.ClientRectangle;
    rect.Offset(2, 2);
    rect.Width -= 4; rect.Height -= 4;

    StringFormat format = new StringFormat();
    format.LineAlignment = this.Alignment;
    if (this.RightToLeft == RightToLeft.Yes)
        format.FormatFlags = StringFormatFlags.DirectionRightToLeft;

    using (Brush b = new SolidBrush(this.ForeColor))
        dc.DrawString(this.Text, this.Font, b, rect, format);
}

After that the OnPaint() function draws selection frame if the shape is currently selected.

Just before the function closes, it calls the ControlPaint.DrawSizeGrip() function to draw the sizing grip that the user would resize the shape from.

  • Painting Quality

In our library Geming.Sisc.Infrastructure, we have created a helper class, GraphicsManager, which contains only one function, SetQuality().

This function accepts two input parameters, the graphics object and the quality you wish to set to that object. The following is the code for the SetQuality() function.

Listing 3 – GraphicsManager.SetQuality() Method Code Listing
public static void SetQuality(System.Drawing.Graphics g, PaintingQuality quality)
{
    if (g == null)
        throw new ArgumentNullException("g");

    if (quality == PaintingQuality.High)
    {
        g.CompositingQuality = CompositingQuality.AssumeLinear;
        g.InterpolationMode = InterpolationMode.HighQualityBicubic;
        g.PixelOffsetMode = PixelOffsetMode.Half;
        g.SmoothingMode = SmoothingMode.AntiAlias;
        g.TextRenderingHint =
        System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
    }
    else if (quality == PaintingQuality.Medium)
    {
        g.CompositingQuality = CompositingQuality.HighQuality;
        g.InterpolationMode = InterpolationMode.Bilinear;
        g.PixelOffsetMode = PixelOffsetMode.HighQuality;
        g.SmoothingMode = SmoothingMode.HighQuality;
        g.TextRenderingHint =
        System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
    }
    else
    {
        g.CompositingQuality = CompositingQuality.Default;
        g.InterpolationMode = InterpolationMode.Default;
        g.PixelOffsetMode = PixelOffsetMode.Default;
        g.SmoothingMode = SmoothingMode.Default;
        g.TextRenderingHint =
        System.Drawing.Text.TextRenderingHint.SystemDefault;
    }
}

Download

The application Geming.Sisc along with its code is available for download here.

Download Geming.Sisc

Hard Links vs. Soft Links

Contents

Contents of this writing:

  • Contents
  • Overview
  • Introducing Hard Links
  • Introducing Soft Links
  • Creating a Hard Link
  • Creating a Soft Link

Overview

This writing talks about hard links and soft links; two of the nice features of NTFS file system.

You can divide file links into two categories:

  • Normal Links (shortcuts)
  • Symbolic Links:
    This divided into two subcategories:

    • Hard Links (for files)
    • Soft Links (for directories)

Introducing Hard Links

A hard link is a way to represent a single file (i.e. data volume) by more than one path. In other words, it is a way to create multiple directory entries for a single file.

Consider the following example, we could have two files, e.g. D:DocumentsBills.doc and D:FilesCompanyInvoices.doc, both files represent the same data, so when you change one file the other changes too!

A hard link is almost like a normal link (i.e. shortcut) but there is a big difference. If you delete the source file of a shortcut, the shortcut would be broken (points to a non-existing file.) A hard link on the other hand, would be still working fine if you delete the source file as if it was just a copy.

Notice that, hard disk space is not multiplied for each hard link. Because they are share the same data volume, they share the same size.

There is something that you should be aware of. File attributes are not updated for each of the hard links. This means that if you set a hard link (e.g. D:FilesCompanyInvoices.doc) to read-only, it is the only link that is set to this attribute. Other hard links including the source would be still read-write (unless you set them too.)

Hard links are only supported on NTFS file system; they are not supported by Win9x versions of Windows. Besides this, hard links creation is limited to the same local disk (or network drive) of the source file. For example, you cannot create a hard link on the C: drive for a file on the drive D: or on a network share.

Introducing Soft Links

Soft links (also called junctions,) are identical to hard links except that soft links are designated for directories not files.

For example, if you have one or more soft links to the directory D:Tools (e.g. D:Software and D:ProgramsTools,) all directories (including the source) would be updated when any one of them changes. That is, if you created a new file in one directory, it is created in all directory links, and so on.

Like hard links, soft links require NTFS file system. Plus, deleting the source directory does not affect other directories, all links share the same data volume, changing attributes of one directory does not be applied to others.

Unlike hard links, creating soft links is a special feature of Windows Vista and Windows Server 2008 (and future versions of course.) Therefore, it cannot be applied to earlier versions of Windows (like Windows XP or Windows Server 2003.)

Creating a Hard Link

Unfortunately, neither hard links nor soft links are supported by the .NET Framework. Therefore, you will need to dig into the Windows API to allow your application to consume this feature.

You can create a hard link using a single line of code using a simple call to the Win32 function, CreateHardLink(), that resides in the Kernel32.dll library. The definition of this function is as follows:

BOOL CreateHardLink(
    LPCTSTR lpFileName,
    LPCTSTR lpExistingFileName,
    LPSECURITY_ATTRIBUTES lpSecurityAttributes
    );

This function accepts three arguments;

  • lpFileName:
    The path of the new hard link to be created. Notice that, it should be resides in the location (local disk or network share) as the source file.
  • lpExistingFileName:
    The path of the source file to create the hard link from.
  • lpSecurityAttributes:
    The security descriptor for the new file. Till now, it is reserved for future use and it should not be used.

This function returns TRUE if succeeded or FALSE otherwise.

Keeping the definition of the function in our minds, we can create its PInvoke method in C#. Simply, this is our C# PInvoke method:

[DllImport("Kernel32.dll", CharSet = CharSet.Unicode )]
static extern bool CreateHardLink(
    string lpFileName,
    string lpExistingFileName,
    IntPtr lpSecurityAttributes
    );

As you know, we have set the character encoding to Unicode because Windows NT allows Unicode file names. In addition, we have marshaled lpSecurityAttributes as System.IntPtr to allow passing it a null value using System.IntPtr.Zero.

Then, you simply call the function:

Change the source file to meet an existing file into your system. In addition, check that the directory of the new link exists or the call would fail.

Take into consideration that the function creates the new file for you, it should not be found or the function would fail (as the case with soft links.)

CreateHardLink(
    "D:\Files\Company\Invoices.doc",	// New Link
    "D:\Documents\Bills.doc",			// Source
    IntPtr.Zero);

And this is another link:

CreateHardLink(
    "D:\Important\Work Invoices.doc",			// New Link
    "D:\Files\Company\Invoices.doc",			// Source
    IntPtr.Zero);

As you see, it does not matter if you created the new link from the original file or from another link.

Now try to change any of the links, and check the others. They are all updated at the time you commit the change (save the file, for instance.)

Creating a Soft Link

Like hard links, you can create a soft link (a junction) using a single call to a Win32 API function; it is CreateSymbolicLink() function. Again, this function is only supported by Windows Vista and Windows Server 2008 (and future versions.)

Actually, the function CreateSymbolicLink() can be used to create both types of symbolic links, hard and soft links. The definition of this function is as follows:

BOOLEAN WINAPI CreateSymbolicLink(
    LPTSTR lpSymlinkFileName,
    LPTSTR lpTargetFileName,
    DWORD dwFlags
    );

This function accepts three arguments, the first and the second are identical to the CreateHardLink() function:

  • lpSymlinkFileName:
    The path of the new link (file or directory) that resides in the same drive (local disk or network share) of the target file.
  • lpTargetFileName:
    The path of the existing source (file or directory.)
  • dwFlags:
    The type of symbolic link. It can take the following values:

    • 0x0:
      The link is a hard link (source and destination are files.)
    • 0x1 = SYMBOLIC_LINK_FLAG_DIRECTORY:
      The link is a soft link (source and destination are directories.)

The following is the PInvoke method for our function:

[DllImport("Kernel32.dll", CharSet = CharSet.Unicode)]
static extern bool CreateSymbolicLink(
    string lpSymlinkFileName,
    string lpTargetFileName,
    uint dwFlags
    );

const uint SYMBLOC_LINK_FLAG_FILE		= 0x0;
const uint SYMBLOC_LINK_FLAG_DIRECTORY 	= 0x1;

The following code creates two soft links for one directory:

Again, the source should be the only directory that exists in your machine or the function would fail.

CreateSymbolicLink(
    "D:\Software",         // New Link
    "D:\Tools",            // Source
    SYMBOLIC_LINK_FLAG_DIRECTORY);

// Source
CreateSymbolicLink(
    "D:\Programs\Tools",  // New Link
    "D:\Software",         // Source
    SYMBOLIC_LINK_FLAG_DIRECTORY);

Now try changing one directory, other links change too. Try deleting the source, nothing would be happen to the other links.

Introducing RSS; Developing a Simple RSS Reader

هذه المقالة متوفرة أيضا باللغة العربية، اقرأها هنا.

Code: Geming.WinForms.RssBars.zip

Contents

Contents of this writing:

  • Contents
  • Overview
  • Definition
  • Feeds
  • Aggregators/Readers
  • Version History
  • Schema
  • Sample; RSS Bars Library
  • Download
  • Further Readings

Overview

This writing does not include a full discussion or even the full details of RSS or XML. Rather, it includes a nice introduction to RSS and its XML schema. In addition, it incorporates what you get in a sample application that is easy-to-code, understand, and to extend.

Definition

RSS (commonly expanded as Really Simple Syndication or, sometimes, Rich Site Summary) is a XML content with specific schema used to deliver frequently changing web content (like news headlines, blogs, etc.).

RSS content is also known as a feed, web feed, syndication feed, web syndication, and a channel. It is widely known and distinguished by its icon €œ€.

RSS feeds are usually files that reside in a specific location. Those files usually (not extensively) has the extension rss or xml.

Feeds

Today, most -if not all – of the blogs and sites that have frequently changing web content incorporate RSS.

For instance, The New York Times has more than one hundred of RSS feeds available for subscription (listed here.) Every feed delivers the latest headlines for a specific category (Technology, Sports, etc.)

Aggregators/Readers

How can you benefits from RSS feeds? Surely, XML data is not the flexible and readable content that can be used. Thus, users usually access feeds via applications (or web clients) that are known as Feed Readers, RSS Readers, and Aggregators. Those applications read the RSS XML content, parse it, and display feed items (e.g. news headlines) to the user in a friendly interface.

Version History

RSS undergoes several changes that result in different versions and two major branches:

  1. RDF (Resource Description Framework) or RSS 1.* in other words.
  2. RSS 2.*

We will assume RSS 2.* is our discussion.

Schema

As any other XML format, RSS has a specific schema that RSS contents (i.e. feeds) should comply with; they required to implement obligatory elements, and they had the choice to implement other optional elements.

As a matter of discussion, we will take the CodeProject latest articles RSS feed (available here) as an example and extract the RSS schema from it.

The following is a sample from the CodeProject RSS feed content (at the time of this writing):


    
        The Code Project Latest Articles
        http://www.codeproject.com
        Latest Articles from The Code Project
        en-us
        
            The Code Project
            
            http://www.codeproject.com
            100
            30
            The Code Project
        
        Copyright &copy; CodeProject, 1999-2010
        webmaster@codeproject.com
        Sun, 04 Apr 2010 12:27:00 GMT
        20
        C# Hand-coded goodness
        
            Comparison of Architecture presentation patterns MVP(SC),MVP(PV), ...
            This article will compare four important architecture ...
            http://www.codeproject.com/KB/aspnet/ArchitectureComparison.aspx
            Shivprasad koirala
            Sun, 04 Apr 2010 12:27:00 GMT
            ASP.NET
            http://www.codeproject.com/KB/aspnet/ArchitectureComparison.aspx
        
        
            Arcade Button in Expression Blend & Silverlight
            Discover the power of the Grid object, & how it controls ...
            http://www.codeproject.com/KB/expression/ArcadeButton.aspx
            Alan Beasley
            Sun, 04 Apr 2010 11:42:00 GMT
            Expression
            http://www.codeproject.com/KB/expression/ArcadeButton.aspx
        
        
            How to translate your forms application
            Translate your forms application to multiple languages ...
            http://www.codeproject.com/KB/dotnet/forms-translator.aspx
            Davide Vitelaru
            Sun, 04 Apr 2010 07:19:00 GMT
            .NET Framework
            http://www.codeproject.com/KB/dotnet/forms-translator.aspx
        
    

If we have a look at the file in XML Notepad we can see the following results:

To gain more understanding of the schema, let’s have a look at this diagram:

Required elements surrounded by the red border. Many other optional elements are available.

Most elements are self-explanatory from their names. However, the following maybe need more explanation:

  • rsschannellanguage:
    Content language (e.g. en-us for English for United States.)
  • rsschannellastBuildDate:
    The date of the last change of the content.
  • rsschannelttl:
    Time to Live. The number of minutes that indicate how long a channel can be cached before refreshing from the source. You would ignore this element, in many cases.
  • rsschannelgenerator:
    The name of the program used to generate this feed.
  • rsschannelitemguid:
    A Globally Unique Identifier used to identify this feed item.

Sample; RSS Bars Library

Our sample project is not an application in itself. Actually it is a WinForms control library that is called Geming.WinForms.RssBars. This library includes bars that read RSS content from a specific feeds and display it to the user.

This is an extensible library you can extend it to read from any RSS feed you like. The following are snapshots of the RssBars controls (reading CodeProject, Just Like a Magic, BBC, and the Nile News channels.)




The following figure shows library class diagram:

As you see, we have only one business object, RssItem structure. It encapsulates fields related to a feed item.

The RssBar is the base MustInherit (abstract in C#) class. It defines the base functionality of a RSS bar. All other classes are just children of the base class. They incorporate the functionality of RssBar by just setting its RSS feed path.

The RssBar requires two parameters for instantiation, the RSS path, and the banner image. For the sake of performance, we have required the developer to pass a banner image of the feed instead of automatically loading it from the image element of the feed content.

To avoid duplication, members of the RssBar are documented in the code.

The core function of the RssBar is the ReadRss() function. This function accesses the RSS feed and populates the list of feed items and display them to the user.

Since RSS is a XML format, we will need to reference the System.Xml.dll library as it is the core library for accessing XML via .NET. (Do not forget to import the System.Xml namespace.)

The following is a sample from the function code. Code abbreviated for clarity.

Public Sub ReadRss()
    ' Checking design mode, exit if True

    ' Preparing the screen

    ' The XML Document
    Dim xmlDoc As New Xml.XmlDocument
    ' Loading the RSS feed, should fail if network is not available
    xmlDoc.Load(m_rss)

    ' Accessing the €œchannel€ element
    Dim ndChannel As Xml.XmlNode = xmlDoc.Item("rss").Item("channel")

    ' Comparing publication date to one we have
    ' continue if something new

    ' Item cocollection
    Dim collItems As New Collections.Generic.List(Of RssItem)
    Dim ndItem As Xml.XmlNode

    ' Enumerating through the items
    ' and populating the collection
    For i As Integer = 0 To ndChannel.ChildNodes.Count - 1
        ndItem = ndChannel.ChildNodes(i)
        If (ndItem.Name = "item") Then
            collItems.Add( _
            NewRssItem(ndItem.Item("title").ChildNodes(0).InnerText, _
            ndItem.Item("link").ChildNodes(0).InnerText, _
            ndItem.Item("description").ChildNodes(0).InnerText))
        End If
    Next

    ' Checking if items cound

    ' Clear existing items

    Do While enumerator.MoveNext

        ' Item

        lbl = New Label
        ' Creating a Label for the item
        ' and filling its fields
        Me.CtrlsPanel.Controls.Add(lbl)

        ' Adding handlers, so we could fire events
        AddHandler lbl.Click, AddressOf Me.RssItem_Click
        AddHandler lbl.MouseMove, AddressOf Me.RssItem_MouseMove

        ' Image
        image = New PictureBox
        ' Adding the banner image between items
        Me.CtrlsPanel.Controls.Add(image)

        ' Adding event handlers, so we could fire events
        AddHandler image.Click, AddressOf Image_Click
        AddHandler image.MouseMove, AddressOf Image_MouseMove
    Loop

    ' Finalization
End Sub

Child classes simply do not contain any code, just the line that instantiates the base RssBar and sets the RSS feed path and image. For instance,

    Public Sub New()
MyBase.New("http://www.codeproject.com/webservices/articlerss.aspx?cat=1", m_image)
Me.RightToLeft = Windows.Forms.RightToLeft.No
End Sub

Download

Download Geming.WinForms.RssBars Sample Code

Further Readings

Need more about RSS? Here are a few good references: