Twitter Feed Popout byInfofru

Authenticated File Access using HTTP Handler.

In this post I will explain you how authenticate the request directly coming to access a file that is downloadable. some thing like *.pdf or *.zip.

Mostly, people make it working by creating an *.aspx page and then write binary of that file in Response.WriteFile. So, user will have no idea where the file is coming from. now this is the fair approach but what if somebody, somehow know the path of downloadable files.

So, to stop the un authenticated access to our files, we will first create a session enable HTTP handler.

public class MyHttpHandler : IHttpHandler, IReadOnlySessionState
{
 
    public void ProcessRequest(HttpContext context)
    {
        if (context.Session["userId"] == null)
        // I am using a session variable you can also use context.User.Identity.IsAuthenticated
        {
            context.Response.Redirect("/login.aspx?retUrl=" + context.Request.RawUrl);
            //Redirecting to the login page ... alternatively you can also set context.Response.StatusCode 
        }
    }
 
    public bool IsReusable
    {
 
        get { return false; }
    }
}

Now, once we have created that. Let me register my newly creater handler for *.zip and *.pdf files in web.config.

 

<httpHandlers>
  <add verb="*" path="*.zip" type="LearningApp.MyHttpHandler, LearningApp"/>
  <add verb="*" path="*.pdf" type="LearningApp.MyHttpHandler, LearningApp"/>
</httpHandlers>

That’s it. If you want more file types to be authenticated add more verbs in handler section of HttpHandler.

Don’t try to put *.* : That can create some serious problem because then each of your *.aspx, *asmx and all your logic stuff will need authentication.

Disable Special Keys in Win App C#

Today, when I was planning to write an article on Grid View. I got a message from a very good friend of mine who is asking to disable the special keys(Windows Keys) in his application. When I start researching on it, I was thinking that it can be done using e.KeyChar but unfortunately, it is not showing any information about windows keys.

So in this post I will explain you, how can we disable the special keys (in our case windows keys) in C# Application.

1. Crete a c# windows application project
2. On the code behind of your default form add the following references

   1: using System.Diagnostics;
   2: using System.Runtime.InteropServices;

3. Now before the constructor of your form place the following code.

   1: // Structure contain information about low-level keyboard input event
   2: [StructLayout(LayoutKind.Sequential)]
   3: private struct KBDLLHOOKSTRUCT
   4: {
   5:     public Keys key;
   6:     public int scanCode;
   7:     public int flags;
   8:     public int time;
   9:     public IntPtr extra;
  10: }
  11:  
  12: //System level functions to be used for hook and unhook keyboard input
  13: private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
  14: [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  15: private static extern IntPtr SetWindowsHookEx(int id, LowLevelKeyboardProc callback, IntPtr hMod, uint dwThreadId);
  16: [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  17: private static extern bool UnhookWindowsHookEx(IntPtr hook);
  18: [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  19: private static extern IntPtr CallNextHookEx(IntPtr hook, int nCode, IntPtr wp, IntPtr lp);
  20: [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  21: private static extern IntPtr GetModuleHandle(string name);
  22: [DllImport("user32.dll", CharSet = CharSet.Auto)]
  23: private static extern short GetAsyncKeyState(Keys key);
  24:  
  25:  
  26: //Declaring Global objects
  27: private IntPtr ptrHook;
  28: private LowLevelKeyboardProc objKeyboardProcess; 

4. Now add the following code on your constructor.

   1: public Form1()
   2: {
   3:     ProcessModule objCurrentModule = Process.GetCurrentProcess().MainModule; //Get Current Module
   4:     objKeyboardProcess = new LowLevelKeyboardProc(captureKey); //Assign callback function each time keyboard process
   5:     ptrHook = SetWindowsHookEx(13, objKeyboardProcess, GetModuleHandle(objCurrentModule.ModuleName), 0); //Setting Hook of Keyboard Process for current module
   6:  
   7:  
   8:     InitializeComponent();
   9: }

5. Now Implement the callback function

   1: private IntPtr captureKey(int nCode, IntPtr wp, IntPtr lp)
   2: {
   3:     if (nCode >= 0)
   4:     {
   5:         KBDLLHOOKSTRUCT objKeyInfo = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lp, typeof(KBDLLHOOKSTRUCT));
   6:  
   7:         if (objKeyInfo.key == Keys.RWin || objKeyInfo.key == Keys.LWin) // Disabling Windows keys
   8:         {
   9:             return (IntPtr)1;
  10:         }
  11:     }
  12:     return CallNextHookEx(ptrHook, nCode, wp, lp);
  13: }

6. Now go to your designer class and replace your dispose method.

   1: /// <summary>
   2: /// Clean up any resources being used.
   3: /// </summary>
   4: /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
   5: protected override void Dispose(bool disposing)
   6: {
   7:     if (disposing && (components != null))
   8:     {
   9:  
  10:         components.Dispose();
  11:     }
  12:     if (ptrHook != IntPtr.Zero)
  13:     {
  14:         UnhookWindowsHookEx(ptrHook);
  15:         ptrHook = IntPtr.Zero;
  16:     }
  17:     base.Dispose(disposing);
  18: }

So, in this way we can stop the windows key operation till your application is running.You can find the VS 2008 Source code here.

How to pass parameters to the dynamically added user control

In this post, I will explain how you can pass parameter to the dynamically added (from code behind) User Control. Most of you might aware of how we can achieve this in web application project. Following is the code for that

   1: Dim objCon As Control = Page.LoadControl("~/Controls/MyControl.ascx")
   2: Ctype(objCon,MyControl).PropertyOne = "Test"
   3: Ctype(objCon,MyControl).PropertyTwo = "USAM"
   4: MyPanel.Controls.add(objCon)

Now what if you have a web site project which does not have the pre-compiled assemblies and you are no more able to access the class of your user control. That is what the sum of last two ays.

Here is how you can do that by using System.reflection.

   1: Dim objCon As Control = Page.LoadControl("~/Controls/MyControl.ascx")
   2: 'Creating Dynamic Assmebly which holds control
   3: Dim objAssembly As Assembly = Compilation.BuildManager.GetCompiledAssembly("~/Controls/MyControl.ascx")
   4: 'You should definately know the name of your user control class
   5: Dim objType As Type = objAssembly.GetType("Controls_MyControl")
   6:  
   7: 'Properties
   8: Dim objPropOne As PropertyInfo = objType.GetProperty("PropertyOne")
   9: Dim objPropTwo As PropertyInfo = objType.GetProperty("PropertyTwo")
  10:  
  11: 'Setting Value of Properties
  12: bjPropOne.SetValue(objCon, 1, Nothing)
  13: objPropTwo.SetValue(objCon, 13, Nothing)
  14:  
  15: 'Finally placing control on the page
  16: pnlCommentsCon.Controls.Add(objCon)

So in this way you can pass parameters to the dynamically added user control.  

Saving and Retrieving File Using FileStream SQL Server 2008

FileStream data type is a very important feature of SQL Server 2008 and gradually getting popular amongst developer for it’s feasibility. And in the past few days specially after “Configure SQL Server 2008 for File Stream” post. I received several feedbacks regarding the usage of FileStream with Ado.net and Frankly there is not much stuff available on Google for this topic.

In this post, I will guide you to use FileStream Data type in Ado.net. But before we start make sure you have configure your SQL Server 2008 instance to use File Stream Data type and for this you can read this post.

Once you finish with the Configuration execute the following script

   1: CREATE TABLE [dbo].[tbl_Files](
   2:     [Id] [uniqueidentifier] ROWGUIDCOL  NOT NULL,
   3:     [SystemNumber] [int] NOT NULL,
   4:     [SystemFile] [varbinary](max) FILESTREAM  NULL,
   5:     [FileType] [varchar](5) NULL,
   6: UNIQUE NONCLUSTERED 
   7: (
   8:     [Id] ASC
   9: )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
  10: ) ON [PRIMARY] FILESTREAM_ON [FileStreamGroup1]
  11:  
  12: GO
  13:  
  14: ALTER TABLE [dbo].[tbl_Files] ADD  CONSTRAINT [DF_tbl_Files_Id]  DEFAULT (newid()) FOR [Id]

This will create a table with FileStream Data type. Notice the FileType field I have used here to determine the type of file which we will use when we were downloading the file.

Now that we have created a table, lets now move on to the Stored Procedures by which we will access this newly created table.

Security Setting:

Don’t get confused with the heading, there is no additional setting required. You need to do one of the two available options. Either you need to specify “Integrated Security = true” in Connection String or you need to implement Asp.net Impersonation. It is there because SQL Server 2008 will not allow un authenticated user or instance to read/modify the file.

Most of the developers usually aware of Integrated Security stuff but let me give a little detail about asp.net impersonation. Actually, it is a way to Authorize the Instance of your asp.net application on SQL Server by using Credential Information.

Following links will help you to understand or implement impersonation.

http://blogs.msdn.com/shawnfa/archive/2005/03/21/400088.aspx
http://blogs.msdn.com/saurabhkv/archive/2008/05/29/windowsidentity-impersonation-using-c-code.aspx
http://www.west-wind.com/WebLog/posts/1572.aspx

Add Procedure:

Lets create a procedure call it “”FileAdd” and past the following script.

   1: Create PROCEDURE [dbo].[FileAdd]
   2: @SystemNumber int,
   3: @FileType varchar(5),
   4: @filepath varchar(max) output
   5: AS
   6: BEGIN
   7:     -- SET NOCOUNT ON added to prevent extra result sets from
   8:     -- interfering with SELECT statements.
   9:     SET NOCOUNT ON;
  10:  
  11:     DECLARE @ID UNIQUEIDENTIFIER
  12:     SET @ID = NEWID()
  13:  
  14:     INSERT INTO [dbo].[tbl_Files]
  15:     ([Id],[SystemNumber],SystemFile,FileType)
  16:     VALUES (@ID ,@SystemNumber,CAST('' AS VARBINARY(MAX)),@FileType)
  17:     
  18:     select @filepath = SystemFile.PathName() from tbl_Files where Id = @ID
  19:  
  20:     
  21:  
  22: END

In the above procedure, we add new records in our table and just pass empty (null) to the FileStream field because we first want our SQL Server to create an empty file on NTFS location which we can access from our code behind by using the path which we have taken as Output Parameter here.

notice the SystemFile.PathName(), it is a new function introduced in SQL Server 2008 which will return the NTFS location of the file.

 

Get Procedure:

Create a procedure and call it “FileGet”

   1: CREATE PROCEDURE [dbo].[FileGet]
   2: @Id varchar(50)
   3: AS
   4: BEGIN
   5:      select  SystemFile.PathName(),FileType from tbl_Files where Id = @ID
   6: END

This is a simple stuff, we are returning PathName and FileType by specifying ID. Just to read the record.

Upload and Store:

To save the file in the file stream, we will use FileUpload control to upload the file and then save it to FileStream field. For that we have created a page and drag FileUpload control with an Upload button.

Now on the click event of the button write the following code.

   1: byte[] buffer = new byte[(int)FileUpload1.FileContent.Length];
   2: FileUpload1.FileContent.Read(buffer, 0, buffer.Length);
   3:  
   4:  
   5: if (FileUpload1.FileContent.Length > 0)
   6: {
   7:     SqlConnection objSqlCon = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);
   8:     objSqlCon.Open();
   9:     SqlTransaction objSqlTran = objSqlCon.BeginTransaction();
  10:  
  11:     SqlCommand objSqlCmd = new SqlCommand("FileAdd",objSqlCon,objSqlTran);
  12:     objSqlCmd.CommandType = CommandType.StoredProcedure;
  13:  
  14:     SqlParameter objSqlParam1 = new SqlParameter("@SystemNumber", SqlDbType.Int);
  15:     objSqlParam1.Value = "1";
  16:  
  17:     SqlParameter objSqlParam2 = new SqlParameter("@FileType", SqlDbType.VarChar,4);
  18:     objSqlParam2.Value = System.IO.Path.GetExtension(FileUpload1.FileName);
  19:  
  20:     SqlParameter objSqlParamOutput = new SqlParameter("@filepath", SqlDbType.VarChar, -1);
  21:     objSqlParamOutput.Direction = ParameterDirection.Output;
  22:  
  23:     objSqlCmd.Parameters.Add(objSqlParam2);
  24:     objSqlCmd.Parameters.Add(objSqlParam1);
  25:     objSqlCmd.Parameters.Add(objSqlParamOutput);
  26:  
  27:  
  28:     objSqlCmd.ExecuteNonQuery();
  29:  
  30:     string Path = objSqlCmd.Parameters["@filepath"].Value.ToString();
  31:  
  32:     objSqlCmd = new SqlCommand("SELECT GET_FILESTREAM_TRANSACTION_CONTEXT()", objSqlCon, objSqlTran);
  33:  
  34:     byte[] objContext = (byte[])objSqlCmd.ExecuteScalar();
  35:     
  36:  
  37:     SqlFileStream objSqlFileStream = new SqlFileStream(Path, objContext, FileAccess.Write);
  38:     
  39:     objSqlFileStream.Write(buffer, 0, buffer.Length);
  40:     objSqlFileStream.Close();
  41:  
  42:     objSqlTran.Commit();

Well, in the first two lines we have saved the uploaded file in byte and call this variable “buffer”.
As we are simply using ADO.net, that is why in line 7 and 8 we have created and open a connection. Where as it is worth to mention here, we need to use transaction when we want to do any operation on FileStream field that is why we have begin a new transaction in line no 9.

On line number 11 to 30, we have a simply setup command object and parameter stuff and then execute the procedure and save the output parameter in a variable called “Path”.

This new variable will contain the NTFS location of the file which is stored on SQL Server FileStream. It should be clear that, this file is empty yet as we have not stored any thing in it.

Now on line number 32 we have reused command object and this time we are executing a simple statement “GET_FILESTREAM_TRANSACTION_CONTEXT”. It is also a newly added feature in SQL Server 2008 which will return current transaction context to be used in the next few lines. Now, on line number 34 we have stored the output of the above statement in byte.

In line number 37, here is some thing new which is called “SqlFileStream”. It is a new class which you can find under “System.Data.SqlTypes”. It seems more like FileStream of “System.IO” but it should be cleared here that the file stored in FileStream field cannot be access using regular file stream object of “System.IO” we need to use SqlFileStream to access those files which are stored in FileStream field.

In line no 39 and on, we are writing the file with the content of uploaded file (Remember we have stored our uploaded file in bytes and call it “buffer”). and that’s it.

Read The Stored File:

We have finished with storing the file, now lets see how can we read this file back. To do this, Drag a Grid View and make it similar to the following

   1: <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" 
   2:        onrowcommand="GridView1_RowCommand">
   3:        <Columns>
   4:            <asp:BoundField DataField="ID" HeaderText="ID" />
   5:            <asp:BoundField DataField="SystemNumber" HeaderText="System Id" />
   6:            <asp:TemplateField>
   7:                <ItemTemplate>
   8:                    <asp:LinkButton ID="lbGetFile" runat="server" CommandName="GetFile" CommandArgument='<%#Eval("ID") %>' Text="Get File"></asp:LinkButton>
   9:                </ItemTemplate>
  10:            </asp:TemplateField>
  11:        </Columns>
  12:    
  13:    </asp:GridView>

And bind the GridView using the following code.

   1: protected void bindData()
   2: {
   3:     SqlConnection objSqlCon = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);
   4:     objSqlCon.Open();
   5:  
   6:     SqlCommand objSqlCmd = new SqlCommand("Select * from tbl_Files", objSqlCon);
   7:     SqlDataAdapter objSqlDat = new SqlDataAdapter(objSqlCmd);
   8:     DataTable objdt = new DataTable();
   9:     objSqlDat.Fill(objdt);
  10:  
  11:     GridView1.DataSource = objdt;
  12:     GridView1.DataBind();
  13: }

Well, the above markup and the code is enough self explaining but the little important stuff to mention here is the link button. We will use the same link button to download the stored file. Lets quickly move on to the RowCommand implementation of the GridView.

   1: protected void GridView1_RowCommand(object sender, GridViewCommandEventArgs e)
   2: {
   3:     if (e.CommandName == "GetFile")
   4:     {
   5:  
   6:         SqlConnection objSqlCon = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);
   7:         objSqlCon.Open();
   8:         SqlTransaction objSqlTran = objSqlCon.BeginTransaction();
   9:  
  10:         SqlCommand objSqlCmd = new SqlCommand("FileGet", objSqlCon, objSqlTran);
  11:         objSqlCmd.CommandType = CommandType.StoredProcedure;
  12:  
  13:         SqlParameter objSqlParam1 = new SqlParameter("@ID", SqlDbType.VarChar);
  14:         objSqlParam1.Value = e.CommandArgument;
  15:  
  16:         objSqlCmd.Parameters.Add(objSqlParam1);
  17:         string path = string.Empty;
  18:         string fileType = string.Empty;
  19:  
  20:         using (SqlDataReader sdr = objSqlCmd.ExecuteReader())
  21:         {
  22:             while (sdr.Read())
  23:             {
  24:                 path = sdr[0].ToString();
  25:                 fileType = sdr[1].ToString();
  26:             }
  27:             
  28:         }
  29:         
  30:         objSqlCmd = new SqlCommand("SELECT GET_FILESTREAM_TRANSACTION_CONTEXT()", objSqlCon, objSqlTran);
  31:  
  32:         byte[] objContext = (byte[])objSqlCmd.ExecuteScalar();
  33:  
  34:  
  35:         SqlFileStream objSqlFileStream = new SqlFileStream(path, objContext, FileAccess.Read);
  36:  
  37:         byte[] buffer = new byte[(int)objSqlFileStream.Length];
  38:         objSqlFileStream.Read(buffer, 0, buffer.Length);
  39:         objSqlFileStream.Close();
  40:  
  41:         objSqlTran.Commit();
  42:         Response.AddHeader("Content-disposition", "attachment; filename=" + Path.GetFileName(path) + fileType);
  43:         // Here you need to manage the download file stuff according to your need
  44:         Response.ContentType = "application/octet-stream";
  45:  
  46:         Response.BinaryWrite(buffer);
  47:  
  48:  
  49:         
  50:     }
  51: }

Well, in the first 8 lines, we have created and opened a connection and then begin the transaction. from line no 10 to 28, we are setting the parameter stuff, executing the procedure and save the output in the two variable called “path” and “fileType”.

In line no 30 to 32, we are executing the the transaction context statement and then save the output the bytes. (Same we have done when we were writing the file)

In line no 35 to 40, we have used the same SqlFileStream and instead of writing, we are reading the file this time(notice line no 38) and save the content of the file to the bytes. Now we have file content in bytes, So we have now commit the transaction in line no 41.

In line no 42 and 44, we are setting the content type and specifying the file name with the extension. That is why, we have also saved file type in the database so that at the time of downloading we can make it available in its original state.

And in line no 46, we are simply writing the binary of the file to the browser so that it can be downloaded.

Conclusion:

I have tried my best to explain the integration of FileStream field with ADO.net, and I found this is an easy way to accomplish the task. You can download the VS 2008 solution which contain the complete source code along with procedures and table SQL.

Since, it is a new featured in SQL Server 2008 which is still in CTP by the time I am posting this stuff that is why we can expect some modifications in the method of reading and saving files using FileStream. If somebody face any challenge in the above code. Please feel to contact me.

Get Column name From Stored Procedure

The requirement of the day is to extract the name of the columns returned by procedures. Stored Procedures are dynamic that is why we need to create a function that takes Stored Procedure name as parameter and return the column names in string. So here is the quick snippet for that

   1: Public Shared Function getMetaData(ByVal spName As String) As String()
   2:        Dim sqlCon As New SqlConnection(ConfigurationManager.ConnectionStrings("lmString").ConnectionString)
   3:        sqlCon.Open()
   4:  
   5:        Dim sqlCmd As New SqlCommand("sp_helptext " + spName, sqlCon)
   6:        Dim sqlDataAdapter As New SqlDataAdapter(sqlCmd)
   7:        Dim dt As New DataTable
   8:        Dim strTempQuery As String = String.Empty
   9:        Dim strColumns As String()
  10:        Dim strCol As String = String.Empty
  11:  
  12:        sqlDataAdapter.Fill(dt)
  13:        If dt.Rows.Count > 0 Then
  14:            For Each dr As DataRow In dt.Rows
  15:                strTempQuery += dr.Item(0)
  16:            Next
  17:        End If
  18:  
  19:        If Not strTempQuery = "" Then
  20:  
  21:            'Dim objRegex As New Regex("select([^<]*)from")
  22:  
  23:  
  24:            Dim objMatches As MatchCollection = Regex.Matches(strTempQuery, "select([^<]*)from", RegexOptions.IgnoreCase)
  25:  
  26:            For Each mymatch As Match In objMatches
  27:                strCol += mymatch.Groups(1).Value
  28:            Next
  29:  
  30:            If Not strCol = "" Then
  31:                strColumns = strCol.Split(",")
  32:                For a As Integer = 0 To strColumns.Length - 1
  33:                    strColumns(a) = strColumns(a).Trim()
  34:                Next
  35:            End If
  36:        End If
  37:        Return strColumns
  38:    End Function

 

Restriction : Though, we have achieved the target, but since we have used sp_helptext to extract the Stored Procedure data that is why it is not possible to process encrypted stored procedure.

Will make it more better in the future to accommodate all type of Stored Procedures.

Prevent Request Timeout in Asp.net

In one of our application we want our user to upload data up to 1GB and the most interesting part is we want this uploading via HTTP. I mean, we have plan to use FTP but that is for future. For now we need to make it with HTTP.

Off course, uploading data in GB's requires much extra time then the default configuration and same problem with the request size. So, to make it done we need to change the configuration of following items.

  1. executionTimeOut : Default value is 110 seconds, we need to make it for at least 12 hours.
  2. maxRequestLength : Default value is 4096 KB (4 MB), we want it to accommodate 1 GB size of data.
  3. Session Timeout : Default value is 20 minutes, we need to make it according to the executionTimeOut.

The above two are the configuration of httpRuntime attribute, but along with that why do we need to change the session timeout. Because, if session timeout is less then execution timeout it means when the request finishes its processing meanwhile, the session get's end which can raise an error.

So, here is the configuration which we need to make in web.config.

   1: <httpRuntime executionTimeout="43200" maxRequestLength="104856"  />
   2: <sessionState mode="InProc" cookieless="false" timeout="720"/>

Notice, executionTimeout needs to be filled in seconds where as timeout of sessionstate needs minutes as input.

But that doesn't work in my case. I spent many hours replacing the configuration settings and no results.

Finally, I get to know that Application Pool also have idle timeout settings which can be useful.


To get application pool in Windows 2003  :

  1. Right click on your website and click properties
  2. Then Home Directory Tab and notice the last most drop down of  screen. Application Pool
  3. Notice the name, and close screen.
  4. Then go to Application Pools right click the application name which you have in our website and then properties. You will see the following Screen.

 

idle

The default value of idle timeout is 20 minutes. I have changed this to 720 (same as of session) minutes and every thing start working.

I did never even hear of this idle timeout before neither I think that while uploading the file connection thread is idle but now this is for sure, if you are increasing the session timeout greater then 20 you must need to configure the same amount in your Application Pool Settings.

Passing Parameter To User Control On A Modal Popup

This is the most demanded scenario for the one who are using Asp.net AJAX Control Toolkit, I mean I have seen most of the people asking for this feature on asp.net forum.

Unfortunately, we have no such functionality provided in Modal Popup because the control get rendered once the page is loaded and the Modal Popup is just a JavaScript which work is just to display a div which is hidden.

Here is a little work around for those who wants to pass parameter to user control using Modal Popup. Basically, the idea is to keep the user control in a separate page and call that page using JavaScript and put the response on the a Modal Popup Div.

Remember, as this is only a JavaScript we need to set the parameter using JavaScript or we need to save the parameters on Page_Load in any Hidden field and access that later from JavaScript.

To start, I have created two pages and a user control following is the naming stuff for them

  1. Default.aspx (Contains Modal Popup)
  2. ControlCaller.aspx  (Contains User Control)
  3. Controls.ascx (User Control)

Default.aspx page html will look like as follows

   1: <form id="form1" runat="server">
   2:     <div>
   3:     
   4:         <asp:ScriptManager ID="ScriptManager1" runat="server">
   5:         </asp:ScriptManager>
   6:         <asp:Button ID="Button1" runat="server" Text="Show Popup" OnClientClick="setupParam()" />
   7:     
   8:     </div>
   9:     <cc1:ModalPopupExtender ID="ModalPopupExtender1" runat="server" PopupControlID="pnlControl" TargetControlID="Button1">
  10:     </cc1:ModalPopupExtender>
  11:     <asp:Panel ID="pnlControl" runat="server">
  12:             
  13:     </asp:Panel>
  14:     
  15:     <asp:HiddenField ID="hf_username" runat="server" />
  16:     <asp:HiddenField ID="hf_password" runat="server" />
  17:     <asp:HiddenField ID="hf_registredDate" runat="server" />
  18:     
  19:     </form>

Keep in mind that the three hidden fields I have taken here are for the Parameters.

Now Let's see what we have in ControlCaller.aspx

   1: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ControlCaller.aspx.cs" Inherits="LearnWebApp.ControlCaller" %>
   2: <%@ Register src="Controls/Control.ascx" tagname="Control" tagprefix="uc1" %>
   3:     <uc1:Control ID="Control1" runat="server" />

Just user control implementation and nothing else. Please make sure to remove all the head, html and form tags.

Following is the html of Control.ascx

   1: <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="Control.ascx.cs" Inherits="LearnWebApp.Controls.Control" %>
   2: User Name :
   3: <asp:Label ID="lblUserID" runat="server" Text="Label"></asp:Label>
   4: <br />
   5: Registered Date :
   6: <asp:Label ID="lblDate" runat="server" Text="Label"></asp:Label>
   7: <br />
   8: Password:
   9: <asp:Label ID="lblPassword" runat="server" Text="Label"></asp:Label>

And here is the code behind of the Control

   1: private string _userName;
   2:         private string _RegisteredDate;
   3:         private string _Password;
   4:  
   5:         public string userName
   6:         {
   7:             get { return _userName; }
   8:             set { _userName = value; }
   9:         }
  10:         public string RegisteredDate
  11:         {
  12:             get { return _RegisteredDate; }
  13:             set { _RegisteredDate = value; }
  14:         }
  15:         public string Password
  16:         {
  17:             get { return _Password; }
  18:             set { _Password = value; }
  19:         }
  20:         protected void Page_Load(object sender, EventArgs e)
  21:         {
  22:             if (!Page.IsPostBack) 
  23:             {
  24:                 lblDate.Text = _RegisteredDate;
  25:                 lblPassword.Text = _Password;
  26:                 lblUserID.Text = _userName;
  27:             }
  28:         }

Please bear in mind, that the properties we have taken here are for the parameters as you can see I am also setting these properties on label at Page_Load.

and here we have the script which we need to add on the default.aspx head section

   1: <script language="javascript">
   2:     var request = false;
   3:        try {
   4:          request = new XMLHttpRequest();
   5:        } catch (trymicrosoft) {
   6:          try {
   7:            request = new ActiveXObject("Msxml2.XMLHTTP");
   8:          } catch (othermicrosoft) {
   9:            try {
  10:              request = new ActiveXObject("Microsoft.XMLHTTP");
  11:            } catch (failed) {
  12:              request = false;
  13:            }  
  14:          }
  15:        }
  16:        
  17:       function GetResult() {
  18:         var divComm = document.getElementById('pnlControl');
  19:         divComm.innerHTML = "Please wait processing your request!!!";
  20:         var rnd = Math.random() * 1000000;
  21:         var url = 'ControlCaller.aspx?userName=' +document.getElementById("hf_username").value + "&password=" + document.getElementById("hf_password").value  + "&Date="+ document.getElementById("hf_registredDate").value  +'&rnd=' + rnd;
  22:         request.open("GET", url, true);
  23:         request.onreadystatechange = GetResultComplete;
  24:         request.send(null);
  25:     }
  26:     function GetResultComplete() {
  27:         if (request.readyState == 4) {
  28:             //alert(request.responseText);
  29:             if (request.status == 200) {
  30:                 var divComm = document.getElementById('pnlControl');
  31:                 if (divComm) {
  32:                     divComm.innerHTML = request.responseText;
  33:                 }
  34:             }
  35:         }
  36:     }
  37:     function setupParam()
  38:         {
  39:             document.getElementById("hf_username").value = "User is for test !!!";
  40:             document.getElementById("hf_password").value  = "testing.....";
  41:             document.getElementById("hf_registredDate").value  = "10/04/84";
  42:             GetResult();
  43:             
  44:         }
  45:     </script>

Notice the setupParam() function, we are setting our parameter here and then call the GetResult function which is making an AJAX call to the page we have created. That's it by using this method we can have parameterized user control inside Modal Popup.

You can download the full Visual Studio 2008 Project from here.

Disable Control When Asp.net AJAX is in progress

If the AJAX call to the server take too much time, then there is a possibility that user might press some other buttons or some other event occurred which will cause your AJAX call to stop and make a new request.

To overcome this situation, I decided to have div on the top of the page so that while AJAX call is in progress user cannot do any thing else.

So I simply Drag and Drop a Update Progress Control and write the following stuff.

<asp:UpdateProgress ID="UpdateProgress1" runat="server">
    <ProgressTemplate>
        <div id="Progress">Please wait ......</div>
       <div id="bgDiv"></div> 
    </ProgressTemplate>
</asp:UpdateProgress>

And add the following style on head section of the page.

   1: <style>
   2:     #bgDiv {
   3:       position:absolute;
   4:       top:0px;
   5:       bottom:0px;
   6:       left:0px;
   7:       right:0px;
   8:       overflow:hidden;
   9:       padding:0;
  10:       margin:0;
  11:       background-color:black; 
  12:       filter:alpha(opacity=50);
  13:       opacity:0.5;
  14:       z-index:500;
  15:     }
  16:     #Progress
  17:     {
  18:         position: absolute;
  19:         background-color:Red;
  20:         width: 300px;
  21:         z-index: 600;
  22:     }
  23:  
  24:  
  25:     </style>

There we go whenever AJAX call made, a background div will appear and on top of that we have our message "Please wait"

Here is the snapshot

image

Send Welcome email to user using Membership API

While using Membership API, It is a very normal task to send user a registration or verification email. We can achieve that using two ways one is using the default email setting of Create User Control and other is by implementing CreateUserWizard.CreatedUser event

Method 1:

Drag and Drop the create user control, right click then properties and find MailDefination and set appropriate values for each property.

membership_sc1

BodyFileName is basically the location of the file which contains the body of the email. It can be text file or html file.

HTML file which I am using is a very simple html file which can be found in the project source specified at the end of this post.

Create User Control uses the SMTP settings in web.config to send the email that is why before checking it you need to make sure that you have specify the correct settings in configuration file. For example

   1: <system.net>
   2:     <mailSettings>
   3:         <smtp deliveryMethod="Network">
   4:             <network defaultCredentials="true" host="localhost" port="25"/>
   5:             <!--userName="" password="" if required specify after port-->
   6:         </smtp>
   7:     </mailSettings>
   8: </system.net>

That's it your control is now ready to send emails.

Method 2:

In this method, we will manually shoot an email when the user get registered successfully. For that we need to implement our logic in CreatedUser event of this control.

Following is the screen shot of different events provided by CreateUserControl

image

As you can see I have generated a method against CreatedUser and here is the code for that

   1: Protected Sub CreateUserWizard1_CreatedUser(ByVal sender As Object, ByVal e As EventArgs) Handles CreateUserWizard1.CreatedUser
   2:     Dim ToAddress As String = CreateUserWizard1.Email
   3:     Dim mm As New MailMessage("Support@mysite.com", ToAddress)
   4:     Using objSr As New StreamReader(Server.MapPath("MailTemplate\\welcome_ver.htm"), FileMode.Open)
   5:         With mm
   6:             .Subject = "Welcome to my site"
   7:             .Body = objSr.ReadToEnd()
   8:             .Body = .Body.Replace("{verification_code}", New Random().Next()) 'Any Random number you can put your logic here...
   9:             .IsBodyHtml = True
  10:         End With
  11:         Dim smtp As New SmtpClient
  12:  
  13:         smtp.Send(mm)
  14:     End Using
  15: End Sub

In this method I am also using the SMTP Settings specified in web.config however, you can change that according to your own need.

In line no 10, I have put a token which will replace the string which I have in my source html file with a random number. For those who are facing difficulty in understanding the token stuff, please see the following html

   1: <html>
   2:  
   3: <body>
   4: Hi, 
   5:  
   6: Thank you for the registration and welcome to my site
   7: Your verification code : {verification_code}
   8: </body>
   9: </html>

That is very simple and very easy. I personally recommend the Method 2 as it can give you some extra control over the process.

Here is the VS 2008 code

Long Waited Task in Asp.net

Yesterday, one my my friend ask me a query about sending some 500+ emails using an asp.net page. Sending bulk email using asp.net is obviously an issue. I mean, You cannot keep the page on the post back state for the five minutes. Your page will get expired and even if you increase the Request Timeout period it is still not a good approach.So for that, There are couple of approaches like1. You can have a table in which you store all the emails and create a Windows Service or Schedule Task to read tables of send emails accordingly.2. Open a thread on the server side using delegate and return the page to the browser and show the progress using Asynchronous operation. As We were operating on shared hosting first approach doesn't seem fruitful for us. So, we decided to go with the second approach and for that we use delegates. Let me show you step by step.
1: Public Delegate Sub LongTimeTask_Delegate()2: Private fileName as String
Here I have declared a delegate and a global variable called filename which I will use for getting the status of the process.Now on the click event of my button which start the process. I have write the following code
1: Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click2: Dim rnd As Double = New Random().Next()3: fileName = Session.SessionID & "_" & rnd & ".txt"4: Dim d As LongTimeTask_Delegate5: d = New LongTimeTask_Delegate(AddressOf LongTimeTask)6:  7: Dim R As IAsyncResult8: 9: R = d.BeginInvoke(New AsyncCallback(AddressOf TaskCompleted),nothing)10: Dim strScript As String = "<script language='javascript'> setInterval('GetResult(""" + fileName + """)',2000); </script>"11:  12: Button1.Enabled= False13: Response.Write(strScript) 'TODO : Can take write on literal14: End Sub
On line no 3, I am setting filename variable to the session id and a random numberOn line no 4, Declare and Initialize Delegate and passed a function that will be executed by DelegateOn line no 7 and on, We invoke the delegate and store the result in IAsyncResult and also specify the function that will execute once the delegate is complete.On line no 10, we are writing a script on the page which is there to show us the status of process timely.Now, lets come LongTimeTask function, in which we have the whole stuff.
1: Public Sub LongTimeTask()2: Dim totCount As Integer = 5003: For a As Integer = 1 to totCount4: Try5: If Not File.Exists(fileName)6: File.Create(fileName)7: End If8: using sw As StreamWriter = New StreamWriter(Server.MapPath(fileName))9: sw.WriteLine("Processing " + a.ToString() + " of " + totCount.ToString() + " emails")10: End Using11: Catch 12:  13: End Try14: System.Threading.Thread.Sleep(2000)15: Next16: End Sub
Notice that, system will be in the loop for 500 times and each time before start processing it will update the status which will display to the browser. The Try/Catch block is updating the status where as notice the line no 14 in which I pause the thread. You can add your email sending or other long process stuff here but for me it is enough to sleep the thread for twenty seconds.Let me show you what happen when the task gets finished.
1: Public Sub TaskCompleted(ByVal R As IAsyncResult)2: Try3: using sw As StreamWriter = New StreamWriter(Server.MapPath(fileName))4: sw.WriteLine("Process has been completed successfully <a href='default.aspx'>Click here </a> to go back") 'You might shoot an email or some thing else here5: End Using6: If File.Exists(fileName)7: File.Delete(fileName)8: End If9: Catch 10:  11: End Try12: End Sub
This time, we have updated the status with some other text and a hyper link. You can also have any kind of alert here. This is just the part of the code behind stuff. ASPX file contain the actual Asynchronous logic here is the HTML
1: <form id="form1" runat="server">2: <div>3:  4: <asp:Button ID="Button1" runat="server" Text="Start IAsync" />5: <div id="resultDiv"></div>6: </div>7: </form>
The resultDiv is responsible of showing the status. Now to get the status following Javscript will do the complete magic
1: <script language="javascript">2: var request = false;3: try {4: request = new XMLHttpRequest();5: } catch (trymicrosoft) {6: try {7: request = new ActiveXObject("Msxml2.XMLHTTP");8: } catch (othermicrosoft) {9: try {10: request = new ActiveXObject("Microsoft.XMLHTTP");11: } catch (failed) {12: request = false;13: } 14: }15: }16: 17: function GetResult(filename) {18: var rnd = Math.random() * 1000000;19: var url = filename + '?rnd=' + rnd;20: request.open("GET", url, true);21: request.onreadystatechange = GetResultComplete;22: request.send(null);23: }24: function GetResultComplete() {25: if (request.readyState == 4) {26: 27: if (request.status == 200) {28: var divComm = document.getElementById('resultDiv');29: if (divComm) {30: divComm.innerHTML = request.responseText;31: }32: }33: }34: }35: </script>
There it is ... we are all done. Following is the snapshot of the working long waited task in asp.netasyncYou can find the complete code for Visual Studio 2008 ... cheers