Speed up your ASP.NET site with compression


Loading speed is a crucial issue for every professional web site. If your website takes too long to load, your visitors, who are your potential customers, will go away and never come back no matter how much effort you have put to develop a professional code or design a charming user interface. There are several techniques to speed up your website, pre creation of pages, caching … and compression.

Server level and page level compression

Compression can be performed in two stages: IIS and each individual web site. If you enable compression on IIS (6.0 and later), as described here, all web sites and virtual directories are affected. Therefore, this technique is recommended only if you have the full control over that server and you own all the web sites on it. You must also get sure that all web sites on the server have no problem with getting compressed. If there are several web sites on the server who you are not the owner or administrator of them, you would better not bother by trying to compress them.

Page level compression lets you compress your own web site or even only specific pages or file types. For example we can set our compression mechanism to compress .aspx files only, and not html files, or not pages that use ASP.NET Ajax controls.

What do I need?

If you are using ASP.NET 2.0 and above (not 1.1) you do not need any 3rd party compression module or tool. All you need is provided by .NET Framework.

How to do it?

All we need to do is to compress the response output. It can be easily done by GzipStream or DeflateStream classes. Both of them use the same algorithm and the only difference is that GzipStream stores more header information on compressed file/stream to be used by 3rd party zip tools. Therefore DeflateStream is more light-weight. Before we compress the response output, we should get sure that the page we are doing to compress, supports compression. Otherwise, the user’s browser might not be able to decompress and display the page.

There are two ways of enabling response compression: First via HttpHandlers and Web.Config file, second by adding some code to global.asax file. Using a HttpHandler lets you change your compression module later without recompiling your web application and re-deploying it.

To compress the response output, all you need to do is to compress the Response.OutPut and assign it to Response.Filter. Response.Filter allows you wrap the Response.Output before it is transmitted towards the web browser.

context.Response.Filter = new
DeflateStream(context.Response.OutputStream,CompressionMode.Compress);

To create a HttpHandler, add a new class library project to your solution, including a public class which inplements IHttpHandler interface. Then implement it as the following code:

public
class
CompressMe:IHttpHandler

{

:

public
void ProcessRequest(HttpContext context)

{

string pageEncoding = context.Request.Headers[“Accept-Encoding”];

if (!string.IsNullOrEmpty(pageEncoding))

if (pageEncoding.ToLower().Contains(“gzip”))

{

context.Response.AppendHeader(“Content-encoding”, “gzip”);

context.Response.Filter = new
GZipStream(context.Response.Filter, CompressionMode.Compress);

} }

As seen in the above code, first we check to see if the page header indicates compression is supported or not. If it is supported, we create a new instance of DeflateStream (belongs to .NET Framework 2.0 +) and pass Response. Filter to it. This is the stream that we like to compress. The resulting stream is assigned to Response.Filter , meaning that a compressed copy of Response. Filter is transmitted to the browser.

The context.Response.AppendHeader(“Content-encoding”, “gzip”) thing says the browser that it must decompress the page before displaying it. Today’s browsers usually are smart enough to figure it out, but better safe than sorry.

Now build the class library and copy it into /bin folder of your web site or reference to it from within your web application. Suppose that your class library is called CompressionModule, it includes a namespace called CompressionModule and class named CompressMe (which implements IHttpHandler interface). Now add the following line of code to web.config file, under <httpHandlers> tag:

<add
verb=*
path=*.aspx
type=

CompressionModule.
CompressionModule,
CompressMe
/>

This means that all .aspx files, with both GET and POST methods, must be redirected to CompressMe class before being sent to the browser.

NOTICE: YOU MUST RUN YOUR WEB SITE UNDER IIS AND NOT VISUAL STUDIO DEVELOPMENT SERVER. OTHERWISE YOUR PAGE SIZE WILL BE ZERO BYTES.

Another way to activate the compression is to implement Application_PreRequestHandlerExecute in global.asax file. To do so, add an event handler to global.asax file, convert sender argument to HttpApplication and use its Reponse property exactly like you did in above code:

void Application_PreRequestHandlerExecute(object sender, EventArgs e)

{


HttpApplication app = sender as
HttpApplication;

…………

}

More benefits of compression in ASP.NET applications

Compression can even be more of help in your ASP.NET web site. For example you can compress uploaded files, especially if you store them in a data base since databases are very expensive and accessing them is time/resource consuming.

The following code demonstrates how to compress an uploaded file and save it:

As the code exposes, we access to the uploading file via FileUpload.PostedFile.InputStream which helps us to create a GzipStream object based on it. We also create a FileStream to store the compressed file in a .zip file. Afterwards, we start to read 2048-byte packets of the GzipStream and write it into the FileStream. That’s all J


private
void DoUpload(FileUpload fileUpload)

{


const
int bufferSize = 2048;


string fileName = Path.ChangeExtension(Path.Combine(Server.MapPath(Request.Url.AbsolutePath), fileUpload.PostedFile.FileName), “.zip”);


FileStream fs = new
FileStream(, FileMode.Create);


GZipStream zip = new
GZipStream(fileUpload.PostedFile.InputStream, CompressionMode.Compress);


try

{


byte[] buffer = new
byte[bufferSize];


int offset = 0;


int count = 0;

count = zip.Read(buffer, offset, bufferSize);


while (count > 0)

{

fs.Write(buffer, offset, count);

offset += count;

}

}


finally

{

fs.Close();

zip.Close();

}

}

You can download the sample projects from here.

Advertisements

Some MyCaptcha Improvements


Hi All,

I recently received a review about MyCaptcha control in CodePlex that proposed a couple of improvements in the source code.  This review indicates that :

  1. Thread.Sleep is not a good way to ensure that unique characters are selected for teh Captcha text.
  2. Sending the Captcha text to rendering component (.ashx file) is insecure.

Based on the above objections, I modified the source code and re-uploaded it to Codeplex. The modifications are as follows:

First, I removed Thread.Sleep thing from MyCaptcha.ascx.cs and replaced it with a do {..} while (…) loop to ensure that no letter is duplicate:

char newChar = (char)0;
do
{
newChar = Char.ToUpper(Valichars[new Random(DateTime.Now.Millisecond).Next(Valichars.Count() – 1)]);
}
while (Captcha.Contains(newChar));
Captcha += newChar;

To improve security when passing the Captcha text to rendering component (GetImgText.ashx) to ways come to my mind:

  • To include text generation logic into .ashx file
  • To encrypt captcha text before passing it to .ashx file.

As I mentioned before, generating a random text should not be depended on the way we display it. Therefore I don’t like mingling up the two codes. So I added some code to use a Symmetric algorithm to encrypt/decrypt Captcha text.

Thus, I added a new static class called SecurityHelper which includes two encryption and decryption methods . Symmetric algorithms  require a 16-letter key. Therefore I added a private property to give back the key. This key is optional and you can change it to whatever you would like:

private static byte[] SymmetricKey
{
get
{
return  Encoding.UTF8.GetBytes(“1B2c3D4e5F6g7H81”);
}
}

This post is too short to include the body of EncryptString and DecryptString methods. You can download the source code and see to it.

Back to MyCaptcha.ascx.cs , we need to use SecurityHelper.EncryptString to encrypt Captcha text before passing it to GetImgText.ashx generic handler:

ImgCaptcha.ImageUrl = “GetImgText.ashx?CaptchaText=” + SecurityHelper.EncryptString(Captcha);

We also need to change GetImgText.ashx.cs to make it decrypt QueryString value:

var CaptchaText = SecurityHelper.DecryptString(
Convert.FromBase64String(context.Request.QueryString[“CaptchaText”]));

This will make sure that nobody can sniff the captcha text. If you get the Captcha image properties in your browser, it will look like this:

http://localhost:51093/MyCaptcha/GetImgText.ashx?CaptchaText=V1rX8fZjL%2f8ghm1ZkCCsRoTryWDde8tJ7sejoIjoJKA%3d

I also tried to make the text more hard-to-read and a bit prettier so I changed the GetImgText.ashx file to draw an image background for Captcha. Background images come from /images/captcha folder so you need to create such a folder in your web site or modify the source code to point to an appropriate location. I have also added three sample background images, like a Valentine Day one:

vday

try other background images and decide which one do you like the most.

Please visit CodePlex page of this project and download the source code.

Pluggable modules for ASP.NET


When you design a modular ASP.NET application, soon or later you will need to think about adding extensibility features to your project so that it will be possible to add new modules at runtime.  There are a few architectures and designs that let you develop an extensible application, like ASP.NET MVP. However, many of them add a lot of complexity almost to everything and one should learn many concepts to use them.  Therefore, it’s a good idea to use other simple but innovative methods like the one I will explain bellow.

The method I am going to mention lets you develop an ASP.NET application and add some more modules to it later at runtime. In a nutshell, it has the following benefits:

  1. Allows adding new pages to an existing web application at runtime and does not need any recompilation.
  2. Allows adding new web parts to an existing content management system (Portal) at run-time.
  3. Several developers can develop different parts of an application.
  4. It is very easy to understand, develop and use.
  5. Does not exploit any 3rd party library so nothing is needed except Visual Studio.

And it has the following drawbacks:

  1. One should know the exact structure of an existing ASP.NET application, like folder hierarchies.
  2. May not cover all possible scenarios (Actually I have not taught about many scenarios).

How to implement it?

The design I am going to explain is possible only if you develop an ASP.NET Web Project rather than an ASP.NET web site.  As far as I remember, visual studio 2005 does not let you create a web project. If I am right, you need to use Visual Studio 2008. However, there are two main parts that we need to develop:

  • A web application project that includes main modules, main pages, and loads plugged modules, checks licensing, perform security tasks etc.
  • Plugged modules, which will add more pages, web parts and functionalities.

Main application and modules must match. It means that they must have same structure (i.e. folders), use same master pages and follow same rules.

The main reason that I used a Web Application Project, rather than a Web Site, was the benefits of a Web Application Project for developing a plug-in based web site. After building a web application project, there will be one assembly and several .aspx, .ascx, .ashx … files. After the web application is published, there is possibility to add more pages and files to it. Therefore, if at a later time we add several .aspx pages along with their .dll files, the web application will be able to work with those pages with no problem.

When developing the main application, you should consider a well formed directory structure, language specific contents, master pages etc. For example, your application should have a master page with a general name, like Site.Master. It also needs to maintain each module’s pages in a separate folder so that new modules can follow the same rule and avoid naming conflicts etc.

To develop the main application, follow the steps bellow:

  1. Create an empty solution in VS 2008.
  2. Add a new ASP.NET Web Project (not a web site) to the solution.
  3. Add any required folders like App_Themes and implement any required authentication, authorization and personalization mechanisms. Your web application must be complete and working.
  4. Add a master page to the web application project and name it Site.Master or another general name.
  5. Add a new Class Library Project and call it Framework (i.e. mycompany.myproject.Framework), common or whatever name that indicates this class library will be shared between the main application and dynamic modules.
  6. Add a new interface to the mentioned class library and call it IModuleInfo. This interface will be implemented with a class inside any pluggable module and will return root menu items that must be added to main application’s menu (or items to be added to a site navigation). It also can return a list of WebParts that introduces web parts that exist inside the module.

public interface IModuleInfo

{

List<MenuItem> GetRootMenuItems(string[] UserRoles);

}

UserRoles parameter is not mandatory. It shows that you can pass parameters to the method that returns a module’s main menu items. In this example, it indicates which Roles the current user has so that menu items will be filtered properly.

  1. Add a new ASP.NET Web Application project to the solution and name it SampleModule.
  2. Add a folder called SampleModule and if necessary, add more sub-folders.
  3. Add a web.config file to SampleModule folder and define which users/roles can access which folder.
  4. Add a master page named Site.Master. In fact , it must have same name with your master page in the main application.
  5. Add a public class with any name (I call it ModulePresenter) that implements IModuleInfo (this interface was added to Common or Framework library).

ModulePresnter class will return a list me menu items to main application. Main application will add those menu items as root items to its main menu afterwards. I will not bring a detailed code for the part that a module creates these items; it is dependent on your project.

public class ModulePresenter : IModuleInfo

{

#region IModuleInfo Members

public List<System.Web.UI.WebControls.MenuItem> GetRootMenuItems(string[] UserRoles)

{

List<MenuItem> items = new List<MenuItem>();

//:

//:

return items;

}

#endregion

}

  1. Compile this application and go back to the main application.
  2. Add an XML file and call it PluggedModules.xml. This file maintains the qualified type name of each module that must be loaded. A qualified type name includes assembly, namespace and class name

<?xml version=”1.0″ encoding=”utf-8″ ?>

<modules>

<module name=”SampleModule” type=” SampleModule.ModulePresenter, SampleModule.dll”></module>

</modules>

  1. Write a code to query PluggbedModules.xml, get menu items and attach them to main menu:

public static void LoadModules(Menu menuControl , string[] userRoles, string xmlName)

{

XDocument document = XDocument.Load(HttpContext.Current.Server.MapPath(string.Format(“~/{0}” , xmlName)));

var allModules = document.Elements(“modules”);

foreach(XElement module in allModules.Elements())

{

string type = module.Attribute(“type”).Value;

IModuleInfo moduleInfo = Activator.CreateInstance(Type.GetType(type)) as IModuleInfo;

List<MenuItem> allItems = moduleInfo.GetRootMenuItems(userRoles);

foreach(MenuItem item in allItems)

{

menuControl.Items.Add(item);

}

}

}

As seen in the above code, we query PluggedModule.xml file , extract introduced files and create an instance of it using Activator.CreateInstance method. Then extract IModuleInfo implementation, call GetRootMenuItems to get module’s menu items and add it to main menu.

After doing all the above steps, copy modules .dll file (generated after you build the project) to main application’s \bin folder and add it’s main folder (SampleModule) to main application’s root folder. It will work fine until all naming matches (for example both use master pages with a same name) and when specifying target URL in menu items, they point to a relative path, i.e. SampleModule/MyPage.aspx.

Please download the sample code from here.

Creating a Captcha control – Part:2


In this post I will explain how to generate a hard-to-read image out of our Captcha text. The new Captcha with image will look like this:

Final Captcha image

In MyCaptcha control, each letter is different from other ones in three properties:

1- Font

2- Size

3- Distance from the next letter (character spacing)

Therefore, I wrote a class named Letter that each instance of it holds a character along with all its properties like it’s font name and size. The class has a constructor that accepts a character argument and assigns random properties to it:

public class Letter

{

string[] ValidFonts = {“Segoe Script”, “Century”, “Eccentric Std”,“Freestyle Script”,“Viner Hand ITC”};

public Letter(char c)

{

Random rnd = new Random();

font = new Font(ValidFonts[rnd.Next(ValidFonts.Count()-1)], rnd.Next(20)+20, GraphicsUnit.Pixel);

letter = c;

}

public Font font

{

get;

private set;

}

public Size LetterSize

{

get

{

var Bmp = new Bitmap(1, 1);

var Grph = Graphics.FromImage(Bmp);

return Grph.MeasureString(letter.ToString(), font).ToSize();

}

}

public char letter

{

get;

private set;

}

public int space

{

get;

set;

}

}

As you see in the above source code, I pick a random font name from ValidFonts array. The font names in ValidFonts array are Windows Vista fonts. You must keep in mind that you use font names that exist on your Web Server. I also recommend you to use fantasy fonts (like gothic) to make the produced image more hard-to-read.

I also have added a property of type Size to get the width and height of the letter when it is rendered with its own font. To get the character size I use Graphics.MeasureString method.

The ‘space’ property is set when the Captcha text is being rendered. To render the captcha image, I use a generic handler (.ashx) file. Using a generic handler we can render any output type and send it to the output stream. An .ashx file can be treated like an .aspx file but has noticeably fewer overhead. For example we can pass query strings to it and generate the output based on it.

I will send the captcha text as a query string called CaptchaText to GetImgText.ashx generic handler. In the .ashx code, I will make an instance of Letter class for each character.

var CaptchaText = context.Request.QueryString[“CaptchaText”];

if (CaptchaText != null)

{

List<Letter> letter = new List<Letter>();

int TotalWidth = 0;

int MaxHeight = 0;

foreach (char c in CaptchaText)

{

var ltr = new Letter(c);

int space = (new Random()).Next(5) + 1;

ltr.space = space;

letter.Add(ltr);

TotalWidth += ltr.LetterSize.Width+space;

if (MaxHeight < ltr.LetterSize.Height)

MaxHeight = ltr.LetterSize.Height;

System.Threading.Thread.Sleep(1);

}

As the above piece of code shows, all Letter instances are stored in letter generic list. The width of each letter plus its distance with the next letter is summarized in TotalWidth local variable. We also get the height of the biggest letter so that we get sure all letters fit in the captcha image.

I also have two constants for vertical and horizontal margins:

const int HMargin = 5;

const int VMargin = 3;

Thus, our image will have a size of VMargin+MaxHeight and HMargin+TotalWidth:

Bitmap bmp = new Bitmap(TotalWidth + HMargin, MaxHeight + VMargin);

var Grph = Graphics.FromImage(bmp);

At next step, I will draw each letter with it’s own font size and position it according to it’s space property:

int xPos = HMargin;

foreach (var ltr in letter)

{

Grph.DrawString(ltr.letter.ToString(), ltr.font, new SolidBrush(Color.Navy), xPos, VMargin );

xPos += ltr.LetterSize.Width + ltr.space;

}

Now the image is cooked and ready! We should send it to the output stream:

bmp.Save(context.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);

Up to now the Captcha text is sketched well but I’d like to smudge it a little more in order to make it more hard-to-read. To do is I will draw a number of circles on random positions of the image. One nice idea is to define an IShape interface with a method named Sketch(). Then define different classes that implements IShape and draws different shapes on the image.

By the way, the code that smudges the image is this:

Color[] Colors = { Color.Gray , Color.Red, Color.Blue, Color.Olive };

for (int i = 0; i < 200; i++)

{

var rnd = new Random();

var grp = Graphics.FromImage(bmp);

grp.DrawEllipse(new Pen(Colors[rnd.Next(3)]), rnd.Next(bmp.Width – 1), rnd.Next(bmp.Height – 1), 5, 5);

System.Threading.Thread.Sleep(1);

}

You can download the source code of this control from HERE. The password of archived file is: aspguy.wordpress.com

There is also a project for this control at CodePlex. You can upload any changes you might make on the source code.

Have fun with Captchaing!

Aref Karmi

9 Apr 2009

Creating a Captcha control – Part:1


In this post and a post after I will explain how to develop a Captcha control and use it in an ASP.NET web site.

As described in Wikipedia,  A CAPTCHA or Captcha (IPA: /ˈkæptʃə/) is a type of challenge-response test used in computing to ensure that the response is not generated by a computer. In a crude term, a captcha control shows some hard-to-read letters on the screen and asks the user to enter the text in a box. Then checks to see whether the entered text is correct or not.

There are a lot of Captcha controls for ASP.NET that you can download and use in your project but, none is as interesting as the one that you write yourself and know exactly how it works!

I will explain the techniques of writing a simple but powerful Captcha control in two posts because it is made of two parts:

  1. The captcha control that displays a text and asks users to enter text in a box then validates it.
  2. The class that renders the Captcha text as a hard-to-read image.

Because the 2nd part can be reused for different purposes, for example in your own Captcha control, I will describe it in a different part.

OK let’s get started. The Captcha control that we are going to write has the following specs:

  1. Is developed as an .ascx control so it is reusable in every ASP.NET website.
  2. Saves nothing on your hard-disk so if you open a same page in two different windows (or tabs) there will be no conflict between two Captcha controls.
  3. Is very easy to use and needs no complicated concept like understanding and using http handlers.

The only drawback that I can count about this control is that it stores the generated text in ViewState thus, you must always encrypt it.

To create this control first I new a website. Then, I add a .ascx (web user control) to it called MyCaptcha. It looks like this:

Captcha xhtml script

In the above code,  lbltext displays the captcha text. txtCpatcha is a text box in which the user must enter the code. There is also a button named btnTryNewWords that re-generates the code if user has difficulties in reading it.

The control looks like this in design mode:

c2

The control has a property named LetterCount that specifies the number of letters in the code:

public

int LetterCount { get; set; }

It also has a private property that holds the generated key in ViewState.

private

string GeneratedText{

get{

return ViewState[this.ClientID + “text”] != null ?ViewState[

this.ClientID + “text”].ToString() : null;}

set

{

// Encrypt the value before storing it in viewstate.

ViewState[this.ClientID + “text”] = value;

As commented in the above code, you are highly recommended to encrypt the key before you put it in ViewState.

To generate the captcha text I wrote a public method called TryNew() that picks random letters from a list of characters and combine them to each other:

public void TryNew()
{
char[] Valichars = {‘1′,’2′,’3′,’4′,’5′,’6′,’7′,’8′,’9′,’0′,’a’,’b’,’c’,’d’,’e’,’f’,’g’,’h’,’i’,
‘j’,’k’,’l’,’m’,’n’,’o’,’p’,’q’,’r’,’s’,’t’,’u’,’v’,’w’,’x’,’y’,’z’ };
string Captcha = “”;
int LetterCount = MaxLetterCount >5 ? MaxLetterCount : 5;
for (int i = 0; i < LetterCount; i++)
{
int index = new  Random(DateTime.Now.Millisecond).Next(Valichars.Count()-1);
Captcha += Valichars[index].ToString().ToUpper();
Thread.Sleep(1);
}
GeneratedText = Captcha;
lbltext.Text = Captcha;
}

Because the captcha control won’t be case-sensitive, there is no capital letter in ValidChars array.

You also may have noticed the Thread.Sleep(1) statement in the above code! Why should we stop the running thread for one millisecond?!

The answer is that Random class uses DateTime.Now.Millisecond as it’s default seed. If the computer is too fast and the loop that selects and combines the letters is too short (for example 5 loops only) , the loop begins and ends in a millisecond or even a shorter period of time. Therefore, you will get equal letters only. To see and feel what I mean (!) remove Thread.Sleep(1) line and you will get a text like AAAAAA or 333333.

Anyway, there is also another public property called IsValid that indicates if the entered text is equal to captcha text:

public

bool IsValid{

get

{

bool result = GeneratedText.ToUpper() == TxtCpatcha.Text.Trim().ToUpper();

if (!result)TryNew();

return result;

}

}

That’s it. Now to test the control , drag MyCaptcha.ascx and drop it on Default.aspx page.  Your .aspx page will be like this:

<

uc1:MyCaptcha ID=”MyCaptcha1″ runat=”server” />

<br />

<asp:Label ID=”lblCheckResult” runat=”server” Text=”?”></asp:Label>

<br />

<asp:Button ID=”btnCheck” runat=”server” onclick=”btnCheck_Click”

Text=”Check it!” />

in btnCehck_Click event handler, write the following piece of code:

if

(MyCaptcha1.IsValid)lblCheckResult.Text =

“It is ok” ;

else

lblCheckResult.Text =

“oops!, invalid text was entered.” ;

After running the website, you will have a captcha control like the image bellow:

c3

In the next post I will show you how to scramble the text in a way that only a human with a high eye-sight can read it 🙂

I also might add some extra codes so that it can work along with other Validation controls on the page.

Desktop Sharing!


In some applications, there is a need to desktop/application sharing, whiteboard etc. Microsoft Office Communications Server 2007 is a great solution for you in case you need Video Conferencing, Desktop/App. sharing, whiteboard and so on. Of course, only MSCS R2 Web Access which can be installed on a x64 OS is suitable for web-based desktop sharing.

Since MSCS is a sort of expensive server and it will make your customer to pay an arm and a leg to purchase required licenses, you may need a simpler and cheaper solution. For instance, in a web-based Learning Management System (LMS) application, there was a need to enabled desktop sharing for teachers. Only teachers would share their desktop and there was no mutual interaction  between the instructor and learners. As you may guess, the customer could not afford the cost of MSCS licenses.

To answer this need, I used Microsoft Media Services and Microsoft Media Encoder. Using these two services, the instructor was able to capture his/her computer’s screen and broadcast it over the network so that students could see what he/she was doing.

What you need:

1- Windows 2003 Server

2- Windows Media Encoder

3-Windows Media Player

To install Windows Meddia Services, on the computer which should be your streamign server, go to Control Panel -> Add Remover Programs -> Add/Remove Windows Component. Then at the buttom of  Windows Component Wizard dialogbox, check  Windows Media Services checkbox and click OK.

windows_media_srv

After installing Windows Media Services, you need to configure it. There are two things that should be configured:

  1. Authentication
  2. Protocol Control

Go to Administrative Tools ->Windows Media Services snap-in. Then click on the name of server computer and navigate to Properties tab. Find Authentication and open properties of each authentication method (if availible). Specify who can access this service and grant each user or group proper access rights.

authorization

Logically, students should have Read access but Teachers should have Create and Write access along with Read access because a teacher pushes the media content to media server.

At the next step you should enable control protocol and configure it. If your server is configured as a web server and has IIS installed, IIS is likely to occupy port 80. Therefore, you need to specify another open HTTP port thorough which Windows Media Encoder can push media content to server. If port 80 is used, WMS Http control protocol is disabled. right-click on it and choose Enabled item. Afterwards, open properties window and specify a new port number as shown in the picture bellow:

controlprotocol1

At this stage Windows Media Services is configured and ready to use.

On each instructor’s client computer, install Windows Media Encoder. To begin broadcasting, run it and from New Session window, go to Quick Starts tab and choose Broadcast Company meeting (then click next).

New Session Window

Then Windows Media Services Publishing Point window is appeared. In this window, enter server IP or name along with the Port number you specified in WMS Http Control Protocol window. You must also specify a name for your publishing point. For example, VisualC#1 !

Publishing point windowBy clicking on OK button, Windows Media Encoder begins and Properties window is seen. If not, click on “Properties” button on toolbar. In this place, for Video, choose Screeen Capture. You can click on Configure button and tell Windows Media Encoder how to capture your computer screen. Available options are: Entire Screen, Region or Window.

If you wish to broadcast your computer’s audio, enable Audio and select your preferred audio device.

Also, for “At End” option, choose “Stop”!

Properties window

At the next step go to Output tab, and uncheck these two options:

  1. Pull from encoder
  2. Archive file

Output

After doing this, go to Compression tab and from Destination drop-down, choose Windows Media Server option. Then from the Video drop-down control, select “Screen Capture (CBR)”. You can also change the Bit rate. The higher Bit tare meand the higher quality but slower transmition.

Compression

Ok! This process might be a little complicated and hard to memorise. No worries, go to File menue and save your configuration. You can provide your saved configuration file to your teachers and ask them to simply double-click on the file to begin recording (except they should change the name of publishing point)!

The Publishing Point name must be generated by your application and shown to the instructor to be used in Windows Media Encoder and also should be used in the client’s viewer for connecting to media server.

Clients can visit the streamed video by Windows Media Player. The protocol used for watching streamed videos is MMS, like http! for example , we may use mms://videoserver/myvideo

At the above address, Videoserver is server’s name and MyVideo is the publishing point name.

To create the client’s viewer we need to embed Windows Media Player ActiveX into our .aspx page. Visit this address to see how this is done:

http://www.mioplanet.com/rsc/embed_mediaplayer.htm

After embeding, URL parameter must be set properly. The best way is to write a function (with Internal access modifier) to generate the url and bind it to URL parameter:

<PARAM NAME=”URL” VALUE=”<%=GetMMSURL()%>”>

Each time the instructor begins broadcasting, students can open their viewing page and watch the deskop of their teacher! The teacher can display his/her desktop, open an application and show it (like in a Demo session) or open Microsoft Paint and use it as a white board!

The approach above is a one-way method unless you provide a facility for clients to send text messages to teacher and ask questions. However, for low incomers, this should be a reliving way of desktop viewing 🙂

Creating a Log Visit Report


oops.. today I’d decided to post a new article but surprisingly noticed that the blog was suspended! Thanks to Matt, it is alive and kicking now!

Anyway, lets see how we can generate a log visit report for our ASP.NET web site. By the term of Log Visit, I mean the statistics of a website which shows at which time, which page (or file) has been viewed by who!

The good news is that IIS can record the above information for us. It can log all activities done by your web site in the following ways and formats:

W3C Extended log file format

World Wide Web Consortium (W3C) Extended format is customizable and is saved in an ASCII text file. You can select your desired fields among a lot of choices.

IIS log file format

IIS log file format has  fixed columns and is stored as an ASCII text file. This file includes more information than W3C format.

NCSA Common log file format

National Center for Supercomputing Applications (NCSA) Common log file format is a fixed ASCII format that is available for Web sites, but not for FTP sites

ODBC logging

Open Database Connectivity (ODBC) logging format is a record of a fixed set of data properties in a database.

Logging into text files is quicker than logging into a database. It does not have any performance pitfall but when it comes to reporting, you probably should import the text file into a database.

Logging by ODBC will slow down your website to some extent  because it records very much of (sometimes unnecessary) information. For example, in W3C format, only information about pages who have been loaded successfully is stored. While in ODBC method, all referred objects, like .js or image files, are recorded, no metter if the resource have been loaded successfully or not.

By the way, since ODBC will make this post shorter, I will choose ODBC method for my sample program.

To enable logging with ODBC, first you must create a table for storing log information and then create an ODBC data source thorough which IIS can store logs.

To create the log table, which is usually called INETLOG, go to System32\InetSrv folder and find LogTemp.Sql file. This file includes a script that creates the required table into your database. This script has been written for Sql Server, but you can modify it to be executed on other databases like MS Access or even My Sql.

In the next step, create a SYSTEM DNS that points to the above table. You must create a System DNS because IIS will use the DNS with a user different from the one you have logged into your computer (usually NetWork Service account).

To create a new System DSN, go to Control Panel-> Administrative Tools and then double-click Data Sources (ODBC) . Then, go to System DNS tab and click Add. Select SQL Server, enter the name of your server and database and specify a user/password. You would better enter a SQL SERVER login (do not choose Windows Authentication) and specify a password that will not be changed.

After doing so, change the default database of this DSN to the database that logging table resides on. Click OK and close the dialog box.

Now go to Control Panel -> Administrative Tools  and double-click on Internet Information Services Manager.

Open Web Sites, and then find your Virtual Directory or Web Site. Right-click on it, choose properties and under the Home Directory tab, find Enable Logging check box. Enable the check box and In the Active log format list box, select ODBC. Afterwards, click on Configure button to view the ODBC settings. In this dialog box, enter the name of ODBC DSN you created in the previous step, name of the table (inetlog), user name and password of logging data base.

For example:

ODBC: WordPressLogDSN

Table: InetLog

Login: sa

Password: 123

In the above sample I used “SA” SQL SERVER’s default login name. This is a risky job and I do not recommend you do it! You would better use a user name with no administrative permission which only has access to logging table or database.

From now on, any visit to a page will be recorded!

NOTICE: The logging table will be filled with tons of records, therefore, I strongly recommend you to store InetLog table on a seperate FileGroup. Also, You would better to schedule a script that removes unnecessary records from inetlog table.

Writing The Code

In the rest of the post I am going to write a sample page that displays how many times each .aspx or .html page has been visited. I will show the stats in numeric and bar chart format.

As I mentioned, data about any referred resources will be stored in InetLog table. Thus, we should be able to determine if a record belongs to a Page or not. Only pages whose ServiceStatus is equal to 200 have been loaded or access with no error. So, I need a Sql function that determines if a resource is a page (i.e. .aspx) and if it has been accessed successfully.

I will call the function IsPage. The code is very simple so I just bring the script body here:

create function IsPage(   @PageName varchar(200) )
returns int
begin
Declare @len int
set @len = len(@PageName)

if Substring(@PageName, @Len, 1) = ‘/’
set @PageName = Substring(@PageName,1,@Len-1)

Declare @Extension  Varchar(4)
Set @Extension = Upper(Substring(@PageName,@Len – 3 , 4))

if @Extension = ‘ASPX’ or @Extension = ‘HTML’
return 1
return  0
end

I will use this function to create my stats and also to remove records of non-page resources like .js files (if I don’t need them).

At the next step I will write a stored procedure that determines how many times each page has been visited. To let the script run on older versions of Sql Server, I have written it as simple as possible:

create procedure rep_LogVisit
@BeginDate smalldatetime,
@EndDate smalldatetime
as
select Count(1) as FileCount, target as PageName
from inetlog
where ServiceStatus = 200 and dbo.isPage(target) =1
and LogTime>=@BeginDate and LogTime<=@EndDate
group by dbo.GetFileName(target)
go

As the above code indicates, rep_LogVisit counts the records who’s ServiceStatus equals to 200 (successful) , the resource is a page and the log has been recorded between a given date.

In order to display the result, I use a GridView control, having two columns. One for the page name, and one for the visit count:

<asp:GridView ID=”GridView1″ runat=”server” AutoGenerateColumns=”False”

CellPadding=”4″ ForeColor=”#333333″

GridLines=”None”>

<Columns>

<asp:BoundField DataField=”target” HeaderText=”Page Name” />

<asp:TemplateField HeaderText=”Visit Count”>

<ItemTemplate>

<asp:Panel runat=”server” ID=”divBar” style=”background-color:Red” Width='<%#GetLength((int)Eval(“TotalCount”)) %>’>

<asp:Label runat=”server” ID=”lblCount” text='<%#Eval(“TotalCount”) %>’ ForeColor=”White”></asp:Label>

</asp:Panel>

</ItemTemplate>

</asp:TemplateField>

</Columns>

</asp:GridView>

Well, I will call  rep_LogVisit stored procedure, bind the grid to it’s result. I also have to write a function called GetLength in order to calculate the lenth of each bar:

int MaxCount = 0;

protected void Page_Load(object sender, EventArgs e)

{

if (!IsPostBack)

{

SqlConnection conn = new SqlConnection(“Your Connection String Here”);

SqlCommand cmd = conn.CreateCommand();

cmd.CommandType = CommandType.StoredProcedure;

cmd.CommandText = “rep_LogVIsit”;

// Add date parameters here. skipped to simplify the code.

DataTable table = new DataTable();

SqlDataAdapter adapter = new SqlDataAdapter(cmd);

adapter.Fill(table);

// Get the bigger visit count

MaxCount = (int)(from C in table.AsEnumerable()

select C).Max(c=>c[“TotalCount”]);

GridView1.DataSource = table;

GridView1.DataBind();

}

}

// Calculate the length of each bar

internal int GetLength(int TotalCount)

{

if (MaxCount == 0)

return 0;

else

return Convert.ToInt32(Math.Round(((double)TotalCount / MaxCount)*300));

}

At run-time, you will see something like the image bellow:

untitled1