在C#中,我們如果要確定我們要通訊的USB裝置是否已連接,
通常會用PID/VID去判斷裝置是否存在。
最近遇到一個問題,有一個DFU裝置,驅動安裝失敗,但依然可以偵測到PID/VID。
但沒有驅動程式,是無法正確運作的。
最終我是用USB Descriptor 去判斷是否有安裝驅動。
在未安裝驅動前,它的USB Descriptor為"DFU in FS Module",
但在安裝驅動後,它的USB Descriptor為"STM Device in DFU Mode"
一開始誤以為沒有驅動,它不會被讀到。。。。。觀念錯誤。
上了一課,怪不得出現莫明其妙偵測的到,卻無法動彈的情況。
2017年8月2日 星期三
2017年6月20日 星期二
[C#]Array.Join
最近遇到一個需求,把1234 ---> 1,2,3,4
所以用到String.Join這個方法
參考網頁:
https://msdn.microsoft.com/zh-tw/library/57a79xd0(v=vs.110).aspx
所以用到String.Join這個方法
public static string Join( string separator, params string[] value )
第一個參數separator是表示你要用什麼分隔符號,例如,。
第二個參數則是要合併的String Array
使用範例如下,輸出為1,2,3,4
String source="1234"; char[] charArray=source.ToCharArray(); String dist=String.Join(",",charArray); Console.WriteLine(dist);
參考網頁:
https://msdn.microsoft.com/zh-tw/library/57a79xd0(v=vs.110).aspx
2016年12月30日 星期五
[C#] 讀取center 306 thermometer
今天遇到一個很神奇的溫度計,Center 306 thermometer ,需要用PC透過RS232 ,
將溫度值讀出來。
首先它的連接參數如下:
Baud Rate : 9600 , N , 8 , 1
需要設定啟用以下設定,否則會一直發送卻沒有人回你。
DTR – Data Terminal Ready
RTS – Request To Send
接下來說明Command
主要會用到2個,一個是K,一個是A。
K是會回傳型號,例如'3' '0' '6' ASCII(13) ,'3' '0' '1' ASCII(13) 。
而A則是溫度資訊,其他的功能本次沒有實作。
301和306在溫度的回傳的資料,有差一個byte,在實作時要注意。
我有一組306的回傳值,共九個BYTE。
02 80 00 B2 29 BB 00 B2 28 03
只會用到其中5個byte
第2個byte的第1個bit和第4個bit (由0開始計算)表示T1和T2是否為負值(1為負值)。
例如目前為00, 則T1和T2都是正值。
第3~4個BYTE代表T1的溫度值,B2 29 --> 022.9 ℃
第7~8個BYTE代表T2的溫度值,B2 28 --->022.8 ℃
一開始我是把B給濾掉,但後來發現降到9.8℃ 時,會出現BB.98 ,才推測B是0 。
以下是我的程式碼
呼叫方式如下:
當有回傳值時,會自動顯示在c306_temp1及c306_temp2 二個Lable上
參考資料
C306
https://www.instrumart.com/assets/MARTEL306_manual.pdf
C301差異截圖
將溫度值讀出來。
首先它的連接參數如下:
Baud Rate : 9600 , N , 8 , 1
需要設定啟用以下設定,否則會一直發送卻沒有人回你。
DTR – Data Terminal Ready
RTS – Request To Send
接下來說明Command
RS232 command
|
Function
|
Remarks
|
K(ASC 4BH)
|
Ask for model No.
|
Return 4 bytes
|
A(ASC 41H)
|
Inquire all encoded data
|
Return encoded 10 byte
|
H(ASC 48H)
|
Hold button
| |
M(ASC 4DH)
|
MAX/MIN button
| |
N(ASC 4EH)
|
Exit MAX/MIN mode
| |
T(ASC 52H)
|
TIME button
| |
C(ASC 43H)
|
C/F button
| |
U(ASC 55H)
|
Dump all memory of thermometer
|
return 32768 bytes
|
P(ASC 50H)
|
Load recorded data
|
主要會用到2個,一個是K,一個是A。
K是會回傳型號,例如'3' '0' '6' ASCII(13) ,'3' '0' '1' ASCII(13) 。
而A則是溫度資訊,其他的功能本次沒有實作。
301和306在溫度的回傳的資料,有差一個byte,在實作時要注意。
我有一組306的回傳值,共九個BYTE。
02 80 00 B2 29 BB 00 B2 28 03
只會用到其中5個byte
第2個byte的第1個bit和第4個bit (由0開始計算)表示T1和T2是否為負值(1為負值)。
例如目前為00, 則T1和T2都是正值。
第3~4個BYTE代表T1的溫度值,B2 29 --> 022.9 ℃
第7~8個BYTE代表T2的溫度值,B2 28 --->022.8 ℃
一開始我是把B給濾掉,但後來發現降到9.8℃ 時,會出現BB.98 ,才推測B是0 。
以下是我的程式碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;
using System.Collections;
namespace TEST
{
class Center_306
{
private SerialPort port;
private Test.Form1.MyDelegate md;
public Center_306(String comport, TH300Monitor.Form1.MyDelegate md)
{
this.md = md;
port = new SerialPort(comport, 9600, Parity.None, 8, StopBits.One);
// Enable DTR
bool s = port.DtrEnable = true;
port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
}
public void QueryTemp()
{
port.Write(new char[] { 'A' }, 0, 1);
}
private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
int bytes = port.BytesToRead;
byte[] comBuffer = new byte[bytes];
port.Read(comBuffer, 0, bytes);
if (bytes == 9)
{
byte[] t1 = new byte[2];
byte[] t2 = new byte[2];
bool t1_sign = (comBuffer[2] & (1 << 1)) != 0;
bool t2_sign = (comBuffer[2] & (1 << 4)) != 0;
Array.Copy(comBuffer, 3, t1, 0, 2);
Array.Copy(comBuffer, 7, t2, 0, 2);
string hex1=BitConverter.ToString(t1);
string hex2 = BitConverter.ToString(t2);
hex1 = hex1.Replace("-", "").Replace("B", "0");
hex2 = hex2.Replace("-", "").Replace("B", "0");
double temp1 = Double.Parse(hex1) / 10.0;
double temp2 = Double.Parse(hex2) / 10.0;
if (t1_sign == true)
{
temp1 = temp1 * -1;
}
if (t2_sign == true)
{
temp2 = temp2 * -1;
}
Console.WriteLine(temp1 + " " + temp2);
md.Invoke(temp1, temp2);
}
}
public void Open()
{
port.Open();
}
public void Close()
{
port.Close();
}
}
}
呼叫方式如下:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using TH300; using System.IO.Ports; namespace TH300Monitor { public partial class Form1 : Form { //add for Center 306 public delegate void MyDelegate(double temp1, double temp2); Center_306 c306; MyDelegate oMyDel; bool isConnect=false; public Form1() { InitializeComponent(); } private void btnConn_Click(object sender, EventArgs e) { TimerRead.Enabled = true; //add center 306 oMyDel = new MyDelegate(Show); c306 = new Center_306(C306_com.Text, oMyDel); c306.Open(); } //for center 306 display MyDelegate uu; public void Show(double temp1, double temp2) { if (this.InvokeRequired) { uu = new MyDelegate(Show); this.Invoke(uu, temp1, temp2); } else { c306_temp1.Text = temp1.ToString(); c306_temp2.Text = temp2.ToString(); } } private void btnDisconn_Click(object sender, EventArgs e) { TimerRead.Stop(); System.Threading.Thread.Sleep(1000); //ad for center 306 c306.Close(); } private void btnStop_Click(object sender, EventArgs e) { TimerRead.Stop(); } /// <summary> /// Read data per sec /// </summary> private void TimerRead_Tick(object sender, EventArgs e) { //add for c306 c306.QueryTemp(); } private void Form1_Load(object sender, EventArgs e) { // Set Timer TimerRead.Interval = 1000; } } }
當有回傳值時,會自動顯示在c306_temp1及c306_temp2 二個Lable上
參考資料
C306
https://www.instrumart.com/assets/MARTEL306_manual.pdf
C301差異截圖
2016年12月25日 星期日
[C#] 簡單繼承例子
一個簡單的繼承例子
輸出為:
boywhyblogger
因為父類別的建構子中己經輸出了boywhy,而繼承的子類別中的建構子也輸出blogger,
所以輸出為boywhyblogger
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { new MyClass(); Console.ReadKey(); } } class BaseClass { public BaseClass() { Console.Write("boywhy"); } } class MyClass:BaseClass { public MyClass() { Console.Write("blogger"); } } }
輸出為:
boywhyblogger
因為父類別的建構子中己經輸出了boywhy,而繼承的子類別中的建構子也輸出blogger,
所以輸出為boywhyblogger
2016年12月24日 星期六
「C#」掛載遠端資料夾為磁碟機
弄了一個Class,可以掛載遠端磁碟。
記得要引入 Windows Script Host Object Model參考
記得先去一個空的磁碟機代號,可以參考這篇 ,以下是使用範例。
注意一下,遠端磁碟是採用一種叫UNC的路徑格式, \\IP\\Folder ,
不要多打了一個\ ,變成\\IP\\Folder\\ ,會連到Timerout。
記得要引入 Windows Script Host Object Model參考
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using IWshRuntimeLibrary; //Add "Windows Script Host Object Model" COM reference using System.IO; using System.Collections; namespace Mount_Data { class NetworkDriver { private WshNetwork _networkShell = new WshNetwork(); private void MapDrive(string driveLetter, string UNC, bool isPersistent, string userName, string password) { if (driveLetter != "") { DisconnectDrive(driveLetter, true, true); } else { DisconnectDrive(UNC, true, true); } object persistent = isPersistent; object user = userName; object pwd = password; _networkShell.MapNetworkDrive(driveLetter, UNC, ref persistent, ref user, ref pwd); } private void DisconnectDrive(string UNC_or_DriveName, bool willForce, bool isPersistent) { try { object force = willForce; object updateProfile = isPersistent; _networkShell.RemoveNetworkDrive(UNC_or_DriveName, ref force, ref updateProfile); } catch { // } } /// <summary> /// Mount Remote disk /// </summary> /// <param name="sour_path">remote source path , ex: @"file://123.456.789.123/qq" </param> /// <param name="DriveLable">remote disk lable , ex: X, Y ,Z </param> /// <param name="user_id"></param> /// <param name="password"></param> public void MAP_DRIVER(String sour_path, string DriveLable,string user_id,string password) { MapDrive(DriveLable+":", sour_path, false, user_id, password); } /// <summary> /// Umount /// </summary> public void DIS_MAP_DRIVER(string DriveLable) { DisconnectDrive(DriveLable+":", true, true); } public char getFreeLabel() { char letter = ' '; ArrayList driveLetters = new ArrayList(26); // Allocate space for alphabet for (int i = 65; i < 91; i++) // increment from ASCII values for A-Z { driveLetters.Add(Convert.ToChar(i)); // Add uppercase letters to possible drive letters } foreach (string drive in Directory.GetLogicalDrives()) { driveLetters.Remove(drive[0]); // removed used drive letters from possible drive letters } foreach (char drive in driveLetters) { //comboDrives.Items.Add(drive); // add unused drive letters to the combo box letter = drive; break; } return letter; } } }
記得先去一個空的磁碟機代號,可以參考這篇 ,以下是使用範例。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApplication2 { public partial class Form1 : Form { //«Å§i¤@ÓNetworkDiriverª«¥ó¡A¥Î¨Ó±¾¸ü»·ºÝºÏºÐ NetworkDriver nd = new NetworkDriver(); public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { //UNC format \\IP\folder , don't use \\IP\folder\\ nd.MAP_DRIVER(@"\\192.168.1.109\個人資料夾","z","username","password"); } private void button2_Click(object sender, EventArgs e) { nd.DIS_MAP_DRIVER("z"); } } }
注意一下,遠端磁碟是採用一種叫UNC的路徑格式, \\IP\\Folder ,
不要多打了一個\ ,變成\\IP\\Folder\\ ,會連到Timerout。
按下Mount後,會Mount指定的遠端磁碟,按下Umount會中斷連接
按下Mount後會出出一個遠端磁碟
按下Umount之後會消失
[C# ] TabPage Hide and Show
讓TabPage顯示或隱形有一個蠻簡單的方法,效果如下
將TabPage顯示
將TabPage隱形
程式碼如下
將TabPage顯示
將TabPage隱形
程式碼如下
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApplication2 { public partial class Form1 : Form { TabPage mMoneyPage; public Form1() { InitializeComponent(); //get Tabpage ref for (int i = 0; i < tabControl1.TabPages.Count; i++) { if (tabControl1.TabPages[i].Name.Equals("money")) { mMoneyPage = tabControl1.TabPages[i]; } } } private void checkBox1_CheckedChanged(object sender, EventArgs e) { if (mMoneyPage != null) { if (((CheckBox)sender).Checked) { mMoneyPage.Parent = tabControl1; } else { mMoneyPage.Parent = null; } } } } }
2016年11月24日 星期四
[C#] 改變陣列的長度
C# 提供一個方式,讓我們可以很輕的改變Array長度,並且保留原本的資料
執行結果:
你可以發現,資料都還在,陣列也如我所願變長了,接下來我們試試變短看看
Array.Resize(ref number,10);
輸出結果如下
public static void Resize<T> ( ref T[] array, int newSize )
使用方法如下
Array.Resize(ref 你的陣列 , 新的長度)
以下是一個範例,把陣列加長,並印出資料
using System; using System.Collections.Generic; namespace Rextester { public class Program { public static void Main(string[] args) { int[] number=new int[20]; for (int i=0;i<number.Length;i++) { number[i]=i+1; } Array.Resize(ref number,30); int idx=0; foreach(int n in number) Console.WriteLine("number["+(idx++)+"]="+n); } } }
執行結果:
number[0]=1 number[1]=2 number[2]=3 number[3]=4 number[4]=5 number[5]=6 number[6]=7 number[7]=8 number[8]=9 number[9]=10 number[10]=11 number[11]=12 number[12]=13 number[13]=14 number[14]=15 number[15]=16 number[16]=17 number[17]=18 number[18]=19 number[19]=20 number[20]=0 number[21]=0 number[22]=0 number[23]=0 number[24]=0 number[25]=0 number[26]=0 number[27]=0 number[28]=0 number[29]=0
你可以發現,資料都還在,陣列也如我所願變長了,接下來我們試試變短看看
Array.Resize(ref number,10);
輸出結果如下
number[0]=1 number[1]=2 number[2]=3 number[3]=4 number[4]=5 number[5]=6 number[6]=7 number[7]=8 number[8]=9 number[9]=10
資料依然完好無損。
[c#] 取得字型目錄
取得字型所在地的目錄
string fontsfolder = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Fonts);
「C#」將DataTable的值輸出成PDF
首先你需要到這裡下載一個範例程式,它裡面會有我們需要的itextsharp.dll。
http://www.aspsnippets.com/Articles/Export-Windows-Forms-DataGridView-to-PDF-using-iTextSharp-C-and-VBNet.aspx
他內附的範例程式會有一個蠻冏的問題,中文字會不見,不過外國人沒這煩惱。
我爬了一些文之後,修正了這個問題,需要指定字型。
首先,你必須在專案中建用itextsharp.dll。
接下來將以下這段範例程式將會把datagridview1的內容,包含標題列,都丟到PDF中
參考連結:
Export Windows Forms DataGridView to PDF using iTextSharp, C# and VB.Nethttp://www.aspsnippets.com/Articles/Export-Windows-Forms-DataGridView-to-PDF-using-iTextSharp-C-and-VBNet.aspx
中文輸出問題解法參考
https://dotblogs.com.tw/kevinya/2015/12/24/163050
http://www.aspsnippets.com/Articles/Export-Windows-Forms-DataGridView-to-PDF-using-iTextSharp-C-and-VBNet.aspx
他內附的範例程式會有一個蠻冏的問題,中文字會不見,不過外國人沒這煩惱。
我爬了一些文之後,修正了這個問題,需要指定字型。
首先,你必須在專案中建用itextsharp.dll。
接下來將以下這段範例程式將會把datagridview1的內容,包含標題列,都丟到PDF中
//設定中文字體 string fontsfolder = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Fonts); string chFontPath = fontsfolder+ "\\KAIU.ttf";//windows內建的SimHei字體(中易黑體) BaseFont chBaseFont = BaseFont.CreateFont(chFontPath, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); Phrase helloPhrase = new Phrase(); iTextSharp.text.Font twFont = new iTextSharp.text.Font(chBaseFont, 14); //Creating iTextSharp Table from the DataTable data PdfPTable pdfTable = new PdfPTable(dataGridView1.ColumnCount); pdfTable.DefaultCell.Padding = 3;
//use 100% page width
pdfTable.WidthPercentage = 100;
pdfTable.HorizontalAlignment = Element.ALIGN_LEFT;
pdfTable.DefaultCell.BorderWidth = 1;
//Adding Header row
foreach (DataGridViewColumn column in dataGridView1.Columns)
{
//Asign Chinese Font to cell PdfPCell cell = new PdfPCell(new Phrase(column.HeaderText,twFont));
cell.BackgroundColor = new iTextSharp.text.Color(240, 240, 240); pdfTable.AddCell(cell); } //Adding DataRow foreach (DataGridViewRow row in dataGridView1.Rows) { foreach (DataGridViewCell cell in row.Cells) { if (cell.Value!=null) { PdfPCell datacell = new PdfPCell(new Phrase(cell.Value.ToString(), twFont)); datacell.BackgroundColor = new iTextSharp.text.Color(255, 255, 255); pdfTable.AddCell(datacell); } } } //Exporting to PDF string folderPath = Environment.CurrentDirectory+"\\"; if (!Directory.Exists(folderPath)) { Directory.CreateDirectory(folderPath); } using (FileStream stream = new FileStream(folderPath + "blogger.pdf", FileMode.Create))
{
Document pdfDoc = new Document(PageSize.A2, 10f, 10f, 10f, 0f);
PdfWriter.GetInstance(pdfDoc, stream);
pdfDoc.Open();
pdfDoc.Add(pdfTable);
pdfDoc.Close();
stream.Close();
}
參考連結:
Export Windows Forms DataGridView to PDF using iTextSharp, C# and VB.Nethttp://www.aspsnippets.com/Articles/Export-Windows-Forms-DataGridView-to-PDF-using-iTextSharp-C-and-VBNet.aspx
中文輸出問題解法參考
https://dotblogs.com.tw/kevinya/2015/12/24/163050
[C#] TextBox Hint Text
C# TextBox沒有像Android的Hint,可以在使用者未輸入內容時,顯示提示字。
在網路上找到一段程式碼,感覺蠻好用的。
執行效果

注意一下,第3個參數是0或1的差別
當是0的時候,只要TextBox取得焦點,把輸入內容刪除後,依然還是顯示空白,
必須當其他控制項取得焦點時,才會顯示提示字,如下所示。

當是1的時候,只要沒有輸入內容,就會出現提示字。
不過,在XP似乎沒有效果@@
在網路上找到一段程式碼,感覺蠻好用的。
private const int EM_SETCUEBANNER = 0x1501; [DllImport("user32.dll", CharSet = CharSet.Auto)] private static extern Int32 SendMessage(IntPtr hWnd, int msg, int wParam, [MarshalAs(UnmanagedType.LPWStr)]string lParam); private void Form1_Load(object sender, EventArgs e) { SendMessage(textBox1.Handle, EM_SETCUEBANNER, 0, "Input Number");
}
執行效果
注意一下,第3個參數是0或1的差別
當是0的時候,只要TextBox取得焦點,把輸入內容刪除後,依然還是顯示空白,
必須當其他控制項取得焦點時,才會顯示提示字,如下所示。
當是1的時候,只要沒有輸入內容,就會出現提示字。
不過,在XP似乎沒有效果@@
2016年11月22日 星期二
[C#] 寫TXT換行失效解法
有時候我們在寫文字檔時,在字串最後加上\n\r,但卻沒有成功換行。
後來發現一個有效的解法,也不用去在意各個系統的換行字元。
用這個就可以換行了。
後來發現一個有效的解法,也不用去在意各個系統的換行字元。
Environment.NewLine
用這個就可以換行了。
[C#] .Net Framework 3.5 在For迴圈中跑執行緒
Net Framework 4 推出了Parallel.For ,讓迴圈中發揮多核心的威力,簡化許多。
但在.Net Framework 3.5 中並沒有這種好東西可以用,如果要實作在F or迴圈中擠出
執行結果:
如果我們沒有使用ManualResetEvent來控制流程,則會變成以下結果
把mre.set()以及mre.WaitOne()註解掉
就會變成我也不知道Thread何時跑完,但我己先印出結束時間了。
但在.Net Framework 3.5 中並沒有這種好東西可以用,如果要實作在F or迴圈中擠出
多核心的效能,可以用以下方式實作。
首先介紹ManualResetEvent ,這是一個很好用的東西,可以用來卡住目前的流程。
當ManualResetEvent.Reset時,流程會卡住在ManualResetEvent.WaitOne()的地方,直到
ManualResetEvent.set()為止,若一直忘記Reset,會卡到無怨無悔,畫面臉色發白。
以下是一個簡單的範例,當所有執行緒執行完畢後,才會印出目前時間。
ManualResetEvent.set()為止,若一直忘記Reset,會卡到無怨無悔,畫面臉色發白。
以下是一個簡單的範例,當所有執行緒執行完畢後,才會印出目前時間。
using System; using System.Threading; using System.Diagnostics; using System.Collections.Generic; using System.Linq; namespace ConsoleApplication { public class Program { public static void Main(string[] args) { new Program(); } //Define Thread Control Var int DoneCount = 0; int DoneRequired =0; ManualResetEvent mre = new ManualResetEvent(false); public Program() { round(); } private void round() { int N=10; ParameterizedThreadStart pts ; Thread mThread; DoneCount = 0; DoneRequired =N; mre.Reset(); for (int i=0;i<N;i++) { pts = new ParameterizedThreadStart(ThreadFunction); mThread=new Thread(pts); mThread.Start(i); } //wait all thread finish mre.WaitOne(); Console.Write("Done "+DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss")+Environment.NewLine); } private void ThreadFunction(object data) { int n=Convert.ToInt32(data); System.Threading.Thread.Sleep(n*100); Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss")+" sleep:"+n*100); Interlocked.Increment(ref DoneCount); if (DoneCount == DoneRequired) { mre.Set(); } } } }
執行結果:
2016-11-22 10:22:34 sleep:0 2016-11-22 10:22:34 sleep:100 2016-11-22 10:22:34 sleep:200 2016-11-22 10:22:34 sleep:300 2016-11-22 10:22:34 sleep:400 2016-11-22 10:22:35 sleep:500 2016-11-22 10:22:35 sleep:600 2016-11-22 10:22:35 sleep:700 2016-11-22 10:22:35 sleep:800 2016-11-22 10:22:35 sleep:900 Done 2016-11-22 10:22:35
如果我們沒有使用ManualResetEvent來控制流程,則會變成以下結果
把mre.set()以及mre.WaitOne()註解掉
Done 2016-11-22 10:25:15 2016-11-22 10:25:15 sleep:0 2016-11-22 10:25:15 sleep:100 2016-11-22 10:25:15 sleep:200 2016-11-22 10:25:15 sleep:300 2016-11-22 10:25:15 sleep:400 2016-11-22 10:25:15 sleep:500 2016-11-22 10:25:15 sleep:600 2016-11-22 10:25:15 sleep:700 2016-11-22 10:25:15 sleep:800 2016-11-22 10:25:16 sleep:900
就會變成我也不知道Thread何時跑完,但我己先印出結束時間了。
訂閱:
文章 (Atom)







