jump to navigation

Trigger a Timer Event at the Specific time of the Day in a Windows Service June 7, 2012

Posted by Bilal in .NET, C#, Programming.
Tags: , , , , , , , , , ,
5 comments

I have used timer many times in Windows Form Application. It’s a simple control that is easy to use and implement. Lately, I was working on a Windows Service where there was a requirement of implementing a periodic job. The timer available in Windows Form is located in System.Windows.Form namespace which is definitely not available (by default) in a Windows Service. I tried to dig into the matter and I came to know that there are three types of timers available in .NET:

  • A server-based timer, which you can add to the Toolbox
  • A Windows-based timer, which is always in the Toolbox
  • A thread timer, which is available programmatically

You can find the details about these on this MSDN Link. I shall not be going into the details of implementing each and every timer, I will just try to focus on the subject. By the way, it is very much confusing for a general user to choose the best timer. Each timer is more suitable in certain circumstances. Here is an old article (since 2004) from MSDN Magazine that will give you a great insight on these timers and their differences and usage. The source code is also available there. I would highly recommend checking the page.

After a lot of tests and search I got this Stack Overflow page which was close to my requirements.

private System.Threading.Timer myTimer;
private void SetTimerValue ()
{
   // trigger the event at 7 AM. For 7 PM use 19 i.e. 24 hour format
   DateTime requiredTime = DateTime.Today.AddHours(7).AddMinutes(00);
   DateTime currentTime = DateTime.Now;
   if (currentTime > requiredTime)
   {
      requiredTime = requiredTime.AddDays(1);
   }
   // interval between the timer events is set 10 min
   TimeSpan periodTS = DateTime.Now.AddMinutes(10) - DateTime.Now;
   myTimer = new System.Threading.Timer(new TimerCallback(TimerAction), null, (requiredTime - DateTime.Now), periodTS );
}

private void TimerAction (object e)
{
   // do some work
}

This piece of code completed the initial requirement to trigger the timer at the specific time of the day but with the passage of time, the event trigger time started decreasing by 1 second after 10 hours, with the interval of 10 min. If the interval is decreased the error will propagate upward much quickly.

I revised the code and made some modifications:

private System.Threading.Timer myTimer;
private void SetTimerValue ()
{
   // trigger the event at 7 AM. For 7 PM use 19 i.e. 24 hour format
   DateTime requiredTime = DateTime.Today.AddHours(7).AddMinutes(00);
   if (DateTime.Now > requiredTime)
   {
      requiredTime = requiredTime.AddDays(1);
   }

   // initialize timer only, do not specify the start time or the interval
   myTimer = new System.Threading.Timer(new TimerCallback(TimerAction));
   // first parameter is the start time and the second parameter is the interval
   // Timeout.Infinite means do not repeat the interval, only start the timer
   myTimer.Change((int)(requiredTime - DateTime.Now).TotalMilliseconds, Timeout.Infinite);
}

private void TimerAction(object e)
{
   // do some work
   // now, call the set timer method to reset its next call time
   SetTimerValue();
}

AutoCompleteCustomSource – Specified Cast is Not Valid January 3, 2011

Posted by Bilal in Programming.
Tags: , , , , , , , , , , , , , , , ,
2 comments

There was a requirement to implement a textbox with a databound AutoComple feature. As the Autocomplete source was a database, I have to go for a custom DataSource.

The main requirements for an auto-complete textbox (in this scenario) are:

  • AutoCompleteMode is set to Suggest(or SuggestAppend).
  • AutoCompleteSource is set to CustomSource
  • AutoCompleteCustomSource is set AutoCompleteStringCollection.

A sample code for this: (source)

namespace AutoCompleteTextBox
{
   public partial class frmAuto : Form
   {
      public string strConnection = 
             ConfigurationManager.AppSettings["ConnString"];
      AutoCompleteStringCollection namesCollection = 
             new AutoCompleteStringCollection();

      public frmAuto()
      {
         InitializeComponent();
      }

      private void frmAuto_Load(object sender, EventArgs e)
      {
         SqlDataReader dReader;
         SqlConnection conn = new SqlConnection();
         conn.ConnectionString = strConnection;
         SqlCommand cmd = new SqlCommand();
         cmd.Connection = conn;
         cmd.CommandType = CommandType.Text;
         cmd.CommandText = "Select distinct [Name] from [Names] order by [Name] asc";
         conn.Open();
         dReader = cmd.ExecuteReader();

         if (dReader.HasRows == true)
         {
            while (dReader.Read())
               namesCollection.Add(dReader["Name"].ToString());
         }
         else
         {
             MessageBox.Show("Data not found");
         }
         dReader.Close();

         txtName.AutoCompleteMode = AutoCompleteMode.Suggest;
         txtName.AutoCompleteSource = AutoCompleteSource.CustomSource;
         txtName.AutoCompleteCustomSource = namesCollection;
      }

      private void btnCancel_Click(object sender, EventArgs e)
      {
         Application.Exit();
      }
      private void btnOk_Click(object sender, EventArgs e)
      {
         MessageBox.Show("Hope you like this example");
      }
   }
}

I implemented the above properties and other related coding. The build was okay but when I executed the application, I got a Specified Cast is Not Valid exception.

I thought that I made some mistake while implementing, I revised the code and could not find anything wrong. The error was still there! I googled and came to know that I am not alone with this exception. I got a different implementation from MSDN for loading to the custom source.

AutoCompleteStringCollection source = new AutoCompleteStringCollection ();
source.AddRange(new string[]
   {
      "January",
      "February",
      "March",
      "April",
      "May",
      "June",
      "July",
      "August",
      "September",
      "October",
      "November",
      "December"
   });

// Create and initialize the text box
var textBox = new TextBox
{
   AutoCompleteCustomSource = source,
   AutoCompleteMode = AutoCompleteMode.SuggestAppend,
   AutoCompleteSource = AutoCompleteSource.CustomSource,
   Location = new Point(20, 20),
   Width = ClientRectangle.Width - 40,
   Visible = true
};

I created a String[] of the size of the DataTable row count and then added it to the AutoCompleteStringCollection object (similar to the above code). On executing the application, the result was same.

As I placed the code in the middle of my application, to avoid any confusion, I added a new windows form and updated my Program.cs to start from that form. On executing the application, the same error was there to greet me. I even tried to replace my DataBound source with the above static code, but no way. A simple thing that was supposed to be done in less than 30 min took hours of my time, still unsolved.

Since my Program.cs was having a lot of startup configurations and checks, for a final test I created a new WindowsFormApplication Project and used the MSDN code in the FormLoad event. On executing the application, surprisingly it executed normally giving the desired results. This was totally a wired situation. Most of the times, we are worried when something does not work properly BUT some other times, we are worried when some thing works. It was one of those rare cases.

Upon further googling, I came to know that it has surprisingly something to do with the STAThread attribute. In my application, I am having different forms being executed in different threads so I have removed the STAThread. It was not possible to apply the attribute in my application. It revealed that this thing has been reported to Microsoft as a BUG in Visual Studio 2005 in 2004 and separately 2007. If you happen to get the same scenario, please rate this issues and post your comments to ask Microsoft from wake up from a 7+ year long sleep.

To get the thing done, I have to go for the alternative solution. I replaced the TextBox with a ComboBox and set its DropDownStyle to DropDown. It was not EXACTLY same as the desired output but still usable.

Deploying .NET 4.0 Application on IIS 6.0 November 21, 2010

Posted by Bilal in Programming, Web.
Tags: , , , , , , , ,
5 comments

I had my ASP.NET 2.0 application hosted on Windows 2003 Sever with IIS 6.0. After Visual Studio 2010 or more precisely after .NET Framework 4.0, I upgraded the desktop modules to 4.0 (version 3.0 and 3.5 are superset, not the framework). The transition of desktop application from 2.0 to 4.0 was smooth enough. Although there were some deep level changes required in some scenarios (minor ones) but overall it was simple. The application executed properly in the first attempt.

I updated the Web Modules also but did not deploy them (in production environment only). Recently, it was planned to deploy the upgraded module. I installed the framework 4.0 on the web server and using the existing Publish settings, I published the upgraded application on the server (test server, not making experiments on the production server). On browsing the page, I got a 404 Page not found error. It was really a wired and unexpected situation. I have read about the issue of upgrading from IIS 6.0 to IIS 7.0 specially related to web.config but this transition error was totally not expected.

After a lot of search and attempts, I was able to execute the application. Below are the minimum steps required for it:

  1. In IIS 6 console, right click you project and click the properties and check the ASP.Net tab whether Framework 4 is selected or not. If not select the framework 4.
  2. In IIS 6 single application pool can’t be used for two different frameworks. Add a new pool with any meaningful name. Application Pools is above the Default
    Website node in the main tree hierarchy. (IIS 7 supports)
  3. To assign the application pool, in IIS console open the properties section of the web application, and click on the Home Directory tab and select newly created application pool from the drop-down-list.

Basically Page not found issue is cause of other problem which is set hidden by IIS6. But you need to see the real cause.

  1. For this open IIS6 console and in the main hierarchy, select Web Service Extension node which is right below the Default Website node. You will see the entire ASP.Net framework list over there, by default these frameworks might be set to Prohibited so select ASP.Net Framework 4 and click Allow button.

Browse you website now and it should be working.

If you are having some other errors, check the source link.