ASP.NET เมื่อใช้ Response.BinaryWrite() แล้วชื่อภาษาไทยกลายเป็นยึกยือ ปัญหาของIE

เป็นเรื่องของการเขียน asp.net ให้ download file โดยการอ่าน File เข้ามาเป็น byte[] 

แล้วค่อยใช้ Response.BinaryWrite(byte[])  ออกไป

มันเป็นปัญหาเฉพาะ IE เท่านั้น  

หากเจอ อักขระ ที่ต้องห้าม  file name ภาษาไทยจะเอ๋อกินทันที 

ซึ่งนั่งปวดหมองอยู่วันนึงแล้วก็พบทางสว่าง  

เลยต้องแก้ไขตามนี้

        public string FileNameEncoder(string filename)

        {

            if (Request.Browser.Browser.Contains("IE"))

            {

                filename = Uri.EscapeDataString(Path.GetFileNameWithoutExtension(filename)).Replace("%20", " ") + Path.GetExtension(filename);

            }

            return filename;

        }

ในตอนเรียกใช้ก็เขียนประมาณนี้

        private void WriteFile(string pdfFileName,string type)

        {

            try

            {

                var bytefile = File.ReadAllBytes(pdfFileName);

                pdfFileName = FileNameEncoder(pdfFileName); 

                Response.Clear();

                Response.ClearContent();

                Response.ClearHeaders();

                Response.Charset = System.Text.Encoding.UTF8.WebName;

                Response.HeaderEncoding = System.Text.Encoding.UTF8;

                Response.ContentEncoding = System.Text.Encoding.UTF8;

                Response.ContentType = "Application/" + type;

                Response.AppendHeader("content-disposition", "attachment; filename=" + Path.GetFileName( pdfFileName));

                Response.BinaryWrite(bytefile);

                Response.End();

            }

            catch (Exception ex)

            {

                Response.Write(ex);

            }

        } 

Happy ครับ

ปล.  แต่ปัญหาจะไม่เกิด ถ้าใช้ Chrome, Firefox, IE9

WCF Series : เริ่มต้น (Begin) I

WCF Series : เริ่มต้น (Begin) I

WCF Series : xxx II

Tools and Language

  1. Visual Studio 2010 RC or later 
  2. C#

แนะนำบทความ

บทความนี้ตั้งใจจะสร้างชุมชนผู้ใช้งาน WCF อย่างจริงจัง เนื่องจากไม่ค่อยจะพบเห็นใครใช้งาน อาจจะเพราะติดปัญหาไม่ว่าจะเรื่องความเข้าใจพื้นฐาน ความรู้ความเข้าใจในการพัฒนา  การนำไปใช้งาน ติดปัญหาเรื่องความปลอดภัย ไม่ประสบความสำเร็จการนำไปติดตั้งใช้งานจริง ซึ่งซีรี่ย์ที่ผมเขียนนี้จะครอบคลุมเรื่องเหล่านี้ทั้งหมด และจะคอยตอบและแก้ไขปัญหาให้ด้วยครับ

ซึ่งในตอนแรกจะเป็นการกล่าวถึง WCF แบบกว้าง ๆ ทำความเข้าใจโครงสร้างและการทำงานเบื้องต้น รวมไปถึงวิธีการสร้างเพื่อนำไปใช้งานเบื้องต้นครับ

ยุคเริ่มต้น ASP.NET Web Services 

คงไม่ย้อนยุคกลับไปสมัยโบราณมากมาย เริ่มต้นกันที่ Web Services ใน .NET นั้นมีมาตั้งแต่รุ่น .NET 1.0 จนมาถึงปัจจุบัน .NET 3.5 SP1 ซึ่งได้หยุดพัฒนาแล้ว ASP.NET Web Service เป็นอะไรที่เรียบง่ายมาก ๆ สามารถเขียน WebMethod ได้เหมือนการเขียนฟังชั่นปกติธรรมดา แม้กระทั่งการคืนค่ากลับจากก็เหมือนการเขียนฟังชั่นเช่นกัน แต่มาถึงยุคการสื่อสารไร้พรหมแดนอย่าง SOA สำหรับ ASP.NET Web Services นั้น ไม่พอเพียง ทั้งเรื่อง interoperaion, performance, security, extensibility  ที่ต้องเพิ่มแติมและเปลี่ยนแปลงอยู่เรื่อย ๆ จึงจะทำให้สิ้นยุคของ ASP.NET Web Service ในไม่นานนี้

WCF นั้นคืออะไร? (What’s WCF)

WCF (Windows Communication Foundation) มีมาตั้งแต่ .NET 3.0 แล้วครับ วัตถุประสงค์ก็เพื่อช่วยขยับขยายขีดความสามารถของ ASP.NET Web Service ที่เราใช้กันอยู่ในอดีตมาจนถึงปัจจุบันนี้ก็คิดว่ายังมีหลายท่านเลือกใช้กันเป็นระบบหลัก    ซึ่งทางไมโครซอฟต์ตั้งใจพัฒนา WCF ขึ้นเพื่อรองรับลักษณะงานประเภท SOA ในกรณีที่มีระบบ(System) หรือผู้ใช้(User) ที่มีความหลากหลายในด้านมาตรฐานการสื่อสาร(Protocol) มีประสทธิภาพการทำงานที่รวดเร็วขึ้นกว่าเดิม      สามารถนำ Service ที่พัฒนาไว้นั้นไปรันบนโฮสต์ชนิดต่าง ๆ ได้ และยังรวมไปถึงระบบรักษาความปลอดภัยที่พัฒนาให้รองรับกับมาตรฐานที่มีและรองรับอนาคตอีกด้วย     ซึ่ง ASP.NET Web Service ไม่สามารถตอบโจทย์ที่ว่านี่ได้ จึงเป็น New Generation Web Service ของทางไมโครซอฟต์ที่มีมาให้เลือกใช้ครับ

รายละเอียดเพิ่มเติมอ่านต่อได้ที่นี่ วิวัฒนาการก่อนจะมาเป็น WCF คุณนเรศเขียนไว้ได้ละเอียดดีครับ

วิวัฒนาการของ WCF (WCF Evolution)

เริ่มต้นก้าวแรกสำหรับ WCF ได้ออกตามหลัง Visual Studio 2005 เป็นปลั๊กอินชื่อว่า Visual Studio 2005 extensions for.NET Framework 3.0    เป็นก้าวแรกที่ microsoft ตั้งใจที่จะพัฒนาให้ .NET ได้มีที่ยืนในกลุ่มโปรดักของ SOA   ซึ่งถือว่าได้เปิดตัว WCF เป็นครั้งแรกโดยมีความสามารถตามที่ได้กล่าวไปข้างต้น    ต่อมาสำหรับ WCF 3.5 นั้นได้ออกมาพร้อมกับ Visual Studio 2008 โดยเพิ่มเติมการทำงานในส่วนของ SOA เข้าไปอีกก็คือ Workflow Services และเมื่อ Visual Studion 2008 ได้ปล่อย Service Pack 1 ก็ได้มีการอัพเกรดเวอชั่น WCF เป็น SP1 ไปด้วยในตัวโดยเพิ่มเติมด้าน Web Programming เข้ามา     ซึ่ง WCF 3.5 SP1 เป็นตัวที่ใช้กันเป็น production อยู่ในปัจจุบัน    จนกระทั่งที่เขียนบทความนี้อยู่ WCF 4.0 Release Candidate version ก็มีให้เราได้ทดสอบใช้งานกัน และบทความนี้จะใช้ WCF 4.0 เป็นหลัก ตั้งแต่พื้นฐานเป็นต้นไปครับ

WCF FEATURE TABLE: (แต่ละฟีเจอร์จะอธิบายแนะนำการใช้งานรอบหลังอีกครั้ง)

WCF 3.0

  • One-way and duplex messaging 
  • Synchronous and asynchronous remote procedure calls 
  • Callbacks  
  • Sessions   
  • Multi-contract services 
  • Transport- and message-based security, reliability, and ordered delivery  
  • Queued messaging   
  • Transaction support  

WCF 3.5 and SP1

  • WCF and WF Integration—Workflow Services 
  • Durable Services
  • WCF Web Programming Model
  • WCF Syndication
  • WCF and Partial Trust
  • WCF and ASP.NET AJAX Integration
  • Web Services Interoperability
  • Expanded UriTemplate syntax
  • Syndication OM for the Atom Publishing Protocol.
  • Attribute-free Data Contract serialization.
  • Interoperable Object References.
  • Improved logging/tracing in Partial Trust.
  • Scalability Improvements on IIS7

WCF 4.0

  • Configuration Based Activation.
  • System.Web.Routing Integration
  • Multiple IIS Site Bindings Support
  • Routing Service
  • Support for WS-Discovery
  • Standard Endpoints
  • Workflow Services
  • Target Framework Attribute
  • WCF REST
    1. Caching
    2. Formats Support
    3. HTTP REST Error Handling
    4. Deployment Features
    5. Cross Domain JavaScript

 

โครงสร้างสถาปัตยกรรมของ WCF (WCF Architecture)

กล่าวถึงสถาปัตยกรรมของตัว WCF แล้วนั้นจะมีโครงสร้างที่ไม่ซับซ้อน และควรจะเรียนรู้ไว้ เนื่องจากมีความจำเป็นที่จะต้องทำความเข้าใจ เพื่อใช้ในการวิเคราะหฺ์ออกแบบการทำงานของเซอร์วิสที่เราจะพัฒนาด้วย  อ่านไปเรื่อย ๆ ทำความเข้าใจกันก่อนจะ coding กัน

 

ไดอะแกรมด้านบนนี้เป็นโครงสร้างพื้นฐานของ WCF โดยได้แบ่งเอาไว้เป็นชั้น ๆ (Layer) ดังที่เห็น ซึ่งจำเป็นที่เราจะต้องเข้าใจครับว่าแต่ละส่วนคืออะไร ผมขออธิบายสั้น ๆ ได้ดังนี้

Application

คือส่วนที่เราได้ทำการ implement code ตัวเซอร์วิสไว้เช่น method, code logic, entity ต่างๆ ที่ใช้ในการทำงานของตัวเซอร์วิส

Contracts

Contract Layer เป็นชั้นที่เชื่อมต่อกับ Application Layer ซึ่งนักพัฒนาจะทำงานโดยตรงกับส่วนนี้เพื่อใช้ในการสร้างตัวเซอร์วิสต่าง ๆ     

  • Service contracts

- เป็นส่วนที่บ่งบอกว่าเซอร์วิสที่เราสร้างขึ้นนั้นมี operation (Method) อะไรไว้ให้บริการบ้าง ตัวอย่างเช่น บริการคำนวนภาษี, ให้ข้อมูลอัตราแลกเปลี่ยนค่าเงิน เป็นต้น

[ServiceContract]
public interface IMoneyServices
{
  [OperationContract]
   string CalculateTAX(decimal money, float vatRate);
}

จากตัวอย่างโค้ดด้านบน จะมีการใช้งานอินเตอร์เฟสในการกำหนดโครงสร้างของตัวเซอร์วิสของเรา โดยอาศัยแอททริบิ้วที่ชื่อ [ServiceContract] เป็นตัวบอก WCF ให้ทราบว่านี่คือเซอร์วิสที่ให้บริการ  และใช้แอททริบิ้ว [OperationContract] กำหนดบนเม็ดธอดของอินเตอร์เฟสเพื่อบอก WCF ว่าเม็ดธอดนี้จะเปิดเผยแก่ผู้ขอใช้บริการเซอร์วิสนี้    ส่วนนี้ยังไม่สิ้นสุดการสร้างเซอร์วิสเป็นเพียงการกำหนดโครงสร้างเท่านั้น ต้องนำอินเตอร์เฟสที่ได้ไปทำการ implement บนคลาสใด ๆ อีกครั้งหนึ่ง

สงสัย Interface นั้นคืออะไร? กดที่นี่

  • Data contract

- เป็นส่วนที่กำหนดโครงสร้างข้อมูลต่าง ๆ ที่มีรับส่งข้อมูลทั้งอินพุทและเอ้าท์พุทโดย Operation Contract ของ Service Contracts ซึ่งอาจจะประกอบไปด้วย ชนิดข้อมูลพื้นฐาน (Primitive Data Type) เช่น string, int, decimal, float, double, bool  และกลุ่มข้อมูลโครงสร้างที่ซับซ้อน (Complex Data Type) อย่างเช่น Class, Enumeration Data ต่าง ๆ ดังตัวอย่าง

[DataContract]
public class PurchaseOrder
{
   private int poId_value;

   [DataMember]
   public int PurchaseOrderId
   {
      get { return poId_value; }
      set { poId_value = value; }
   }
}

จากตัวอย่างโค้ดด้านบนจะเห็นว่าแอททริบิ้ว [DataContract] จะถูกกำหนดไว้บนคลาส เพื่อบอก WCF ว่าให้ทำการเปิดเผยคลาสนี้ลงใน WSDL ด้วย  และแอททริบิ้ว [DataMember] นั้นจะเป็นตัวบอก WCF ให้ทำการเปิดเผย data property นี้ลงไปใน WSDL ด้วย   ตัวอย่างต่อไปคือความสัมพันธ์ระหว่าง ServiceContract และ DataContract

[ServiceContract]
public interface ISampleInterface
{
    [OperationContract]
    double SquareRoot(int root);

    [OperationContract]
    bool ApprovePurchaseOrder(PurchaseOrder po);
}

จากโค้ดด้านบนนี้จะเห็นว่า OperationContract ทั้งสองตัวมีการใช้งาน Primitive Data Type และ Complex Data Type

  1. SqaureRoot มีพารามิเตอร์เข้าชื่อ root มีชนิดเป็น int  และส่งค่ากลับเป็น double
  2. ApprovePurchaseOrder มีพารามิเตอร์เข้าชื่อ po มีชนิดเป็น PurchaseOrder Class และส่งค่ากลับเป็น bool     ซึ่ง PurchaseOrder Class เราได้ประกาศเป็น DataContract ไว้ก่อนนี้ เนื่องจากเป็น Complex Data Type จึงต้องระบุให้ WCF ทำการนำโครงสร้างของคลาสไปบรรยายไว้ใน WSDL ด้วย
  • Message Contract

- โดยปกติข้อมูลที่ส่งข้ามกันระหว่าง Service และ Client นั้น ถูกกำหนดรูปแบบอยู่ใน SOAP Message อยู่แล้ว แต่บางครั้งเนื่องจากความต้องการในการรองรับการทำงานต่าง ๆ เช่น ต้องการจัดรูปแบบโครงสร้างเมสเสจใหม่, อยากใช้ Header, การเพิ่มเติม Status เหล่านี้  เราจะต้องอาศัย Message Contract เป็นตัวช่วยครับ

  • Policies and Binding

- ส่วนของ Policies และ Binding นั้นเป็นตัวกำหนดตั้งค่าเงื่อนไขและวิธีการให้เซอร์วิสของเรามีการติดต่อสื่อสารในรูปแบบใด (Protocol) ใช้ระบบการรักษาปลอดภัย (Security) แบบไหน และอื่น ๆ อีกมากมาย

Service Runtime

- เป็นส่วนที่กำหนดพฤติกรรม (Behavior) การทำงานของเซอร์วิส ขณะที่เซอร์วิสทำงานอยู่ มีดังนี้

  • Throttling Behavior- ใช้กำหนดจำนวนของเมสเสจที่จะเข้ามาทำงานในเซอร์วิส
  • Error Behavior – หากมีปัญหาเกิดขึ้นภายในเซอร์วิส ใช้มาช่วยทำการจัดการปัญหาดังกล่าว
  • Metadata Behavior – ใช้กำหนดการเปิดเผยข้อมูลโครงสร้าง (MetaData) ของเซอร์วิส แก่ผู้ใช้งาน
  • Instance Behavior – ใช้กำหนดจำนวนของผู้ใช้ที่จะเข้ามาใช้งานในเซอร์วิสโดยพร้อม ๆ กันขณะที่เซอร์วิสทำงานอยู่ 
  • Transaction Behavior – ช่วยในการย้อนกลับ (Rollback) การทำงานหากโอเปอเรชั่นเกิดการทำงานที่ผิดพลาดขึ้น 
  • Dispatch Behavior – เป็นตัวควบคุมว่าเมสเสจต่าง ๆ นั้นจะเข้าการโปรเซสด้วยสถาปัตยกรรมใดของ WCF

Messaging

- ใน Messaging Layer นี้เป็นการจัดการเมสเสจที่ส่งเข้าออกภายในเซอร์วิส ทั้งการกำหนดรูปแบบของเมสเสจโด การวิธีการแลกเปลี่ยนเมสเสจซึ่งพอจะแบ่งได้เป็นสองกลุ่มดังนี้

  • Transport channels :  จะทำการอ่านและเขียนเมสเสจจากเน็ตเวิร์กภายในหรือภายนอกองค์กร มีบาง transports จำเป็นต้องมีการแปลง (encoder) เมสเสจจากหน่วยสตรีมไบท์ที่ใช้รับส่งกันในเน็ตเวิร์กมาเป็น XML เช่น HTTP, named pipes, TCP, และ MSMQ
  • Protocol channels :  เป็นการทำงานโปรโตคอลระดับเมสเสจ โดยส่วนใหญ่เป็นการเขียนอ่านในส่วนของหัวเมสเสจเช่น WS-Security Protocol เป็นการกำหนดใช้ความปลอดภัยในระดับชั้นเมสเสจ และ WS-Reliability Protocol เป็นการการันตีการส่งเมสเสจระหว่างกัน

Activation and Hosting

- เซอร์วิสที่ได้สร้างไว้เป็นเพียงไลบราลี่เท่านั้น เราต้องการนำไปรันบนโฮสครับ เพื่อให้เซอร์วิสสามารถซึ่งมีให้เลือกใช้อยู่หลายวิธีดังนี้

  • Internet information Service (IIS)

    ก็คือเว็บเซอร์นั้นเอง เป็นโฮสที่ดีมีข้อดีหลายข้อในการเลือกใช้งานเป็นโฮสให้แก่ WCF Service ของเรา เน้นไปในเรื่องของการทำ Load Balance  การขยับขยายความสามารถในการรองรับผู้ใช้ในอนาคต  ข้อเสือคือทำงานบน HTTP ได้เพียงโปรโตคอล และเซอร์วิสจะไม่ทำงานจนกว่าจะมีการร้องขอการทำงานมาจากผู้ใช้หรือระบบอื่น ๆ

  • Windows Activation Service (WAS)

    เป็นของใหม่ที่ส่งมาพร้อมกับ IIS 7.0 จะทำงานเมื่อมีการร้องขอบริการเข้ามา รองรับเกือบทุกโปรโตคอล HTTP, TCP, Named Pipes, MSMQ

  • Self-Hosting

    เป็น executeable ได้เช่น console app, win app, WPF app เป็นต้น ไม่สามารถทำงานอัตโนมัติได้ ต้องรันโปรแกรมทุกครั้งเพื่อให้โฮสทำงาน

  • Windows Service

    คล้ายกับ Executable โฮส แต่จะทำงานอัตโนมัติทุกครั้งที่ Windows Start

  • COM+ 

    โฮสตัวนี้ส่วนใหญ่ไม่มีคนได้ใช้งาน เพราะค่อนข้างจะลึกลึกเรื่อง Windows Component Programming คงไม่ขอพูดถึงตลอดบทความนี้

 

 

ลงมือสร้าง (My First WCF)

อ่านหลักการทฤษฎีกันมายาว เรามาเริ่มสร้าง WCF ในเบื้องต้นกันต่อครับ

ตัวอย่าง Requierment คร่าว ๆ :  ทางบริษัทต้องการให้พัฒนาส่วนให้บริการข้อมูลสินค้าแก่ผ้ใช้งานทั่วไปผ่านทาง Web Service

1. เปิด Visual Studio

2. เลือก Menu File –> New –> Project..

3. เลือก Visual C# –> WCF –> .NET Framework 4.0 –> WCF Service Application –> Name : Vol1.Sample01.ProductSvc, Location: C:\WCFSeries, Solution Name: WCFSeries –> OK

4.  สังเกตูดูโครงสร้างโปรเจ็คที่ Visual Studio สร้างออกมาให้ดังนี้ครับ  

    • IService1.cs – คือ Service Contract
    • Service1.svc – คือ Host file ใช้สำหรับ Host ที่เป็น Web Site (IIS)
    • Web.config – คือ configuration file ของ wcf ที่จะใช้ตั้งค่าต่าง ๆ

5. แก้ไขไฟล์ IService1.cs เพื่อระบุข้อกำหนด (Contract) ของเซอร์วิสเราดังนี้    <———-  (Design Concept)

  • เปลี่ยนชื่อเป็น IProductService.cs
  • แก้ไข code ดังนี้

        using System.Collections.Generic;
        using System.Linq;
        using System.Runtime.Serialization;
        using System.ServiceModel;
        using System.ServiceModel.Web;
        using System.Text;

        namespace Vol1.Sample01.ProductSvc
        {
            // 1. สร้าง Interface สำหรับเป็น Contract ของระบบ
            [ServiceContract]
            public interface IProductService
            {
               // 2. กำหนด operation สำหรับ contract ที่จะใช้เป็น web method ให้ user ใช้งาน
                //    2.1 สร้าง interface method ชื่อ GetCurrentVatRate มีการรีเทิร์นค่ากลับเป็น float type
                [OperationContract]
                float GetCurrentVatRate();

                //    2.2 สร้าง interface method ชื่อ GetAllProduct มีการรีเทิร์นค่ากลับเป็น Product Class
                //    แต่เนื่องจาก Product Class นั้นยังไม่ได้สร้าง จึงต้องไปสร้าง class ที่ข้อ 2.3

                [OperationContract]
                List<Product> GetAllProduct();
            }

           // 2.3  สร้าง Product Class เพื่อใช้เป็น DataContract สำหรับ IProductService.GetAllProduct() method ในข้อ 2.2
             [DataContract] // DataContract เป็นตัวระบุว่าให้เปิดเผย Type นี้ผ่านทาง web service
            public class Product
            {
                [DataMember] // DataMember เป็นตัวระบุว่าให้เปิดเผย member ตัวนี้ผ่านทาง web service
                public string ProductID { get; set; }
                [DataMember]
                public string ProductName { get; set; }
                [DataMember]
                public decimal Price { get; set; }
                [DataMember]
                public float Weight { get; set; }
                [DataMember]
                public string Model { get; set;}
            }

        }

    6. แก้ไขไฟล์ Service1.svc ซึ่งเป็น host file จะผูกกับ Service1.svc.cs ซึ่งเป็น Service File  ให้เราทำการแก้ไขดังนี้  <———-  (Implement Concept)

    • 6.1 เปลี่ยนชื่อเป็น ProductService.svc ให้ถูกต้อง
    • 6.2 ดับเบิ้ลคลิ๊กที่ไฟล์ ProductService.svc.cs แล้วแก้ไข Code ดังนี้

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.ServiceModel.Web;
    using System.Text;

    namespace Vol1.Sample01.ProductSvc
    {
       // ProductService คือ Service Class ที่เราจะเขียนโค้ดควบคุมการทำงานของเซอร์วิส
        public class ProductService : IProductService // นำ contract ที่ประกาศไว้มาใช้งานกับ service class
        {
            // operation จาก contract ถูกบังคับให้ประกาศใช้งานใน Service Class
            public float GetCurrentVatRate()
            {
                return 0.7f;
            }
           // operation จาก contract ถูกบังคับให้ประกาศใช้งานใน Service Class
            public List<Product> GetAllProduct()
            {
                return new List<Product>
                {
                    new Product{ProductID = "01", ProductName = "VIAO", Model = "VGN-FE44S/W", Price = 70000, Weight = 2.8f},
                    new Product{ProductID = "02", ProductName = "VIAO", Model = "VGN-FE45S/W", Price = 80000, Weight = 2.5f},
                    new Product{ProductID = "03", ProductName = "VIAO", Model = "VGN-FE46S/W", Price = 90000, Weight = 2.5f},
                    new Product{ProductID = "04", ProductName = "VIAO", Model = "VGN-FE47S/W", Price = 100000, Weight = 2.2f},
                    new Product{ProductID = "05", ProductName = "VIAO", Model = "VGN-FE48S/W", Price = 120000, Weight = 1.9f}
                };
            }
        }
    }

        • 6.3  แก้ไข Class Name ใน host file ใหม่ให้ถูกต้องเนื่องจากมีการแก้ไขจากด้านบน  คลิ๊กขวาที่ไฟล์ ProductService.svc.cs –> เลือก Open with –> เลือก Web Service Editor –> OK และแก้ไขตามตารางด้านล่างนี้
            <%@ ServiceHost Language="C#" Debug="true" Service="Vol1.Sample01.ProductSvc.ProductService" CodeBehind="ProductService.svc.cs" %>
      • 7.  ทำการทดสอบ WCF Service เบื้องต้น
        • 7.1  Build ด้วยการกด Ctrl+Shft+B หรือ menu –> Build –> Build Solution
        • 7.2  คลิ๊กขวาที่ไฟล์ ProductService.svc –> View in Browser เพื่อตรวจการทำงานเบื้องต้น

             

      • 8. ทดสอบสร้าง Client เพื่อเรียกใช้ WCF Service
        • 8.1 ไปที่ Menu –> File –> Add –> เลือก New Project..
        • 8.2  เลือก Visual C# –> Windows –> .NET Framework 4.0 –> Console Application –> Name: Vo1.Sample01.ConsoleTest –> กด OK

     

        • 8.3  จากนั้น คลิ๊กขวาที่ Project Vo1.Sample01.ConsoleTest  เลือก Add Service Reference…
        • 8.4 จะได้ output ตามรูปด้านล่างนี้ จะเห็นได้ว่ามี Service References (ProductService) คือ WCF Proxy Class  และ app.config ถูกสร้างขึ้น
        • 8.5 ทำการเรียกใช้ WCF ภายใน code ที่เราสร้างไว้ โดยดับเลิ้บคลิ๊กเปิด Program.cs มาทำการแก้ไข ตามตารางด้านล่างนี้

          using System;
          using System.Collections.Generic;
          using System.Linq;
          using System.Text;

          namespace Vol1.Sample01.ConsoleTest
          {
              class Program
              {
                  static void Main(string[] args)
                  {

                     // ประกาศเรียกใช้ Service Client
                      var wsClient = new ProductService.ProductServiceClient();

                      // ทดสอบเรียก GetCurrentVatRate()
                      Console.WriteLine("GetCurrentVatRate() result = {0} \n", wsClient.GetCurrentVatRate());

                      // ทดสอบเรียก GetAllProduct()
                      var productResult = wsClient.GetAllProduct();
                      Console.WriteLine("———- GetAllProduct() —————–");
                      Console.WriteLine("———- List All Product —————–");

                      Console.WriteLine("\tProductID\tBrandName\tModel\tPrice\tWeight");
                      foreach (var product in productResult)
                      {
                          Console.WriteLine("\t{0}\t{1}\t{2}\t{3}\t{4}", product.ProductID, product.BrandName, product.Model,
                                            product.Price, product.Weight);
                      }

                      Console.ReadKey();
                  }
              }
          }

     

        • 8.6  ทำการทดสอบ Client ด้วยการกด F5 จะได้ผลดังภาพด้านล่างนี้

     

    สรุปจากที่ได้เห็น โดยการเลือก WCF Service Application Project ในตอนแรกทำให้เราเลือกสร้าง WCF พร้อมเลือกโฮสประเภท Web host ไปด้วยในตัว  ซึ่งเหมือนกับการสร้าง ASP.NET Web Service แต่ขั้นตอนการสร้างอาจจะมากกว่า ASP.NET Web Service      เพราะ wcf มีโฮสใช้งานมากกว่า 1 ชนิดนั่นเองจึงต้องแยกพัฒนา service contract ออกมาอย่างชัดเจน      ซึ่งจะอธิบายการสร้าง Service และการเลือกใช้ Host ประเภทต่าง ๆ ในตอนหน้า

     


    สามารถ download source code ได้ที่นี่ 

       WCFSeries Code Sample Download Here!!

     

    Chalermpon Areepong Nine (นาย)

    Microsoft MVP Thailand ASP.NET

    email : nine_biz-talk.net at hotmail dot com

    Hello world!

    Welcome to WordPress.com. This is your first post. Edit or delete it and start blogging!

    Article: มาสร้าง Windows Service ใช้กันดีกว่า (ตอนที่ 1)

    Article: มาสร้าง Windows Service ใช้กันดีกว่า (ตอนที่ 1)

    ผมเชื่อว่ามีเพื่อน ๆ หลายคนที่ยังไม่เคยสร้างหรือใช้งานโปรเจ็คที่ต้องเขียนให้ การทำงานของโปรแกรมนั้นเป็น Service ตัวนึงโดยที่จะทำงานด้วยตัวเองไม่ต้องคอยคำสั่งจาก user ลองมาดูกันครับ

     

    What article covering?

    • แนะนำความรู้พื้นฐานเพื่อทำความเข้าใจเกี่ยวกับ Windows Service
    • สร้าง Windows Service Project โดยใช้ตัวอย่างงานจริง
    • Deployment (MSI Package, MSBuild ตอนที่2)
    • Debug Windows Service (ตอนที่2)

     

    What’s a Windows Service?

    คือ Program ที่สร้างและ deploy ไปแล้ว จะทำงานเป็น Service รันอยู่บน Windows โดยสามารถที่จะตั้งค่าให้ run program ไปพร้อม ๆ กับ Windows start up ได้ โดยทำงานเป็น Background Service โดยที่ user ไม่ต้องสั่งงานใด ๆ

    โดยการทำงานนั้นอาจจะเป็นในลักษณะของการทำงานตามช่วงเวลา, การทำงานตาม event ที่ต้องการ เช่น การทำงานร่วมกับ FileSystemWatcher หรือ System.Timers.Timer เพื่อใช้ Event ที่เกิดขึ้นเป็น Trigger ในการทำงานบางอย่างตามเงื่อนไขที่ต้องการ                 

    Mention the Windows service.

                เนื่องจากประโยชน์ของ Widows Service หลัก ๆ ก็คือ Program ที่เราสร้างนั้นสามารถที่จะทำงานได้โดย automatic ทันทีที่ Windows ได้ Start up และการทำงานของโปรแกรมนั้นจะเป็นในลักษณะของ Background Service คือจะทำงานตามที่เราได้ตั้งเงื่อนไขการทำงานไว้ หลังจาก Service Start แล้ว และ Windows Service นั้นจะสามารถเลือก User เพื่อที่จะให้สิทธิ์สั่งการทำงานของ service นั้นได้ (ค่า default ส่วนใหญ่จะใช้ LocalService, NetworkService User)  หากเพื่อน ๆ เคยเปิดดูใน Administrative Tools -> Services จะเห็น Windows Service จำนวนมากดังรูปข้างล่างนี้

     

        ซึ่งโปรแกรมส่วนใหญ่ทั้ง MSSQL, ASP.NET, IIS, etc. ต่างก็มีการใช้ Windows Service เป็นตัวคอยควบคุมการทำงานของระบบตามที่ได้วางเงื่อนไขการทำงานต่าง ๆ ของระบบไว้ในตัว service ทำให้เสมือนโปรแกรมนั้นมีชีวิต สามารถทำงานได้ด้วยตัวเองอยู่บน Windows แต่เป็นไปตามเงื่อนไขที่เราได้กำหนดการทำงานไว้

     

    Create my first Windows Service.

     

    Create Solution and Windows Service Project

    เริ่มแรกเลือกสร้างโปรเจ็คแบบ Windows Service ตามรูปข้างล่าง

     

    จากนั้นก็จะได้โครงสร้างโปรเจ็คในหน้าต่าง Solution Explorer ตามรูปข้างล่างนี้

     

    ซึ่ง Service1.cs จะเป็น file ที่เราจะทำการ coding ลงไปเพื่อควบคุมการทำงานตามที่เราต้องการ

    เมื่อเราเปิดกดเลือกดู   View code icon เราจะเห็น code ตาม list ข้างล่างนี้  

     

    using System;

    using System.Collections.Generic;

    using System.ComponentModel;

    using System.Data;

    using System.Diagnostics;

    using System.ServiceProcess;

    using System.Text;

     

    namespace myService

    {

        public partial class Service1 : ServiceBase

        {

            public Service1()

            {

                InitializeComponent();

            }

     

            protected override void OnStart(string[] args)

            {

                // TODO: Add code here to start your service.

            }

     

            protected override void OnStop()

            {

                // TODO: Add code here to perform any tear-down necessary to stop your service.

            }

        }

    }

     

    จะพบได้ว่ามี 2 methods ที่ได้ override จาก base class คือ OnStart() และ OnStop() ซึ่งจะเป็น Event default ที่ถูกสร้างมาให้ และจะเกิด Event เหล่านี้ขึ้นตามสถานะการทำงานของ service นั้น ๆ ซึ่งผมได้สรุปมาเท่าที่เราจะใช้กันบ่อย ๆ ได้ดังนี้

     

    Event Name

     Description

    OnStart()

    จะเกิด event นี้เมื่อ service ถูกสั่งให้ start ซึ่งเรานิยมใช้ในการสั่งการให้ตัวTrigger (Timer, FileSystemWatcher, Thread) ที่เราใช้ใน Service นั้นทำงาน

    OnStop()

    จะเกิด event นี้เมื่อ service ถูกสั่งให้สั่งให้ (stop) นิยมใช้ในการ save states ต่าง ๆ ของ Service ที่จำเป็นไว้

    OnPause()

    จะเกิด event นี้เมื่อ service ถูกสั่งให้สั่งให้ค้างการทำงาน (pause)

    OnContinue()

    จะเกิด event นี้เมื่อ service ถูกสั่งให้สั่งให้ทำงานต่อ (resume)

    OnShutdown()

    จะเกิด event นี้เมื่อ windows ถูกสั่งให้ restart / shutdown

     

    Implement Functionality

                ต่อไปจะทำการใช้ Windows Service ในการทำ Backup file จาก Drop File Folder ย้ายไปเก็บยัง Backup File Folder ซึ่งจะมีการเพิ่มการทำงานดังนี้

     

    1. คอยตรวจสอบหากมีการ create/ paste file ลงใน drop file folder ให้ raise event
    2. ให้เก็บ full path ของ file ที่ notify เก็บไว้ใน collection ก่อนนำไป process ต่อไป
    3. ใช้ Timer ในการ process file list ใน collection ในแต่ละรอบเวลา โดยกำหนดจำนวน file ในแต่ละรอบได้ เพื่อเพิ่มประสิทธิภาพในการทำงานให้เสถียรขึ้น
    4. ให้ทำการ move file โดยอ่าน path จาก collection ไปยัง Backup Folder
    5. หากทำการ move file สำเร็จให้ลบ file path นั้นใน collection ทิ้งไป
    6. เก็บ service status(start, stop) , Application Error ไว้ใน Event Log

     

    เราก็จะสร้าง file ขึ้นมา 3 files ดังนี้ 

    1. เพิ่ม App.config เข้ามาเพื่อใช้ในการตั้งค่าการทำงานต่าง ๆ ของ windows service โดยทำการคลิ๊กขวาที่ project -> Add -> New Item… -> Application Configuration File จากนั้น เพิ่มค่า config เข้าไปตามตารางข้างล่างนี้

    <?xml version="1.0" encoding="utf-8" ?>

    <configuration>

          <appSettings>

                <!– ServiceMode you can config for Copy=0, Remove=1, Delete=2 –>

                <add key="ServiceMode" value="0"/>

                <!– FileFilter = file mask such as *.txt, *.jpg, *.* –>

                <add key="FileFilter" value="*.*"/>

                <!– FileMonitoringPath = –>

                <add key="FileMonitoringPath" value="c:\test\master"/>

                <!– ServiceTimer = Time for service process job in Second –>

                <add key="ServiceTimer" value="3"/>

                <!– TargetFolder use to copy or move file–>

                <add key="TargetFolder" value="c:\test\backup"/>

                <!– FileNoProcess the number of file to process each timer round –>

                <add key="FileNoProcess" value="3"/>

               

          </appSettings>

    </configuration>

     

    1. จากนั้นเพิ่ม class เข้ามาใน project โดยตั้งชื่อว่า FileBackupService.cs และเพิ่ม code ใน table ข้างล่างนี้ ซึ่งจะเป็น Class ส่วนที่เกี่ยวข้องกับการ process file ทั้ง copy, move, delete และจะมีการบันทึกค่าลงใน EventLog ซึ่งClassนี้จะถูกเรียกใช้ใน windows service

    using System;

    using System.Collections.Generic;

    using System.Text;

    using System.Configuration;

    using System.Diagnostics;

    using System.Collections;

     

    using System.IO;

    using System.Timers;

     

    namespace myService

    {

        internal class FileBackupService

        {

     

            internal EventLog LogService = new EventLog();

            internal Timer ServiceTimer = new Timer();

            internal ArrayList queueFile = new ArrayList();

            internal FileSystemWatcher fsw = new FileSystemWatcher();

     

            string serviceMode = "";

            string targetFolder = "";

     

            int fileNoProcess = 0;

     

            internal void InitConfig()

            {

     

                serviceMode = ConfigurationManager.AppSettings["ServiceMode"];

                targetFolder = ConfigurationManager.AppSettings["TargetFolder"];

                fileNoProcess = int.Parse(ConfigurationManager.AppSettings["FileNoProcess"]);

     

                // file monitor config

                fsw.Filter = ConfigurationManager.AppSettings["FileFilter"];

                fsw.Path = ConfigurationManager.AppSettings["FileMonitoringPath"];

     

                fsw.EnableRaisingEvents = true;

                fsw.NotifyFilter = ((System.IO.NotifyFilters)

                    ((((System.IO.NotifyFilters.FileName

                        | System.IO.NotifyFilters.DirectoryName)

                        | System.IO.NotifyFilters.Size)

                        | System.IO.NotifyFilters.LastWrite)));

     

                fsw.Created += new FileSystemEventHandler(fsw_Created);

     

     

                // timer config

                ServiceTimer.Interval = double.Parse(ConfigurationManager.AppSettings["ServiceTimer"]) * 1000;

                ServiceTimer.Enabled = true;

                ServiceTimer.Elapsed += new ElapsedEventHandler(ServiceTimer_Elapsed);

     

                // event log config

                LogService.Log = "Application";

                LogService.Source = "FileBackupService";

     

            }

     

            private void ServiceTimer_Elapsed(object sender, ElapsedEventArgs e)

            {

                ServiceTimer.Stop();

                if (queueFile.Count > 0)

                    for (int i = 0; i < fileNoProcess; i++)

                    {

                        if (queueFile.Count > 0)

                        {

                            ProcessFileList(queueFile[0].ToString());

                            queueFile.RemoveAt(0);

                        }

                        else

                        {

                            break;

                        }

                    }

                ServiceTimer.Start();

            }

     

            private void ProcessFileList(string filepath)

            {

                switch (serviceMode)

                {

                    case "0":

                        FileProcess(filepath, targetFolder, FileServiceAction.COPY);

                        break;

                    case "1":

                        FileProcess(filepath, targetFolder, FileServiceAction.MOVE);

                        break;

                    case "2":

                        FileProcess(filepath, targetFolder, FileServiceAction.DELETE);

                        break;

                }

            }

     

            private bool FileProcess(string pathFileTarget, string pathFileCopy, FileServiceAction action)

            {

     

                string filename = Path.GetFileName(pathFileTarget);

                string taskMessage = "";

     

                try

                {

                    switch (action)

                    {

                        case FileServiceAction.COPY:

                            taskMessage = "Copy ";

                            File.Copy(pathFileTarget, pathFileCopy + filename);

                            break;

                        case FileServiceAction.MOVE:

                            taskMessage = "Move ";

                            File.Move(pathFileTarget, pathFileCopy + filename);

                            break;

                        case FileServiceAction.DELETE:

                            taskMessage = "Delete ";

                            File.Delete(pathFileTarget);

                            break;

                    }

                    WriteEventLog(taskMessage + pathFileTarget + " : Successful. ", EventLogEntryType.Information);

                    return true;

                }

                catch (Exception ex)

                {

                    WriteEventLog(taskMessage + pathFileTarget + " : Error Message = " + ex.Message, EventLogEntryType.Error);

                    return false;

                }

            }

     

            private void fsw_Created(object sender, FileSystemEventArgs e)

            {

                queueFile.Add(e.FullPath);

            }

     

            internal void WriteEventLog(string message, EventLogEntryType elet)

            {

                if (LogService != null)

                {

                    LogService.WriteEntry(message, elet);

                }

            }

        }

     

        internal enum FileServiceAction

        {

            COPY,MOVE,DELETE

        }

    }

     

     

    1. กลับมาที่ Service1.cs file เมื่อเปิดขึ้นมาแล้วใส่ code ลงไปตาม table ข้างล่างนี้ เพื่อทำการเรียกใช้ FileBackupService Class ที่ได้สร้างไว้ในขั้นตอนที่แล้ว

    using System;

    using System.Collections.Generic;

    using System.ComponentModel;

    using System.Data;

    using System.Diagnostics;

    using System.ServiceProcess;

    using System.Text;

     

    using System.Timers;

    using System.Configuration;

    using System.IO;

    using System.Collections;

     

    namespace myService

    {

        public partial class Service1 : ServiceBase

        {

            private FileBackupService fbs;

     

            public Service1()

            {

                InitializeComponent();

                fbs = new FileBackupService();

            }

     

            protected override void OnStart(string[] args)

            {

                fbs.InitConfig();

                fbs.ServiceTimer.Start();

            }

     

            protected override void OnStop()

            {

                fbs.ServiceTimer.Stop();

                fbs = null;

            }

     

        }

    }

     

     

    1. ต่อมาทำการเพิ่มตัว Installer Class ให้ Service ที่เราได้สร้างขึ้นมาดังนี้
      1. ไปที่ Solution Explorer เลือกที่ Service1.cs คลิ๊กขวาเลือก Open
      2. จากนั้นเปิดมาก็จะได้จอ เทา ๆ ให้คลี๊กขวาที่พื้นที่ของจอเทา ๆ แล้วเลือก Add Installer
      3. จากนั้นจะได้หน้าจอที่มี 2 components ปรากฎอยู่ตามรูปด้านล่างนี้

     

      1. จากนั้นให้เลือกที่ serviceProcessInstaller1 แล้ว config properties ตาม รูปข้างล่างนี้

      1. และเลือก serviceInstaller1 แล้ว config properties ตาม รูปข้างล่างนี้

      

    1. ทำการ build project และ ให้ไปใน folder bin ของ project ที่ build ได้มา
      1. จากนั้นทำการสร้าง bat file ด้วยการ new file เป็น text file แล้วเปิดขึ้นมาด้วย notepad หรืออะไรก็แล้วแต่ copy คำสั่งในตารางข้างล่างนี้ใส่ไป และ save as เป็นชื่อ InstallService.bat

    %WINDIR%\Microsoft.NET\Framework\v2.0.50727\RegSvcs.exe myService.exe

     

    NET START "FileBackupService"

     

    โดยคำสั่งด้านบนนี้จะเป็นการเรียก utility ที่ชื่อ RegSvcs.exe มาทำการ install windows service ที่เราได้สร้างไว้ และเรียกคำสั่ง net start เพื่อให้ทำการ start service ของเรา

      1. ต่อมาสร้าง bat file ขึ้นมาอีกตัว สำหรับการ Uninstall Service ที่เรา install ไปโดยทำตามขั้นตอนของ a. ด้านบน แล้ว copy คำสั่งในตางรางด้านล่างนี้ไปวาง จากนั้น save as เป็น UninstallService.bat

    NET STOP "FileBackupService"

     

    %WINDIR%\Microsoft.NET\Framework\v2.0.50727\RegSvcs.exe /u myService.exe

     

     

     

     

    โดยคำสั่ง NET STOP ด้านบนเป็นการหยุด Windows Service ของที่ยัง Start อยู่ก่อน

    จากนั้นก็ใช้ Regsvcs.exe /u เพื่อทำการ uninstall

     

     

    1. ทำการทดลองใช้งาน Windows Service ที่เราสร้าง
      1. สร้าง folder ไว้ใน C:\Test\master ไว้สำหรับเป็น folder ที่จะมีมี file มาdrop เพื่อให้ตัว Service ทำ monitor file
      2. สร้าง folder ไว้ใน C:\Test\backup ไว้สำหรับให้ service ทำการ copy file จาก master folder มา drop ลงที่นี่เพื่อทำ backup
      3. จากนั้นเข้าไปใน Bin folder ของ project แล้วทำการ run InstallService.bat  file เพื่อเริ่มทำการ Install Windows Service ที่เราสร้างขึ้น
      4. จากนั้นไป หา file อะไรก็ได้ทำการ copy แล้วนำมา paste ลงใน C:\Test\master เพื่อทดสอบ
      5. หลังจาก paste เสร็จแล้วให้เข้าไปดูที่ C:\Test\backup จะเห็นว่ามี file ที่เรา paste ไว้ใน C:\Test\master เกิดขึ้นที่ C:\Test\backup นี้ด้วย
      6. หากมีอะไรผิดปกติให้เข้าไปเปิด EventLog ที่ Administrative Tools -> Event Viewers เลือกดูใน Application Group จะเห็นว่า Service ที่เราสร้างไว้มีการบันทึก Log เก็บไว้ตลอดตามรูปข้างล่างนี้

     

    Download Source code here!

    http://greatfriends.biz/files/WinService.rar

     
     

    to be continued…

    การ Release COM Object ออกจาก Memory ของระบบ

    ปกติแล้วบางครั้งอาจจะหลีกเลี่ยงไม่ได้ที่จะต้องใช้ COM object ในโปรแกรมของเรา

    เมื่อเรา add COM  เข้ามาในโปรเจ็ค .NET จะทำตัว Wrapper ที่เรียกเป็นทางการว่า Runtime Callable Wrapper (RCW) ห่อหุ่มและให้เราเรียกใช้ COM Object  นั้นได้อย่างง่ายดาย
     
    เพียงแต่ว่าหลังจากการสร้างมันขึ้นมาแล้วนั้น หลังจากใช้งานเสร็จ
    จะทำลายทิ้งและคืนหน่วยความจำให้ระบบนั้นช่างแสนยากเย็น และเป็นอันตรายค่อนข้างมาก ในกรณีที่เป็นระบบใหญ่ ๆ
    เพราะตัวที่จะถูก GC เก็บกวาดนั้นมีเพียงแค่ RCW ซึ่งหากคุณลืมที่จะลด reference count ของ COM Object ให้เหลือค่า 0 นั้น คุณก็จะไม่สามารถเข้าถึงและควบคุมการทำงานของมันได้อีกเลย เพราะ RCW ได้ถูกทำลายทิ้งไปก่อนแล้ว และ COM Object นั้นจะคงอยู่ใน Startup Time ของ Server นั้นไปตลอด
     
    ใน .NET 1.0 , 1.1 นั้นเรามี Method ที่จะช่วย repeat ค่าให้ลดลงจน เป็น 0 นั่นคือ

    System.Runtime.InteropServices.Marshal.ReleaseComObject()

    แต่ method ตัวนี้เราต้อง repeat ค่าใน Loop จนค่าเป็น 0 ดังนี้

     

    ตัวอย่าง

    If Not Cipher Is Nothing Then

        Do

            If System.Runtime.InteropServices.Marshal.ReleaseComObject(Cipher) = 0 Then

                Exit Do

            End If

        Loop

    End If

     

     

    แต่ใน .NET 2.0 นั้นเราสามารถสั่งเพียงครั้งเดียวด้วย

    System.Runtime.InteropServices.Marshal.FinalReleaseComObject()

    ง่าย ๆ ดังนี้

     
    ตัวอย่าง
    System.Runtime.InteropServices.Marshal.FinalReleaseComObject(Cipher)
     
     
     
    เมื่อค่า Reference Count เป็น 0 COM object ก็จะถูกทำลายและคืนหน่วยความจำคืนแก่ระบบ  
    จบละ  

    สร้าง Ping tool แบบง่าย ๆ กับ .NET 2.0

    ใน .Net 2.0 นี้มี Class ต่าง ๆ เพิ่มเข้ามามากมายเลย ซึ่งช่วยให้เราสามารถที่จะเรียกมาใช้งานได้อย่างง่าย ๆ

    หลายครั้งที่เราจะเขียนโปรแกรมเพื่อที่จะตรวจสอบ DNS, Host , IP ด้วยการ Ping นั้นลำบากพอสมควร
    เราอาจจะเข้าไปใช้ ping ใน Command ของ Windows หรือ อาจจะใช้ Sockets สร้าง Ping tool ขึ้นมา
    ซึ่งต้องเขียนเพิ่มเติมอีกพอสมควร
     
    มาวันนี้ในกลุ่ม  Namespace System.Net.NetworkInformation มี Ping Class สำเร็จรูปมาให้เราได้ใช้งาน
    เพียงแค่ code ไม่กี่บรรทัดเราก็จะได้ Network Utility Tool ในการ Ping กันแล้วครับ
     
    จากรูปข้างล่างนี้เราสามารถที่จะเลือกใส่ทั้ง IP Address , HostName, DNS เพื่อทำการ Ping ได้ครับ
     
     
    มาดู Code
    ที่ผมทำ code ตัวใหญ่ ๆ คือส่วนที่เกี่ยวข้องกับการ Ping ครับ

    Imports

    System

    Imports

    System.Threading

    Imports

    System.Net.NetworkInformation

     

    delegate สำหรับการ update ข้อมูลใน Textbox จาก WorkerThread

    Public Delegate Sub SetText(ByVal txt As String)

     

    Public

    Class Form1

    ‘จำนวนรอบของการ Ping

    Private round As Integer

     

    ‘Update ผลการ Ping ใน Textbox1

    Public Sub SetTextBox(ByVal txt As String)

        TextBox1.AppendText(txt)

    End Sub

     

    ‘ป้องกันการ access control ข้าม Thread จาก WorkTThread ไป MainThread

    Public Sub CheckUICrossThread(ByVal txt As String)

        If TextBox1.InvokeRequired Then

            Dim d As SetText = New SetText(AddressOf SetTextBox)

            Me.BeginInvoke(d, txt)

        Else

            TextBox1.AppendText(txt)

        End If

    End Sub

     

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        If TextBox2.Text.Length < 1 Then

            Return

        End If

        TextBox1.Clear()

        ‘ใช้ ThreadPool โดย การส่ง method ที่ต้องการให้ทำงานพร้อม parameter ที่เป็น host / ip / dns

        ThreadPool.QueueUserWorkItem(

    AddressOf PingHost, TextBox2.Text)

    End Sub

     

    Public Sub PingHost(ByVal state As Object)

        Try

            ‘สร้าง instance ของ ping class

            Dim p As New Ping

            ‘ตรวจสอบการข้ามThread ส่ง Header detail

            CheckUICrossThread(

    "Starting Ping IP: " & state & vbCrLf)

            For i As Integer = 0 To round

                Thread.Sleep(300)

                Dim rep As PingReply = p.Send(state, 100)

                If rep.Status = IPStatus.Success Then

                    ‘ตรวจสอบการข้ามThread ส่ง ผลการ Ping

                    CheckUICrossThread(

    " Success – IP Address: " & state & " Time:" & rep.RoundtripTime.ToString & "ms " & vbCrLf)

                Else

                    ‘ตรวจสอบการข้ามThread ส่ง Status อื่น ๆ หากไม่สำเร็จ

                    CheckUICrossThread(rep.Status & vbCrLf)

                End If

            Next

        Catch ex As Exception

            ‘ตรวจสอบการข้ามThread ส่ง Error Msg

            CheckUICrossThread(ex.Message)

        End Try

    End Sub

    Private Sub ComboBox1_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ComboBox1.SelectedIndexChanged

        round =

    CInt(ComboBox1.SelectedItem)

    End Sub

    End Class

     

     
    Download Source Code ได้ที่นี่

    – มาติด Turbo ให้ App ตอน Start App ด้วย NGen Tool (จะหายสงสัยทำไม .NET ไม่เปิดปุ๊บติดปั๊บแบบ VB6)

    มีเสียงบ่นกันมากมายเหลือเกินนะครับ ทำไม ๆ ๆ เปิดโปรแกรมแล้วมันช้างี้ หน่วง ๆ มากมาย ไม่เห็นเหมือน VB6 เลย
    แบบเรียกปุ๊บโผล่ปั๊บ เปิดปั๊บติดปุ๊บ อะไรทำนองนี้ จะได้หายสงสัยกันซะที
    มาเข้าเรื่องกันเลยดีกว่า
     
    พระเอกของเรางานนี้คือ NGen.exe Tool
    เจ้าตัว NGen tool นั้นได้มีติดมากับ .Net Fx ทุก version ซึ่ง Tool ตัวนี้มีมาตั้งแต่สมัย .Net fx 1.0  แล้ว
    และ MS ก็ใช้มันทำ Core dll ต่าง ๆ ของ .Net Fx ในตอนติดตั้งด้วยซึ่งจะ build ลง GAC นั้นเอง
     
    เป็นที่ทราบกันดีว่า JIT(Just-In-Time) ถูกใช้เป็น Compiler เพื่อทำการคอมไพล์ MSIL Code (Exe, DLL) ของโปรแกรมที่เราสร้างขึ้นจาก .NET
    และจะแปลง MSIL Code เหล่านี้ไปเป็น Native Code ของเครื่อง ก่อนที่จะทำการ execute code นั้น ๆ
    ตัว code ส่วนนี้จะถูกสร้างขึ้นชั่วคราวทุกครั้ง ที่มี Process การทำงานเรียกใช้ในครั้งแรก
    และจะถูกทำลายทิ้งเมื่อจบโปรเซส(ใน .net fx 2.0 จะมีการเก็บ code นี้ไว้ใน temp ไว้เพื่อให้ใช้งานทำให้เปิดโปรแกรมครั้งที่ 2 ทำได้รวดเร็วขึ้น แต่จะถูกลบเมื่อมีการ reboot เครื่องใหม่)
     
    ตัว NGen นั้น พูดได้ว่าเป็นการข้ามขั้นตอนการ Compile exe,dll ต่าง ๆ ที่เราเรียกใช้
    จาก JIT คือเราจะไปทำ Image ไว้ให้ CLR เรียกใช้โดยตรงได้เลย จากใน GAC
     
    อธิบายการทำงานของ NGen
    เราจะเรียกว่า เป็นการทำ Precompile หรือ pre-JITing
    คือการสร้าง native code ของ MSIL ไว้บนเครื่องที่มีการใช้งานโปรแกรม
    และผลทีได้จาก pre-JITing จะเป็น native code ซึ่งใช้แทนการ compile code ของ JIT ทุกครั้งที่มีการรันโปรแกรม
    ตัว Native code ที่ได้จาก Ngen จะเป็น Win32 PE code จริง ๆ ซึ่งเช่นเดียวกับ VB6 (เปิดปุ๊บติดปั๊บ)
    แต่เราจะเรียกกันว่า native images หรือ ngen images
    ซึ่งจะถูกเก็บลงไปเป็น native image cache หรือ Ngen cache (GAC)
    ขั้นตอนการทำงานขณะสร้าง Native Images ของ NGen
    1. ต่อมาจะเรียก JIT มาทำการ Compile MSIL Code เพื่อสร้าง native code จาก exe หรือ dll
    2. ซึ่งเมือ่ได้ทำการสร้าง native image แล้วก็เก็บลง native image cache (GAC)
     
    ขั้นตอนการทำงานเมื่อเราเรียกใช้ Application ที่ได้สร้าง Native Images ไว้
    เมื่อมีการเปิด App หรือจะ load DLL ตัว CLR จะทำการค้นหาที่ตั้งของ exe/dll ที่ได้ทำ Image ไว้แล้วไป query  ใน Native Image Cache และจะทำการ Load Image จาก GAC มาใช้งาน
     
     
    มาดูกันใช้ยังงัย NGen ใน .NET FX 2.0

    เราสามารถใช้ NGen ใน command line (DOS) ครับโดยตัว NGen.exe จะอยู่ที่ C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727

    โดยที่เราสามารถจะใช้กับ Exe, DLL ก็ได้ครับ เพราะเป็น MSIL เหมือนกัน(ยังไม่ขอพูดถึงคำสั่ง update)
     
    คำสั่งที่ใช้ในการ Install ก็คือ
    >NGen install fileName.exe
    คำสั่งที่จะใช้ Uninstall ก็คือ
    >NGen uninstall fileName.exe
     
    หลังจากได้ ติดตั้งดูแล้วก็ใช้คำสั่ง Display เพื่อดูว่า Native Images ที่เราสร้างนั้นได้ติดตั้งลงใน GAC จริงหรือยังด้วยคำสั่ง
    >NGen display fileName.exe
    หรืออยากเห็นอะไรแบบเป็นรูปร่างมากกว่า DOS ก็ไปดูได้ที่
    Administrative Tools -> Microsoft .NET Framework 2.0 Configuration ->
    แล้วก็ดูใน List หาชื่อ exe / dll ที่ใช้ จะพบว่า Native Image ของเราลง GAC ไปเรียบร้อยแล้ว
     
    แล้วต่างกันยังงัย กับเอา DLL ลง GAC เองเลย
    DLL ที่เราเอาลง GAC เองนั้น เพียงแค่ทำการ Share กันใช้ระหว่าง appต่าง ๆ เท่านั้น และยังคงต้องผ่านการ Compile จาก JIT เพื่อสร้าง Native Code อยู่ครับ และมี Cache Type เป็น GAC สามารถ remove ได้ทันที จาก Microsoft .NET Framework 2.0 Configuration หรือ GACUtil
     
    แต่ Native Image ที่เราได้สร้างขึ้นนั้นนอกจะจะสามารถ share กันใช้งานได้แล้ว อีกทั้งยังไม่ต้องผ่านขั้นตอนการ Compile จาก JIT และมี Cache Type เป็น Zap(ทำไมใช้ชื่อ แซ่บ ก็ไม่รู้สิครับ)  และไม่สามารถ remove ออกได้จาก tools ของ GAC แต่ต้อง uninstall โดยใช้ NGen เท่านั้น
     
    เท่านี้เราก็สามารถ เปิด App แบบทันใจใช้งาน เหมือน VB6 แล้ว (ไม่น้อยหน้าเรื่องความเร็วกันแล้วนะ หุหุ)
     
     
    ปล. ผิดพลาดประการใด ขออภัยไว้ ณ ที่นี้ด้วยครับ ผมก็เพิ่งจะใช้เหมือนกัน หุหุ

    เปิด BLOG แรกครับ

    ที่เปิด BLOG ไว้ก็จะพยายาม ทำบทความที่เกี่ยว Tools ต่าง ๆ เทคนิคเล็กน้อย และแนะนำผลิตภัณฑ์ใหม่ ๆ
    พร้อมทั้งจะพยายามรวบรวมแหล่งความรู้ ที่เกี่ยวข้องกับ งานด้าน IT มาฝากกันครับ
     
    เด๋วขอไปคิดก่อนว่าเรื่องแรกจะประเดิมด้วยเรื่องอะไรดี ..
    สวัสดีครับ
     
    Follow

    Get every new post delivered to your Inbox.