Introduction
Have you ever wanted to show up an ASP.NET project where IIS was not accessible? Have you ever thought of if there can be a way to carry your web server wherever you go, i.e., on a USB flash stick or even a CD?
If so, then this cool TinyWebServer is for you! This tiny portable web server can be used for testing and developing ASP.NET projects, wherever IIS is not available.
Background
Very often, I had looked for a way to run an ASP.NET project without needing IIS to be installed. I had wondered how Microsoft Visual Studio 2005 could run its ASP.NET Development Webserver regardless of IIS. I tried more and more to find such a solution ... I made Google angry with my no-result searches, but still there was no solution. Therefore, I decided to find a way to write my own ...
As you know, VS2005 has a nice development server included, called "ASP.NET Development Server". If you look at how it actually does this, you'll find a console application named WebDev.WebServer.EXE located in your .NET 2.0 installation folder, which usually is "C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727". If you run it, you will see something like this:
As you see, there are command-line options that can be set in order to run the ASP.NET Development Server on your specified folder. Well, seems to be okay, but there still are some issues remaining:
- You should pass the parameters you specified whenever you want to run it again, or you should make a batch file to do it for you. There should be a better way!
- Working with a console application such as this in a GUI-based platform is not so interesting. There should be a better way!
- Although VS2005 finds a free and available port to run the web server for you, this console application will take it from a parameter you pass, which means the port you specify in your command line or batch file may not necessarily be available.
- This console application uses a component called WebDev.WebHost.dll which is registered in the GAC (Global Assembly Cache). Since you won't see it and it won't be available in your search results, it can't be copied using XCOPY, as you might think!
What can we do then?
TinyWebServer at a Glance
The main part of the server is in a nice and tiny component named WebDev.WebHost.dll which we will try to reach. This tiny DLL is located in the following directory:
c:\WINDOWS\assembly\GAC_32\WebDev.WebHost\8.0.0.0__b03f5f7f11d50a3a\
While you try to reach to this path through Explorer, you will find out that Explorer can not easily show the folder due to an Explorer extension for GAC that gets installed while installing the Microsoft .NET framework.
So what? Easily try to reach this path using the command prompt? Found it? OK! Copy it to an accessible path and do the following ...
Start Microsoft Visual Studio 2005, create a new Windows Forms VB.NET project, add a reference to the DLL you copied, and create a class and name it WebSite
.
Imports Microsoft.VisualStudio.WebHost
Imports System.IO
Public Class WebSite
Inherits Server
Implements IDisposable
This class I've inherited from Server
, is the main class used for approaching the Web Server I was looking for. This class provides a constructor with these parameters: Port
[the port you are going to use for your site], PhysicalPath
[used for specifying the folder your site will be on], VirtualPath
[which is used as your website virtual root], and Start
and Stop
methods to start and stop your service.
As you can see, the main idea and tool was what was mentioned above, all other things are done just for the GUI, finding a random available port, loading and saving configuration into an XML file, and etc.
Points of Interest
- One of the things I encountered since developing is that the
Server
class which my WebSite
class is inherited from has no writable properties to set the Port
, PhysicalPath
, and VirtualPath
. They are all read-only properties!
But I needed to be able to change a particular website I've defined whenever I needed to. To solve this problem, I decided to create a shadow class named WebSiteData
, which would be serializable and could contain all the properties from WebSite
, and some other properties like a GUID
, and a property named Enabled
to store the started/stopped status of the website.
- Another interesting thing is how I approached to find a random available port, and also checking the availability of a specified port:
Imports System.Net
Imports System.Net.Sockets
Public Class PortHelper
Public Shared Function RandomPortAvailable() As Integer
Dim _portAvailable As Boolean = False
Dim _port As Integer = 0
Dim _listener As TcpListener = Nothing
Dim _rnd As New Random(Now.Millisecond)
While Not _portAvailable
_port = _rnd.Next(1001, 1999)
Dim _ipEntry As IPHostEntry = _
Dns.GetHostEntry(Environment.MachineName)
Dim _ipAddr() As IPAddress = _ipEntry.AddressList
Try
_listener = New TcpListener(_ipAddr(0), _port)
_listener.Start()
_portAvailable = True
_listener.Stop()
Catch ex As Exception
_portAvailable = False
End Try
End While
Return _port
End Function
Public Shared Function IsPortAvailable(ByVal iPort As Integer) _
As Boolean
Dim _listener As TcpListener = Nothing
Dim _ipEntry As IPHostEntry = _
Dns.GetHostEntry(Environment.MachineName)
Dim _ipAddr() As IPAddress = _ipEntry.AddressList
Try
_listener = New TcpListener(_ipAddr(0), iPort)
_listener.Start() : _listener.Stop()
Return True
Catch ex As Exception
Return False
End Try
End Function
End Class
And the thing is, I wanted to use the PropertyGrid
object for my website properties dialog, so, I was looking for a way to enable a user to randomly find and select a free port through the PropertyGrid
interface. As you can see, the UITypeEditor
inherited class is the result.
Imports System.Drawing.Design
Imports System.Windows.Forms.Design
Imports System.ComponentModel
Public Class PortEditor
Inherits UITypeEditor
Public Overrides Function GetEditStyle _
(ByVal context As ITypeDescriptorContext) _
As System.Drawing.Design.UITypeEditorEditStyle
Return UITypeEditorEditStyle.Modal
End Function
Public Overrides Function EditValue(ByVal context As ITypeDescriptorContext, _
ByVal provider As System.IServiceProvider, _
ByVal value As Object) As Object
Return PortHelper.RandomPortAvailable
End Function
End Class
And a part of the WebSiteData
class in which I defined the Port
property:
<Category("Service")> _
<Description("Specifies the port, on which service will be available.")> _
<Editor(GetType(PortEditor), GetType(System.Drawing.Design.UITypeEditor))> _
Public Property Port() As Integer
Get
Return _port
End Get
Set(ByVal value As Integer)
If PortHelper.IsPortAvailable(value) Then
_port = value
If _name.IndexOf("WebSite on Port:") > -1 Then
Me.Name = String.Format("WebSite on Port:{0}", _port)
End If
Else
MessageBox.Show(String.Format("The port you specified, {0}, " & _
"is busy by another process. try another.", value), _
"Alert", MessageBoxButtons.OK, MessageBoxIcon.Warning, _
MessageBoxDefaultButton.Button1)
End If
End Set
End Property
History
- Version 1.0.0.1: 17th Nov., 2007