Serving up Silverlight without IIS/webserver

In my previous post, I showed how to host WCF services. In the kiosk application that I am working on, I have, as previously mentioned, decided to not have IIS run locally on the machine. I have also decided host the Silverlight application locally. I do not want it to be fetched from the server for different reasons. So how can I serve up a Silverlight application without a webserver? Well…I am already hosting WCF services in a windows application…why not let the windows application serve up the Silverlight application as well?

WCF actually gives us this possibility very easily, and here is how it is done. I’m going to re-use the application from the previous post, so if you haven’t read it, I recommend doing so…

I start off by adding my XAP-file as well as the TestPage.html file to the console project by using the “Add Existing Item” dialog. The TestPage.html will be available in the build location of the Silverlight project. It is the file created when debugging the project. Next, I set the Build Action to embedded Resource.

Now that both of the files are embedded in the application, it is time to actually serve them up. I have decided to do it using the SilverlightService that is already available. So I change the ISilverlightService interface by adding 2 new methods. One called GetHtmlPage() and one called GetXapFile(). I also set OperationContract and WebGet attributes in the methods

[ServiceContract]
public interface ISilverlightService
{
[OperationContract, WebGet(UriTemplate = "/clientaccesspolicy.xml")]
Stream GetClientAccessPolicy();

[OperationContract, WebGet(UriTemplate = "/index.htm")]
Stream GetHtmlPage();

[OperationContract, WebGet(UriTemplate = "/DarksideCookie.WCFHosting.SilverlightClient.xap")]
Stream GetXapFile();
}

The UriTemplate’s used by the WebGet decides what Url’s to respond to. So the one for the GetHtmlPage() method can be anything, but i use index.htm as it is standard. But the one used for the Xap-file, needs to correspond to what is in the Html code. In this case, the Silverlight object tag looks like this

<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
<param name="source" value="DarksideCookie.WCFHosting.SilverlightClient.xap"/>
<param name="onError" value="onSilverlightError" />
<param name="background" value="white" />
<param name="minRuntimeVersion" value="3.0.40624.0" />
<param name="autoUpgrade" value="true" />
<a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=3.0.40624.0" style="text-decoration:none">
<img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style:none"/>
</a>
</object>

So, the Xap file will be requested at the same location as the Html file, thus making the UriTemplate look as above.

Implementing the interface is simple. It is more or less identical to the GetClientAccessPolicy() method, except for the name of the resource streams…

public class SilverlightService : ISilverlightService
{
public System.IO.Stream GetClientAccessPolicy()
{
return Assembly.GetExecutingAssembly()
.GetManifestResourceStream("DarksideCookie.WCFHosting.ServiceConsole.ClientAccessPolicy.xml");
}

public System.IO.Stream GetHtmlPage()
{
return Assembly.GetExecutingAssembly()
.GetManifestResourceStream("DarksideCookie.WCFHosting.ServiceConsole.TestPage.html");
}

public System.IO.Stream GetXapFile()
{
return Assembly.GetExecutingAssembly()
.GetManifestResourceStream("DarksideCookie.WCFHosting.ServiceConsole.DarksideCookie.WCFHosting.SilverlightClient.xap");
}
}

 

 

That’s it. If you run the console app and then browse to http://localhost:8731/index.htm, the browser will be served up an Html page and a Silverlight control nicely without the need for a webserver. Or rather…I guess I have just created a mini-webserver using WCF.

In the Ticket Direct kiosk application, I am actually taking the whole thing a step further. In that application I am hosting the Silverlight application just like this, but I skip the web browser part by hosting that in the windows application as well. So it is becomes a all-in-one application. A simplified version could look like this… Remember this is a VERY simplified version with a lot of things that should be done differently, but it shows the idea…

Add a new Windows Forms project to the solution. And to save time, just add a reference to the Consol project. The console will not actually be used, but it exposes all of the service classes needed. The correct way would be to recreate the services in the windows project, but that would take to long… Or even better, the services should have been defined in its own project…but they aren’t…

Start off by adding a reference to System.ServiceModel. Then, in the Program.cs file, add a method called StartServices(). It should be static and take no arguments and return void. In the method, create 2 new ServiceHost objects to host the SilverlightService and the TimeService. Next, at the start of the Main() method, spawn a new thread using the StartServices() method as the thread start and start it. At the end of the method, kill it. The implementation should look something like this

static class Program
{
[STAThread]
static void Main()
{
Thread t = new Thread(new ThreadStart(StartServices));
t.Start();

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());

t.Abort();
}

private static void StartServices()
{
ServiceHost agHost = new ServiceHost(typeof(SilverlightService));
ServiceHost host = new ServiceHost(typeof(TimeService));
agHost.Open();
host.Open();
}
}

That should be enough to host the services.

Next, we need to add a web browser control to the window. Just drag a new web browser onto the form and you are done. In the forms Load event, use the web browsers Navigate() method to get it to browse to the correct place, in this case http://localhost:8731/index.htm.

private void Form1_Load(object sender, EventArgs e)
{
webBrowser1.Navigate("http://localhost:8731/index.htm");
}

Finally, we need to migrate the app.config file form the console project. Or rather…just copy it since there isn’t an app.config file in the Windows Forms project.

When that is done, the application is done. If you run the application, you will see a Silverlight application inside the browser window. It can be sent around as any other application…well…it needs administrative privileges to open the service ports…but except for that…

In the kiosk case, the window that is opened, opens in full screen mode, taking over the entire screen. And all that is needed is that one executable file. Well…I need a bit more in form of external assemblies and so…but it is true XCopy deployment. All services are embedded in the application….

Hope you have use for the idea…

Source: Windows Hosted Silverlight.zip (170.09 kb)

Cheers!

Comments (19) -

Can you explain why you are using Silverlight vs. WPF?  It seems that everything you are trying to do would work in WPF so you wouldn't need the WCF service, etc.

Hi Matt!
Well, it has to do with some stuff inside the application. We needed the same functionality available in Silverlight for a website and wanted code re-use. The application uses a custom perspective 3D based control and since perspective 3D is not available in WPF, we decided to go with Silverlight. We did initially aim for WPF as it is a better fit, but sometimes circumstances forces you to make somewhat "odd" choices...
Cheers
Chris

Thank you for the explanation.  I actually had something come across my desk today about something very similar.  Should come in very handy!

Hi Chris, I need to do the same thing (hosting SL without IIS) exactly, while I also want to use WCF RIA services. Do you know if possible to use WCF RIA services in self host mode? I didn't find any clues about this. Thanks!

James - that is a good question. I'm not sure you can. Not in the way that I am doing it here at least.
I had a quick look at how RIA is setup (I haven't tried it yet as I haven't had time) and it seems to be using an httpmodule called DomainServiceHttpModule for the RIA services. This module has 2 responsibilities. It registers a virtual path provider celled DomainServiceVirtualPathProvider and then rewrites any incoming request, rerouting it to be handled by the VPP if it is a request for a RIA domain service.
The VPP dynalically creates an svc file for you pointing to a DomainServiceHostFactory and the domain service type in question. So if you know what domain services you are going to expose, you should be able to to register WebGet methods for these in your hosting app. These just need to return the correct svc file, which is not that complicated. The VPP does something like this...

string.Fromat("<%@ ServiceHost Factory=\"{0}\" Service=\"{1}\" %>", typeof(DomainServiceHostFactory).AssemblyQualifiedName, DomainServiceType.AssemblyQualifiedName);

I haven' had any chance to test this at all. It is all just speculation... But it seems interesting. If you need more info or wonder what the heck I am talking about, drop me a line and I will see if I can get a sample working. I like the idea. Might be a blog post about it in the future... Smile

Sorry for my lousy spelling BTW. I just realized that I should read my comments before posting them...

Oh...and yeah...I looked at RIA services for Silverlight 4. It seems to be different in Silverlight 3. In 3, it seems to be using an .axd in the ClientBin folder. I assume that that axd file is hooked up in the web.config as an httphandler. Reflectoring that handler should give you a clue what is happening. THere is no magic, I promise...

Thanks mate! Smile I didn't receive the "new comments" notification, a bug? will try that. drop me an email if you get further on this RIA self host thing.

James

Hi, I am trying to solve a similar problem and I found your solution very interesting.
After failing to get the required behaviour, I downloaded your source code and gave it a shot. However, it doesn't seem to work on my machine. I get the same exception "attempting to access a service in a cross-domain way"....
From what I see the client is not requesting the ClientAccessPolicy.xml and I 'm not sure if it ought to.

Am I doing something wrong?

Sorry, I was actually refering to the code in your previous post "Silverlight and non-IIS hosted WCF-services"

A third comment on the same issue Smile

Sorry for bombarding with comments here. So when I go through the DarksideCookie.WCFHosting.WindowsApp, everything works. But when I start the console client and the server together, I get the exception I mentioned before.

Once sorry for the 3 comments which could easily be 1.

Hi Christos!
Can you explain what you are doing? The two posts are 2 different things. They are not meant to be running side by side. This might even cause problems as they both open ports. But if you explain what you are doing, I might be able to help you. Have you checked that the configured ports are correct and everything?

Yes, well what I am trying to do is have a silverlight windows sidebar gadget consume a local WCF service, in the absence of IIS. I 've tried using named pipes but silverlight does not support NetNamedPipeBinding.
And your solution would suit me fine. Except I can't get it to work, that is....Smile I simply downloaded the code in your previous post (and this one as well) and started both projects together (client-server). The server starts correctly, the silverlight client displays the "click me" button, but throws an exception when I click the button (An error occurred while trying to make a request to URI 'http://localhost:8731/TimeService/'. This could be due to attempting to access a service in a cross-domain....). I 've tried different ports and even switched the firewall off completely.

Many thanks for any help

Well...a guess is that that has to do with the different http schemes used. Your widget will be loaded from disk using file://. The "server" will be runnint http://. I don't think you are allowed to switch schemes during calls. I'm not sure, but I think I have seen something like that before...

This is very useful.

I was looking for a simple way to host silverlight without a webserver and thought of WCF, but I just wasn't sure how to do it (until now Smile)

Thank you.

hi dude, your code works perfectly. but it works only if we have IE 8. when i run this application in a system with IE 7, it is showing "Install silverlight", check the following screenshot for more details

http://i49.tinypic.com/2m7ssxk.jpg

you can get my sample application from here

cid-01a0be21578ff0ed.office.live.com/.../slhosting3.rar

when i run the application, it says "install silverlight", so i minimized it and i opened the testpage.html from the project folder and i see the output in browser.

this is the only problem. how do i solve this?

do i need to set any thing in my IE 7 or IE 6 browser or do i have to set anything in the code?

please help.

Hi Harsha!
I tried your code, and it seems to work. But I assume that that is because I have IE8 installed. I can unforunately not check IE7 as I don't have that on any of my machines.
I would however guess that the reason for the problem is due to some security settings in IE. Not sure though. But it might freak out about loading plug-ins in the web browser control or something. Have you tried hitting a page with Flash, just to see if it loads the Flash plug-in?
Sorry that I don't have a better answer for you...
Cheers!

hi dude, i tried with flash (.swf) file and it working. but only problem is with silverlight.

i didnt understand one thing. when i gave a sample silverlight template url

www.templatemonster.com/.../28362.html

to the webbrowser control, its working, it is displaying silverlight in it. but how ?

Hmm...that is weird. Well, I can't actually answer you. I suggest asking on the Silverlight forum or whatever Microsoft contact you have. I can't test it since I only run IE8...
Cheers!

Comments are closed