顯示具有 C# 標籤的文章。 顯示所有文章
顯示具有 C# 標籤的文章。 顯示所有文章

2011年9月23日 星期五

db4o 物件的唯一性:disconnected objects 的概念與 web apps

db4o 在單一 object container 的環境下,在物件管理上會確保儲存在資料庫內的物件的唯一性。但是,若是啟用了 client-server 或是 session-container 的架構,則會變成多 object-container 的情況,因此同一個物件可能會在不同的 container 中有不同的 memory identity。這種情況在 web 環境下一定會發生。這時候就要借助額外的 ID,如 db4o 內建的內部 ID:UUID,或是自行產生 ID。

簡而言之,最重要的概念就是加入 EventRegistry,在建立時去檢查是否有 ID 已經存在:

// register 
IEventRegistry eventRegistry = EventRegistryFactory.ForObjectContainer(icontainer);
eventRegistry.Creating += delegate(object sender, CancellableObjectEventArgs args)
{
    if (args.Object is Article)
    {
        Article a = (Article)args.Object;
        if (a.HasDb4oID()) {                       
           // don't insert because Article is already
           // in the container
           args.Cancel();
           Article existing = LookupArticle(icontainer, a.Db4oID) as Article;
           // merge change
           existing.MergeChanges(a);
       } else {
           a.SetDb4oID(a.ArticleID);
       }
    }    
};

相關參考資料如下:

2011年9月14日 星期三

iKVM

iKVM: Using Java API’s in .NET Applications

編譯時需加入 bin 目錄底下的 dll 檔。舉例來說,需要用到 java.util.Set,則需加入 IKVM.OpenJDK.Core.dll

Runtime 時需把 bin 內的 DLL 檔複製到與執行檔相同的目錄內。

2009年3月19日 星期四

VS2005+VSS 經由 Internet

  1. 安裝 VSS
  2. 安裝完畢後,開啟 VS2005
  3. 工具->選項->原始檔控制->外掛程式選擇:選取 Microsoft Visual SourceSafe (Internet)
  4. 開啟->方案->SourceSafe (Internet)->Add SourceSafe Database
    • Address: 輸入 VSS server 網址(如 localhost)
    • Folder: 輸入 VSS folder(如 \\localhost\VSS)
  5. 在跳出的視窗中輸入 VSS 的帳號和密碼
  6. 成功加入 SourceSafe Database 後,在列表中選取加入的資料庫,接著選取要開啟的專案即可

2008年9月12日 星期五

db4o 與結構化的物件;如何處理物件間的關連

我們導入另一個更複雜的物件 Article:

儲存結構化的物件

如同儲存一般物件一樣,我們也是呼叫 Set() 方法來儲存 Article 物件。與 Article 物件關連的 Sentence 物件會被自動儲存!
string pmid = "123";
string title = "sent0";
Article article = new Article(pmid, title);
List abs=new List();
abs.Add(new Sentence("sent1","METHOD"));
abs.Add(new Sentence("sent2","METHOD"));
abs.Add(new Sentence("sent3","CONCLUSION"));
article.SentencesOfAbstract=abs;

db.Set(article);
上面的程式碼相當於我們手動將各物件存入資料庫:
List abs=new List();
abs.Add(new Sentence("sent1","METHOD"));
abs.Add(new Sentence("sent2","METHOD"));
abs.Add(new Sentence("sent3","CONCLUSION"));
db.Set(abs);

string pmid = "123";
string title = "sent0";
Article article = new Article(pmid, title);
article.SentencesOfAbstract=abs;
db.Set(article);

取回結構化物件

QBE

要取回所有的 Article 物件,我們跟以前一樣,只要提供一個空的 prototype 即可:
Article proto = new Article(null,null);
IObjectSet result = db.Get(proto);
ListResult(result);
當然,我也可以取回所有的 Sentence 物件!
Sentence proto = new Sentence(null,null);
IObjectSet result = db.Get(proto);
ListResult(result);
我們也可以取得所有 Article 物件中滿足 section 為 TITLE 的 Sentence 物件:
Sentence title = new Sentence(null,"TITLE");
Article article = new Article(null,title);
IObjectSet result = db.Get(article);
ListResult(result);

Native Queries

對結構化物件使用 Native Query 跟一般物件幾乎一模一樣。下面示範如何取回 一群 Article 物件,使其 Title Sentence 滿足指定的參數:
/// C# .NET 1.1
public class RetrieveArticlesByTitlePredicate : Predicate
{
   readonly string _title;
   public RetrieveArticlesByTitlePredicate(string title)
   {
      _title = title;
   }
   public bool Match(Article candidate)
   {
      return candidate.SentenceOfTitle.RawText == _title;
   }
}

// 使用方法
string title = "sent0";
IObjectSet results = db.Query(new
RetrieveArticlesByTitlePredicate(title));
ListResult(results);

/// C# .NET 2.0
string title = "sent0";
List
results = db.Query
(delegate(Article article) { return article.SentenceOfTitle.RawText == title; }); listResults(results);

SODA Query API

使用 SODA 的話,必須 descend 兩次以便取得 rawText:
IQuery query = db.Query();
query.Constrain(typeof(Article));
query.Descend("title").Descend("rawText")
.Constrain("sent0");
IObjectSet result = query.Execute();
ListResult(result);
另外,我們也可以藉由指定 SentenceOfTitle 欄位來達到相同的效果:
IQuery query = db.Query();
query.Constrain(typeof(Sentence));
Sentence proto = new Sentence("sent0", null);
query.Descend("title").Constrain(proto);
IObjectSet result = query.Execute();
ListResult(result);
下面我們示範如何取得所有 PMID 為 123 的 SentencesOfAbstract 欄位:
IQuery artQuery = db.Query();
artQuery.Constrain(typeof(Article));
artQuery.Descend("pmid").Constrain("123");
IQuery sentQuery = artQuery.Descend("_SentencesOfAbs");
IObjectSet result = sentQuery.Execute();
ListResult(result);

更新結構化物件

如同一般物件一般,要更新結構化物件,也是對它們再次呼叫 Set() 方法:
IObjectSet result = db.Get(new Article("123",null));
Article found = (Article)result.Next();
found.SentenceOfTitle = new Sentence("sentXYZ",null);
db.Set(found);
下面我們嘗試直接更新 SentenceOfAbstract 物件內容:
IObjectSet result = db.Get(new Article("123",null));
Article found = (Article)result.Next();
List abs=new List();
abs.Add(new Sentence("sent1","INTRODUCTION"));
abs.Add(new Sentence("sent2","METHOD"));
abs.Add(new Sentence("sent3","CONCLUSION"));
found.SentenceOfAbstract=abs;
db.Set(found);
看起來似乎一切都很簡單、美好?但是實際上,若是我們再度存取資料庫,檢查我們的更新:
result = db.Get(new Article("123",null));
ListResult(result);
我們會發現,更新並未成功!這是因為效能上的考量,db4o 不會自動的更新結構化物件內的所有成員類別!為了兼顧效能與程式碼撰寫的簡潔,db4o 導入了 update depth 的概念,來控制程式在更新時,物件成員樹的更新深度。

update depth

對於所有的物件而言,預設的 update depth 是 1,代表只有 primitive 以及 String 成員會被更新。

db4o 提供了相當精細的方法來控制 update depth。下面我們藉由設定 CascadeOnUpdate 為 true,指定 db4o 完整的更新 Article 物件的內容:

Db4oFactory.Configure().ObjectClass(typeof(Article))
.CascadeOnUpdate(true);
// 接下來才去設定 Article 物件內容
要注意的地方是,先設定好 update depth 後才去更新才有效。

刪除結構化物件

一樣,用 Delete() 方法即可:
IObjectSet result = db.Get(new Article("123",null));
Article found = (Article)result.Next();
db.Delete(found);
result = db.Get(new Car(null));
我們應該會認為 db4o 應該也會把 Article 物件內一堆的 Sentence 物件也刪除了吧?但是實際上, db4o 並不會幫我們做這件事!

遞迴刪除結構化物件內成員

跟更新時一樣,我們可以設定 db4o,幫我們完成這件事:
Db4oFactory.Configure().ObjectClass(typeof(Article))
.CascadeOnDelete(true);

// 接下來才去刪掉
// ...
另外要注意的一點是,目前的 db4o 版本(6.0)中,就算我們要刪除的資料庫物件,目前仍然被程式內的變數參考到,它們經由遞迴刪除後,一樣會被從資料庫內移除。

2008年9月11日 星期四

db4o 的 query 方法簡述

db4o 提供三種 query 系統。

Query-By-Example;QBE

QBE 使用上相當簡單,只要提供 db4o 一個 template 物件,db4o 會自動回傳符合 template 物件中非預設欄值的所有物件。這是經由 reflecting 機制加以建立一個 query expression,其利用 AND 將所有非預設值欄位加以結合。

這個方式有下列幾點限制:
  • db4o 必須 reflect template 物件的所有成員
  • 我們無法進行更進接的 query expressions(比如 AND、OR、NOT)
  • 我們無法建立某些特殊的限制條件,比如值必須是整數 0、空字串或是 null。因為它們會被解譯為非條件。
這些限制,都可以藉由 Native Query(NQ)系統加以解決。

Native Queries

Native queries 是 db4o 主要的 query 介面。其概念可以參考如下兩篇白皮書:

範例

在 C# 1.1 中,我們必須建立一個繼承自 com.db4o.Predicate 的類別,並且必須實作 Match() 方法,其傳入的參數決定了物件的範圍。
bool Match(Sentence candidate);

下面我們看一個更複雜的 query;我們嘗試找出所有的 Sentence 物件,並滿足:section 必須為 TITLE,並且其 sentence 長度不能大於 20 個字:

// C# .NET 2.0
IList <Sentence> result = db.Query%lt;Sentence> (delegate(Sentence sent) {
return sent.RawText.Length < 20 && sent.SECTION == "TITLE";
});
原則上,我們可以將任意的程式碼轉化為 Native Query,只要我們知道自己在做什麼就好了...我們再看一個例子:

SODA Query API

SODA query API 是 db4o 最底層的 query API,使用它能讓我們直接存取 query graph 上的節點。因為 SODA 使用字串來識別欄位,因此本身缺乏程式語言的支援(比如編譯時期型態檢驗),而且撰寫起來也比較冗長。因此對於大部分的應用而言, Native Query 會是比較好的選擇!然而,因為有一些應用,有動態產生 query 的需求,這時就需要用到 SODA。

範例

下面示範如何利用 SODA 取回全部的 Sentence 物件。我們利用 ObjectContainer 的 Query() 方法建立了 Query 物件,接著我們可以藉由 Constraint 物件加入限制到 Query 物件中:
IQuery query = db.Query();
query.Constrain(typeof(Sentence));
IObjectSet result = query.Execute();
ListResult(result);
要經由 section 名取得某個 Sentence 物件,我們必須利用 Descend() 方法進一步的加強限定它的 Section 欄位:
IQuery query = db.Query();
query.Constrain(typeof(Sentence));
query.Descend("section").Constrain("TITLE");
IObjectSet result = query.Execute();
ListResult(result);
值的一提的是,類別限定其實可以拿掉;假若去掉類別限定,我們將會 query 取得所有包含指定的 section 資料欄位值的物件。再看另一個例子:
IQuery query = db.Query();
query.Constrain(typeof(Sentence));
query.Descend("rawText").Constrain("Pain management: evaluating the effectiveness of an educational programme for surgical nursing staff.");
IObjectSet result = query.Execute();
ListResult(result);

進階範例

有時我們並不確定某特定欄位,但是需要去 query 符合特定範圍值的物件,或是不包含特定值的物件,這些都可以經由 Constraint API 辦到。

下面的例子找出,所有 RawText 不是「Pain management: evaluating the effectiveness of an educational programme for surgical nursing staff.」的 Sentence 物件:
IQuery query = db.Query();
query.Constrain(typeof(Sentence));
query.Descend("rawText").Constrain("Pain management: evaluating the effectiveness of an educational programme for surgical nursing staff.").Not();
IObjectSet result = query.Execute();
ListResult(result);
我們也可以加入其它的邏輯運算:
IQuery query = db.Query();
query.Constrain(typeof(Sentence));
IConstraint constr = query.Descend("rawText")
.Constrain("Pain management: evaluating the effectiveness of an educational programme for surgical nursing staff.");
// 也可以用 .Or(constr);
query.Descend("section")
.Constrain("TITLE").And(constr);

IObjectSet result = query.Execute();
ListResult(result);
我們也利用限制來比較給定的值:
IQuery query = db.Query();
query.Constrain(typeof(Sentence));
// 找到長度大於 50 的 Sentence 物件
query.Descend("length")
.Constrain(50).Greater();
IObjectSet result = query.Execute();
ListResult(result);
SOAD API 也允許我們去 query 欄位值為資料型態的預設值的物件:
Sentence s = new Sentence("Sentence 1", null);
db.Set(s);
IQuery query = db.Query();
query.Constrain(typeof(Sentence));
query.Descend("section").Constrain(null);
IObjectSet result = query.Execute();
ListResult(result);
db.Delete(s);
我們也可以請 db40 對 query 結果進行排序:
IQuery query = db.Query();
query.Constrain(typeof(Sentence));
query.Descend("length").OrderAscending();
IObjectSet result = query.Execute();
ListResult(result);
query.Descend("length").OrderDescending();
result = query.Execute();
ListResult(result);
前述的所有範例都可以任意結合,形成更複雜的 query。最後我們示範如何清除資料庫內所有的 Sentence 物件:
IObjectSet result = db.Get(typeof(Sentence));
foreach (object item in result)
{
  db.Delete(item);
}

2008年8月31日 星期日

C# 的 Regex.Replace

在 Perl 中正規表示法的取代,可以簡單的利用 $1、$2 等方式來輕鬆指代記憶的群組;比如:

$text="Hypermethylation of E-cadherin is an independent predictor of improved survival in head and neck squamous cell carcinoma. Shematic illustration of E-cadherin in adherens junction. E-cadherin homodimer on the cytoplasmic membranes of adjacent cells is shown. Moreover, overexpression of E-cadherin, which was found underexpressed in MTX-resistant cells, also sensitized the cells toward the chemotherapeutic agent";
$text=~s/([^>])(\bE-cadherin\b)([^<])/$1$2<\/span>$3/g;
print $text;

# 輸出:
Hypermethylation of E-cadherin is an independent predictor of improved survival in head and neck squamous cell carcinoma. Shematic illustration of E-cadherin in adherens junction. E-cadherin homodimer on the cytoplasmic membranes of adjacent cells is shown. Moreover, overexpression of E-cadherin, which was found underexpressed in MTX-resistant cells, also sensitized the cells toward the chemotherapeutic agent

在 C# 中,也可以辦到:

2008年8月30日 星期六

C# static constructor

在 Java 的類別宣告裡,可以使用 static 區塊來宣告靜態初始化的變數:
class Demo{
    private static int i;
    private static String s;
    static{
        i=24;
        s="abc"
    }
}
在 C# 則可以利用 static constructor 達到同樣的效果:
class Demo{
    private static int i;
    private static String s;
    static Demo(){
        i=24;
        s="abc"
    }
}

2008年8月26日 星期二

db4o 簡介與初探

db4o object database 引擎主要由一單獨的 DLL 組成。目前版本支援了 Microsoft .NET. framework 1.0/1.1 以及 2.0。

一般 .NET framework 的 DLL 檔位於安裝目錄內的 bin/net/ 目錄底下;.NET Compact Framework 則位於 bin/compact/ 下(我使用的 6.4 版本檔名為 Db4objects.Db4o.dll)。在開發時,必須將這個 DLL 加入到參考中。

Db4objects.Db4o namespace

包含了大部分開發所需要用到的功能。其中最重要的兩個類別是 Db4objects.Db4o.Db4oFactory 和 Db4objects.Db4o.IObjectContainer。

  • Db4oFactory:提供的靜態方法讓我們可以開啟資料庫檔案、啟動伺服器或是連線到既存的伺服器。另外,它也允許我們再開啟資料庫前先設定好 db4o 的環境變數。
  • IObjectContainer:則是我們最常會使用到,也是最重要的資料庫程式介面。它代表的就是我們的 db4o 資料庫。
    • IObjectContainer 可能是處於單一使用者模式的某個資料庫,或是一個連線到某個 db4o 伺服器的客戶端連線。
    • 每一個 IObjectContainer 都擁有一個 transaction。每個執行的動作都是處於 transaction 狀態;當我們開啟一個 IObjectContainer,我們就處於一個 transaction 之中;當我們下達 Commit() 或是 Rollback(),將會馬上切換到另一個 transaction。
    • 只要我們有在操作 IObjectContainers,它將會持續保持開啟連接的狀態。當我們關閉一個 IObjectContainer,所有存在記憶體中的資料庫參考將會被丟棄。

第一個 db4o 程式

我們首先常是如何儲存、擷取、更新和刪除某個只包含 primitive 以及字串成員的類別 。

public class Sentence
{
 private string rawText;

 /// raw 參數代表原本的文字內容
 /// section 參數則為該文字所屬的 section
 public Sentence(string raw, string section)
 {
     rawText = raw;
     this.section = section;
 }
 public string RawText
 {
     get { return rawText; }
     set { rawText = value; length=value.Length}
 }
 private int length;
 public int Length
 {
     get { return length; }
     set { length = value; }
 }

 private string html;
 public string HTML
 {
     get { return html; }
     set { html = value; }
 }
 private string section;
 public string Section
 {
     get { return section; }
     set { section = value; }
 }
}

開啟資料庫

要存取一個 db4o database 資料檔或是建立一個新的資料庫,我們只要呼叫 Db4oFactory.OpenFile(),並提供我們想要存取的資料庫檔案路徑當作參數及可。該方法會回傳一個 IObjectContainer 實例。

如前述,IObjectContainer 代表的就是一個資料庫,並且扮演了我們跟 db4o 將的主要溝通橋梁。藉由呼叫 Close() 方法,將關閉該 IObjectContainer 資料庫檔,並且是放所有參考到的資訊。

// accessDb4o
IObjectContainer db = Db4oFactory.OpenFile(dbfilename);
try
{
// do something with db4o
}
finally
{
  db.Close();
}

儲存物件

要儲存某個物件,我們只要對我們的資料庫呼叫 Set() 方法,並且存給它某個物件當作參數及可!

// store First Sentence object
Sentence s = new Sentence("Pain management: evaluating the effectiveness of an educational programme for surgical nursing staff.", "Title");
db.Set(s);
Console.WriteLine("Stored {0}", s);

取回物件

db4o 提供三種不同的 query 系統:

  • Query by Example (QBE)
  • Native Queries (NQ)
  • SODA Query API (SODA)
這裡我們先嘗試利用 QBE。而 NQ 則是 db4o 主要的 query 介面,文件也建議開發者使用該介面為主。

當使用 QBE 時,我們會提供 db4o 一個原型物件(prototypical object)作為我們想要取得的物件的一個範例,並呼叫 Get() 方法。db4o 將會依據傳給 Get() 方法的物件的內容,從資料庫內取得包含跟我們提供的物件一樣資料值欄位的所有物件(非預設值;一般物件的預設值為 null,int 為 0),並以 ObjectSet 實例回傳(代表所有符合的物件)。

下面我們示範如何從資料庫中取得所有的 Sentence 實例。方法很簡單,我們只要提供一個空的 Sentence 原型即可(所有的資料欄位值都為預設值):

Sentence sents = new Sentence(null, null);
ObjectSet result = db.Get(sents);
ListResult(result);

// ...略

public static void ListResult(IObjectSet result)
{
    Console.WriteLine(result.Size());
    while (result.HasNext())
    {
        Console.WriteLine(result.Next());
    }
}

db4o 另外提供了一個簡便的方法讓我們可以取得某類別的所有實例:

ObjectSet result = db.Get(typeof(Sentence));
ListResult(result);
對於泛型物件,可以利用 query() 方法取得:
IList <Sentence> sents = db.query<Sentence>(typeof(Sentence));

下面示範如何依據 section 來查詢:

Sentence sent = new Sentence(null, "BACKGROUND");
ObjectSet result = db.Get(sent);
ListResult(sent);

更新物件

更新物件跟儲存物件的動作很類似;我們也是呼叫同樣的 Set() 方法:

ObjectSet result = db.Get(new Sentence(null, "TITLE"));
Sentence found = (Sentence)result.Next();
found.Section="BACKGROUND";
db.Set(found);
Console.WriteLine("Modified section for" + found);

注意很重要的一點是我們第一個步驟是先去 query 物件。假若,當我們呼叫 Set() 方法去修改一個未知的(但是實際上我們想要修改的是既存的物件)的物件;也就是說我們沒有先去 queyr 物件,那麼 db4o 將會在資料庫內插入一個新的物件。db4o 並不會自動的把我們產生的物件跟資料庫內的物件進行比對;它假設我們實際上要插入一個剛剛好有同樣值的物件到資料庫內。

刪除物件

在資料庫內的物件可經由 DELETE() 方法加以移除。
IObjectSet result = db.Get(new Sentence("Pain management: evaluating the effectiveness of an educational programme for surgical nursing staff.", null));
Sentence found = (Sentence)result.Next();
db.Delete(found);
Console.WriteLine("Deleted {0}", found);

2006年2月9日 星期四

Bio NER Tagger

展示網頁

Web Service

Web Service 所建立的資料並不是全部都能在網路上傳遞,因此使用 Web Service 實應注意。以下是在 .Net 中常用且可在網路上傳遞的資料型別:
  • 基本資料型別(bool、string、DataTime、int 以及所有數值類型的資料型別)
  • 列舉資料型別(透過 enum 所定義的資料型別)
  • 以 XML 結構描述的資料
  • DataSet 物件(DataSet 物件是利用 XML 來描述的因此可以傳遞)
  • 上述資料型別所組成的陣列

Web Service 物件的方法必須在程式碼前加上:

  • [WebMethod]
  • public
這樣才可以被外部程式所參考。

.NET 呼叫(啟動)外部程式

如何利用 C# 執行外部程式

System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.EnableRaisingEvents=false;
proc.StartInfo.FileName="filename"; // 檔案名稱
proc.Start();

除了使用 System.Diagnostics.Process 以外,傳統的 Shell 方法也可以,不過必須注意存在的風險:

一般的命令提示列指令大多會存取系統資源,要使用 Web 應用程式呼叫命令列程式,可能會因為 Web 程式權限不足無法無法執行,也可能因為開放權限而造成安全漏洞。

這問題有兩種解法:

  • 將呼叫命令列的程式獨立寫成元件 將元件加入 COM+,若是 Windows 2003 可以設定成以 Windows service 方式執行。在 COM+ 上設定 Web 應用程式的存取權。
  • Web Service 將此一 Web services 獨立一個應用程式集區,並只提供本機呼叫,授與一個較高的權限執行系統命令。

重新導向輸出

當一個程式啟動時,作業系統會相對應的產生一個行程,而我們可以經由 process ID 和行程名稱來辨識該行程。

在 .NET 中,Process 元件可以用來產生、刪除、暫停一個新的行程;我們也可以經由下面的屬性來取的相關的資訊:

  • NonpagedSystemMemorySize:
  • PagedMemorySize:
  • PagedSystemMemorySize:
  • PeakPagedMemorySize:
  • PeakVirtualMemorySize:
  • PeakWorkingSet
  • PriorityClass
  • PrivateMemorySize
  • PrivilegedProcessorTime

Process 元件中,包含了靜態和實例方法。比較常用的靜態方法如:

  • EnterDebugMode:讓該行程進入除錯模式
  • GetCurrentProcess:回傳目前正在執行的行程
  • GetProcessById:回傳指定的行程 ID 參考
  • GetProcesses:回傳目前機器上所有執行的行程所組成的陣列
  • Start:啟動一個新行程

Process 類別方法:

  • Close:釋放 Process 元件所使用的相關資源
  • Kill:終止正在執行的行程
  • CloseMainWindow:藉由傳送訊息到行程所在的主視窗來結束該行程

Process 類別屬性:

  • Responding:取得目前行程的狀態;true 表示還在執行中,false 代表無回應
  • StandardError:取得行程的標準錯誤輸出的檔案描述子(file descriptor)好將其重新導向到 Stream Reader,以檔案的方式來讀取之
  • StandardInput:取得行程的標準輸入的檔案描述子(file descriptor)好將其重導向,使得我們寫入資料到行程就好像寫資料到 StremWriter 一樣
  • StandardOutput:類似標準錯誤輸出,但是用來讀取行程的標準輸出
  • StartInfo:在行程啟動前,傳遞初始化參數給行程的屬性;若是在行程啟動後才改變行程的參數,將不會有任何影響

要重新導向標準的輸出入,首先必須初始化一個 ProcessStartInfo 類別,並以我們想要啟動的應用程式名稱作為該類別的建構子引數,並設定一些參數,最後把它傳遞給 Process 實例:

ProcessStartInfo psI = new ProcessStartInfo("cmd"); 

psI.UseShellExecute 屬性必須設定為 false,以便重新導向 StandardInput 等其它標準輸出入。所以,下面的這些屬性

psI.RedirectStandardInput 
psI.RedirectStandardOutput 
psI.RedirectStandardError 

都被設定為 true。

為了避免顯示命令列提示字元那個惱人的視窗,我們也將 psI.CreateNoWindow 屬性設定為 true,因此 cmd 將不會顯示視窗。最後,設定 p.StartInfo 為剛剛建立的 ProcessStartInfo 實例。

為了能夠擷取 p.StandardInput、p.StandardOutput 和 p.StandardError,我們必須取得檔案描述子(StreamReaders 和 StreamWriter 類別),來讀取 StandardOutput、StandardError 和寫入 StandardInput;就跟我們讀取或寫入檔案一樣的方式。當我們關閉 p.StandardInput 檔案描述子時,cmd 行程也會被終止。

最後,我們讀取 p.StandardOutput 和 p.StandardError 檔案描述子的內容到 text box。

下面是整個完整範例:

private void start()
{
   Process p = new Process();
   StreamWriter input;
   StreamReader output;
   StreamReader err;

   ProcessStartInfo psI = new ProcessStartInfo("cmd");
   psI.UseShellExecute = false;
   psI.RedirectStandardInput = true;
   psI.RedirectStandardOutput = true;
   psI.RedirectStandardError = true;
   psI.CreateNoWindow = true;
   p.StartInfo = psI;

   p.Start();
   input = p.StandardInput;
   output = p.StandardOutput;
   err = p.StandardError;

   input.AutoFlush = true;
   if (tbComm.Text != "")
      input.WriteLine(tbComm.Text);
   else
      //execute default command
      input.WriteLine("dir \\");
   input.Close();

   textBox1.Text = output.ReadToEnd();
   textBox1.Text += err.ReadToEnd();
}

C# Thread

.NET Framework 在 System.Threading 命名空間中定義了一些跟執行緒相關的類別。下面我們示範如何在 C# 中產生一個執行緒:

步驟一:建立一個 System.Threading.Thread 物件

建立一個 System.Threading.Thread 物件會在 .NET 環境中產生一個 managed 執行緒。Thread 類別只有一個建構式,其接受一個 ThreadStart delegate 做為參數。ThreadStart delegate 是一個回呼方法,當我們啟動該執行緒時,會呼叫該方法。

步驟二:建立回呼函式

該方法將會是我們新產生的執行緒的起始點。它可能是一個類別物件的實例方法(instance function)或是一個靜態方法。

若是該方法為實例方法,我們必須在建立 ThreadStart delegate 前,建立該類別物件;對於靜態方法而言,我們只需直接使用方法名稱來初始化 delegate。另外要注意的是,回呼方法必須以 void 來當作其回傳類型和參數;因為 ThreadStart delegate 函數的宣告為如此!

步驟三:啟動執行緒

我們可以使用 Thread 類別的 Start 方法來啟動新建立的執行緒。這個方法是非同步的方法,該方法會要求作業系統來啟動建立的執行緒。

例子

// 執行緒的回呼方法

public static void MyCallbackFunction()
{
    while (true)
    {
        System.Console.WriteLine("Hey!, My Thread Function Running");
        ………
    }
}

public static void Main(String []args)
{
    // 建立執行緒物件
    Thread MyThread = new Thread(new ThreadStart
        (MyCallbackFunction));

    MyThread.Start()
    ……
}

刪除執行緒

藉由呼叫執行緒物件的 Abort 方法,我們可以刪除一個執行緒。呼叫 Abort 方法會造成目前的執行緒拋出 ThreadAbortException 例外,並結束執行。

MyThread.Abort();

暫停和恢復執行緒

我們可以用 Suspend 方法來暫停一個正在執行中的執行緒,或從另一個執行緒中使用 Resume 方法來恢復一個執行緒的執行。

MyThread.Suspend() // 暫停執行緒執行
MyThread.Resume() // 恢復執行緒執行

執行緒狀態

執行緒可能處於下列幾種狀態:

  • Unstarted:執行緒已經被建立於 Common Language Runtime 中,但是還未啟動
  • Running:執行緒的 Start 方法被呼叫後,即進入此狀態
  • WaitSleepJoin:當呼叫執行緒的 wait、Sleep 或是 Join 方法時,會進入此狀態
  • Suspended:當 Suspend 方法被呼叫時,會進入此狀態
  • Stopped:執行緒終止(不論是正常終止或是 Abort)

藉由使用 Thread 的 ThreadState 屬性我們可以得知目前執行緒所處的狀態。

執行緒優先權

Thread 類別的 ThreadPriority 屬性用來設定該執行緒的優先權。可用的值包括:Lowest、BelowNormal、Normal、AboveNormal、Highest。預設值為 Normal。

Cross compile

因為從 GENIA 中下載的 geniatagger 只有 Linux 版本,所以我們必須使用 cygwin 來將它編譯成為可以在 windows 上使用的版本。

要做 cross compile 很簡單,只要在安裝 cygwin 時記得安裝 g++、gcc 以及 vim (可以不安裝)後,就可以在 cygwin 的視窗中下達 gcc/g++ 來做編譯。要注意的是,若是要將編譯好的 exe 檔在其它的電腦上執行,必須要複製 cygwin1.dll 檔。