2012年3月22日 星期四

C# BackgroundWorker

使用 .net  內建的元件去實現非同步作業。當一個作業需要花很長的時間去完成,大都會使用背景處理,

這樣使用者還是可以繼續其他的操作,並且 UI 介面也不會鎖死。

當然,有許多方法可以達到背景作業的效果,這邊使用 backgroundworker 元件測試。


Example

public partial class Form1 : Form {

    //宣告 BW 變數
    private BackgroundWorker _BW;

    //宣告 _Limit 變數,並且設定初值
    private int _Limit = 100000;

    public Form1() {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e) {
        this.btnStartBackgroundWork.Enabled = true;
        this.btnCancelBackgroundWork.Enabled = false;
    }

    private void btnStartBackgroundWork_Click(object sender, EventArgs e) {
        //關閉 btnStartBackgroundWork 按鈕
        btnStartBackgroundWork.Enabled = false;

        //開啟 btnCancelBackgroundWork 按鈕
        btnCancelBackgroundWork.Enabled = true;

        //設定 ProgressBar 最大值
        toolStripProgressBar1.Maximum = _Limit;

        //建立 BW 物件
        _BW = new BackgroundWorker();

        //設定是否支援非同步作業取消
        _BW.WorkerSupportsCancellation = true;

        //繫結 DoWork 事件,當非同步作業啟動時,會呼叫該事件
        _BW.DoWork += new DoWorkEventHandler(_BW_DoWork);

        //啟動非同步作業,並將參數傳入
        //在 DoWork 函式中,參數會由 DoWorkEventArgs 帶出
        _BW.RunWorkerAsync(0);
    }

    //非同步作業
    void _BW_DoWork(object sender, DoWorkEventArgs e) {
        //設定 BW 物件可以更新表單資訊報告進度
        _BW.WorkerReportsProgress = true;

        //繫結 ProgressChanged 事件,當 WorkerReportsProgress 屬性設為 true ,
        //會觸發該事件,同時在這麼函式下,可以更新表單資訊
        _BW.ProgressChanged += new ProgressChangedEventHandler(_BW_ProgressChanged);

        //當背景作業已完成時、取消、例外發生時皆會觸發該事件
        _BW.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_BW_RunWorkerCompleted);

        //呼叫自訂函式
        DoAdd((BackgroundWorker)sender, e);
    }

    //非同步作業已完成
    void _BW_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
        if (e.Cancelled) // 如果非同步作業已被取消...
        {
            UpdateProgressBar("非同步作業已被取消。");

        } else // 如果非同步作業順利完成....
        {
            UpdateProgressBar("非同步作業順利完成。");
        }
    }

    private void DoAdd(BackgroundWorker worker, DoWorkEventArgs e) {
        int iComplete = (int)e.Argument;
        while (iComplete < _Limit) {

            //判斷使用者是否已經取消背景作業
            if (worker.CancellationPending == true) {

                //設定取消事件
                e.Cancel = true;

                //跳出迴圈
                break;
            }

            //呼叫這個方法,會觸發 ProgressChanged 事件            
            worker.ReportProgress(iComplete);
            
            iComplete++;

            //這邊睡一下,給緩衝去給表單的 UI 重劃,
            //系統會另外開一條執行緒去觸發事件 (_BW_ProgressChanged)。
            System.Threading.Thread.Sleep(1);
        }

    }

    //在這麼事件裡,可以存取表單的資訊
    void _BW_ProgressChanged(object sender, ProgressChangedEventArgs e) {
        //若是已經取消背景作業,就不去更新表單資訊,
        //否則會引發 [引動過程的目標傳回例外狀況]。
        if (((BackgroundWorker)sender).CancellationPending == false) {
            UpdateProgressBar(e.ProgressPercentage);
            UpdateTxtBox(e.ProgressPercentage);
        }
    }


    private void btnCancelBackgroundWork_Click(object sender, EventArgs e) {
        //取消背景作業
        this.CancelBackgroundWork();
    }

    private void CancelBackgroundWork() {
        // 在嘗試取消非同步作業前,先判斷非同步作業是否仍在執行中。
        if (_BW.IsBusy) {
            UpdateProgressBar("非同步作業計算中...");

            // 呼叫 BackgroundWorker 物件的 CancelAsync 方法來要求取消非同步作業。
            _BW.CancelAsync();
        }
    }

    //更新狀態文字
    private void UpdateProgressBar(string text) {
        toolStripStatusLabel1.Text = text;
    }

    //更新 ProgressBar
    private void UpdateProgressBar(int value) {
        toolStripProgressBar1.Value = value;
    }

    //更新 TxtBox
    private void UpdateTxtBox(int value) {
        textBox1.Text = value.ToString();
    }

    //關閉表單發生時
    private void Form1_FormClosing(object sender, FormClosingEventArgs e) {
        //取消背景作業
        this.CancelBackgroundWork();              
    }  
    
}
輸出結果
當背景作業發生時,會不斷更新畫面,同時還是可以編輯 textbox 。

2012年3月15日 星期四

Subversion 出現 svn: Unrecognized URL scheme for 'http://.....' 時的解決方式

前言

公司在針對各專案,一直都是使用Subversion(簡稱svn)這套軟體來針對各專案的檔案做版本的控制。至於何謂Subversion以及要如何使用這套軟體可參考這篇文章,這裡就不再贅述。這篇文章僅用來記錄在安裝完新的主機後,當我們要從遠端某台svn主機要把專案的檔案取出時出現以下訊息:
svn: Unrecognized URL scheme for 'http://www.sample.com/svn/bnq/trunk'
的解決方法。現在就讓我們來看看吧!

解說

一般來說,當我們費了千辛萬苦搞定一台主機後,如果要把網站的檔案利用svn取出放到新主機上,我們通常會下指令:
svn co http://www.sample.com/svn/trunk /var/www/html/
所謂的co其實是check out的縮寫,意思就是要把檔案取出;http://www.sample.com/svn/trunk 就是svn主機放置該專案的檔案位置;/var/www/html/ 則是我們打算把從svn取回檔案後要放置的位置。正常來說下完這行指令後依據svn主機的設定會要你做登入的認證,如果輸入正確則會開始看到檔案一個個放到我們設定的位置。但有時候卻會出現如前言中的訊息時又該怎麼辦呢?其實這是因為svn預設提供兩種方式來取得檔案,一種是file://,另一種是svn://,並沒有提供http://的方式來取檔。因此我們要幫svn掛上支援http方式的取檔模組,也就是neon這套程式。以下說明如何安裝neon。

安裝neon

neon(官方網站:http://www.webdav.org/neon/)提供HTTP及WebDAV的client端函式庫。安裝指令如下:
1. tar xvzf neon-0.28.3.tar.gz
2. tar xvzf subversion-1.5.5.tar.gz
3. mv neon-0.28.3 subversion-1.5.5/neon
4. cd subversion-1.5.5
5. ./configure
6. make && make install
這裡我們採用把neon掛入subversion下再重新安裝subversion的方式。指令1.2分別把neon與subversion解壓縮,指令3表示我們把解壓縮後的neon資料夾整個搬到subversion目錄下的neon資料夾中,指令4則是連到subversion目錄;至於指令5.6則是一般在linux下常見的編譯及安裝指令。當編譯及安裝完畢後,我們要怎麼確定svn已經有支援http方式的取檔方式呢?我們可以下以下指令:
svn --version
畫面如果有出現以下訊息
* ra_neon : Module for accessing a repository via WebDAV protocol using Neon.
- handles 'http' scheme
即表示我們現在可以透過svn使用http的方式來取檔了!

2012年3月14日 星期三

Using Installer Classes to Ease Deployment in VS.NET

In the last article I described using Visual Studio .NET to build and deploy your applications.  In this article I will demonstrate how to incorporate installer classes with your Visual Studio .NET msi's to handle any supporting tasks that your assemblies may need.  For example, these installer classes can handle creating MSMQ message queues, creating necessary directory structures, even creating databases that your assemblies rely on.   The idea is to include installer classes with your assemblies and then any one else who needs to use your assembly can simply add your assembly to their MSI project and execute your installer class that will handle all required tasks.

We will begin with the solution we worked with in the first article that can be found here
  1. Right click on the top project named myAssembly and choose Add New Item:


  2. Next choose the Installer Class from the dialog box name it whatever you want and click ok.
  3. You should now have a solution that looks like this


    If you look inside the installer class you will see a lot of Visual Studio .NET created code.   It is best to leave the majority of this code alone and add another call to a function inside the constructor, lets put a function call to WriteToEventLog in the constructor.
  4. Now the installer class should look something like this.



    As you can see the WriteToEventLog entry does nothing but write a message to the event log.  This is obviously a simplistic example in reality you would have the installer class create directory structures, create msmq queues, or some other supporting task that your class requires.
  5. Now click on the Set-up project and navigate to the View Editor Custom Actions as shown below:



  6. You should now see the Custom Actions interface in the IDE you now need to Right Click on the install folder and choose the Add Dialog menu item.   This will bring up the Select Item In Project dialog and you need to double click on the Application Folder icon.  At this point you should have a dialog that looks like the following:




    Click ok and you will see a graphical representation of this action in the IDE.

  7. Now rebuild the entire solution.
  8. Right click on the Set-up project and choose install this will invoke the MSI and allow you to test the Set-up project.  Follow all the way through the installer instructions until the MSI is done installing.
  9. Now check the event viewer and you should see an entry made by the installer class.
    In summary installer classes allow you as an assembly developer to be sure that all the required external tasks are completed when your class is installed.  The developer knows exactly what his/her class needs in order to function properly.  In this simple example I have shown how an installer class is executed inside an MSI.  This example could be extended to handle any number of tasks required in enterprise development.