Windows Live Development Part 1 - Authenticating users via Windows Live ID

June 20, 2008 00:01 by MartinHN

With so many online services out there, you nearly claim a new online identity somewhere every day. You tend to use the same username and password couple for all of them, but sometimes your preferred username is taken, or your password does not meet the sites' password policy. I reckon I have well over 100 online identities out there - I rely on the same 3-4 combinations of username and password, and it's really messy. In this blog post I'm going to cover how to authenticate users of an ASP.NET application against Windows Live, using the SDK for Windows Live ID. I'm going use the built-in ASP.NET Membership mechanism and go from there.

I could have gone the OpenID way, but Scott Hanselman already did an example on OpenID, so I thought I'd try out Windows Live. Jeff Atwood asked the question, if we really need another username and password in the pursuit of the best authentication model for stackoverflow.com.

Conceptual idea of using Windows Live ID

tmpF3A8Windows Live ID, is the technology you should be quite familiar with when authenticating on Microsoft web sites, and Messenger. If you want to know more about what Windows Live ID is, take a look at this video from Channel9.

When using Windows Live ID, you redirect the user to the Windows Live login-page, with a return URL specified. When the user authenticates, Windows Live will redirect the user back to your site, and delivering a user id and a token. Here's an overview of how it looks:

image

Getting your application ID to use Windows Live ID

To get started using Windows Live ID, first of all you need an application ID. Basically you need to sign up for one, and register your application. Go to https://msm.live.com/app/default.aspx and click 'Register an Application'. (You can find Microsoft's guide to this here: http://msdn.microsoft.com/en-us/library/cc287659.aspx)

tmpF3F5

Leave Domain Name blank. Save your self-chosen Secret Key for later use, and submit the form. When you see the confirmation page, your application ID is shown. Copy that, as we're going to use it later.

In the return URL field, type the URL of your application followed by a name of a page to handle the Windows Live ID communication, like http://localhost/demoapp/webauth-handler.aspx.

Download and install Windows Live ID Web Authentication SDK

Microsoft has made it quite easy for ASP.NET developers to get started, by providing an SDK. Download it here: http://www.microsoft.com/downloads/details.aspx?FamilyId=24195B4E-6335-4844-A71D-7D395D20E67B&displaylang=en and install the SDK. By default it is installed here: C:\Program Files\Windows Live ID\WebAuth

From the SDK we need a class called WindowsLiveLogin, located in the App_Code folder of the sample. Keep this file in mind for a few seconds.

Start coding - create a new website

Open Visual Studio 2005/2008 and create a new ASP.NET website the way you want it. First we will add the WindowsLiveLogin.cs file to our App_Code folder. (C:\Program Files\Windows Live ID\WebAuth\Sample\App_Code\WindowsLiveLogin.cs).

Leave the default.aspx page, and put the following HTML in the markup:

<html>
<head>
  <title>Windows Live&trade; ID</title>
</head>
<body>
  <iframe id="WebAuthControl" name="WebAuthControl" src="http://login.live.com/controls/WebAuth.htm?appid=<%=AppId%>&style=font-size%3A+10pt%3B+font-family%3A+verdana%3B+background%3A+white%3B"
    width="80px" height="20px" marginwidth="0" marginheight="0" align="middle" frameborder="0"
    scrolling="no"></iframe>
  <%
   1:  if (UserId == null)
   2:      { 
%> <p> This application does not know who you are! Click the <b>Sign in</b> link above.</p> <%
   1:  }
   2:      else
   3:      { 
%> <p> Now this application knows that you are the user with ID = "<b><%
   1: =UserId
%></b>".</p> <%
   1:  } 
%> </body> </html>

And this piece of code in its code-behind class.

using System;
using System.Web;
using System.IO;
using WindowsLive;

public partial class DefaultPage : System.Web.UI.Page
{
  const string LoginCookie = "webauthtoken";

  // Initialize the WindowsLiveLogin class.
  private static WindowsLiveLogin wll = new WindowsLiveLogin(true);
  
  protected static string AppId = wll.AppId;
  protected string UserId;

  protected void Page_Load(object sender, EventArgs e)
  {
    HttpRequest req = HttpContext.Current.Request;
    HttpApplicationState app = HttpContext.Current.Application;

    HttpCookie loginCookie = req.Cookies[LoginCookie];

    if (loginCookie != null)
    {
      string token = loginCookie.Value;

      WindowsLiveLogin.User user = wll.ProcessToken(token);

      if (user != null)
      {
        UserId = user.Id;
      }
    }
  }
}

Default.aspx will work as a login page. Users click the Sign-in button, and is redirected to Windows Live. When they authenticate, they're redirected back to our handler page, that we will now create...

Create a new aspx page, and name it webauth-handler.aspx. Recall that this was the page we provided as the return URL when we registered the application at Windows Live.

This page will serve as the handler page for the user, when Windows Live redirects back to your site. Windows Live will post authentication specific values for you to use. Because this page is never seen by a user (we will send the user back where they began), we can simply remove all the markup.

Put the following code in your code-behind class for webauth-handler.aspx.

using System;
using System.Web;
using System.IO;
using WindowsLive;

public partial class HandlerPage : System.Web.UI.Page
{
  // Relative path to the login- and logoutpage.
  const string LoginPage = "default.aspx";
  const string LogoutPage = "default.aspx";
  const string LoginCookie = "webauthtoken";
  static DateTime ExpireCookie = DateTime.Now.AddYears(-10);
  static DateTime PersistCookie = DateTime.Now.AddYears(10);

  // Initialize the WindowsLiveLogin class.
  static WindowsLiveLogin wll = new WindowsLiveLogin(true);

  protected void Page_Load(object sender, EventArgs e)
  {
    HttpRequest request = HttpContext.Current.Request;
    HttpResponse response = HttpContext.Current.Response;
    HttpApplicationState application = HttpContext.Current.Application;

    string action = request["action"];

    if (action == "logout")
    {
      HttpCookie loginCookie = new HttpCookie(LoginCookie);
      loginCookie.Expires = ExpireCookie;
      response.Cookies.Add(loginCookie);
      response.Redirect(LogoutPage);
      response.End();
    }
    else if (action == "clearcookie")
    {
      HttpCookie loginCookie = new HttpCookie(LoginCookie);
      loginCookie.Expires = ExpireCookie;
      response.Cookies.Add(loginCookie);

      string type;
      byte[] content;
      wll.GetClearCookieResponse(out type, out content);
      response.ContentType = type;
      response.OutputStream.Write(content, 0, content.Length);

      response.End();
    }
    else if (action == "login")
    {
      HttpCookie loginCookie = new HttpCookie(LoginCookie);

      WindowsLiveLogin.User user = wll.ProcessLogin(request.Form);

      if (user != null)
      {
        loginCookie.Value = user.Token;

        if (user.UsePersistentCookie)
        {
          loginCookie.Expires = PersistCookie;
        }
      }
      else
      {
        loginCookie.Expires = ExpireCookie;
      }

      response.Cookies.Add(loginCookie);
      response.Redirect(LoginPage);
      response.End();
    }
    else
    {
      response.Redirect(LoginPage);
      response.End();
    }
  }
}

The first login test

Now we've got everything setup as we should, and it should be possible to login using Windows Live. View the default.aspx page in your favorite browser, and let's try.

First you will see out login page:
tmp9D6

Click the Sign in link, which will take you to Windows Live:
wlive_signin

Sign in with your Windows Live ID, and you will return to our login page - which will now welcome you and show your user ID:
wlive_signin 

Now your user has authenticated against Windows Live, using the Windows Live ID. Now it's up to you to handle the user's Windows Live ID, which is what I'm going to blog about in part 2 of the Windows Live Development series here...


kick it on DotNetKicks.com

Currently rated 4.3 by 3 people

  • Currently 4.333333/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Add to: Facebook Add to: Digg Add to: Del.icio.us Add to: Reddit Add to: Furl Add to: Yahoo Add to: Spurl Add to: Google Add to: Technorati

Designing for Internationalization

June 10, 2008 21:17 by MartinHN

Web sites and Web Applications today, are very often exposed beyond the borders of your home country, and therefore users speak different languages, and has a different currency, date- and time formats etc. ASP.NET provides you with an entire namespace for handling things like this. That is the System.Globalization namespace, where you will find a lot of classes for handling your every day globalization tasks. I'm not going to cover anything in this namespace now, if you want to get your hands dirty take a look at this video: http://asp.net/learn/videos/video-40.aspx where you will see how to use local and global resources for your application.

Using a global resource file for a place to store display text on buttons, labels, validation controls etc. is fine. But if you have an e-commerce site, selling products in multiple regions with different languages, you need an extra level. That level is a way to globalize e.g. the name and description of your product. When a user changes language, the name and description of your "display product details page" should change accordingly.

For me, the ideal solution should not result in extra database columns like name_us, name_da, name_es. This would be a very static solution, as you would have to change your database whenever a new language is added to your application. Nor should it require extra tables, so you need to join like hell, when you need to select a product.

I've decided to store e.g. the Name values as XML in the database, and parse that XML into a Dictionary<string, string> property on my Product object, with the key of the Dictionary being the language code (en-US for US English). To me this seems to work just fine. My database design is not getting more complex, and I can get and set values quite easily.

The XML string that goes into my ProductName column in the database table, looks like this:

<cultures><culture code="en-us">Logitech SmartCam 124</culture></cultures>

On my Product object, the Name property is a Dictionary:

private Dictionary<string, string> _Name;

public Dictionary<string, string> Name
{
  get { return _Name; }
  set
  {
    if (_Name != value) MarkDirty("Name");
    _Name = value;
  }
}

When I need to get the US English value of the Product.Name property, I call:

p.Name["en-us"]

To get a Dictionary from my XML, I use this helper method:

public static Dictionary<string, string> GetDictionary(string xml)
{
  if (String.IsNullOrEmpty(xml))
    return new Dictionary<string, string>(owner);
    
  XmlDocument doc = new XmlDocument();
  doc.LoadXml(xml);
    
  Dictionary<string, string> dic = new Dictionary<string, string>();

  foreach (XmlNode node in doc.DocumentElement)
  {
    dic.Add(node.Attributes["code"].Value, node.InnerText);
  }

  return dic;
}

And when I want to update my database, after I change the Name of the Product, I convert to Dictionary to an XML string using this helper method:

public static string GetXmlDocument(Dictionary<string, string> dic)
{
  XmlDocument doc = new XmlDocument();
  XmlNode docElement = doc.CreateNode(XmlNodeType.Element, "cultures", "");

  foreach (string key in dic.Keys)
  {
    XmlNode node = doc.CreateNode(XmlNodeType.Element, "culture", "");
    XmlAttribute att = doc.CreateAttribute("code");
    att.Value = key;
    node.Attributes.Append(att);

    node.InnerText = dic[key];

    docElement.AppendChild(node);
  }

  doc.AppendChild(docElement);

  return doc.OuterXml;
}

If you need more information on this huge topic, take a look at the ASP.NET Wiki: http://wiki.asp.net/page.aspx/55/internationalization/


Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Add to: Facebook Add to: Digg Add to: Del.icio.us Add to: Reddit Add to: Furl Add to: Yahoo Add to: Spurl Add to: Google Add to: Technorati

ASP.NET Server Control: FeedList - List most recent items from an RSS feed as links

April 21, 2008 22:30 by MartinHN

RSS feeds is everywhere, and rightly so. Just about any modern browser, e-mail client, search engine or mobile phone is compliant with the RSS technology, and it gives web developers an easy and very convenient way to share and consume content across the web. From time to time I need to show a list of recent items form an RSS feed. This is a very easy thing to do, and I always end up copying the same usercontrol around, which becomes nothing but a mess! Eventually I need to change a few things, which means I get different versions of the same control spread throughout my projects.

This mess has got to stop, and from now on I'll put all my common controls in a class library project, which I can reference from all the other projects.

Back to my RSS feed reader - this is the first control I've made into a server control, from one of my many usercontrols around the projects folder. The control downloads the XML at the Url of the RSS feed, and renders a list of links. The controls contains a few properties:

  • FeedUrl: The Url of the RSS feed to download
  • NumberOfItems: The number of items which should be rendered. Defaults to 5 if none is specified.
  • CssClass (Part of the WebControl class, from which FeedList derives): Use this so you have style the control from CSS.

The code

It really is straight forward. Below is the method that renders the links.

private void RenderFeedItems(HtmlTextWriter writer)
{
  if (!String.IsNullOrEmpty(FeedUrl))
  {
    XmlDocument doc = new XmlDocument();
    doc.Load(FeedUrl);

    XmlNodeList items = doc.DocumentElement.SelectNodes("channel/item");

    if (items != null)
    {
      int bounce = NumberOfItems;
      if (items.Count < NumberOfItems)
        bounce = items.Count;

      for (int i = 0; i < bounce; i++)
      {
        XmlNode titleNode = items[i].SelectSingleNode("title");
        XmlNode urlNode = items[i].SelectSingleNode("link");
        XmlNode pubDateNode = items[i].SelectSingleNode("pubDate");

        string title = String.Empty;
        string url = String.Empty;
        DateTime pubDate = new DateTime();

        if (titleNode != null)
          title = titleNode.InnerText;

        if (urlNode != null)
          url = urlNode.InnerText;

        if (pubDateNode != null)
          DateTime.TryParse(pubDateNode.InnerText, out pubDate);

        writer.RenderBeginTag(HtmlTextWriterTag.Li);

        writer.AddAttribute(HtmlTextWriterAttribute.Href, url);
        writer.AddAttribute(HtmlTextWriterAttribute.Title, title);
        writer.RenderBeginTag(HtmlTextWriterTag.A);
        writer.Write(title);
        writer.RenderEndTag();

        writer.RenderEndTag();
      }
    }
    else
    {
      RenderErrorMessage(writer, String.Format("No RSS-feed items found at: {0}", FeedUrl));
    }
  }
  else
  {
    throw new Exception("FeedUrl cannot be empty!");
  }
}

To use the control, put it in your website's App_Code folder, and add the following to the <controls> collection of your <pages> section in web.config:

<add tagPrefix="dnknormark" namespace="dnknormark" />

This should make your <pages> section look like this one:

<pages>
  <controls>
    <add tagPrefix="dnknormark" namespace="dnknormark" />
  </controls>
</pages>
 
And to put it on an aspx page, and show the latest 5 items from this blog, add this line to the page:
 
<dnknormark:FeedList ID="lstFeeds" runat="server" CssClass="feedList" 
NumberOfItems="5" FeedUrl="http://www.dnknormark.net/syndication.axd" />

Use it as you'd like. Feel free to change it, the way you need.


kick it on DotNetKicks.com


Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Add to: Facebook Add to: Digg Add to: Del.icio.us Add to: Reddit Add to: Furl Add to: Yahoo Add to: Spurl Add to: Google Add to: Technorati

Adding a Website to IIS7 programmatically

April 16, 2008 20:32 by MartinHN

Some time ago, I blogged about Adding an Application Pool to IIS7 programmatically. The result was a new Application Pool, that uses the Integrated Pipeline in IIS7.

In this post I will show you how to add a new Website, that uses the Application Pool from the other blog post.

I've loaded the Console Application I used to add the Application Pool, and moved the logic from Main to a method called AddApplicationPool, to split it up nicely. It is actually even easier to add a website programmatically. Below is the code for doing just that:

private static void AddWebSite()
{
  ServerManager mgr = new ServerManager();

  if (!Directory.Exists(@"c:\inetpub\wwwroot\iis7test"))
  {
    Directory.CreateDirectory(@"c:\inetpub\wwwroot\iis7test");
  }

  // Add a new Site to the server, configured to use our the iis7test home directory.
  Site site = mgr.Sites.Add("MyWebSite", @"c:\inetpub\wwwroot\iis7test", 80);
  
  // Set the application pool name of the site, to use the MyAppPool application pool.
  site.ApplicationDefaults.ApplicationPoolName = "MyAppPool";

  // Clear all bindings.
  site.Bindings.Clear();

  // Make the site listen to incoming HTTP requests using host header iis7test, on port 80.
  site.Bindings.Add("*:80:iis7test", "http");
  
  // Set auto start to true.
  site.ServerAutoStart = true;

  // Commit the changes
  mgr.CommitChanges();
}

Notice how we add Bindings to the website. Bindings is the information that tells IIS7 when to serve our website. We use this string to configure bindings: *:80:iis7test. The first * tells IIS to listen on all IP addresses on your system. 80 is the port number, and iis7test is the host header value for this site.

To browse our website, we need to add iis7test to the computers hosts file (located in %WINDIR%\System32\Drivers\etc), and point it to 127.0.0.1.


kick it on DotNetKicks.com

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Add to: Facebook Add to: Digg Add to: Del.icio.us Add to: Reddit Add to: Furl Add to: Yahoo Add to: Spurl Add to: Google Add to: Technorati

Adding an Application Pool to IIS7 programmatically

April 16, 2008 20:21 by MartinHN

Internet Information Services 7 (IIS7) has a great new set of features regarding configuration. Using the Integrated Configuration system, you can configure your server from XML files, the IIS7 manager, the command prompt using the APPCMD tool, but IIS7 also lets you manage your server from managed code in a very intuitive manner.

If you navigate to the %WINDIR%\System32\InetSrv folder in Windows Vista, you'll find all the executables, DLL's and XML (.config) configuration files you need. It doesn't matter if you use the IIS7 Manager, managed code or the APPCMD command-line based tool to manage your server - at the end of the day you are changing an XML file. That is applicationHost.config which is located here: C:\Windows\System32\inetsrv\config\applicationHost.config.

The integrated configuration system on IIS7 is great news for hosters, and web developers. Hosters can easily automate server management through managed code, and as a web developer, you can configure your server from your web.config file, which makes it easy to move your web application from development, to test, and further up towards production. Read my post on how to set a websites default document, from within the web application's web.config file.

In this post, I will create an application pool on my local IIS7.

Launch Visual Studio 2005 or 2008 - whatever you've got will work.

Create a new Console Application, and give it a name of your own choice.

Right click References in the Solution Explorer and add a new reference.

image

Locate Microsoft.Web.Administration.dll from the C:\Windows\System32\inetsrv folder.

To access IIS7, we use the ServerManager class, as shown below.

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Text;
   5:  using Microsoft.Web.Administration;
   6:   
   7:  namespace Iis7ManagementTest
   8:  {
   9:    class Program
  10:    {
  11:      static void Main(string[] args)
  12:      {
  13:        ServerManager mgr = new ServerManager();
  14:   
  15:        // Add a new application pool called MyAppPool
  16:        ApplicationPool myAppPool = mgr.ApplicationPools.Add("MyAppPool");
  17:        
  18:        // Configure my new app pool to start automatically.
  19:        myAppPool.AutoStart = true;
  20:   
  21:        // What action should IIS take when my app pool exceeds 
  22:        // the CPU limit specified by the Limit property
  23:        myAppPool.Cpu.Action = ProcessorAction.KillW3wp;
  24:   
  25:        // Use the Integrated Pipeline mode
  26:        myAppPool.ManagedPipelineMode = ManagedPipelineMode.Integrated;
  27:   
  28:        // Set the runtime version of ASP.NET to 2.0
  29:        myAppPool.ManagedRuntimeVersion = "V2.0";
  30:   
  31:        // Use the Network Service account
  32:        myAppPool.ProcessModel.IdentityType = ProcessModelIdentityType.NetworkService;
  33:        
  34:        // Shut down after being idle for 5 minutes.
  35:        myAppPool.ProcessModel.IdleTimeout = TimeSpan.FromMinutes(5);
  36:   
  37:        // Max. number of IIS worker processes (W3WP.EXE)
  38:        myAppPool.ProcessModel.MaxProcesses = 1;
  39:   
  40:        // Commit the changes
  41:        mgr.CommitChanges();
  42:      }
  43:    }
  44:  }
 

After the application has been executed, we will see our new application pool inside the IIS7 Manager:

image

That's all for now. In another blog post I will show how to add a new web site, that will use our new application pool - also from managed code.


kick it on DotNetKicks.com

Currently rated 5.0 by 2 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Add to: Facebook Add to: Digg Add to: Del.icio.us Add to: Reddit Add to: Furl Add to: Yahoo Add to: Spurl Add to: Google Add to: Technorati

Event performance optimization with the EventHandlerList

December 25, 2007 23:35 by MartinHN

When using custom events (event delegates) in your ASP.NET pages, user controls and server controls, many developers might define their Events as public fields, like this:

public event EventHandler<EventArgs> MyCustomEvent;


And use this code to fire the event:

protected void OnMyCustomEvent(EventArgs e)
{
  if(MyCustomEvent != null)
  {
    MyCustomEvent(this, e);
  }
}

 

Nothing is wrong with the approach of the latter, but defining the MyCustomEvent as a public field introduces two problems:

    1. Even though no clients have registered any delegates to the invocation list of your event, the compiler generates
      one private delegate field for each event delegate in your class. This is a waste of server memory, especially if
      you have several events inside your class.

    2. One Add and one Remove method is generated for each event field of your class. When clients use += and -= to add
      and remove delegates from the invocation lists of your class' events, these methods are called behind the scenes.
      These two compiler-generated methods are thread-safe, which means they include extra code to synchronize threads
      that are accessing these methods. This means, that everytime a client adds or removes a delegate to or from the
      invocation list of an event, they have to get a lock before they can do the actual work. This introduces an
      unnecessary overhead because most page developers don’t use multiple threads and therefore there is no need for
      thread synchronization.

     

    The EventHandlerList is the answer to our issues. This class comes with the .NET Framework. The EventHandlerList is a linked list of delegates,which is optimized for adding and removing delegates. To use the EventHandlerList in your classes, you need to add a private static key for each event your class exposes. The code bellow defines a key for MyCustomEvent:

    private static readonly object MyCustomEventKey = new object();

    The memory is allocated only once, because the key is static.

    After this, you need to define your events as properties - not fields. These event properties has a different syntax that normal get-set properties. Event properties uses add-remove instead of get-set. The following property, is an event property for MyCustomEvent:

    public event EventHandler<EventArgs> MyCustomEvent
    {
      add { Events.AddHandler(MyCustomEventKey, value); }
      remove { Events.RemoveHandler(MyCustomEventKey, value); }
    }
     

    Every Page, UserControl and WebControl - among others - has a protected property of type EventHandlerList named Events. With this approach, when clients use += to add a delegate to the invocation list of the MyCustomEvent, the add method of the event property calls the AddHandler method of the EventHanderList class, as the code above will tell you. As for -= the RemoveHandler method is called.

    The EventHandlerList class maintains a linked list which can have none or one entry for each event. The AddHandler method checks whether this internal linked list contains an entry for an event with the given event key. If it does, the method calls the Combine method of the Delegate class to add the client delegate to the invocation list of the event. If this internal list doesn’t contain an entry for an event with the given event key, the AddHandler method just adds a new entry.

    With this new approach, we need to update our OnMyCustomEvent Method, which is responsible for firing the event:

    protected void OnMyCustomEvent(EventArgs e)
    {
      EventHandler<EventArgs> handler = Events[MyCustomEventKey] as EventHandler<EventArgs>;
    
      if (handler != null)
        handler(this, e);
    }
     

    This method uses the MyCustomEventKey as an index in the Events list to access the MyCustomEvent-event. The Events list will return null if it does not contain an entry at the specified index. This will happen when no clients has subscribed to the MyCustomEvent.

    Using the EventHandlerList class automatically resolves the two previously mentioned performance problems with the event fields.

    Technorati Tags: ,

     

    kick it on DotNetKicks.com

    Currently rated 5.0 by 1 people

    • Currently 5/5 Stars.
    • 1
    • 2
    • 3
    • 4
    • 5

    Add to: Facebook Add to: Digg Add to: Del.icio.us Add to: Reddit Add to: Furl Add to: Yahoo Add to: Spurl Add to: Google Add to: Technorati

    Windows Vista / Mac debugging a Silverlight app using Visual Studio 2008

    December 24, 2007 01:38 by MartinHN

    As I wrote recently in another blog post about a .NET Rocks interview with Brad Abrams, I was very impressed with his keynote at ReMix Boston, where he did the Linux / Vista / Mac debugging. I referred to Richard Campbell's opinion that he didn't believe it would work. At the end of the post, I wrote that I want to try it for myself, to see if it really works. Brad Abrams posted a comment to my blog post, saying that it should work just fine. Well. This post tells the story.

    Prerequisites

    • A Windows machine with Visual Studio 2008 and Silverlight Tools Alpha for Visual Studio 2008 installed.
    • A Macintosh with the Silverlight 1.1 Alpha installed.
    • A network connection on both machines and a way to transfer files between the Vista box and the Mac. (A USB flash drive/stick is fine)
    • A Silverlight 1.1 Alpha project linked to a website, ready to run.
    • The website has to be remotely accessible. (Otherwise you can copy the website to the Mac as static HTML files, and run it locally from there.)

    Debugging environment

    This is the environment I will use for the debugging. For starters, I want to concentrate on the Vista / Mac debugging, so I won't set up a Linux box with Apache and serve the Silverlight app on a PHP page. But there really should be no problem to it. You can even copy a static HTML page with the XAML page, and the assemblies to the Mac, and run the Silverlight app locally, and attach the Visual Studio debugger to the Safari process on the Mac. Let's save the Linux box for another time.

    The mac: Basically just a Macbook with default configurations, and no special software.

    The Vista box: This is my development box. Windows Vista with Visual Studio 2008, IIS 7 and an ASP.NET website set up at localhost.

    Before we get started, locate your Visual Studio 2008 folder on the Vista box (usually: C:\Program Files\Microsoft Visual Studio 9.0), and find the Silverlight folder. This folder contains the necessary files to do the initial set-up. You'll also find a guide to set-up and start debugging, but I've found a few mistakes in there, and you can find yourself confused.

    Setting up the Mac for Silverlight debugging

    Remote debugging is not as easy as pointing Visual Studio to a remote computer, and selecting a process to debug. You have to authenticate against the remote machine. This is done via an SSL encrypted TCP channel between the debugger (The Vista box) and the target machine (The Mac). You will need to set-up the TCP port along with a private/public key pair. This is done in 6 easy steps:

    1. Copy the contents of the C:\Program Files\Microsoft Visual Studio 9.0\Silverlight\MacIntel folder to a destination on the Mac of your choosing.
    2. Start up a Terminal on the Mac, and execute the 'configcoreclrdebug' using an absolute filepath. e.g.: /MacIntel/configcoreclrdebug.
    3. First, your private/public key pair will be generated, and then you have to specify a TCP port in the range of 49152 - 65535. Just pick a number.
    4. Then you need to enter a password, and confirm it. Choose a password of at least 8 characters.
    5. A hidden directory is created in your Users folder, located here: /Users/username/.coreclrdbg - copy the "ConfigureWindowsCoreCLRDebugging.exe" file to the Vista box, in a destination of your choosing.
    6. Execute the 'core_clr_proxy' file on the Mac from the Terminal, using an absolute filepath like this: /MacIntel/coreclr_dbg_proxy

    image  image

    That was all the set-up needed on the Mac for now. Next up is setting up the Vista box.

    Setting up the Vista machine for Silverlight debugging

    This is the easy part.

    1. From a command prompt, run the "ConfigureWindowsCoreCLRDebugging.exe" which you just copied from the Mac.
    2. Enter the password you created on the Mac in step 4, and hit Enter.

    Starting the debugger

    Now to the exciting part. Will it work, or not?

    1. Launch Safari on the Mac, and point the address to the URL of your Silverlight application you wish to debug.
    2. Launch Visual Studio 2008 on the Vista box, and open the Silverlight project that you wish to debug.
    3. Open up a source file, and set a breakpoint.
    4. From the Debug menu, click Attach to Process. (Or hit Ctrl + Alt + P)
    5. From the Transport drop down menu, select 'CoreCLR Remote Cross-platform Debugging'.
    6. Enter the IP address or hostname of the Mac in the Qualifier box bellow and hit Enter.
    7. Now you should see a list of processes running on the Mac. Anyone that has CoreCLR Remote in the Type column can be attached.
    8. Select the Safari process and hit Attach.
    9. There you go. You've just attached the Safari process on the Mac to the Visual Studio debugger.

    image  image  image

    Now. Perform whatever action on the Mac that makes you hit the breakpoint you set before, and take a moment to enjoy real-time cross-platform remote debugging.

    Screenshots of the Mac showing the webpage with the Silverlight app:

    image image

    The second picture above, has the SetDefaultView() Button highlighted as if I was holding down the mouse bottom. This is because the screenshot is taken when I hit the breakpoint you can see on the third picture of Visual Studio further above.

    One thing I noticed while using the Silverlight charts from the Mac, was the speed. When I paste the serialized string in to the textbox and click LoadData() - the Silverlight loads the string and deserializes it to some custom classes I made. On Vista it takes less than a second. On the Mac it takes about two seconds or maybe a bit more. And it is not because of network latency. Silverlight runs in the browser, which means the app is downloaded to the Mac, and when I click the buttons, all the logic handling events and doing stuff runs locally. So Silverlight is lot slower on the Mac than on Windows. Maybe this will get better when Silverlight 1.1 is officially released.

     


    kick it on DotNetKicks.com

    Be the first to rate this post

    • Currently 0/5 Stars.
    • 1
    • 2
    • 3
    • 4
    • 5

    Add to: Facebook Add to: Digg Add to: Del.icio.us Add to: Reddit Add to: Furl Add to: Yahoo Add to: Spurl Add to: Google Add to: Technorati

    Configuring IIS 7 default document from web.config

    December 20, 2007 21:14 by MartinHN

    IIS 7 has a lot of changes regarding the configuration model. Using the Integrated Configuration System in IIS 7 you can set configuration of your server on different levels, compared to IIS 6, where the ASP.NET developer were more restricted. Configuring IIS 7 from an ASP.NET Web Application's web.config file works from both ASP.NET 2.0 and ASP.NET 3.5. Note that the server can be restricted to lock certain configuration settings from above the application level, you might encounter this if your website runs in a shared hosting environment.

    Setting up the website

    To have a test website, I will setup a new website on my Vista machine running IIS 7. I don't want the website to run under the Default Web Site, so first thing, I'll add a hostname to my hosts file. This is located in the drivers\etc folder of your windows directory's system32 folder. Add the following line:

    127.0.0.1         iis7test

    Open up Internet Information Services (IIS) Manager, and create a new Application Pool. Name it iis7test, and leave the settings as default:

    image

    Add a new website with the following settings:

    image

    This should get us started. Open up Visual Studio 2008 (2005 will also work). Open the website you just created inside IIS.

    image

    The only item in the website is a web.config. Add a new Web Form called Test.aspx.

    Right-click the Test.aspx file and click View in browser. You could get an HTTP Error 500.00, like this:

    image

    To get rid of it, set impersonate to false in the web.config file by adding this line to the system.web section:

    <identity impersonate="false" />

    Now your site should work fine. Edit the Test.aspx file, so it has some content. Just write Test IIS 7 or something in the HTML mark-up.

    If you right-click the website icon in the Solution Explorer, and click View in browser, you will get an