So if your goal is to create a protocol handler so that you can create a link with some data in it, click on it and have your clickonce, silverlight or xbap application open up and react to the given data in the link... this is how you do it. As a matter of semantics - you probably know an xbap IS a clickonce deployed application - xml browser application. Actually the application manifest is almost identical to any other clickonce deployed application except that it has an additional few sections indicating to launch in a browser. All three of these will work using this technique though there may be slight variations necessary. I'll cover the more tricky parts that I ran into.
Protocol Handler
So let's say you've defined your protocol as MYPROTOCOL. when you normally open a webpage you type in http://whatever - but once your protocol is registered you would type in MYPROTOCOL://whateverarguments and it will execute your deployed application passing the argments specified. You can type that into Vista's start search, or into the browser - or even create a link in an email. Perhaps you will include the ID to some data element and when your application opens it will launch the appropriate UI to load that data based on the ID. This is the scenario I'll be describing - not search protocols or other implementations.
Approach
When the user clicks your new custom link it will execute a bootstrapper passing it the command line arguments. That bootstrapper will in turn execute the URL for your clickonce (or silverlight or xbap) deployed application passing the command line arguments as URL parameters. If you were to try to execute the local installed version of your clickonce exe - you will find that it does not execute under the proper permisisons designated by the certificate you used to sign it. The only way to reliably ensure that your application executes under the correct permissions is to launch the URL and pass querystring parameters to it. Henceforth I'll refer to all of these (clickonce, silverlight, xbap) as simply "application". Note: there is a nifty way to keep a single instance of your application even if they click the link multiple times... however as that applies only to clickonce and not to the other application types I won't cover that here. Tip: look at using the VB libraries in your C# library. Otherwise - each link click will open a new instance.
So here's the task break-down:
1) Configure your application to accept querystring parameters.
2) Modify the protocol handler bootstrapper (download included) for your application url.
3) Create an MSI installer to register your protocol and install the bootstrapper... (yes - you need to install something on the user's client machine!)
Configure your application to accept querystring parameters
The first step is to prepare your application to receive and act on these querystring parameters. You will want to set that up and test it out first. In the end clicking on a link such as http://mydeploymentserver/MyApplicationFolder/MyApplication.application?param1=foo¶m2=bar should open your application and do whatever you want it to do.
There's ton's of articles out there detailing how to do that customized per application type - so I won't regurgitate but instead point you in the right direction...
> For clickonce check out this article here.
> For xbaps check out this article here.
> For silverlight check out this article here.
Protocol Handler Bootstrapper
Ok so you've got that first part working now, right? Pretty cool, huh? At this point you may be asking yourself why in the world do I even need a protocol handler? I could just use the full link to do whatever I need and WITHOUT requiring the end user to install anything on their client machine. That's absolutely true - you could and if you don't need any of the advantages listed below then that's what I suggest you do.
Advantages of the protocol handler vs. using the full application url + querystring params directly:
1) The URL data doesn't change per deployed environment.
> This one was a big deal for us. We have a lot of test case data pointing to links such as OurProtocol://id=123 that we didn't want to have to change the data per environment url (dev, smoke, test, uat, etc).
2) The user can type a much shorter entry into Start or Browser (such as openMe:\\id=123 instead of http://www.whatever.com?id=123. )
3) You can piggy back on this to install different desktop shortcuts that will open your application to particular given states.
Ok - so you've weighed the pros and cons and still want the protocol handler. :) Your bootstrapper will convert the arguments given into a url and launch your browser with the given URL. See the full solution here.
Register your Protocol
You have a few options here. In the end you will want to have an addition to the registry that has the path to your ProtocolBootstrapper.exe and the cmd args - as shown below. Note - if you need to bypass some security restrictions use the "runas" instead of the "open" as shown in the second example below. You could create a .reg file and then execute that with elevated permissions directly from your clickonce application (that's what we did) but that won't work for Silverlight and Xbap. The better approach is to put your new ProtocolBootstrapper.exe into a new MSI that will also update the user's registry with the necessary new keys - then have the users that want to enable this functionality install the MSI.
For articles on creating an MSI see here, here and here!
Example Registry Entry
[HKEY_CLASSES_ROOT]
[YourProtocol]
(Default) = "URL:Your Protocol Description"
URL Protocol = ""
[shell]
[open]
[command]
(Default) = "c:\whereever\ProtocolBootstrapper.exe "%1""
[HKEY_CLASSES_ROOT]
[YourProtocol]
(Default) = "URL:Your Protocol Description"
URL Protocol = ""
[shell]
[runas]
[command]
(Default) = "c:\whereever\ProtocolBootstrapper.exe "%1""
There are a lot of things to download and install in order to get started writing Silverlight and WPF applications..
Download Microsoft® Visual Studio 2008 90-day trial version here.
Download Microsoft® Visual Studio 2008 Tools here.
Download Microsoft® Silverlight 2 Beta 1 Runtime here.
Download Microsoft® Silverlight™ 2 Software Development Kit Beta 1 here
Download Microsoft® ASP.NET Futures here
Download Microsoft® Expression Design here
Download Microsoft® Expression Blend 2.5 March 2008 Preview here
Download Deep Zoom Composer here
Documentation / Labs
Download Microsoft® Silverlight™ 2 Software Development Kit Beta 1 Documentation here
Download the WPF labs from here
Download the Silverlight labs from here
Silverlight Hosting (discountasp.net)
[ I'll provide a full solution download to anyone interested ]
Our Goals were:
1) Use images in XAML syntax like this:
<Image Name="imageSave" Height="16" Width="16" Source="ui:Images.ico_save_16.png"/>
2) Break on invalid image references at COMPILE - not at runtime.
3) Automatically add new when new images are copied into project.
4) Share resources within solution across projects.
5) Get intellisense for images from different projects in the same solution where possible (currently only in the codebehind - but the xaml editor will eventually provide intellisense here too).
6) VS 2008
_________________________________________________________
We created a separate "Resources" class library. In that class library we created an Images folder and under that an "ico" and "png" folders with all our images. Not added to "Resources" or "settings" as these generate only internal strongly typed - you need public.
Step 2:
Inside that class library create a text template - I called it Generator.tt. This text template will scan your directory of files and output something like this (below). We have two sub properties "ico" and "png" for the same resource... but you don't have to have that complexity.
// <auto-generated>
public static class Images
{
public static class ico_user_16
{
public static BitmapFrame png
{
get { return ResourceLocator.GetImageSource("ico_user_16.png"); }
}
}
//end ico_user_16
#endregion
------------------------------------------
The only really tricky part in creating the tt file was figuring out how to find the current directory to scan the project files...
string icoPath = this.Host.ResolvePath(@"Images\ico");
Now you can add references to that "Resources" class library from any project in your solution and get static resources in your xaml files... and share them across projects. As you add new images to your resource library simply save your Generator.tt file to re-generate your static resources.
Enjoy!!!
I live on Capital Hill in Seattle. A diverse neighborhood filled with world-class restaurants, shops and coffee shops. In my travels outside of Seattle it seems coffee shops are simply a place where people go to buy coffee. I see them in a frantic rush zipping in and zipping out. I get the impression that if I were to attempt to strike up a conversation I would be pushed out of their way!
In Seattle, however, coffee shops are a place for socializing. On Capital Hill you can relax in a comfortable couch, plug in your laptop, and chat with your neighbors while working and sipping on your coffee. Having grown up in English boarding schools – I particularly enjoy coffee shops as they remind me of the “common room” where students would gather to do their homework and socialize.
Every day after work I find myself putting in an extra 2-3 hours work at one of these coffee shops. Unchecked, I can easily work 60-80 hour weeks. This is not uncommon.
Last night as I was working in a coffee shop on Olive way - a man came and sat at the table across from me. It was snowing last night and the man’s cheeks were red from the cold and his face was long and weary. His jacket was horribly stained. His long scraggly beard had remnants of some meal left in it. About the time that I noticed how his fingernails were caked black with years of accumulated dirt… the smell hit me. The kind of mixture of months of accumulated body odor and urine that indicates a homeless man.
My heart skipped a beat as I was overwhelmed with compassion for this old man. It was particularly cold out and I figured this poor homeless man had come in to escape the cold. I noticed he didn’t even have a coffee to drink. I was reminded of the horror stories I had heard while doing volunteer work for the low income housing project downtown.
As I was imagining the rough life this man must lead… and pondering whether I should buy him a coffee… he whipped out a brand new top of the line Dell XPS laptop! Tricked out – it shined a fluorescent blue. He plugged in the shining machine and started typing away furiously! He took on the air of an experienced and focused computer user... perhaps even a software developer?
Taken aback – I pondered… perhaps consumed by his technical passion he just let the rest of his life slide? Perhaps things like personal hygiene no longer mattered to him – just the code! A sort of Matrix meets Hemmingway? Maybe he is an eccentric genius? Like a Buddhist Monk whose only possessions are his robe and bowl - his only possessions are his laptop and code? I strained to see if I could spot an “MS Asset” tag on the laptop or some other sign that he too may be a blue badge... but I could not see any.
Of course I personalized the event and asked myself – “Is that me in 30 years?” There have been times when I was overzealous in my work at the neglect of my other life responsibilities... would that eventually lead to life like that?
Whatever the explanation – this has motivated me to regain some sense of life-work balance!
Ramblings (in response to a question)…
I would have to know a lot more about his requirements before making a recommendation. In particular – what are the skill sets of his existing resources?
I’m guessing the primary things you are looking for are ease of deployment for first-time use as subsequent updates. Performance over high latency. Resiliency – say when your internet connection goes down. Client-server transactions. Etc..
· High performance over high latency networks comes from asynchronous transactions and transferring as little data as possible per transaction. Compression and other factors also come into play.
· Optimum resiliency/stability typically translates to a form of local storage. If you are able to queue transactions locally and securely - you can re-try in the event that the network is down – otherwise your transaction data may be lost.
For us – those two factors eliminated asp.net / ajax. Secure local storage isn’t an option. And - even with maximum caching enabled – in a sizable application most transactions still contain UI rendering information in addition to the data – eg html – that has to be downloaded to the client (browser). Also Silverlight’s or XBAP’s use of the CLR is much faster than the browser’s javascript engine (by about 42%).
Let’s start with XBAP… XBAP (or Xml Browser Application) really just refers to a standard WPF application deployed via clickonce over the internet – and hosted within the browser. Clickonce, of course, allows for incremental updates – so most of the time you don’t need to re-download your presentation layer.
I’ve seen blogs and info stating a variety of limitations inherent in XBAP such as calling web services, opening new windows, talking to the registry and/or local files… but from our experience I know all of those things are possible with an XBAP. Those are not limitations inherent in the technology. So long as your XBAP application and deployment manifests are signed with a certificate that resides in your Trusted Root certificates then you can do pretty much anything with an XBAP application. If you don’t need that much power – you can still write files to isolated storage and go with a less trusted cert – giving you the resiliency.
The cons of using xbap vs. silverlight is that you require the end user to trust your certificate (typically a prompt) and – the big “and” – they have to be on the windows operating system and download the prerequisite version of .NET 3.0 (or greater). The bootstrapper is only around 2.5mb – but the entire framework is around 50mb! Imagine how long that takes to install with 300ms latency. Once they have that installed – as your application advances be prepared for upgrades (3.5, etc).
Silverlight provides capabilities to use Web Services, Isolated Storage and other features but with a small under 5mb initial download, cross-platform capabilities and most of the core features that come in the WPF libraries – hence the name “WPF/e”.
Depending on your resources - if they are C# / web developers – then Silverlight may be the best option for you. Silverlight 2.0 due out this month - is releasing a whole bunch of new controls – mirroring all the standard control in the WPF library. (eg textbox, combobox, etc) – making development in visual studio fairly easy. It isn’t just a “flash” replacement anymore. When *matured* – it’s a substantial new technology.
I’m currently investigating Silverlight 2.0 for migration of our xbap/clickonce WPF application to Silverlight 2.0.
- Rob.
_____________________________________________________________
Hi Rob,
One of my friend has the following requirement,
· Thin client
· Real time update of data from server to client (Since server push is not possible, he is thinking of AJAX. There are commercial frameworks like COMET available that gives the server push which he will not be using )
· Performance (Stability and responsiveness are preferred over jazzy look and feel – say 3d transformations)
He is looking at the following options:
· ASP.net + AJAX
· MS Silverlight
· XBAP
· Smartclients with CAB framework
Each has its own advantages and disadvantages. It is hard to decide as he does not have visibility into the roadmap for some of these options. Can you suggest something on this?
In fact “Adobe Flex” supports all these requirements except the fact that integration with .net framework is not available. So he had to drop the idea of using Flex!!!!
If you have a bluetooth enabled laptop and a bluetooth enabled cellphone you may be able to download the pictures from your phone to your laptop via bluetooth... EVEN if your cell phone provider says it isn't possible (as in my case).
Perhaps they direct you to use their picture place service and to message your phone's images to that picture place. The down-side of this approach is that you loose resolution, it takes forever, and it costs a fortune!
This weekend I discovered a nifty free application that allows me to download my picutures from my camera phone faster, wireless via bluetooth, without loosing picture resolution - and for FREE!
Here's what you need to do:
- Pair your phone to your laptop via bluetooth. (instructions for vista)
- Download and install the bitpim exe from http://www.bitpim.org
- Once Installed start it up. You should now see a screen like:

You can walk through the wizard to setup your phone. For my model phone - I found that the images were downloaded as part of the "wallpaper" download.
In addition - you can find (or create) ringtones and upload them to your phone via bluetooth, wireless and for FREE.
This is a great little jem of a find.

You may run into an error when using SQL 2005 and a .NET 2.0 aspx or windows client UI.
Perhaps you’ve encoded your xml – you are using stored procedures and proper sqlParameters and everything – yet you still run into this exception.
Example repro:
DECLARE @xml xml
SET @xml = N'<A></A>'
SELECT @xml.query('<A>{substring(.,1,1)}</A>')
XML parsing: line 1, character 1, illegal xml character
This is because SQL 2000’s xml parsing was based on MSXML 3.0 – and SQL 2005’s xml parsing is based on MSXML 6.0. The rules have tightened up a bit. You may be able to store and work with xml characters in your .NET UI layer (ASPX or C# Windows Client) – but once you try to INSERT them you will get this exception.
As of SQL 2005 only W3C Compliant characters are allowed in XML. The range of valid XML characters as defined by W3C XML Language specifications 1.0 are #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]. Any characters outside of that range (as in the example above) may actually be INPUT by the user, stored as xml in your client – but will throw an exception when the INSERT command is executed in your SQL server.
One answer to this problem (maybe not the most elegant) is to scrub user input via the method shown below:
|
/// <summary>
/// IsW3CCompliant
/// http://w3c.org
/// The valid XML characters and character ranges (hex values) as defined by the W3C XML language specifications 1.0:
/// #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
/// </summary>
/// <param name="c"></param>
/// <returns></returns>
static private bool IsW3CCompliant(char c)
{
int charInt = Convert.ToInt32(c);
return charInt == 9 || charInt == 10 || charInt == 13 || (charInt >= 32 && charInt <= 55295) || (charInt >= 57344 && charInt <= 65533) || (charInt >= 65536 && charInt <= 1114111);
}
/// <summary>
/// Scrub Xml ensures that each character is W3C compliant.
/// This is a major performance hit. . .
/// #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
/// </summary>
/// <param name="xmlStr"></param>
/// <returns></returns>
static internal string ScrubString(string xmlStr)
{
if (xmlStr == string.Empty || xmlStr == null)
return xmlStr;
string pattern = @"[^\w\.@-]";
StringBuilder strB = new StringBuilder(xmlStr.Length);
//-- If there are no special chars just return the original (99%)
Regex regex = new Regex(pattern);
if (!regex.Match(xmlStr).Success)
return xmlStr;
char[] charArray = xmlStr.ToCharArray();
for (int i = 0; i < charArray.Length; i++)
{
if (IsW3CCompliant(charArray[i]))
{
strB.Append(charArray[i]);
}
}//for
return strB.ToString();
}
/// <summary>
/// XmlTextWriter override that scrubs the attributes
/// </summary>
public class MyXmlTextWriter : XmlTextWriter
{
/// <summary>
/// MyXmlTextWriter scrubs attribute values. Constructor just calls base.
/// </summary>
/// <param name="tw"></param>
public MyXmlTextWriter(System.IO.TextWriter tw) : base(tw) { }
/// <summary>
/// MyXmlTextWriter scrubs attribute values. Constructor just calls base.
/// </summary>
/// <param name="filename"></param>
/// <param name="encoding"></param>
public MyXmlTextWriter(string filename, System.Text.Encoding encoding) : base(filename, encoding) { }
/// <summary>
/// MyXmlTextWriter scrubs attribute values. Constructor just calls base.
/// </summary>
/// <param name="w"></param>
/// <param name="encoding"></param>
public MyXmlTextWriter(System.IO.Stream w, System.Text.Encoding encoding) : base(w, encoding) { }
/// <summary>
/// This class scrubs the attribute value - the sole
/// reason for this class.
/// </summary>
new public void WriteAttributeString(string localName, string value)
{
base.WriteAttributeString(localName, XmlHelper.ScrubString(value));
}
/// <summary>
/// This class scrubs the attribute value - the sole
/// reason for this class.
/// </summary>
public void WriteAttributeStringRaw(string localName, string value)
{
base.WriteAttributeString(localName, value);
}
}//MyXmlTextWriter
|