RSS 2.0 Feed
RSS 2.0


Atom 1.0 Feed
Atom 1.0

  Accessing the Windows RSS Platform with C# 


Internet Explorer 7 introduced the Windows RSS Platform. The Windows RSS Platform will be a core part of Windows Vista, but with IE7 it is now avilable on XP as well. The new functionality in IE7 to consume RSS feeds is made capable by the Windows RSS platform. This new functionality exists in Outlook 2007 also, giving you the ability to consume RSS feeds in Outlook folders ala Newsgator style, which can also synchronize with the Windows RSS Platform.

What makes this all so cool is that you now have the ability to have a single common store for subscribed RSS feeds. Well, this is the goal I suppose, although it will take some time for RSS Reader vendors to switch over - if that ever happens. Newsgator/FeedDemon has a utility for synching the Newsgator online feed store with the Windows RSS Platform.

Accessing the Windows RSS Platform is a simple task. The RSS Platform is exposed by a COM-based API found in msfeeds.dll (in the System32 directory). Add a reference to the COM dll and add a using directive for the namespace in the generated interop assembly and it's all easy work to use. Although, do keep in mind that this is a COM based reference so you'll need to take care to properly release all objects.

Let's say we want to populate a TreeView control with the feeds found in the local RSS store. An easy enough task, take a look:

using System.Runtime.InteropServices;
using Microsoft.Feeds.Interop;
//..

private void loadFeeds()
{
    TreeNode rootnode = treeView1.Nodes.Add("Feeds");
    IFeedsManager feedmgr = null;
    IFeedFolder rootfolder = null;

    try
    {
        // instanciate a new FeedManager
        feedmgr = new FeedsManagerClass();
        
        // get reference to the root feed store folder
        // there is always a root 
        rootfolder = (IFeedFolder)feedmgr.RootFolder;

        // call method to recursively add subfolders and feeds to treeview
        addNode(rootnode, rootfolder);
        treeView1.ExpandAll();
    }
    finally
    {
        Marshal.ReleaseComObject(rootfolder);
        Marshal.ReleaseComObject(feedmgr);
    }
}
private void addNode(TreeNode parentnode, IFeedFolder folder)
{
    // check to see if the current folder has subfolders
    if (folder.Subfolders != null)
    {
        foreach (IFeedFolder subfolder in (IFeedsEnum)folder.Subfolders)
        {
            TreeNode foldernode = parentnode.Nodes.Add(subfolder.Name);
            // recursively add subfolders under current folder
            addNode(foldernode, subfolder);
        }
    }
    
    // if current folder has feeds add them under the folder node
    if (folder.Feeds != null)
    {
        foreach (IFeed feed in (IFeedsEnum)folder.Feeds)
        {
            TreeNode feednode = parentnode.Nodes.Add(feed.Name);
            feednode.Tag = feed;
        }
    }
}

Once you've gotten the list of feeds it is just as easy to get the items from a feed. In the example above I placed a reference to the feed in the tag of the TreeNode objects. Let's grab that on the TreeView's AfterSelect event and get the items from the feed to fill a ListView.

private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
{
    listView1.Items.Clear();
    if (e.Node.Tag != null)
    {
        IFeed feed = e.Node.Tag as IFeed;
        if (feed != null)
        {
            foreach (IFeedItem item in (IFeedsEnum)feed.Items)
            {
                ListViewItem li = new ListViewItem();
                li.Text = item.Title;
                li.SubItems.Add(item.PubDate.ToShortDateString());
                if (!item.IsRead) li.Font = new Font(li.Font, FontStyle.Bold);

                li.Tag = item;
                listView1.Items.Add(li);
            }
        }
    }
}

So now as a last task, when an item in the ListView is selected let's display the text for the item in a webbrowser control.

private void listView1_SelectedIndexChanged(object sender, EventArgs e)
{
    if (listView1.SelectedItems.Count > 0 && listView1.SelectedItems[0].Tag != null)
    {
        ListViewItem li = listView1.SelectedItems[0];
        IFeedItem item = li.Tag as IFeedItem;
        if (item != null)
        {
            // let's mark the item as read
            item.IsRead = true;
            li.Font = new Font(li.Font, FontStyle.Regular);
            
            // set the item's text in the webbrowser
            webBrowser1.DocumentText = item.Description;
        }
    }
}

So, that's easy enough. Just as easy to do other things as well, such as add a new feed to the local store.

private void addRyanFarleysFeed() // ;-)
{
    IFeedsManager feedmgr = null;
    IFeedFolder rootfolder = null;
    IFeedFolder folder = null;
    IFeed feed = null;
    
    try
    {
        // instanciate a new FeedManager
        feedmgr = new FeedsManagerClass();
        
        // get reference to the root feed store folder
        rootfolder = (IFeedFolder)feedmgr.RootFolder;

        // if we wanted to add the feed to an existing folder in the store
        // we could use the following (for example, if we wanted to store
        // the feed in a folder called "My Feeds")
        folder = (IFeedFolder)rootfolder.GetSubFolder("My Feeds");
        
        // if you didn't want to add it in a subfolder (ie: in the root instead)
        // then you'd just use the reference to the root folder
        
        feed = (IFeed)folder.CreateFeed("{ public virtual blog; }", "http://ryanfarley.com/blog/rss.aspx");
        // now force an async download of the items in the feed
        feed.AsyncDownload();
    }
    finally
    {
        Marshal.ReleaseComObject(feed);
        Marshal.ReleaseComObject(folder);
        Marshal.ReleaseComObject(rootfolder);
        Marshal.ReleaseComObject(feedmgr);
    }
}

So now in the course of about 10 minutes you've created your own simple RSS Reader that uses the Windows RSS Platform as the backend. Here's a screenshot of the sample I threw together using the same code I outlined above:


(click for larger view)

There's a whole lot more the Feed API can do, including downloading enclosures and more.




                   



Leave a comment below.

Comments

  1. Jiho Han 11/7/2006 5:59 AM
    Gravatar
    As I was reading this, I vaguely remember fantasizing how vista and forward will all be managed - i.e. no COM.
    I can understand why they might have done it in COM but shouldn't they at least provide a PIA?
  2. Nick 11/13/2006 11:02 AM
    Gravatar
    Excellent entry!
  3. Anonymous 1/3/2007 8:40 PM
    Gravatar
    It's generally bad practice to call ReleaseComObject(...) in this way. Although what you're doing is safe, it's unnecessary because the GC will eventually call Release on the COM interfaces for you.
  4. Ryan Farley 1/4/2007 7:40 AM
    Gravatar
    Anonymous,

    Nothing to back up the claim? Are you referring to the old articles by Mattias Sjögren and Chris Brumme?

    -Ryan
  5. Anonymous 1/4/2007 8:21 PM
    Gravatar
    It's how RCWs work. They manage the reference counts for you. There are rare cases where you may want to decrement the counts yourself, but this is not one of them.

    If you decrement the count yourself, and get it wrong, then you'll end up in trouble. It's safe in this simple example because it's clearly not wrong, but in general, let the GC provide the safety it was designed to provide.
  6. Andreas Brekken 1/24/2007 9:06 PM
    Gravatar
    Great article. Ignore the COM muppets :-)
  7. tom 2/16/2007 3:17 AM
    Gravatar
    Thanks for the arcilte
  8. daniel 9/8/2007 8:08 PM
    Gravatar
    We have a case while the app being connected to (calling my c# plug-in to it) passes an object which was created on the stack (a local var). We think we need ReleaseComObject -- or is setting the object to null enough. Leaving it to the GC causes a later crash as the release seems to be called on an invalid object.

    ideas?
  9. daniel 9/8/2007 8:10 PM
    Gravatar
    We have a case while the app being connected to (calling my c# plug-in to it) passes an object which was created on the stack (a local var). We think we need ReleaseComObject -- or is setting the object to null enough. Leaving it to the GC causes a later crash as the release seems to be called on an invalid object.

    ideas?
  10. Logo Designers 3/2/2008 6:16 AM
    Gravatar
    This is a - big help - one question though - is there a PIA?
  11. DotNetKicks.com 8/15/2008 11:02 AM
    Gravatar
    You've been kicked (a good thing) - Trackback from DotNetKicks.com
Comments have been closed on this topic.



 

News


Also see my CRM Developer blog

Connect:              


Sponsor

Sections