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

2018年11月22日 星期四

C#利用IIS中的FTPS傳輸方法

最近同事煩惱如何使用IIS內置的FTPS(SSL)來傳送及接收檔案,我花了一些時間,作出了以下.NET專案




1. FTP.cs

using System;
using System.IO;
using System.Net;

namespace FTP_DLL
{
 public class FTP
 {
  private string _remoteHost, _remoteUser, _remotePass, _remotePort;

  public FTP(String remoteHost, String remotePort, String remoteUser, String remotePass)
  {
   _remoteHost = remoteHost;
   _remoteUser = remoteUser;
   _remotePass = remotePass;
   _remotePort = remotePort;
  }

  public void SSLUpload(String filename)
  {
   FileInfo fileInf = new FileInfo(filename);
   string uri = String.Format("ftp://{0}:{1}/{2}", _remoteHost, _remotePort, fileInf.Name);
   FtpWebRequest reqFTP;
   reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(uri));
   reqFTP.EnableSsl = true;
   reqFTP.Credentials = new NetworkCredential(_remoteUser, _remotePass);
   reqFTP.KeepAlive = false;
   reqFTP.Method = WebRequestMethods.Ftp.UploadFile;
   reqFTP.UseBinary = true;

   //Method1:Bypass Cert
   ServicePointManager.ServerCertificateValidationCallback = (s, certificate, chain, sslPolicyErrors) => true;

   //Method2:Accept Cert
   //X509Certificate cert = new X509Certificate2("C:\\Path\\cert.pfx", "password");
   //string b = cert.GetCertHashString();
   //Console.WriteLine("hash string " + b);

   //ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
   //{
    //if (certificate.GetCertHashString().Equals(b))
    //{
     //Console.WriteLine("ServerCertificateValidation true");
     //return true;
    //}
    //else
    //{
     //Console.WriteLine("ServerCertificateValidation false");
     //return false;
    //}
   //};

   //reqFTP.ContentLength = fileInf.Length;
   //reqFTP.ContentLength = 10000;
   int buffLength = 2048;
   byte[] buff = new byte[buffLength];
   int contentLen;
   FileStream fs = fileInf.OpenRead();
   try
   {
    Stream strm = reqFTP.GetRequestStream();
    contentLen = fs.Read(buff, 0, buffLength);
    while (contentLen != 0)
    {
     strm.Write(buff, 0, contentLen);
     contentLen = fs.Read(buff, 0, buffLength);
    }
    strm.Close();
    fs.Close();
   }
   catch (Exception ex)
   {
    Console.WriteLine(ex.Message, "Upload err");
   }
  }

  public void SSLDownload(String filename)
  {
   FileInfo fileInf = new FileInfo(filename);
   string uri = String.Format("ftp://{0}:{1}/{2}", _remoteHost, _remotePort, fileInf.Name);
   FtpWebRequest reqFTP;
   reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(uri));
   reqFTP.EnableSsl = true;
   reqFTP.Credentials = new NetworkCredential(_remoteUser, _remotePass);
   reqFTP.KeepAlive = false;
   reqFTP.Method = WebRequestMethods.Ftp.DownloadFile;
   reqFTP.UseBinary = true;

   //Method1:Bypass Cert
   ServicePointManager.ServerCertificateValidationCallback = (s, certificate, chain, sslPolicyErrors) => true;

   //Method2:Accept Cert
   //X509Certificate cert = new X509Certificate2("C:\\Path\\cert.pfx", "password");
   //string b = cert.GetCertHashString();
   //Console.WriteLine("hash string " + b);

   //ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
   //{
    //if (certificate.GetCertHashString().Equals(b))
    //{
     //Console.WriteLine("ServerCertificateValidation true");
     //return true;
    //}
    //else
    //{
     //Console.WriteLine("ServerCertificateValidation false");
     //return false;
    //}
   //};

   using (Stream ftpStream = reqFTP.GetResponse().GetResponseStream())
   {
    using (Stream fileStream = File.Create(filename))
    {
     byte[] buffer = new byte[10240];
     int read;
     while ((read = ftpStream.Read(buffer, 0, buffer.Length)) > 0)
     {
      fileStream.Write(buffer, 0, read);
      Console.WriteLine("Downloaded {0} bytes", fileStream.Position);
     }
    }
   }
  }
 }
}


2. App.Config

Add Setting And Value in XML format.

1. setting name="TargetFtpSource" serializeas="String" value>X.X.X.X
2. setting name="TargetFtpSourcePort" serializeas="String" value>XX
3. setting name="TargetFtpSecureUser" serializeas="String" value>XXXX
4. setting name="TargetFtpSecurePass" serializeas="String" value>XXXX


3. Program.cs

using System;
using FTP_DLL.Properties;

namespace FTP_DLL
{
 class Program
 {
  static void Main(string[] args)
  {
   try
   {
    FTP ftp = new FTP(Settings.Default.TargetFtpSource, Settings.Default.TargetFtpSourcePort, Settings.Default.TargetFtpSecureUser, Settings.Default.TargetFtpSecurePass);
    //Upload and Download Function (Type File Path e.g.:C:\XXX)
    ftp.SSLUpload("FILE_PATH");
    ftp.SSLDownload("FILE_PATH");
   }
   catch (Exception e)
   {
    Console.WriteLine("Caught Error :" + e.Message);
   }
  }
 }
}

2016年3月29日 星期二

C#連接OPC Server (Intouch 2014 R2 using Suitelink)

最近煩惱如何C#連接Intouch 2014 R2,並且要將Tag value寫入Intouch當中,對於我這個Intouch的菜鳥,這是非常的困難,但經過經驗人仕指導一番後就成功了。以下是一個分享:
先下載opcdaauto.dll,作為Project的Reference,再寫入下面經過改良的Class,當中不難看出Class包含連接OPC Server、創建Client Group及寫入Tag Value。
using OPCAutomation;

public class OPC
{
OPCServer KepServer;
OPCGroups KepGroups;
OPCGroup KepGroup;
OPCItems KepItems;
OPCItem KepItem;
string strHostIP = "";
string strHostName = "";
int itmHandleClient = 0;

public bool GetLocalServer()
{
  IPHostEntry IPHost = Dns.GetHostEntry(Environment.MachineName);
  if (IPHost.AddressList.Length > 0)
  {
    strHostIP = IPHost.AddressList[0].ToString();
  }
  else
  {
    MessageBox.Show("GetLocalServer Error 1");
    return false;
  }
  IPHostEntry ipHostEntry = Dns.GetHostEntry(strHostIP);
  strHostName = ipHostEntry.HostName.ToString();
  try
  {
   KepServer = new OPCServer();
   object serverList = KepServer.GetOPCServers();
   KepServer.Connect(((Array)serverList).GetValue(1).ToString(),"localhost");
  }
  catch(Exception ex)
  {
   MessageBox.Show("GetLocalServer Error 2 " + ex.Message);
   return false;
  }
  return true;
}

public bool CreateGroup()
{
  try
  {
    KepGroups = KepServer.OPCGroups;
    KepGroup = KepGroups.Add("MaxsonProgram");
    SetGroupProperty();
    KepGroup.DataChange += new   DIOPCGroupEvent_DataChangeEventHandler(KepGroup_DataChange);
   KepGroup.AsyncWriteComplete += new   DIOPCGroupEvent_AsyncWriteCompleteEventHandler(KepGroup_AsyncWriteComplete);
   KepItems = KepGroup.OPCItems;
  }
  catch (Exception err)
  {
   MessageBox.Show(err.Message, "CreateGroup提示信息", MessageBoxButtons.OK,     MessageBoxIcon.Warning);
   return false;
  }
  return true;
}

public void SetGroupProperty()
{
  KepServer.OPCGroups.DefaultGroupIsActive = true;
  KepServer.OPCGroups.DefaultGroupDeadband = 0;
  KepGroup.UpdateRate = 1000;
  KepGroup.IsActive = true;
  KepGroup.IsSubscribed = true;
}

void KepGroup_AsyncWriteComplete(int TransactionID, int NumItems, ref Array ClientHandles, ref Array Errors)
{
  MessageBox.Show("Success Write");
}

void KepGroup_DataChange(int TransactionID, int NumItems, ref Array ClientHandles, ref Array ItemValues, ref Array Qualities, ref Array TimeStamps)
{
}

public void opcWrite(string tagname, string value)
{
  try
  {
   Array Errors;
   int cancelID;
   KepItem = KepItems.AddItem(tagname, itmHandleClient);
   int[] temp2 = new int[2] { 0, KepItem.ServerHandle };
   Array serverHandles = (Array)temp2;
   object[] valueTemp = new object[2] { "", value };
   Array values = (Array)valueTemp;
   KepGroup.AsyncWrite(1, ref serverHandles, ref values, out Errors, 2009, out cancelID);
   KepItems.Remove(KepItems.Count, ref serverHandles, out Errors);
   GC.Collect();
 }
 catch (Exception ex) { MessageBox.Show(ex.Message); }
 }
}

如果想調用OPC Class,可依下面作為參考。
OPC opc = new OPC();
if (opc.GetLocalServer())
{
 if (opc.CreateGroup())
 {
   opc.opcWrite("SuiteLink.Topic.Test02", "30");   
   opc.opcWrite("SuiteLink.Topic.Rainfall_Value", "10.33"); 
   opc.opcWrite("SuiteLink.Topic.Rainfall_X", "123.456");  
   opc.opcWrite("SuiteLink.Topic.Rainfall_Y", "654.321");  
   opc.opcWrite("SuiteLink.Topic.WinterMode", "1");  
   opc.opcWrite("SuiteLink.Topic.NormalMode", "0");  
   opc.opcWrite("SuiteLink.Topic.TriggerLevel", "1.6");  
   opc.opcWrite("SuiteLink.Topic.RainingState", "0");
  }
} 

Intouch Setting如下:




Visual Studio Setting如下(.net 4.5 + 32 bit CPU):(必須,我試了很多次,這是必須,否則會連接不上,這可是的花了很多時間得出來的結論)


如果設定成功及運行C# Application,你會在Intouch Viewer中看到Tag Value會改變,亦即代表你成功了。

2016年3月2日 星期三

C# Backgroundworker進階教學(Thread.Sleep的正確使用)

System.Threading.Thread.Sleep相信各位Winform的程式員都知道是什麼,就是停止運作一定的時間,但作為一個已進化的菜鳥,不能夠在正常情況可使用,最好是在BackgroundWorker內用,就能夠神不知鬼不覺,因為當你不在BackgroundWorker內使用,該死的界面就會顯示(Not Responding)的字句,客戶就會走來找你,問是不是死機啊‧‧‧以下是一個簡單的例子。

1‧創建一個WINFORM界面,並新增4個Control─label1、button1、toolStripProgressBar1、backgroundworker1

*backgroundworker1不是User Interface*

2‧background新增Event Handler

WorkerReportsProgress一定要set成true,否則不能加入ProgressChanged Event。
this.backgroundWorker1.WorkerReportsProgress = true;
this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);
this.backgroundWorker1.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.backgroundWorker1_ProgressChanged);

3‧ProgressChanged Event

主要是紀錄進度,除了toolStripProgressBar,toolStripLabel也是能夠更改的。
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
   // The progress percentage is a property of e
   toolStripProgressBar1.Value = e.ProgressPercentage;
}

4‧DoWork Event

DoWork這裡會轉換label1的文字,ProgressBar亦會紀錄進度,Thread.Sleep在這裡使用。
在function內接觸Control類的,需要加入Invoke(Action),才可運行,否則會彈出跨線程錯誤。
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
   Args arg = (Args)e.Argument;
   string a = arg.a;
   string b = arg.b;
   backgroundWorker1.ReportProgress(0);
   label1.Invoke(new Action(() => { label1.Text = b; }));
   backgroundWorker1.ReportProgress(30);
   Thread.Sleep(1000);
   backgroundWorker1.ReportProgress(60);
   label1.Invoke(new Action(() => { label1.Text = a; }));
   backgroundWorker1.ReportProgress(100);
   Thread.Sleep(1000);
   backgroundWorker1.ReportProgress(0);
   button1.Invoke((Action)(() => button1.Enabled = true));
}

5‧Args Class

在DoWork拿取Control的數值、文字是比較困難的,最好的方法是由外面掉Parameter入DoWork Function,所以先創建一個Class。
簡簡單單,看得明白就好。
internal class Args
{
   internal string a { get; set; }
   internal string b { get; set; }
}

5‧Button1 Action

Button1用來啟動BackgroundWorker,而且將剛才的Class放入DoWork這個Function裡面。

private void button1_Click(object sender, EventArgs e)
{
   button1.Enabled = false;
   var args = new Args() { a = "YES", b = "NO" };
   backgroundWorker1.RunWorkerAsync(args);
}

6‧結果

運行時可以見到label1由"No"變成"Yes",而ProgressBar亦會上升,為時大約5秒,而且UI不會出現Not Responding字樣,所以成功了。



這個Backgroundworker、progressbar的進階教學也完結了,多謝各位。

2016年2月22日 星期一

C# 及 VB 轉換

該死的Visual Studio,為何你提供這麼多的Language給程序員選擇,導致我有時要寫C#,一時VB。。。辛苦死了。幸好,網上有一個轉換Tool,可快速將VB Code轉成C#,C#轉成VB。(但不是100%能夠成功),以下是網址:
http://converter.telerik.com/

在WEB的左方輸入VB/C# Code,再選選個DropDown,再按個"Convert Code"按鈕,右面就會出現相對應的Code,真是方便至極,愛死Telerik了。

2016年2月20日 星期六

C# Winform 工作計時

最近要做一個Tool,需要Read 26個txt file,每個file有2500行的Data,並紀錄所需時間。原本我以為要用到Timer,但是原來是不用的。 System.Diagnostics 這個Class內有一個好用的Stopwatch,能夠像計時器一樣計出準確時間,而且Coding也是僅僅數句,真是省了很多功夫,數小時就能搞掂(當然包UI,包埋點read)。

下面是Stopwatch的Coding,將所需時間放進一個Label裡面。 var watch = Stopwatch.StartNew();

//Do Work

watch.Stop();
var elapsedMs = watch.ElapsedMilliseconds;
label1.Text = (dlapsedMs / 1000.00).ToString() + " s";


希望以後不會不記得!!!

2016年2月4日 星期四

C# 連接 Oracle Database

由於公司接了一單水務署的工程,需要連接他們的Oracle Database,並拿走少量的數據放進Mysql裡面,在做的過程中,小人從中得知連接Oracle比連接SQL Server、MySQL較為複雜(好似係‧‧‧),所以訂立此帖以作紀錄。

1‧下載Oracle 的ODAC

由於水務署通常使用的系統、設備都 比較舊 (例如:Windows OS、Web Broswer),所以下載低version就 一定無死了 可以了,果然他們使用的是10g (冒汗‧‧‧2016年了‧‧‧)



2‧將已下載的檔案unzip、並將下圖的file轉移至Visual Studio的專案內

只有Oracle.DataAccess.dll能夠成為reference,其餘3個file只需放在一起便可。專案的佈局便大致完成。



3‧創建一個Class(類)

個人習慣,沒有什麼可以解釋。

class OracleData
{
     private string equip_no;
     private string location_code;
     private DateTime comm_date;
     private int maint_threshold;
     private string description;
     private int interval;

     public OracleData()
     {
         // TODO: Complete member initialization
     }

     public string GetEquipNo()
     {
         return equip_no;
     }

     public void SetEquipNo(string eq_no)
     {
         equip_no = eq_no;
     }

     public string GetLocationCode()
     {
         return location_code;
     }

     public void SetLocationCode(string loc_code)
     {
         location_code = loc_code;
     }

     public DateTime GetCommDate()
     {
         return comm_date;
     }

     public void SetCommDate(DateTime comm_dt)
     {
         comm_date = comm_dt;
     }

     public int GetThreshold()
     {
         return maint_threshold;
     }

     public void SetThreshold(int threshold)
     {
         maint_threshold = threshold;
     }

     public string GetDescription()
     {
         return description;
     }

     public void SetDescription(string desc)
     {
         description = desc;
     }

     public int GetInterval()
     {
         return interval;
     }

     public void SetInterval(int interv)
     {
         interval = interv;
     }
 }

4‧引用Oracle.DataAccess.dll


using Oracle.DataAccess.Client; 

5‧Connection String設定

跟SQL Server及MySQL 大致相同 (Username、Password、Host IP、Port),唯一分別是SID,但不一定要使用SID,SERVICE_NAME也可以使用,視乎Server設定
如果server為Express,SERVICE_NAME為XE;如果想直接引用tnsnames.ora,Datasource=ora內的名稱即可。

string connString = "Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=" + oracleIP + ")(PORT=" + oraclePort + ")))" +
                    "(CONNECT_DATA=(SID=" + oracleServiceID + ")));" +
                    "User id=" + oracleUserName + ";" +
                    "Password=" + oraclePassword + ";";


6‧將Oracle 內的Data 打包

query跟SQL Server及MySQL 大致相同,我就不作解釋了,下面的Code大約就是將 Oracle 內的Data加進自訂的List<OracleData>裡面,以便調用。
*DBNull.Value的使用就是防止data field的值是(null)而產生的錯誤。*
*最好將下面的code放進try block以便debug,因為不一定第一次做就能夠成功(你明白的‧‧‧)*
 
OracleConnection conn = new OracleConnection(connString);
OracleCommand cmd = new OracleCommand(query, conn);
cmd.CommandTimeout = 1200;
conn.Open();

OracleDataReader dr = cmd.ExecuteReader();
List<OracleData> content = new List<OracleData>();
while (dr.Read()) {
   OracleData od = new OracleData();
   od.SetEquipNo(dr[0].ToString());
   od.SetLocationCode(dr[1].ToString());
   od.SetCommDate(Convert.ToDateTime(dr[2]));
   if (!DBNull.Value.Equals(dr[3]))
   {
      od.SetThreshold(Convert.ToInt32(dr[3]));
   }
   else
   {
      od.SetThreshold(0);              
   }
   od.SetDescription(dr[4].ToString());
   if (!DBNull.Value.Equals(dr[5]))
   {
      od.SetInterval(Convert.ToInt32(dr[5]));
   }
   else
   {
      od.SetInterval(0);               
   }
   content.Add(od);
}
conn.Close();

C# 連接 Oracle Database 就大致完成了!!