Try   HackMD

GUI Tool 開發紀錄

Issue

Multithreading

若只用一個 thread,在執行 fpr_alg_test.exe 或是檔案操作的時候 UI 介面會無法控制(這個 thread 都在處理其他事件來不及回應 UI )
只有 UI thread 可以控制 UI 元件,跨執行續存取 UI 會跳出 exception。

  • 解法 : 在其他 thread 使用 Invoke() 或 BeginInvoke() 委派函式交給 UI thread 做處理

Invoke() 和 BeginInvoke() 的不同

  • 簡單來說
  1. Invoke 是同步呼叫, calling thread 會等到 Invoke 處理完才會繼續、其底層的時作行為是 : 到 UI thread 執行時會先 lock 住 UI thread,等到執行完時才會釋放 lock 。因此某一個 thread 先呼叫了 Invoke 還沒執行完但另外一個 thread 此時也呼叫了 Invoke , 會產生 dead lock。

  2. BeginInvoke 是非同步呼叫,使用到 thread pool 的 thread ,當 BeginInvoke 呼叫時會將 task push 到一個 task queue。此呼叫不會產生像 1 一樣 deadlock 的情況。但一旦將 BeginInvoke 的需求送出就沒有辦法將其 cancel,在關閉 window 或是資料釋放時有可能 task queue 還有未執行的 task ,需要額外用 flag 等控制訊號將其取消或及早 return

stackoverflow

  • Delegate.Invoke: Executes synchronously, on the same thread.
  • Delegate.BeginInvoke: Executes asynchronously, on a threadpool thread.
  • Control.Invoke: Executes on the UI thread, but calling thread waits for completion before continuing.
  • Control.BeginInvoke: Executes on the UI thread, and calling thread doesn't wait for completion.
  • Tim's answer mentions when you might want to use BeginInvoke - although it was mostly geared towards Delegate.BeginInvoke, I suspect.

For Windows Forms apps, I would suggest that you should usually use BeginInvoke. That way you don't need to worry about deadlock, for example - but you need to understand that the UI may not have been updated by the time you next look at it! In particular, you shouldn't modify data which the UI thread might be about to use for display purposes. For example, if you have a Person with FirstName and LastName properties, and you did:

Pipe

建立 pipe 幫助 C# program 和 C program 可以互相溝通,因此可以直接在 C# pass arguments 給 C program、接到來自 C program 的 output 並將結果顯示在 GUI 介面上

但有以下幾點要注意

  1. 不是及時接到對方 process 的 output,實際上是寫到 buffer,等 buffer 寫到一個程度才會更新

    • 目前解法 : 在 C code 的 printf() 後面加上 "flush" ,強制釋放 buffer
  2. 當呼叫到 system pause 時,flush 可能會卡住(即便是 flush 指令在 pause 之前),故要特別注意

    • 目前解法 : 單純從 C code 拿掉 system pause

Close a multi-threaded .NET Windows Forms

reference

在 multi-threading 的狀態下,如果關掉 window 只會將 UI thread 結束,此時其他 thread 有可能會在資源釋放後繼續使用,會產生 exception

示意圖如下

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

解法如下

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

  1. 在 closing event 被觸發時若其他 thread 還在跑,那先取消關閉,並立一個 flag 通知其他 thread 該 return 了,進而再從其他 thread 去觸發 closed event (注意 closing event 和 closed event 不一樣)。以上操作等同是等到其他 thread 結束時再將整個 app 關閉。

對應程式碼片段

  • UI thread
public void EventClosing(object sender, CancelEventArgs e)
{
    // deferred cancel the closing event if 
    // other threads are runnung
    if (!this.bSafeToClose)
    {
        e.Cancel = true;
    }
    // set the state to notify other threads 
    // that this program is preparing to close.
    this.enumProgressState = ProgressState.CLOSE;
}
private void EventClosed(object sender, FormClosedEventArgs e)
{
    // if calling thread is not UI thread
    if (this.InvokeRequired)
    {
        FormClosedEventHandler handler = 
            new FormClosedEventHandler(this.EventClosed);
        // do again and make it runnig in UI thread
        this.Invoke(handler, new object[] { null, null });
    }
    else
    {
        this.KillProcessAndClearData();
        this.bSafeToClose = true;
        this.Dispose();
        this.Close();
    }
}
  • 其他 thread
public void foo()
{
    While (some_condition)
    {
        // block other threads
        this.semMutexLock.WaitOne(); 
        if (this.enumProgressState == ProgressState.CLOSE)
        {
            // release lock
            this.semMutexLock.Release(); 
            // clear resources
            this.KillProcessAndClearData();   
            // re-trigger closed event
            this.EventClosed(null, null); 
            return;
        }
        /* Critical Section */
        // release lock
        this.semMutexLock.Release(); 
    }
}
  • Create
$mklink /d dst src
  • Delete
$rmdir path

刪除 mklink 的資料很危險,因此在刪除資料夾的資料時會檢查資料夾是不是 mklink (symbolic link)

  • 對應程式碼參考
// prevent user to delete the files in a symbolic link
if (((int)File.GetAttributes(folder) &
(int)FileAttributes.ReparsePoint) != 0)
{
    MessageBox.Show(
        String.Format(@"The folder \"{0}\" is a symbolic link. 
            It is dangerous to delete all the {1} files in it!", 
            folder, 
            filter),
        "Danger", 
        MessageBoxButtons.OK, 
        MessageBoxIcon.Error
        );
    return;
}

Execute cmd.exe in C#

Process process = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo();
process.StartInfo.FileName = "cmd.exe";
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
process.Start();
...
string command = cmdDestroyLink + templAbsoPath + dstTemplFoldersPostfix[i];
process.StandardInput.WriteLine(command);
...
process.WaitForExit();

有時刪除 test_output/ 的 output bitmap image 會出現 error

經 debug 發現無法重載 Bmp 或刪除 Bmp 是因為 C# 在使用 Bitmap bmp = new Bitmap(path) 時會將其檔案 lock 住,若此時對檔案作存取會直接造成 exception 抑或是直接 crash,因此若要重載或是刪除時必須先使用 bmp.Dispose() 釋放其所用的資源才可