Monday, July 28, 2008

Capturing HTTP Status codes with 'WebBrowserControl'

WebBrowser is a winndows forms control which allows you to add web browsing capabilities to windows application developed with .net technology(i.e C#.net or vb.net).This control acts in a similar way to Internet Explorer Browser.Its supports methods & events to navigate to url ,properties to work with HTML Document Object Model(DOM) etc.But it doesn't allow to capture directly some things programatically,for example HTTP status codes.If the requested web page is not found then HTTP Status code '404' is shown on web browser,but with if u want capture this status code programatically,its not straight farward. Actually WebBrowser windows forms control is wrapper WebBrowser ActivexControl in 'Microsoft Internet Control(SHDocVw.dll)' library which is a COM component.WebBrowser windows forms control doesnt support all the events supported by Activex Control (Web Browser).Activex Control supports 'NavigateError' event which will be raised when any error occured while navigating to url.By handling this event one can get HTTP Status codes for a requested url.
To capture 'NavigateError' with windows forms 'Web Browser' control a work around is there.Its possible to cast windows forms 'Web Browser' into Activex control.The under lying ActiveXControl of windows forms WebBrowser control can be accessed through a property called 'ActiveXInstance'.The code looks like this

Code :
Following library to added to the project.
* 'Microsoft Internet Control(SHDocVw.dll) which can be found under COM tab of Add Reference window.


System.Windows.Forms.WebBrowser wb = new System.Windows.Forms.WebBrowser();
System.Windows.Forms.WebBrowserBase wb1;
SHDocVw.WebBrowser wb2 = (SHDocVw.WebBrowser)wb.ActiveXInstance;
wb2.NavigateError += new DWebBrowserEvents2_NavigateErrorEventHandlerwb2_NavigateError);

//event handler for NavigateError event
void wb2_NavigateError(object pDisp, ref object URL, ref object Frame, ref object StatusCode, ref bool Cancel)
{
throw new NotImplementedException();
}

If any error occured while navigating to URL the 'NavigateError' will be raised.The 'StatusCode'
parameter of the event handler gives the HTTP Status code.
Note:Not all the HTTP status codes can be get through this approach.As the event is raised only when an error occured while navigating,only HTTP status codes related to error can be accesed.Following are the list of HTTP Status codes that can be accessed.
HTTP_STATUS_BAD_GATEWAY (502)
HTTP_STATUS_BAD_METHOD (405)
HTTP_STATUS_BAD_REQUEST (400)
HTTP_STATUS_CONFLICT (409)
HTTP_STATUS_DENIED (401)
HTTP_STATUS_FORBIDDEN (403)
HTTP_STATUS_GATEWAY_TIMEOUT (504)
HTTP_STATUS_GONE (410)
HTTP_STATUS_LENGTH_REQUIRED (411)
HTTP_STATUS_NONE_ACCEPTABLE (406)
HTTP_STATUS_NOT_FOUND (404)
HTTP_STATUS_NOT_SUPPORTED (501)
HTTP_STATUS_PAYMENT_REQ (402)
HTTP_STATUS_PRECOND_FAILED (412)
HTTP_STATUS_PROXY_AUTH_REQ (407)
HTTP_STATUS_REQUEST_TIMEOUT (408)
HTTP_STATUS_REQUEST_TOO_LARGE (413)
HTTP_STATUS_RETRY_WITH (449)
HTTP_STATUS_SERVER_ERROR (500)
HTTP_STATUS_SERVICE_UNAVAIL (503)
HTTP_STATUS_UNSUPPORTED_MEDIA (415)
HTTP_STATUS_URI_TOO_LONG (414)
HTTP_STATUS_VERSION_NOT_SUP (505)

FAQS

This post is meant to give some information in question & answer pattern so that it'll be usefull for quick reference.I'l keep on adding stuff to these post when ever i found a deserving thing.

1)Tips to Optimize performance of stored procedure.
A developer has wrriten a stored procedure.Its working fine, in the sense its giving the expected output for set of given input.But you've got a feedback that the performance is not good & you take neccesary action to improove the performance.In such a scenario following the below things may lead to better performance.

a)Set 'SET NOCOUNT ON' before your stored procedure.This will stop sending number of rows affected as a result of this stored procedure.This can reduce network traffic.

b)calling stored procedures using thier fully qualified names may increase performance.Using fully qualifies names increases the chances reusing of stored procedure execution plans.

c)use RETURN statement whereever possible.This stops sqlserver building resultset.

e)dont use 'sp_' for stored procedures which are in databases other than in master database.If u hav a stored procedure with 'sp_' as a prefix , sql server will look in to the master database first & then looks for other database.

f)If the StoredProcedure is very large ,then consider to split it to many sub storedprocedure's .the reason is when any structural changes occured or bulk updates or inserts or deletes occured in table associated with stored procedure then stored procedure will recompile.If it is splitted in to many sub stored procedure's chances are more that few sub sp's will be recompiled.

g)Try to avoid using temporary tables in sp.using of temp tables will reduces the chances of reusing the execution plan of stored procedure.

h)try to avoid using DDL in stored procedure.using DDL in sp again redces the chance of reusing execution plan.

Checking for the above things helps in tracing where to refactor.

Tuesday, July 22, 2008

How to promt for system restart after installing Windows Installer Package(*.msi) developed in Visual Studio

With Visual Studio one can develop variety of applications.After developing application the next step comes deploying application.For this Visual Studio supports project template called 'Setup and Deployment Projects'.With this project template one can develop setup.exe for any application developed using Visual Studio.But for some applications after deploying unless the system is restarted the application wont work as expected.The typical example for this kind of scenario is if application contains any registry additions.In such a cases its always recommended to prompt for system restart soon after completing installation.
This article describes how to achieve this.To do this one has to edit properties of *.msi file.*.msi file is created in Debug or Release folder once your setup & deployment project in visual studio is bulit.
To edit properties of *.msi file one can do it in 'Orca' editor.
'Orca' is a tool which can be used to edit properties of Windows Installer Packages(*.msi) .For more info on Orca visit http://msdn.microsoft.com/en-us/library/aa370557.aspx

The next step is edit properties .open your *msi file in Orca editor with the help of 'Open' menu item in 'File' menu.Take a look at image below.

After opening *.msi file in Orca editor you will find 'Table' pane on right side of the editor.Under 'Table' select 'Property' to select property table.This shows the list of properties & its values in right pane.Right click the 'Property' header & select 'Add Row' menu item.Then a pop up will be opened where name & value of property give.Give 'REBOOT' as name & 'Force' as value & save.Its done now.If you install that package(*.msi) then after completion of installing a automatic popup will appear prompting for system restart.Chekout image below.

Saturday, July 12, 2008

Cookie issue with 'HttpWebRequest' object

Recently i have a requirement to programatically get the HTML mark up of a given url with C# coding.After a bit R&D work i found 'HttpWebRequest' object in 'System.Net' namespace will server my purpose.Following lines of code can get the HTML mark up of a given URL.

//code

HttpWebRequest req = (HttpWebRequest)WebRequest.Create("url");
HttpWebResponse res = (HttpWebResponse)req.GetResponse();
Stream str = res.GetResponseStream();
StreamReader streader = new StreamReader(str);
string htmlContent = streader.ReadToEnd();

in the above code 'htmlContent' contains the HTML mark up of the 'url' .Ideally this code should work for the all the urls.But there may be cases where the requested source is moved to some other web server & the request is redirected to new location by attaching some cookie to incoming request , in such a scenarios the above code wont work, it'l give excetion the reason is by default cookies are disable for 'HttpWebRequest'

The solution for this problem is to enable cookies for 'HttpWebRequest' object.This can be done with following piece of code.

//code
req.CookieContainer = new CookieContainer();

Note: setting of 'CookieContainer' property of 'HttpWebRequest' should be done before calling 'GetResponse()' method.

With this code cookies are enable for 'HttpWebRequest'.

Disable Close option on system menu for Windows Forms

This article decsribes about how to disable a close option of system menu for a windows form with C#.This can be done by calling win API functions.

First let me explain what 'System menu' is ?

System menu:
-----------------
In windows o/s for any opened window a button is added in the task bar.When clicked on the button the window will be opened if it is minimised and vice versa.When right clicked on task bar button a menu will be opened,technically this menu is know as 'System menu',also called as Window menu'.This menu can also be accesed by by right clicking on title bar of the window & clicking on the icon on title bar of windThis consists menu options like 'Close','Minimise','Maximise','Size','Move','Restore'.All these terms are self explanatory.Check out the image below.


The present article explains about how disable any option in system menu.
Win API provides set of methods to deal with menu & submenus.These methods can be used to modify the menu's.
Following are the winAPI functions needed for requirement
1)GetSystemMenu ---- get access to system menu.
2)EnableMenuItem --- modifies system menu.


To call this methods from C# code .Net framework provides a way called 'Platform Invoke'.

PlatformInvoke:

Platform Invoke concept allows to call methods from managed code which are in unmanaged DLL's. For more information on this ckeck out
"http://msdn.microsoft.com/en-us/library/aa288468.aspx"

To call a method in managed code first that method to be declered with [DLLImport] attribure.This attribute should be applied to methods which are declared in managed code & actually implemented in unmanaged DLL.The code looks like this .

[DllImport("user32.dll")] private static extern IntPtr GetSystemMenu(IntPtr Hmenu, bool Brevert);

Parameters Info:
---first parameter of above function is a handle to window.
---Second parameter must be 'false' so that the menu can be modified.

Note:DllImport atribute takes Dll name as parameter.In our case its 'user32.dll'

[DllImport("user32.dll")] private static extern bool EnableMenuItem(IntPtr Hmenu, uint Uposition, uint UEnable);

Parameters Info:
--Fisrt parameter is the handle to system menu.Its return value of the method 'GetSystemMenu'
--Second parameter specifies the which menu item to be modified.Value of the parameter also depends upon the value of 'UEnable' parameter.
--third parameter is combination of values which indicates whether the menu item is enabled, disabled, or grayed.The combination of possible vales are MF_BYCOMMAND or MF_BYPOSITION and MF_ENABLED, MF_DISABLED, or MF_GRAYED.

MF_BYCOMMAND
Indicates that uIDEnableItem gives the identifier of the menu item. If neither the MF_BYCOMMAND nor MF_BYPOSITION flag is specified, the MF_BYCOMMAND flag is the default flag.
MF_BYPOSITION
Indicates that uIDEnableItem gives the zero-based relative position of the menu item.
MF_DISABLED
Indicates that the menu item is disabled, but not grayed, so it cannot be selected.
MF_ENABLED
Indicates that the menu item is enabled and restored from a grayed state so that it can be selected.
MF_GRAYED
Indicates that the menu item is disabled and grayed so that it cannot be selected.
values for the above constants can be found in winuser.h header file in windows platform SDK.

These declarations to be made in global area of the code part of the form(like form.cs).
After declaring the methods required the next step is to call that method in approptiate place.In this case form load event is appropriate place to call the methods.

//Code in form load
EnableMenuItem(GetSystemMenu(this.Handle, false), SC_CLOSE, MF_BYCOMMAND | MF_GRAYED);

Values for the constants
-------------------------
private const int MF_BYPOSITION = 0x00400;
private const int MF_GRAYED = 0x000001;
private const int MF_ENABLED = 0x000000;
private const int MF_BYCOMMAND = 0x00000;
private const int SC_CLOSE = 0xF060;
private const int SC_SIZE = 0xF000;
private const int SC_MOVE = 0xF010;
private const int SC_MAXIMIZE = 0xF030;

After running the code system menu for the form looks something like this.