2009年2月18日 星期三

C# Remoting


Remoting可建立的Channel有(1)TCP Channel. (2)Http Channel. (3)IPC Channel

.Net 1.1 : TCP Channel、Http Channel.
.Net 2.0 : 之後多了IPC Channel 使用Windows程式間通信系統(IPC:Inter process Communication)

Remoting 的使用流程
1.建立Remoting 物件
2.在Server建立使用Remoting物件的Channel
3.在Client使用Remoting


//------------------ Example --------------------//
1.建立Remoting物件









1.1 建立Interface1.2.實作Remoting物件
using System;
using System.Collections;
namespace G5.RemotingEvent.ICommon
{
public delegate void PAEventHandler(Hashtable fax);
public interface IG5Business
{
void SendPA(Hashtable fax);
}
}

using System;
using System.Collections;
using G5.RemotingEvent.ICommon;

namespace G5.RemotingEvent.RemoteObj
{
public class G5Business:MarshalByRefObject,IG5Business
{
// 建立Event
public static event PAEventHandler PASendedEvent;
#region
public void SendPA(Hashtable fax)
{
if (PASendedEvent != null)
{
PASendedEvent(fax);
}
}
#endregion
public override object InitializeLifetimeService()
{
return null;
}
}
}

2.在Server建立使用Remoting物件的Channel





2.1 using
//using System.Runtime.Remoting.Channels.Http;
//using System.Runtime.Remoting.Channels.Tcp;

using System.Runtime.Remoting.Channels.Ipc;

2.2 建立Channel
// IPC Channel
IpcChannel channel = new IpcChannel("G5Channel");
ChannelServices.RegisterChannel(channel);
RemotingConfiguration.RegisterWellKnownServiceType(typeof (G5Business), "G5Loader", WellKnownObjectMode.Singleton);

//TCP Channel
//TcpServerChannel channel = new TcpServerChannel(3210);
//ChannelServices.RegisterChannel(channel, false);
//RemotingConfiguration.RegisterWellKnownServiceType(typeof(G5Business), "G5Loader", WellKnownObjectMode.Singleton);

// Http Channel
// HttpChannel channel = new HttpChannel(8080);
// ChannelServices.RegisterChannel(channel);
// RemotingConfiguration.RegisterWellKnownServiceType(typeof(G5Business),"G5Business.soap",WellKnownObjectMode.Singleton);

G5Business.PASendedEvent += new PAEventHandler(OnPASended);



3.在Client使用Remoting




1.using

//using System.Runtime.Remoting.Channels.Http;
//using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Channels.Ipc;

2.使用Channel
private IG5Business faxBus = null;

// IPC Channel
string stringIpc = "ipc://G5Channel/G5Loader";
faxBus = (IG5Business)Activator.GetObject(typeof(IG5Business), stringIpc);

//TCP Channel
//string stringTcp = "tcp://localhost:3210/TestLoader";
//faxBus = (IG5Business)Activator.GetObject(typeof(IG5Business), stringTcp);
//Http Channel
//HttpChannel channel = new HttpChannel(0);
//ChannelServices.RegisterChannel(channel);
//faxBus = (IG5Business)Activator.GetObject(typeof(IG5Business),
// http://localhost:8080/G5Business.soap);



參考資料:
http://www.cnblogs.com/idior/archive/2007/01/04/611265.html (原理)
http://www.codeguru.com/csharp/csharp/cs_syntax/remoting/article.php/c9251



2009年2月16日 星期一

C# 設定和取得機碼(Registry Key)的值

設定機碼(Registry Key)

string keyPathString = "SOFTWARE\\AB Tool";
Microsoft.Win32.RegistryKey start = Microsoft.Win32.Registry.LocalMachine;
Microsoft.Win32.RegistryKey programName = start.CreateSubKey(keyPathString);
programName.SetValue("Application Path", System.Windows.Forms.Application.StartupPath);
programName.Close();

執行的結果

------------------------------------------------------------------------------------------------------------------------------------

取得機碼(Registry Key) 的值

string pathString = null;
string keyPathString = "SOFTWARE\\AB Tool";
Microsoft.Win32.RegistryKey start = Microsoft.Win32.Registry.LocalMachine;
Microsoft.Win32.RegistryKey programName = start.OpenSubKey(keyPathString);
if (programName != null)
{
pathString = (string)programName.GetValue("Application Path");
}

------------------------------------------------------------------------------------------------------------------------------------

MSMQ


MSMQ
::Microsoft Message Queue(訊息駐列)


啟動MSMQ
::


C#使用方式
傳訊
1.建立要傳送資料
System.Messaging.Message hashtable_Msg = new System.Messaging.Message(strHashtable, new System.Messaging.BinaryMessageFormatter());
2.指定MSMQ Channel 或建立MSMQ Channel
msgQ = new System.Messaging.MessageQueue(@".\Private$\MDSQueue");
msgQ = System.Messaging.MessageQueue.Create(@".\Private$\MDSQueue");

3.傳送到Message Queue
msgQ.Send(hashtable_Msg);

收訊
1.指定MSMQ Channel 或建立MSMQ Channel
msgQ = new System.Messaging.MessageQueue(@".\Private$\MDSQueue");
msgQ = System.Messaging.MessageQueue.Create(@".\Private$\MDSQueue");

2.設定這個Message Queue資訊格式
msgQ.Formatter = new System.Messaging.BinaryMessageFormatter();
3.接收Message
strHashtable = ((System.Collections.Hashtable)msgQ.Receive().Body);

//-------------- 範例 ---------------//
傳訊
Hashtable strHashtable = new Hashtable();
double key = 0;
string valueString = "Dispose";
strHashtable.Add(key, valueString);
// 傳送Hashtable 格式資料時,需要使用BinaryMessageFormatter方式傳送
System.Messaging.Message hashtable_Msg = new System.Messaging.Message
(strHashtable, new System.Messaging.BinaryMessageFormatter());


System.Messaging.MessageQueue msgQ;
// 確認是否有用來傳送的Channel存在,在這 Channel 名稱由使用者定為MDSQueue
if (System.Messaging.MessageQueue.Exists(@".\Private$\MDSQueue"))
{
msgQ = new System.Messaging.MessageQueue(@".\Private$\MDSQueue");
}
else
{
msgQ = System.Messaging.MessageQueue.Create(@".\Private$\MDSQueue");
}

// 使用Message Queue 的 Object 來傳送訊息
try
{
msgQ.Send(hashtable_Msg);
// 中斷在接收訊息的Message Queue Thread
ThreadAll[0].Interrupt();
}
catch
{
}

收訊
public void ListenMDSQueue()
{
// Create MessageQueue Object。
System.Messaging.MessageQueue msgQ;
// 確認是否有用來傳送的Channel存在,在這 Channel 名稱由使用者定為MDSQueue

if (System.Messaging.MessageQueue.Exists(@".\Private$\MDSQueue"))
{
msgQ = new System.Messaging.MessageQueue(@".\Private$\MDSQueue");
}
else
{
msgQ = System.Messaging.MessageQueue.Create(@".\Private$\MDSQueue");
}


// Always listen MDS Queue until interrupt。
while (true)
{
try
{
// 使用Thread時interrup機制在MessageQueue Receive時無法中斷
// 需要MessageQueue收到訊息釋放Thread的控制,interrup才能有作用
// 可以使用 Sleep機制在進入Receive時,先確定是否有Interrup發生.

System.Threading.Thread.Sleep(80);


System.String ContextString = "";
System.Collections.Hashtable strHashtable = new System.Collections.Hashtable();
System.Object o = new System.Object();
System.Type[] arrTypes = new System.Type[2];
arrTypes[0] = strHashtable.GetType();
arrTypes[1] = o.GetType();
// Message 如果沒有定義格式.預設是使用XmlMessageFormatter
// 這個格式無法存放Hashtable型態的訊息,所以改用BinaryMessageFormatter
// msgQ.Formatter = new System.Messaging.XmlMessageFormatter(arrTypes);
msgQ.Formatter = new System.Messaging.BinaryMessageFormatter();

// 收到的訊息轉為Hashtable格式
strHashtable = ((System.Collections.Hashtable)msgQ.Receive().Body);

// Get Type
ContextString = (string)strHashtable[(double)0];

// 如果收到的訊息為"Dispose” 代表傳訊的人要結束接收訊息的動作.那在下一次迴圈Sleep時
// 會執行傳訊的人所下的ThreadAll[0].Interrupt() 指令

if (ContextString == null || !ContextString.Equals("Dispose"))
{
this.setValueTable(strHashtable);
}
}
catch (System.Threading.ThreadInterruptedException e)
{
System.Console.WriteLine("Exception e:"+e.Message);
msgQ.Dispose();
break;
}
}
}


private System.Threading.Thread[] ThreadAll = new System.Threading.Thread[2];
private void StartListenMQThreads()
{
ThreadAll[0] = new System.Threading.Thread(new System.Threading.ThreadStart(ListenMDSQueue));
ThreadAll[0].Start();
}

// 這個例子有二個Thread , Main Thread 和 Linten Message Queue Thread
// 當Message Queeu Thread想改變Main Thread的資料時,要使用delegate的方式來達到目的

private delegate void InvokeUpdateState_forHashtable(System.Collections.Hashtable strHashtable);
private void setValueTable(System.Collections.Hashtable strHashtable)
{
if (this.InvokeRequired)
{
this.Invoke(new InvokeUpdateState_forHashtable(this.setValueTable), new object[] { strHashtable });
}
else
{
this.strHashtable_value = strHashtable;
}
}

C# Gargabe 觀念整理



CLR(Common Language Runtime)

:CLR自動配置與管理的記憶體,稱為Managed資源
:不受CLR管理的便被稱為Unmanaged資源

使用強制釋放資源
System.GC.Collect();

通知 .Net framework不要對這個object進行資源回收
System.GC.SuppressFinalize(this)

// ---以下引用 慮山佚城所寫的文章-- 留做個人以後查資料的記錄 --- //

在執行.NET的程式時,對於不同型別,CLR(Common Language Runtime)會在不同的地方分配資源空間。對於實值型別(Value Type),當宣告一個變數時,CLR會在Stack(堆疊)中配置一塊空間,設定該變數的值時,其值也直接存放於該空間中,如下圖:


而對於參考型別(Reference Type),CLR則在Stack中配置一塊存放記憶體位址的空間,在初始化該型別的實體時(ex: new),則在Heap(堆積)上配置該型別所需的空間,再將該空間的位址傳回給存放在Stack中的那塊空間,如下圖:

這些由CLR自動置與管理的記憶體,被稱為Managed資源;反之,不受CLR管理的便被稱為Unmanaged資源(ex: Stream、與資料庫的連結、COM物件……等)。
而對於資源的釋放,CLR則主要以GC來回收已用不到的記憶體空間,這些空間便被稱為garbage。(上述的記憶體配置及GC的運作可參考
MSDN: CLR的自動記憶體管理)
那麼garbage如何產生或被判定呢?
基本的原則是當該變數「不再有效」時,便會被視為garbage,具體的情況則包括超出該變數的有效範圍(ex:離開了對應的大括號的區域變數)、 將變數指定為null、重新指向其他物件(而原先指向的物件已無法被取得)、重新初始化…等,這時原先變數佔有的空間都會被CLR視為garbage而等待回收。
若變數為數值型別,則當其超出有效範圍時,CLR會直接回收它在Stack上所佔用的空間;若變數為參考型別,則CLR會先回收它在Stack上佔用的空間,而將Heap上的空間視為garbage,等待GC回收。若參考型別的變數在其有效範圍內重新初始化,則原先所指向的物件亦會被視為garbage。
然而,被視為garbage的變數,並不是馬上就被GC回收,而是根據GC內的演算法,依變數被判定的generation而有不同。在Managed Heap上的物件會被CLR分為三個generation:0、1、2,數字越大表示存活時間越長。這樣的設計乃是建構在「只壓縮部份的Heap會比一次壓縮整個Heap來得有效率」的事實上,因此將物件分成不同的層級,在回收時針對不同層級做回收,是.NET Framework考量效率後所採用的方法。
CLR會將新建立的物件擺在第0個generation,當GC進行gen 0的回收時,會將仍為有效的gen 0物件提昇至gen 1。而若gen 0的空間回收後仍不足以建立新物件,則GC會繼續檢查gen 1的物件,存留下來的gen 1物件會被提昇至gen 2(最高只到gen 2,故若gen 2被檢查後仍存活,只會停留在gen 2)。進行gen 1的回收時,也會進行gen 0回收;同理進行gen 2回收時,也會進行gen 1、gen 0的回收。在這樣的設計下,程式中常會應用到的區域物件與暫時物件因都屬於gen 0,故可確保被回收的頻率最高,對一般不會用到大量暫時物件的程式而言,不需擔心資源的浪費。
至目前為止,我們了解了Managed資源的回收機制,但還有至少二個問題殘留著:
「若是程式員希望在某個時間點確保Managed資源被回收,應該怎麼做?」
「若是類別中含有Unmanaged資源,又該如何釋放?」
對於這二個問題的回答,在這裡便開始要提到程式員如何撰寫明確釋放資源的函式了。在C++中,程式員撰寫類別的Destructor來達成類別資源的釋放,而在C#中,同樣有類似效果的函式有Finalize()與Dispose() ,而這二個函式也常令許多C#的初學者混淆,以下便解釋二者的差別:
前面提到Managed資源會自動被GC回收,但若類別中含有Unmanaged資源,則程式員除了撰寫該Unmanaged資源本身的釋放函式(如Destructor)之外,又該在何處確保這些釋放函式會被呼叫呢?答案就是C#類別的Finalize()函式中。然而,因Finalize()本身的存取層級是protected,所以在實作上,對於類別T,程式員是撰寫名為 「~T()」的函式(如同C++中的解構函式語法),該函式經過編譯後,編譯器便會產生類別T的Finalize()方法,並將函式的內容包在try 區塊中。
public class T
{
Unmgd un;
~T()
{
un.Release(); //假設Unmgd的釋放資源函式為Release();亦可呼叫~Unmgd()
}
}
然而,Finalize()的呼叫是不明確的,因為它只被GC給呼叫,亦即程式員只能預期GC在進行某個T的實體的回收時,會呼叫T.Finalize(),卻無法在程式員自己想要的特定位置呼叫Finalize()。另一方面,Finalize()也並非在GC對該物件進行第一次回收時便被呼叫,事實上,GC的第一次回收,僅是將該物件的參考移至FReachable Queue中,標示為不再使用,等到GC下一次的回收時,Finalize()才真正地被呼叫。從這些事實來看,程式員沒有辦法預期Finalize()究竟會在程式中的何處被呼叫,只能被動地等待GC去呼叫它。因此,為了讓程式員能明確執行Unmanaged資源釋放的工作,C#提供了另一個函式Dispose()。
Dispose()與Finalize()最大的不同,在於Dispose()是明確地在「程式員呼叫」與「using語句區塊結束時」二種情況下被呼叫的。也就是說,當程式員想確保某個類別T中的Unamaged資源會在某個特定位置被釋放掉時,他便可以實作IDisposable介面中的Dispose()函式;而為了提供程式員在操作常用的暫時物件上不會忘記呼叫Dispose(),C#中提供了using陳述式,確保在小括弧中的變數,在離開using區塊時,其Dispose()會被明確地呼叫。
using(T t = new T())
{
.........
} //<-此時T.Dispose()會被呼叫
當一個物件的Dispose()被呼叫後,亦即標示了這個物件為無效,則在第一次的GC回收行程中便會被回收到。值得一提的是,即使某個物件的Dispose()已經被呼叫過了,該物件的Finalize()仍然有可能再被GC給呼叫到,這是為了避免當Dispose()失敗時可能產生的資源浪費。然而,若Dispose()成功地執行了,則應該避免再讓GC去呼叫Finalize(),否則會造成程式效率的低落。因此在撰寫Dispose()函式時,必須記得在裡面加上一行GC.SuppressFinalize(this); 這可以告訴GC不需再去呼叫這個物件的Finalize()。

//------------------------------------------------------------------------------------------//

資料來源:
史帝芬心得筆記
http://home.so-net.net.tw/idealist/CS/Basic/Garbage.html
廬山佚城 http://sedc.pixnet.net/blog/post/14184015
Joel Lee http://dialog.bcse.info/archives/memory-leak-in-c-sharp