Click here to Skip to main content
15,881,882 members
Articles / Desktop Programming / Windows Forms

WWW DSL using Irony - Part 2

Rate me:
Please Sign up or sign in to vote.
5.00/5 (3 votes)
9 Jul 2009CPOL3 min read 18.1K   183   13  
A WinForms sample application for the Domain Specific Language created with Irony.

Introduction

In my previous article I explained how to create a Domain Specific Language to automate file downloads from different websites. This time, I want to expand the solution with a WinForms application that allows to easily download an arbitrary number of files from an arbitrary number of websites.

Background

The application uses the wwwdsl assembly presented from the previous article. The download archive contains the sample application and the project with the wwwdsl assembly.

Description

The main point of the solution is to allow the possibility to easily start downloading multiple files. The user does not have to care about the progress (e.g., wait to press a button on the web page). The user has to provide the application with recipes that define the processing. How do we create a recipe file - please check the previous article. The solution contains the files for two sources: a plain file download from a web server (download.wwwdsl) and a Rapidshare recipe (rs.wwwdsl). The user can register a recipe using the "Recipes..." button.

p1.JPG

In the dialog box that appears, the user has to select the recipe file, give the recipe a name, and mark if simultaneous processing is allowed. For the plain file download, it shall be marked as allowed; for Rapidshare, rather not (if you are a free user).

recipesDlg.JPG

After recipes are set, the files to be downloaded can be selected using "Add entries...". Multiple lines with download locations can be pasted to a text box. Also, the recipe for files have to be selected.

additemsDlg.JPG

The order in which files are downloaded depends on the position on the list in the main window (of course, only if download is possible; if not, the next item is chosen). To alter the order, the user can use the "up" and "down" buttons.

The last setting is the maximum number of working threads, i.e., the maximum number of concurrent downloads. After the setting is done, the user can start download using the "Start" button. If for some reason all downloads have to be aborted, the user can press the "Stop" button. Pressing "Start" again will restart the unfinished downloads. Items can be removed using the popup menu (or by pressing the "Delete" key).

p4.JPG

The number of threads and the order can be changed during processing.

Points of interest

Multithreading

The application allows for multiple concurrent downloads, therefore it is a multi-threaded application :).

Threads are started when the user presses "Start". Threads select the items to process. Picking is done in a critical section to avoid race conditions (the same item being selected by two threads). If there are no more items to downloaded, threads wait on the monitor's conditional variable for a change or new items.

C#
private List<Entry> m_entries; 

...

private void WorkerFun(ThreadData td)
{
   while (!td.Stop)
   {
      Entry entry = null;
      RecipeEvaluator rpc;
      WwwDslEvaluationContext evalContext;

      lock (m_entries)
      {
         rpc = null;
         foreach (var e in m_entries.Where(e => e.State == EntryState.Pending || 
                                           e.State == EntryState.Waiting))
         {
            rpc = EntryCheck(e);
            if (rpc != null)
            {
               entry = e;
               break;
            }
         }

         if (rpc == null)
         {
            Monitor.Wait(m_entries);
            if (td.Stop)
            {
               return;
            }
            continue;
         }

         rpc.Processed = true;

         entry.State = EntryState.Processing;

         evalContext = new WwwDslEvaluationContext(
                           rpc.CompilerContext.Runtime, rpc.RootNode);
         m_processedEntries.Add(evalContext, entry);

         m_threads[Thread.CurrentThread].EntryData = entry;
      }

      ...
}

GUI operations from different threads

Here is an additional remark connected with refreshing the grid control from different threads. They must not do this (only the main thread can manipulate the GUI). Therefore, the ControlRefresh method checks if the control can be manipulated (using the InvokeRequired property). In such a case, a call to BeginInvoke starts the ControlRefresh method in the main thread context.

C#
private DateTime _lastRefresh = DateTime.Now;
private void ControlRefresh(bool forced)
{
   if (m_control.InvokeRequired)
   {
      if (forced || (DateTime.Now - _lastRefresh.AddSeconds(1)).TotalSeconds >= 1)
      {
         m_control.BeginInvoke(new MethodInvoker(delegate { ControlRefresh(forced); }));
         _lastRefresh = DateTime.Now;
      }
   }
   else
   {
      m_control.Refresh();
   }
}

Logging

The application performs logging of the operations performed during download. In the solution, logging is directed into a text file. For each download item, a log file is created. To avoid collisions, log file names are GUIDs. The folder where log files are stored can be selected by the user using the "Log dir..." button on the main form.

Additional remark - I am not a GUI guy. I do not know how to make all these WPF blinking buttons. Therefore, please be merciful and do not comment on the GUI side of the solution :)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Poland Poland
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
-- There are no messages in this forum --