2012年5月25日 星期五

[C#] 隱藏設定檔內容 - 內嵌資源

隱藏設定檔內容 - 內嵌資源

開發專案時,某些系統資訊(如連線字串、系統參數等資訊)會選擇寫在XML檔或是文字檔, 例如我們常常使用的App.config,一來是集中管理,一目瞭然; 另外修改參數也方便。但也造成系統資訊暴露,如果想要隱藏設定檔的內容,可以考慮將設定檔編譯成內嵌資源。

Winform專案的App.config為例。
我在App.config加入以下內容
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="My_Name" value="JB" />
  </appSettings>
</configuration>


(A) 直接讀取
使用System.Configuration.ConfigurationManager 可以很快的將我們的系統參數讀取出來。
PS. 需先加入參考System.configuration


/***************************************
* 直接讀取 
***************************************/
String sName = System.Configuration.ConfigurationManager.AppSettings["My_Name"].ToString();
MessageBox.Show(sName);


結果:


(B) 接下是使用內嵌資源的方法
Step1.
先到專案建置輸出的資料夾(\bin\Debug)下,如果已經有App.config的話,請先刪除它。

Step2.
選取App.config,設定他的屬性:【建置動作】為內嵌資源
當然【複製到輸出目錄】仍然選擇預設:不要複製

Step3.
此時,我們無法使用System.Configuration.ConfigurationManager來讀值了。
而是改用System.Reflection.Assembly來把編譯為內嵌資源的App.config裡面的內容讀出來。



/**************************************** 
* 將App.config設成內嵌資源 
****************************************/
System.Reflection.Assembly thisExe;
thisExe = System.Reflection.Assembly.GetExecutingAssembly();

//取得內嵌資源 ...PS:參數是 專案名稱(namespace).檔案名稱 
System.IO.Stream file = thisExe.GetManifestResourceStream("WinForm_Exp.App.config");

//讀取二進位檔.. 
String sXML = "";
using (System.IO.StreamReader sr = new System.IO.StreamReader(file, Encoding.Default))
{
   sXML = sr.ReadToEnd();
}
MessageBox.Show(sXML);




PS.注意sXMLApp.config裡面所有的內容,所以我們後續還要寫一段把XML parsing出來的程式碼,在此就不加累述~

結果:



2012年5月16日 星期三

[C#] Process.Start() 所衍伸的限制

小弟在程式A執行結束後,加上啟動程式B的功能。

String sAM_Path=@"C:\XXX.exe"
Process _process = new Process();
_process.StartInfo.FileName = sAM_Path;
_process.Start();
//Process.Start(sAM_Path);

結果B被叫起來執行後,馬上出現exception message。
(B單獨執行時是正確執行的...-___-)

直覺是在程式initail load的時後有bug,
結果因為無法用工具trace bug,只好一行一行手動trace。
最後發現... 程式B竟然去讀到程式A的設定檔啦!
雖然兩隻程式的設定檔名稱(setup.ini)都一樣,
但是各自放在A和B自己的資料夾下,理論上怎麼會有這種問題?

想了一下,應該是使用Process.Start()和讀取檔案路徑的關係,
所以小弟把讀取設定檔的檔案名稱,改為完整路徑,
再執行一次A-->B後,就不會發生讀到相同檔案的問題了。

//原本直接放設定檔名稱
//String SetupFilePath = "setup.ini";
//StreamReader sr = new StreamReader(SetupFilePath, Encoding.Default);

//改為加上完整路徑+設定檔名稱
String SetupFilePath = Application.StartupPath + @"setup.ini";
StreamReader sr = new StreamReader(SetupFilePath, Encoding.Default);

[C#] Use Linq to read XML

Use Linq to read XML

First we wrote a function named Create_XML_file() to create a XML file for the following example.

/// <summary>
    /// 建立XML資料
    /// </summary>
    static void Create_XML_file()
    {
      XDocument doc = new XDocument
        (
        new XElement("Books" ,
          new XElement("book" , new XAttribute("PubYear" , "2008") ,
            new XElement("id" , "001") ,
            new XElement("name", "Visual C# 2008", new XAttribute("language", "CH") )),

          new XElement("book", new XAttribute("PubYear", "2009"),
            new XElement("id", "002"),
            new XElement("name", "終極7000單字,這本最強", new XAttribute("language", "EN"))),

          new XElement("book", new XAttribute("PubYear", "2010"),
            new XElement("id", "003"),
            new XElement("name", "Android案例開發完全講義", new XAttribute("language", "CH"))),

          new XElement("book", new XAttribute("PubYear", "2011"),
            new XElement("id", "004"),
            new XElement("name", "ASP.NET 4.0專題實務(上)", new XAttribute("language", "CH"))),

          new XElement("book", new XAttribute("PubYear", "2011"),
            new XElement("id", "005"),
            new XElement("name", "ASP.NET 4.0專題實務(下)", new XAttribute("language", "CH"))),

          new XElement("book", new XAttribute("PubYear", "2012"),
            new XElement("id", "006"),
            new XElement("name", "Star Wars : The Return of Jedi", new XAttribute("language", "EN")))

      ));
      doc.Save("example.xml");
    }
The XML would be looked like this …



Then we created four functions that using Linq to read the XML.
The purposes of these functions were written in the comment.

/// <summary>
    /// Output all the books' information
    /// </summary>
    static void Print_Data_01()
    {
      XDocument _doc = XDocument.Load("example.xml");
     
      //Method 1
      IEnumerable<XElement> _elms =
        _doc.Elements("Books").Elements("book");

      /*Method 2
      var _elms = from x in _doc.Elements("Books").Elements()
                  select x;
      */

      foreach (XElement _elm in _elms)
      {
        Console.WriteLine(
          String.Format("ID:{0},TITLE:{1},PUBLISH YEAR:{2},LANGUAGE:{3} ",
          _elm.Element("id").Value,
          _elm.Element("name").Value,
          _elm.Attribute("PubYear").Value,
          _elm.Element("name").Attribute("language").Value ));
      }
    }
Result of Print_Data_01 :
編號:001,書名:Visual C# 2008,發行年份:2008,語言:CH
編號:002,書名:終極7000單字,這本最強,發行年份:2009,語言:EN
編號:003,書名:Android案例開發完全講義,發行年份:2010,語言:CH
編號:004,書名:ASP.NET 4.0專題實務(上),發行年份:2011,語言:CH
編號:005,書名:ASP.NET 4.0專題實務(下),發行年份:2011,語言:CH
編號:006,書名:Star Wars : The Return of Jedi,發行年份:2012,語言:EN

    /// <summary>
    /// Only output the required book of certain title
    /// </summary>
    static void Print_Data_02(String sBookName)
    {
      XDocument _doc = XDocument.Load("example.xml");

      var _elms = from x in _doc.Elements("Books").Elements("book")
                  where x.Elements("name").ElementAtOrDefault(0).Value == sBookName
                  select x;

      foreach (XElement _elm in _elms)
      {
        Console.WriteLine(
          String.Format("ID:{0},TITLE:{1},PUBLISH YEAR:{2},LANGUAGE:{3} ",
          _elm.Element("id").Value,
          _elm.Element("name").Value,
          _elm.Attribute("PubYear").Value,
          _elm.Element("name").Attribute("language").Value));
      }
    }
Result of Print_Data_02 :
編號:003,書名:Android案例開發完全講義,發行年份:2010,語言:CH

    /// <summary>
    /// Only output the books of certain language
    /// </summary>
    static void Print_Data_03(String sLanguage)
    {
      XDocument _doc = XDocument.Load("example.xml");

      var _elms = from x in _doc.Elements("Books").Elements("book")
            where x.Elements("name").Attributes("language").ElementAtOrDefault(0).Value == sLanguage
            select x;

      foreach (XElement _elm in _elms)
      {
        Console.WriteLine(
          String.Format("ID:{0},TITLE:{1},PUBLISH YEAR:{2},LANGUAGE:{3} ",
          _elm.Element("id").Value,
          _elm.Element("name").Value,
          _elm.Attribute("PubYear").Value,
          _elm.Element("name").Attribute("language").Value));
      }
    }
Result of Print_Data_03 :
編號:002,書名:終極7000單字,這本最強,發行年份:2009,語言:EN
編號:006,書名:Star Wars : The Return of Jedi,發行年份:2012,語言:EN

    /// <summary>
    /// Only output the books that were published after 2009 and were wrotten in Chinese
    /// </summary>
    static void Print_Data_04()
    {
      XDocument _doc = XDocument.Load("example.xml");

      var _elms = from x in _doc.Elements("Books").Elements("book")
            where
            int.Parse( x.Attributes("PubYear").ElementAtOrDefault(0).Value) >=2010
            &&
            x.Elements("name").Attributes("language").ElementAtOrDefault(0).Value == "CH"
            select x;

      foreach (XElement _elm in _elms)
      {
        Console.WriteLine(
          String.Format("ID:{0},TITLE:{1},PUBLISH YEAR:{2},LANGUAGE:{3} ",
          _elm.Element("id").Value,
          _elm.Element("name").Value,
          _elm.Attribute("PubYear").Value,
          _elm.Element("name").Attribute("language").Value));
      }
    }
Result of Print_Data_04 :
編號:003,書名:Android案例開發完全講義,發行年份:2010,語言:CH
編號:004,書名:ASP.NET 4.0專題實務(上),發行年份:2011,語言:CH
編號:005,書名:ASP.NET 4.0專題實務(下),發行年份:2011,語言:CH

2012年5月14日 星期一

[C#] Socket Programming in C#


Socket Programming in C#
作者:JB

如果想多了解Socket,可以讀一下這些文章:


其實Socket寫法上,不論是ServerClient,都可以使用.NET提供的TCP物件或Socket物件來達到資料傳輸的目的。(因為Socket本身就是走TCP/IP的架構)
本篇文章的目的,在於建立一個Socket Server,讓多個Client可以傳文字檔到Server,然後Server上可以顯示連線過的Client數,並在資料傳輸完成後,回傳一個文字訊息給Client

流程上大致可分為:(WinForm為例)

1          Server

1.1         為了讓Socket Server開始Listening後,仍可顯示連線的Client資訊。 故建立Socket Server後,使用BackgroundWorker來開啟Socket Server

1.2         Socket Server每次接收到新的Client要求時,再開另一條Thread來處理上傳的資料。

1.3         處理完畢後,回傳訊息給Client

1.4         結束Client的連線。

1.5         (重要) 關閉Socket Server時,要先停止Socket Listener (即第一點的BackgroundWorker),才能關閉Socket Server

2          Client


2.1         將上傳檔案名稱及其內容拆成[檔案名稱長度][檔案名稱] [檔案內容],存成Byte Array

2.2         建立TcpClientSocket物件來連線Socket Server

2.3         資料上傳完畢,利用NetworkStream來接收Server端回傳的訊息。

2.4         斷線。

一、 Server端程式碼
        程式碼包含FTServer.csSocket Server 自訂類別),ClientHandler.csClient後端處理函式),Form1.cs(主程式)

1.          FTServer類別(Socket Server自訂類別)

class FTServer
{
public IPEndPoint ipEnd;
public Socket sock;
public bool CloseSocketServer_Flg; //
結束Socket Server

/// <summary>
/// FTServer
建構
/// </summary>
public FTServer()
{
   CloseSocketServer_Flg = false;
   //
指定IPEndPoint可接受來自任何port5656IP的要求
   ipEnd = new IPEndPoint(IPAddress.Any, 5656);
   //Socket
初始化
   sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream,                    
                     ProtocolType.IP);
   //
ipEnd繫結給Socket
   sock.Bind(ipEnd);
}

/// <summary>
/// StartServer :
啟動Server
/// </summary>
public void StartServer()
{
   Console.WriteLine("Socket Server
啟動...");
   //
設定此Socket Server最多同時可連線Client數目
   sock.Listen(100);
}

/// <summary>
/// StopServer :
停止Server
/// </summary>
public void StopServer()
{
   try
   {
      sock.Close();
      Console.WriteLine("Socket Server
關閉。");
   }
   catch (Exception ex)
   {
      throw ex;
   }
}
}

2.          全域變數:包含一個FTServerBackgroundWorkerclient識別編號

 /// <summary>
/// Socket Server 類別
/// </summary>
FTServer _ftServer = null;

/// <summary>
/// 用來處理 Listener 工作的執行緒
/// </summary>
BackgroundWorker Server_bgWorker = new BackgroundWorker();

/// <summary>
/// Client 識別編號
/// </summary>
private int _ClientNo = 0;

3.          啟動Server事件(包含建立Socket Server Socket Listener
/// <summary>
/// bt_StartServer_Click事件 : 啟動Server
/// </summary>
private void bt_StartServer_Click(object sender, EventArgs e)
{
  
if (_ftServer == null)
  
{
     
_ftServer = new FTServer();
  
}

  
//指定伺服器背景作業的函式
  
Server_bgWorker.DoWork += bgWorker_DoWork;
  
//設定伺服器背景作業listener可取消
  
Server_bgWorker.WorkerSupportsCancellation = true;

  
if (!Server_bgWorker.IsBusy) //當作業背景不忙碌時
  
{
     
//設定Thread可存取元件
     
Form.CheckForIllegalCrossThreadCalls = false;
     
Server_bgWorker.RunWorkerAsync(); //執行bgWorker_DoWork
  
}
}

說明:中間的程式碼 Server_bgWorker.WorkerSupportsCancellation = true;
目的在於設定此背景執行作業(
Socket Listener)可被取消。 若沒有設定,則不允許中斷該背景執行作業。

4.          停止Server事件

先取消Socket Listener背景作業 → 停止Socket Server



/// <summary>
/// StopSocketServer 停止 Socket Server 的服務
/// </summary>
private void StopSocketServer()
{
   
try
  
{
     
if (Server_bgWorker.IsBusy)
     
{
        
//取消伺服器背景作業
        
this.Server_bgWorker.CancelAsync();
        
this.Server_bgWorker.Dispose();
     
}
     
if (_ftServer!=null && _ftServer.CloseSocketServer_Flg == false)
     
{
        
_ftServer.CloseSocketServer_Flg = true;
        
_ftServer.StopServer();
     
}
  
}
  
catch (Exception ex)
  
{
     
MessageBox.Show("[ERR] 停止 Socket Server 的服務失敗:"+ ex.Message);
  
}
}
5.          Socket Listener
/// <summary>
/// bgWorker_DoWork  : 背景作業
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
   //
待補
}

二、 Client端程式碼

1.          全域變數
/// <summary>
/// socket client 物件(連接遠端 socket server )
/// </summary>
private TcpClient _TcpClient;

/// <summary>
/// Client上傳檔案路徑
/// </summary>
private String filePath_org = "Q:\\FTP\\UploadFile.txt";

2.          將檔案名稱及內容轉換成Byte Array

/// <summary>
/// 回傳要上傳檔案的格式化成 Byte Array [檔案名稱長度][檔案名稱] [檔案內容]
/// </summary>
/// <returns></returns>
private byte[] Get_Send_Byte_Stream()
{
  
try
  
{
     
/* 取得檔案名稱 和 路徑 */
     
filePath_org = filePath_org.Replace("\\", "/");
     
String filePath = "" ;
     
String fileName = "";

     
filePath = filePath_org.Substring(0, fileName.LastIndexOf("/") + 1);
     
fileName = filePath_org.Substring(fileName.LastIndexOf("/") + 1);

     
//讀取檔案名稱並轉為Byte[]
     
byte[] fileNameByte = Encoding.ASCII.GetBytes(fileName);
     
if (fileNameByte.Length > 850 * 1024)
     
{
        
//MessageBox.Show("檔案大小超過850kb, 請嘗試其它檔案。");
        
throw new Exception("檔案大小超過850kb, 請嘗試其它檔案。");
     
}
     
//讀取檔案內容並轉為Byte[]
     
byte[] fileData = File.ReadAllBytes(filePath + fileName);

     
//取得檔案名稱的長度並轉為Byte[]
     
//byte[] fileNameLen = BitConverter.GetBytes(fileNameByte.Length);
      
byte[] fileNameLen = Encoding.ASCII.GetBytes(fileNameByte.Length.ToString());

     
//定義 clientData : 存放[檔案名稱長度][檔案名稱] [檔案內容]
     
byte[] clientData = new byte[4 + fileNameByte.Length + fileData.Length];

     
//開始放入資料到 clientData : 存放[檔案名稱長度][檔案名稱] [檔案內容]
     
fileNameLen.CopyTo(clientData, 0);
     
fileNameByte.CopyTo(clientData, 4);
     
fileData.CopyTo(clientData, 4 + fileNameByte.Length);

     
/* 測試 */
     
//String str_clientData_FileNameLen =
         System.Text.Encoding.UTF8.GetString(clientData, 0, 4);

     
//Int16 i_clientData_FileNameLen = Convert.ToInt16(str_clientData_FileNameLen);
     
//String str_clientData_FileName =
        System.Text.Encoding.UTF8.GetString(clientData, 4, fileNameByte.Length);

     
//String str_clientData_Context =
        System.Text.Encoding.UTF8.GetString(
        clientData, 4+fileNameByte.Length, clientData.Length - (4 + fileNameByte.Length));

     
//Console.WriteLine(str_clientData_Context);
     
return clientData;
  
}
  
catch (Exception ex)
  
{
     
throw ex;
  
}
}

3.          使用Socket建立Client連線
待續


4.          使用TcpClient建立Client連線
待續

2012年5月8日 星期二

2012年5月4日 星期五

[問題排除] 無法內嵌 Interop 型別

今天在加入參考某個廠商的dll 後,


Visual Studio 建置時,出現"無法內嵌 Interop 型別" 這個error。


查了一下Google,只要到該參考元件的屬性:[內嵌 Interop 型別] 設定為 False 即可排除。




參考文章

2012年5月3日 星期四

[C#] Socket Programming 心得


花了幾天的功夫,終於把第一支Socket Server & Client寫好了,

不過問題還是多多,譬如傳檔的寫入Lock問題,和圖片格式的Byte寫不完全等囧境。

找時間再來寫這方面的文章了。

Osu!

2012年5月2日 星期三

[電影作品] Zombie Arise : Behind the scene (屍裡求生 - 幕後)


Zombie Arise : Behind the scene

拍攝過程和遺珠鏡頭~~ :)


[電影作品] Zombie Arise (屍裡求生 - 預告)

這是工作室第一次嘗試拍攝和剪接電影類的東西,

拍攝過程很有趣, 剪接後製很辛苦又充滿挑戰 ...

希望你們喜歡~~

Special thanks to my wife and my brother!