有需求或技術問題可以隨時跟我連絡 (MSN上線時)
2009年8月27日 星期四
2009年8月26日 星期三
Reflection in C#
System.Reflection 這個Name Space,有著強大的Hack Function.
以下是MSDN對它的一些描述
反映(Reflection)概觀
Common Language Runtime 載入器可管理 應用程式定義域,這些定義域可在具有相同應用程式範圍的物件周圍構成定義的界限。這個管理包括載入各個組件至適當應用程式定義域,和控制各個組件內型別階層架構的記憶體配置。
組件包含模組,模組包含型別,而型別包含成員。反映提供封裝組件、模組和型別的物件。您可以使用反映,動態建立型別的執行個體、繫結型別至現有物件,或從現有物件取得型別。您可以接著叫用型別的方法,或存取它的欄位和屬性。反映的一般用法如下所示:
-
使用 Assembly 定義並載入組件、載入組件資訊清單 (Assembly Manifest) 中列出的模組,和從這個組件找出型別並建立它的執行個體。
-
使用 Module 探索資訊,例如包含模組和模組中類別的組件。您也可以取得所有全域方法或是在模組上定義的其他特定非全域方法。
-
使用 ConstructorInfo 探索資訊,例如名稱、參數、存取修飾詞 (例如 public 或 private),以及建構函式的實作 (Implementation) 詳細資訊 (例如 abstract 或 virtual)。使用 Type 的 GetConstructors 或 GetConstructor 方法,叫用特定的建構函式。
-
使用 MethodInfo 探索資訊,例如名稱、傳回型別、參數、存取修飾詞 (例如 public 或 Private),以及方法的實作詳細資訊 (例如 abstract 或 virtual)。使用 Type 的 GetMethods 或 GetMethod 方法,叫用特定方法。
-
使用 FieldInfo 探索資訊,例如名稱、存取修飾詞 (例如 public 或 private),以及欄位的實作詳細資訊 (例如 static),並取得或設定欄位值。
-
使用 EventInfo 探索資訊,例如名稱、事件處理常式資料型別、自訂屬性、宣告型別和事件的反映型別,並加入或移除事件處理常式。
-
使用 PropertyInfo 探索資訊,例如名稱、資料型別、宣告型別、反映型別和屬性的唯讀或可寫入狀態,並取得或設定屬性值。
-
使用 ParameterInfo 探索資訊,例如參數的名稱、資料型別、參數是否為輸入或輸出參數,和方法簽章 (Signature) 中參數的位置。
-
使用 CustomAttributeData 探索在應用程式定義域的僅限反映內容中工作時,關於自訂屬性的資訊。CustomAttributeData 讓您可以檢查屬性,而不需要建立這些屬性的執行個體。
System.Reflection.Emit 命名空間的類別提供反映的特殊形式,允許您在 Run Time 建置型別。
反映也可以用來建立稱為型別瀏覽器的應用程式,允許使用者選取型別並接著檢視那些型別的資訊。
反映還有其他用法。語言編譯器,例如 JScript,會使用反映建構符號表。 System.Runtime.Serialization 命名空間中的類別會使用反映存取資料,並判斷哪個欄位要保存 (Persist)。 System.Runtime.Remoting 命名空間中的類別會透過序列化 (Serialization) 間接使用反映。
不說廢話,來一個範例看看它的威力,先看一下程式大概架構,如下圖所示
方案中有兩個專案,(1)TEST(2)物件Reflection
(1)TEST : 在 FormTest.cs 新增 (其他啥事都不做)
private int Add(int a, int b)
{
return a + b;
}
(2)物件Reflection : FormReflection Layout如上圖,FormReflection.cs新增兩個Button Event
private void button2_Click(object sender, EventArgs e)
{
OpenFileDialog myOFD = new OpenFileDialog();
myOFD.Filter = "*.exe|*.EXE";
if (myOFD.ShowDialog() == DialogResult.OK)
textBox4.Text = myOFD.FileName;
}
private void button1_Click(object sender, EventArgs e)
{
Assembly myAssembly = Assembly.LoadFile(textBox4.Text);
Type myType = myAssembly.GetType("EST.FormTEST");
object MyInstance = Activator.CreateInstance(myType);
textBox3.Text = myType.InvokeMember("Add",
BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic,
null, MyInstance, new object[] { int.Parse(textBox1.Text), int.Parse(textBox2.Text) }).ToString();
}
原理:
按下FormReflection button2,選取Test.exe所在路徑
按下FormReflection button1,Call Test.exe 的TEST.FormTEST Add(…),完成運算
以上只是一個最簡單的範例,Reflection還有很多很強大的功能,有待研究...
Source Code [MD5 : 4fbf53d221d4605190bc048f57e32b39]
陣列排序(Arrays Sorting) in C#
陣列可以透過Static Method Array.Sort來達到排序的目的.
(1) C# 基礎型別(Primitive types :int, double or string)
Int Array :
int[] intArray = new int[5] { 8, 10, 2, 6, 3 };
Array.Sort(intArray);
foreach (int i in intArray)
Console.Write(i + " ");
輸出: 2 3 6 8 10
String Array :
string[] stringArray = new string[5] { "X", "B", "Z", "Y", "A" };
Array.Sort(stringArray);
foreach (string str in stringArray)
Console.Write(str + " ");
輸出: A B X Y Z
(2)透過Delegate對自訂型別排序
定義類別
class User
{
public string Name;
public int Age;
public User(string Name, int Age)
{
this.Name = Name;
this.Age = Age;
}
}
定義使用者資料
User[] users = new User[3]
{ new User("Betty", 23),
new User("Susan", 20),
new User("Lisa", 25)
};
//根據姓名排序 Inline delegate
Array.Sort(users, delegate(User user1, User user2)
{
return user1.Name.CompareTo(user2.Name);
}
);
foreach (User user in users) Console.Write(user.Name + ":" + user.Age + " ");
輸出: Betty:23 Lisa:25 Susan:20
//根據年齡排序 Method Delegate
delegate int mySort(User user1, User user2);
…
Array.Sort(users, mySortMethod);
foreach (User user in users) Console.Write(user.Name + ":" + user.Age + " ");
…
private int mySortMethod(User user1, User user2)
{
return user1.Age.CompareTo(user2.Age);
}
輸出: Susan20 Betty23 Lisa25)
(3)透過IComparable對自訂型別排序
定義類別
public class User : IComparable
{
public string Name;
public int Age;
public User(string Name, int Age)
{
this.Name = Name;
this.Age = Age;
}
// implement IComparable interface
public int CompareTo(object obj)
{
if (obj is User)
{
return this.Name.CompareTo((obj as User).Name);//根據姓名排序
}
throw new ArgumentException("Object is not a User");
}
}
定義使用者資料
User[] users = new User[3]
{ new User("Betty", 23),
new User("Susan", 20),
new User("Lisa", 25)
};
//根據姓名排序
Array.Sort(users);設定 DoubleBuffered 屬性(Property)
下面的範例是如何設定 protected 的Control.DoubleBuffered屬性為True,這是一個經常需要去特別修改的屬性,這樣一來就可以避免重繪控制項時造成的螢幕閃爍.
所有的控制項都有DoubleBuffered屬性,但是此屬性是被[protected]保護起來,底下這個Function,利用了Reflection來存取 non-public 的methods 以及properties,如此一來就可以輕易的修改DoubleBuffered屬性...
範例程式:
// set instance non-public pr operty with name "DoubleBuffered" to true typeof
public static void SetDoubleBuffered(Control control)
{
typeof(Control).InvokeMember("DoubleBuffered",
BindingFlags.SetProperty |BindingFlags.Instance | BindingFlags.NonPublic,
null, control, new object[] { true } );
}
2009年8月24日 星期一
保護(加密) 應用程式組態檔 (App.config)
VS2005中提供的一個方便的功能就是[專案]中->[Properties]->[Settings.settings],一個方常方便的功能,提供了User層級及Application層級的應用程式設定值設定.
可以透過VS2005的功能修改及讀取,甚至是Reset為原始值.(下兩圖所示)
但是有時候不想要讓使用者動到Application層級的設定值,或是讓人家知道程式的重要設定值,你可以進行加密的動作,像是下圖
以下是一個簡單的範例Step by Step教你達成...
(1)建立你的應用程式及設定你的Application Setting
(2)在Form1建構式中加入這行
this.Text = MyDemo.Properties.Settings.Default.MyApplicationName;
下圖所示
(3)新增一個安裝程式類別 如下圖
(4)於安裝程式類別 override Install Method & Add ProtectSection Function
using System.Configuration;
public override void Install(System.Collections.IDictionary stateSaver)
{
base.Install(stateSaver);
//get Protected Configuration Provider name from custom action parameter
string sectionName = this.Context.Parameters["sectionName"];
//get Protected Configuration Provider name from custom action parameter
string provName = this.Context.Parameters["provName"];
// get the exe path from the default context parameters
string exeFilePath = this.Context.Parameters["assemblypath"];
//encrypt the configuration section
ProtectSection(sectionName, provName, exeFilePath);
}
private void ProtectSection(string sectionName, string provName, string exeFilePath)
{
Configuration config = ConfigurationManager.OpenExeConfiguration(exeFilePath);
ConfigurationSection section = config.GetSection(sectionName);
if (!section.SectionInformation.IsProtected)
{
//Protecting the specified section with the specified provider
section.SectionInformation.ProtectSection(provName);
}
section.SectionInformation.ForceSave = true;
config.Save(ConfigurationSaveMode.Modified);
}
(5)打開app.config 並加入下面Section
<configProtectedData>
<providers>
<add useMachineProtection="true" name="DPAPIProtection"
type="System.Configuration.DpapiProtectedConfigurationProvider,System.Configuration,
 Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</providers>
</configProtectedData>
(6)新增一安裝專案(加密部分需安裝後才會看的出來) (7)修改安裝專案內容
/sectionName="applicationSettings/MyDemo.Properties.Settings" /provName="DPAPIProtection"
(8) Add Ref System.Configuration
(9) 編譯程式 (如果有按上面步驟操作應該會編譯成功)
(10)安裝應用程式
安裝完後,大功告成...
確認結果
範例程式[MD5 : c77f290441698787fc94d3325a2b41b4]
Source Code[MD5 : 4339ec62e8bfed90c3d6efa177152456]