<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://coredeveloper.net/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>nantcom</title><link>http://coredeveloper.net/blogs/nantcom/default.aspx</link><description /><dc:language>en</dc:language><generator>CommunityServer 2007.1 (Build: 20917.1142)</generator><item><title>RFID with C#</title><link>http://coredeveloper.net/blogs/nantcom/archive/2009/08/25/rfid-with-c.aspx</link><pubDate>Tue, 25 Aug 2009 05:03:00 GMT</pubDate><guid isPermaLink="false">86cc649a-bf28-4d34-a9ef-d75c61f34293:751</guid><dc:creator>นันคอม</dc:creator><slash:comments>6</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://coredeveloper.net/blogs/nantcom/rsscomments.aspx?PostID=751</wfw:commentRss><comments>http://coredeveloper.net/blogs/nantcom/archive/2009/08/25/rfid-with-c.aspx#comments</comments><description>&lt;p&gt;พอดีว่า ผมได้มีโอกาสมาสัมนาเรื่อง RFID ที่ทาง SIPA จัดขึ้น และวิทยาการได้กล่าวถึงเรื่องการติดต่อกับตัว Reader ผ่านทาง Serial Port (ที่ Emulate ผ่าน USB อีกที) วิทยากรเขาแนะนำการเขียนด้วย VB6 และใช้ MSCOMM32 ระหว่างที่ท่านนั่งอธิบายอยู่ ผมเลยใช้เวลานี้ เขียนคลาส C# ที่จะนำมาใช้ในการติดต่อกับตัว RFID Reader ครับ&lt;/p&gt;  &lt;p&gt;สำหรับตัว RFID นั้น ใช้ชิพ Silicon Labs CP210x ในการแปลง USB เป็น Serial ครับ ซึ่งปรากฏว่า เป็นชิพเดียวกันกับ GPS Receiver หลายยี่ห้อ ผมเลยสามารถหา Driver สำหรับ x64 ได้ไม่ยากเย็นนักครับ &lt;a href="http://coresharp.net/files/folders/general/entry1474.aspx"&gt;Download ของทั้ง 32-64 bit&lt;/a&gt; ได้จากหน้าไฟล์ครับ&lt;/p&gt;  &lt;p&gt;&lt;img src="http://coredeveloper.net/blogs/nantcom/image_3D00A2D1.png" style="border-width:0px;display:inline;" title="image" alt="image" width="323" border="0" height="71" /&gt; &lt;/p&gt;  &lt;p&gt;สำหรับ Protocol การติดต่อกับตัว RFID ผมลองนั่งหาดู ปรากฏว่าไปเจอเข้าจนได้ ดาวน์โหลดได้&lt;a href="http://coresharp.net/files/folders/general/entry1473.aspx"&gt;จากที่หน้าไฟล์&lt;/a&gt;เลยครับ &lt;/p&gt;  &lt;p&gt;ส่วนโค๊ดในการติดต่อ ผมทดลองทำง่ายๆ 2 คำสั่ง คือ คำสั่ง Beep และ LED ครับ โดยผมมีฟังก์ชั่นสำหรับส่งให้พร้อมแล้ว น่าจะสามารถเติมคำสั่งอื่นๆ ได้ไม่ยากเย็นนัก &lt;img src="http://coredeveloper.net/emoticons/emotion-1.gif" alt="Smile" /&gt; ขอให้สนุกครับ&lt;/p&gt;  &lt;p&gt;&lt;textarea style="width:550px;height:590px;" class="c-sharp" name="code"&gt;/*
 Copyright 2009 CoreSharp.NET
 * =========================
 * THIS LIBRARY IS PROVIDED AS-IS WITHOUT ANY WARRANTY
 * 
 * You may use this library for any legimate purposes,
 * only requirement is that this license block must 
 * remains in the source file.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.IO.Ports;
using System.Threading;

namespace RFIDTest
{
    public delegate void OperationCompelted&amp;lt;T&amp;gt;( Exception ex, T result );

    public class MifareRfid
    {
        public enum Command : ushort
        {
            Light = 0x0701,
            Beep = 0x0601
        }

        ushort startCommand = (ushort)0xAABB; // 0xAABB;

        /// &amp;lt;summary&amp;gt;
        /// the Serial port object
        /// &amp;lt;/summary&amp;gt;
        private SerialPort _Port;

        /// &amp;lt;summary&amp;gt;
        /// Connects to Reader with the specified port.
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name=&amp;quot;port&amp;quot;&amp;gt;The port.&amp;lt;/param&amp;gt;
        /// &amp;lt;param name=&amp;quot;baudRate&amp;quot;&amp;gt;The baud rate.&amp;lt;/param&amp;gt;
        /// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;
        public bool Connect( string port, int baudRate )
        {
            _Port = new SerialPort();
            _Port.BaudRate = baudRate;
            _Port.PortName = port;

            _Port.Open();
            _Port.DataReceived += new SerialDataReceivedEventHandler(_Port_DataReceived);
            return _Port.IsOpen;
        }

        /// &amp;lt;summary&amp;gt;
        /// Disconnects from the reader.
        /// &amp;lt;/summary&amp;gt;
        public void Disconnect()
        {
            if (_Port.IsOpen &amp;amp;&amp;amp; _Port != null)
                _Port.Close();
        }

        private ManualResetEvent _WaitForResponse = new ManualResetEvent(false);

        /// &amp;lt;summary&amp;gt;
        /// Sends the Raw Command to Reader
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name=&amp;quot;cmd&amp;quot;&amp;gt;The CMD.&amp;lt;/param&amp;gt;
        /// &amp;lt;param name=&amp;quot;address&amp;quot;&amp;gt;The address.&amp;lt;/param&amp;gt;
        /// &amp;lt;param name=&amp;quot;parameters&amp;quot;&amp;gt;The parameters.&amp;lt;/param&amp;gt;
        public byte[] SendCommand( Command cmd, ushort address, byte[] parameters )
        {
            // length w/o parameter is 5 bytes
            ushort totalLength = (ushort)(5 + parameters.Length);
            byte[] buffer = new byte[32];

            MemoryStream ms = new MemoryStream( buffer );
            BinaryWriter bw = new BinaryWriter(ms);

            bw.Write(BitConverter.GetBytes(this.startCommand).Reverse().ToArray());
            bw.Write(BitConverter.GetBytes(totalLength));
            bw.Write(BitConverter.GetBytes(address).Reverse().ToArray());
            bw.Write(BitConverter.GetBytes((ushort)cmd).Reverse().ToArray());
            bw.Write(parameters);

            byte xor = 0;
            for (int i = 4; i &amp;lt; 4 + totalLength; i++)
            {
                xor ^= buffer&lt;img src="http://coredeveloper.net/emoticons/emotion-55.gif" alt="Idea" /&gt;;
            }
            bw.Write(xor);

            _WaitForResponse.Reset();
            _Port.Write(buffer, 0, buffer.Length);

            if (_WaitForResponse.WaitOne(200))
            {
                throw new Exception(&amp;quot;Reader Failed to reply within 200ms&amp;quot;);
            }
            else
            {
                buffer = new byte[_Port.BytesToRead];
                _Port.Read(buffer, 0, buffer.Length);

                return buffer;
            }
        }

        /// &amp;lt;summary&amp;gt;
        /// Sends the RAW command asynchronously
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name=&amp;quot;cmd&amp;quot;&amp;gt;The CMD.&amp;lt;/param&amp;gt;
        /// &amp;lt;param name=&amp;quot;address&amp;quot;&amp;gt;The address.&amp;lt;/param&amp;gt;
        /// &amp;lt;param name=&amp;quot;parameters&amp;quot;&amp;gt;The parameters.&amp;lt;/param&amp;gt;
        /// &amp;lt;param name=&amp;quot;response&amp;quot;&amp;gt;The response.&amp;lt;/param&amp;gt;
        public void SendCommandAsync(Command cmd, ushort address, byte[] parameters, OperationCompelted&amp;lt;byte[]&amp;gt; response)
        {
            if (response == null)
                throw new ArgumentNullException(&amp;quot;response&amp;quot;);

            Action a = delegate
            {
                try
                {
                    var data = this.SendCommand(cmd, address, parameters);
                    response(null, data);
                }
                catch (Exception ex)
                {
                    response(ex, null);
                }
            };
            a.BeginInvoke(null, null);
        }

        private void _Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            if ( e.EventType == SerialData.Eof )
                _WaitForResponse.Set();
        }
    }
}


&lt;/textarea&gt;&lt;/p&gt;  &lt;h2&gt;จุดน่าสนใจ&lt;/h2&gt;  &lt;ul&gt;   &lt;li&gt;เหมือนว่า การส่ง จะต้องส่งเป็นแบบ Big-Endian คือ เอาไบต์น้อยขึ้นก่อน เช่น 0x0106 เวลาส่ง จะต้องส่ง 06 ไปก่อน 01 ผมก็เลยใช้วิธีเอา BitConverter แปลงค่าเป็น Byte ก่อน แล้วสั่ง Reverse แล้ว ToArray เอาครับ &lt;/li&gt;    &lt;li&gt;เนื่องจากความยาวไม่มีทางเกิน 32 (แค่ไบต์เดียว) เลยไม่ต้องกลับ Little/Big Endian&amp;nbsp; ครับ &lt;/li&gt; &lt;/ul&gt;&lt;img src="http://coredeveloper.net/aggbug.aspx?PostID=751" width="1" height="1"&gt;</description><category domain="http://coredeveloper.net/blogs/nantcom/archive/tags/rfid/default.aspx">rfid</category><category domain="http://coredeveloper.net/blogs/nantcom/archive/tags/c_2300_/default.aspx">c#</category></item><item><title>เปลี่ยน Font Windows 7 ให้คมขึ้นด้วย Leelawadee</title><link>http://coredeveloper.net/blogs/nantcom/archive/2009/08/12/font-windows-7-leelawadee.aspx</link><pubDate>Wed, 12 Aug 2009 07:01:43 GMT</pubDate><guid isPermaLink="false">86cc649a-bf28-4d34-a9ef-d75c61f34293:747</guid><dc:creator>นันคอม</dc:creator><slash:comments>3</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://coredeveloper.net/blogs/nantcom/rsscomments.aspx?PostID=747</wfw:commentRss><comments>http://coredeveloper.net/blogs/nantcom/archive/2009/08/12/font-windows-7-leelawadee.aspx#comments</comments><description>&lt;p&gt;จากที่ผมได้เขียนแนะนำเกี่ยวกับ&lt;a href="http://coresharp.net/blogs/article/archive/2008/08/31/font-ui.aspx"&gt;การเปลี่ยน Font ใน Windows Vista&lt;/a&gt; ไปแล้ว ด้วยเทคนิคเดียวกัน ก็ยังสามารถนำมาใช้ได้ใน Windows 7 ครับ โดยคราวนี้ ผมได้ทำเป็นไฟล์ Registry เอาไว้ให้เลย เพื่อความสะดวกในการใช้งาน (แน่นอนว่า ถ้าเครื่องมีปัญหา ผมคงจะรับผิดชอบอะไรให้ไม่ได้นะ อิอิ)&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coresharp.net/files/folders/general/entry1397.aspx"&gt;สามารถดาวน์โหลดได้จากหน้าไฟล์ครับ&lt;/a&gt; ถ้าสงสัยว่า เปลี่ยนแล้วจะได้ผลยังไง ลองเปรียบเทียบ Screenshot ที่ผมเตรียมไว้ให้ กับเครื่องคุณได้ครับ &lt;img src="http://coredeveloper.net/emoticons/emotion-1.gif" alt="Smile" /&gt;&amp;#160; สังเกตว่าตัวอักษรจะบางลง และข้อความภาษาไทยจะคมขึ้นครับ ยิ่งถ้าใช้ร่วมกับการปรับตัวอักษรจาก 8 เป็น 10 ก็จะอ่านสบายตาขึ้นมากทีเดียว&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;margin:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" align="left" src="http://coredeveloper.net/blogs/nantcom/image_76299F3B.png" width="244" height="337" /&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_7B2BDCEA.png" width="261" height="283" /&gt; &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_6157A6BB.png" width="550" height="526" /&gt; &lt;/p&gt;  &lt;p&gt;สำหรับท่านที่ห่ไม่เจอว่า หน้าต่างปรับ Font เปลี่ยนสี มันหายไปไหน มันอยู่ในหน้าจอ Personalize –&amp;gt; Window Color และ Advanced appearance settings.. ครับ&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_79D71E3E.png" width="550" height="480" /&gt; &lt;/p&gt;  &lt;p&gt;นอกจากนี้ Windows 7 ยังมีเครื่องมือใหม่ ชื่อว่า ClearType Text Tuner อยู่ใน Control Panel –&amp;gt; Display เพื่อปรับความคมชัดของตัวอักษรด้วยนะครับ ถ้าต้องการให้อ่านง่ายยิ่งขึ้น ก็สามารถปรับได้จากเครื่องมือนี้&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_540DCB50.png" width="550" height="436" /&gt; &lt;/p&gt;  &lt;p&gt;อ้อ แล้วก็ยังมีเครื่องมือปรับสีจอ LCD ด้วยนะครับ อยู่ในเมนู Calibrate Color&lt;/p&gt;  &lt;p&gt;เล่นแล้วเป็นอย่างไร มาเม้นบอกกันได้นะครับ&lt;/p&gt;&lt;img src="http://coredeveloper.net/aggbug.aspx?PostID=747" width="1" height="1"&gt;</description></item><item><title>Setup SQL Server 2008 และ SP1 พร้อมกัน (Slipstream)</title><link>http://coredeveloper.net/blogs/nantcom/archive/2009/08/02/setup-sql-server-2008-sp1-slipstream.aspx</link><pubDate>Sun, 02 Aug 2009 03:21:35 GMT</pubDate><guid isPermaLink="false">86cc649a-bf28-4d34-a9ef-d75c61f34293:745</guid><dc:creator>นันคอม</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://coredeveloper.net/blogs/nantcom/rsscomments.aspx?PostID=745</wfw:commentRss><comments>http://coredeveloper.net/blogs/nantcom/archive/2009/08/02/setup-sql-server-2008-sp1-slipstream.aspx#comments</comments><description>&lt;p&gt;เนื่องจาก SQL Server 2008 ยังไม่รองรับ Windows 7 ทำให้เวลาเราลง SQL 2008 ก็จะพบหับหน้าจอแบบนี้ ให้เราลงไปก่อน แล้วไปหา SP1 มาติดตั้งอีกที ที่น่าน้อยใจคือ ถ้าคุณ&lt;a href="http://www.microsoft.com/downloads/details.aspx?familyid=7522A683-4CB2-454E-B908-E805E9BD4E28&amp;amp;displaylang=en"&gt;ใช้ SQL Express&lt;/a&gt; เลย มันเหมือนจะมี SP1 มาในตัวอยู่แล้วเนี่ยสิ&lt;img style="border-right-width:0px;margin:10px auto 0px;display:block;float:none;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_31C01F81.png" width="550" height="282" /&gt; &lt;/p&gt;  &lt;p&gt;ไม่เป็นเรา เราทำเองบ้างก็ได้ ผมไปพบ&lt;a href="http://blogs.msdn.com/petersad/archive/2009/02/25/sql-server-2008-creating-a-merged-slisptream-drop.aspx"&gt;บล็อกใน MSDN ที่สอนทำ Slipstream&lt;/a&gt; เข้าพอดี วิธีการไม่ยากครับ เราลองมาดูกัน&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;img style="border-right-width:0px;margin:0px 0px 0px 10px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" align="right" src="http://coredeveloper.net/blogs/nantcom/image_25224C98.png" width="310" height="207" /&gt;&lt;font color="#800000"&gt;ขั้นแรก&lt;/font&gt;&lt;/strong&gt; ไปดาวน์โหลด SQL Server 2008 SP1 มาก่อน &lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=66ab3dbb-bf3e-4f46-9559-ccc6a4f9dc19&amp;amp;displaylang=en"&gt;จากเว็บ Microsoft&lt;/a&gt; เลือกโหลดเฉพาะ 32บิต หรือ 64บิต ก็ได้ หรือจะโหลดมาหมดเลยก็ได้ครับ ไม่ว่ากัน ผมถือว่า &lt;strong&gt;เซฟไว้ที่ C:\ ละกันนะครับ&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;a title="http://download.microsoft.com/download/1/6/3/163A851B-D956-42E9-B426-F5C0EBE6B654/SQLServer2008SP1-KB968369-x64-ENU.exe" href="http://download.microsoft.com/download/1/6/3/163A851B-D956-42E9-B426-F5C0EBE6B654/SQLServer2008SP1-KB968369-x64-ENU.exe"&gt;SQLServer2008SP1-KB968369-x64-ENU.exe&lt;/a&gt; (64-bit - 323MB) &lt;/li&gt;    &lt;li&gt;&lt;a title="http://download.microsoft.com/download/1/6/3/163A851B-D956-42E9-B426-F5C0EBE6B654/SQLServer2008SP1-KB968369-x86-ENU.exe" href="http://download.microsoft.com/download/1/6/3/163A851B-D956-42E9-B426-F5C0EBE6B654/SQLServer2008SP1-KB968369-x86-ENU.exe"&gt;SQLServer2008SP1-KB968369-x86-ENU.exe&lt;/a&gt; (32-bit – 262MB) &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;strong&gt;&lt;font color="#004000"&gt;ขั้นต่อมาเลย&lt;/font&gt;&lt;/strong&gt; ถ้าไฟล์ของคุณเป็น ISO ก็ต้องแตกไฟล์ลงเครื่องก่อนครับ &lt;strong&gt;&lt;font color="#ff8040"&gt;ผมถือว่า แตกไฟล์ไว้ที่ C:\SQL2008SP1 นะครับ&lt;/font&gt;&lt;/strong&gt; เมื่อแตกแล้ว ให้สร้างโฟลเดอร์ ชื่อ PCU ขึ้นมา ดังรูปทางขวามือ เราจะเอาไว้เก็บตัว Update ครับ&lt;/p&gt;  &lt;p&gt;จากนั้น เมื่อดาวน์โหลดมาแล้ว ให้เปิด Command Line โดยการกดปุ่มวินโดวส์ข้างๆ ปุ่ม ALT แล้วกด R&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_1B0DAB6D.png"&gt;&lt;img style="border-right-width:0px;display:block;float:none;border-top-width:0px;border-bottom-width:0px;margin-left:auto;border-left-width:0px;margin-right:auto;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_1F378332.png" width="427" height="235" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;แล้วพิมพ์ว่า…&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;SQLServer2008SP1-KB968369-x64-ENU.exe /x:&lt;strong&gt;c:\SQL2008SP1\PCU&lt;/strong&gt;&amp;#160; [กด ENTER] &lt;/li&gt;    &lt;li&gt;SQLServer2008SP1-KB968369-x86-ENU.exe /x:&lt;strong&gt;c:\SQL2008SP1\PCU&lt;/strong&gt;&amp;#160; [กด ENTER] &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;font color="#004000"&gt;&lt;strong&gt;&lt;u&gt;หมายเหตุ&lt;/u&gt;&lt;/strong&gt; คุณสามารถกด Tab แล้ว Command Line มันจะ Auto Complete ชื่อให้ได้นะครับ ไม่ต้องพิมพ์เองทั้งหมด&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;จากนั้น Copy ไฟล์ &lt;strong&gt;Setup.exe, Setup.rll ในโฟลเดอร์ C:\SQL2008SP1\PCU&lt;/strong&gt; ไปทับไฟล์เดียวกันนี้ ที่ Folder &lt;strong&gt;C:\SQL2008SP1&lt;/strong&gt; ด้านนอก &lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:block;float:none;border-top-width:0px;border-bottom-width:0px;margin-left:auto;border-left-width:0px;margin-right:auto;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_0B3DF09C.png" width="550" height="365" /&gt; &lt;/p&gt;  &lt;p&gt;จากนั้น กลับมาที่ Command Line แล้วพิมพ์คำสั่ง ดังนี้&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;robocopy C:\SQL2008SP1\pcu\x86 C:\SQL2008SP1\x86 /XF Microsoft.SQL.Chainer.PackageData.dll [กด ENTER] &lt;/li&gt;    &lt;li&gt;robocopy C:\SQL2008SP1\pcu\x64 C:\SQL2008SP1\x64 /XF Microsoft.SQL.Chainer.PackageData.dll [กด ENTER] &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;แล้วก็ เข้าไปหาไฟล์ DefaultSetup.ini ใน Folder &lt;strong&gt;C:\SQL2008SP1&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:block;float:none;border-top-width:0px;border-bottom-width:0px;margin-left:auto;border-left-width:0px;margin-right:auto;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_41BB7613.png" width="550" height="388" /&gt; &lt;/p&gt;  &lt;p&gt;ถ้ายังไม่มี ให้สร้างขึ้นมาด้วย Notepad แล้วเซฟไว้ที่ &lt;strong&gt;C:\SQL2008SP1\x86\DefaultSetup.ini&lt;/strong&gt; และ &lt;strong&gt;C:\SQ:2008SP1\x64\DefaultSetup.ini&lt;/strong&gt; ตามลำดับ ครับ&lt;/p&gt;  &lt;p&gt;ภายในไฟล์ ให้เพิ่มบรรทัด PCUSOURCE=&amp;quot;V:\PCU&amp;quot; ลงไปครับ ถ้าสร้างไฟล์ใหม่ขึ้นมา ก็ให้ในไฟล์ มีเนื้อไฟล์ตามนี้&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;strong&gt;;SQLSERVER2008 Configuration File        &lt;br /&gt;[SQLSERVER2008]         &lt;br /&gt;PCUSOURCE=&amp;quot;V:\PCU&amp;quot;&lt;/strong&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;ส่วนของผม เป็นแผ่น MSDN มันจะ Embed Key มาอยู่แล้วในไฟล์นี้ ก็เพิ่ม PCUSOURCE=&amp;quot;V:\PCU&amp;quot; ต่อท้ายลงไปได้เลย&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:block;float:none;border-top-width:0px;border-bottom-width:0px;margin-left:auto;border-left-width:0px;margin-right:auto;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_740F23C5.png" width="500" height="326" /&gt; &lt;/p&gt;  &lt;p&gt;เรียบร้อยแล้ว ก็สร้างไฟล์ใหม่อีกไฟล์ครับ ชื่อว่า &lt;strong&gt;C:\SQL2008SP1\Setup.cmd&lt;/strong&gt; ให้ภายในไฟล์ มีเนื้อหาดังนี้&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:block;float:none;border-top-width:0px;border-bottom-width:0px;margin-left:auto;border-left-width:0px;margin-right:auto;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_49070FF3.png" width="500" height="326" /&gt; &lt;/p&gt;  &lt;p&gt;ไฟล์นี้ จะทำการสร้าง Virtual Drive ว่า Drive V ให้มันชี้ไปที่โฟลเดอร์ปัจจุบันของเรา ซึ่งเมื่อมันทำงาน จะเห็นว่า ใน My Computer มี Drive V โผล่ขึ้นมา ซึ่งภายในมีข้อมูลเหมือนใน Folder C:\SQL2008SP1 เป๊ะ&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:block;float:none;border-top-width:0px;border-bottom-width:0px;margin-left:auto;border-left-width:0px;margin-right:auto;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_0647C4BB.png" width="550" height="363" /&gt; &lt;/p&gt;  &lt;h3&gt;&lt;font color="#800040"&gt;เมื่อพร้อมแล้ว ก็รันไฟล์ Setup.cmd ได้เลยครับ!&lt;/font&gt;&lt;/h3&gt;  &lt;h2&gt;จะรู้ได้อย่างไร ว่ามัน Slipstream?&lt;/h2&gt;  &lt;p&gt;ดูได้จาก 2 หน้าครับ คือ &lt;strong&gt;Installation Rules&lt;/strong&gt; จะเห็นว่า บรรทัดสุดท้าย มีเขียนว่า “Update Setup Media…”&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;margin:10px auto;display:block;float:none;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_2EF30437.png" width="550" height="412" /&gt; และในตอนก่อนที่จะ Install เลื่อนลงมาดู จะเห็นตรงที่เขียนว่า Slipstream ครับ&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_43D460DD.png"&gt;&lt;img style="border-right-width:0px;display:block;float:none;border-top-width:0px;border-bottom-width:0px;margin-left:auto;border-left-width:0px;margin-right:auto;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_0870D552.png" width="550" height="357" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;เอาละครับ ต่อไปถ้าจะลงเครื่องใหม่ ก็ไม่ต้องมานั่งลง SP1 กันแล้ว เจอกันใหม่โอกาสหน้าครับ&lt;/p&gt;&lt;img src="http://coredeveloper.net/aggbug.aspx?PostID=745" width="1" height="1"&gt;</description></item><item><title>ทำไมโปรแกรม WPF ของเราช้า</title><link>http://coredeveloper.net/blogs/nantcom/archive/2009/07/28/wpf.aspx</link><pubDate>Tue, 28 Jul 2009 14:40:03 GMT</pubDate><guid isPermaLink="false">86cc649a-bf28-4d34-a9ef-d75c61f34293:744</guid><dc:creator>นันคอม</dc:creator><slash:comments>2</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://coredeveloper.net/blogs/nantcom/rsscomments.aspx?PostID=744</wfw:commentRss><comments>http://coredeveloper.net/blogs/nantcom/archive/2009/07/28/wpf.aspx#comments</comments><description>&lt;p&gt;พอดีว่าตอนที่ผมไปกระจายข่าวของโปรแกรมผมอยู่ บังเอิญไปอ่านเจอ&lt;a href="http://www.narisa.com/forums/index.php?s=620809e1341785c3c68509c059d1f5e1&amp;amp;showtopic=28505" target="_blank"&gt;โพสในเว็บนาริสาเข้า&lt;/a&gt; เกี่ยวกับเรื่อง Performance ของ WPF ซึ่งเป็นปัญหาที่เคยเกิดขึ้นกับบริษัทที่ผมทำงานเหมือนกันครับ&lt;/p&gt;  &lt;p&gt;อาการก็คือ เวลาที่สร้าง Animation ในโปรแกรม แล้วโปรแกรมทำงานได้ช้า บางทีก็เห็น CPU ขึ้น 100% เลยก็มี บางทีอยู่เฉยๆ CPU ก็ขึ้น เป็นต้น อีกอาการก็คือ หน้าโปรแกรมโหลดช้ามาก ใช้งานบางทีก้อกระตุก กินแรมเยอะอีกต่างหาก &lt;strong&gt;&lt;font color="#008000"&gt;แต่ถ้าลองจูนดูดีๆ ก็ปรากฏว่า ความผิดมันอยู่ที่เราเนี่ยละครับ หลายๆ อย่างเลย เขาก็เขียนบอกไว้อยู่แล้ว แต่เราไม่ได้ไปอ่านมัน&lt;/font&gt;&lt;/strong&gt; &lt;/p&gt;  &lt;p&gt;เพื่อไม่ให้เกิดประวัติศาสตร์ซ้ำร้อย ผมเลยมีเทคนิคเล็กน้อย จากประสบการการใช้งานเทคโนโลยี XAML (ทั้ง WPF/Silverlight) มาฝากครับ&lt;/p&gt;  &lt;h2&gt;1. อย่าใช่ BitmapEffect เด็ดขาด&lt;/h2&gt;  &lt;p&gt;ก่อนที่ผมจะอธิบายอะไรต่อไป ลองดูนี่ก่อนครับ (&lt;a href="http://msdn.microsoft.com/en-us/library/system.windows.media.effects.bitmapeffect.aspx" target="_blank"&gt;จาก MSDN&lt;/a&gt;)&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coresharp.net/blogs/article/image_78E1FF66.png"&gt;&lt;img style="border-right-width:0px;margin:10px auto;display:block;float:none;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_thumb_779D6687.png" width="500" height="95" /&gt;&lt;/a&gt;โดยสรุปก็คือ BitmapEffect นั้น เรนเดอร์ด้วย CPU ครับ (Software Rendering) ไม่ได้ใช้การ์ดจอวาด (Hardware Rendering) อย่างที่มันควรจะเป็น และพาลทำให้วัตถุนั้น เรนเดอร์ด้วย Software ไปด้วยอีกต่างหาก &lt;strong&gt;&lt;font color="#ff8000"&gt;ควรหลีกเลี่ยงอย่างที่สุดครับ&lt;/font&gt;&lt;/strong&gt; &lt;strong&gt;แต่ใน WPF 3.5 จะมีแบบใหม่ ที่ชื่อว่า Effect เฉยๆ ที่ทำงานโดยใช้ความสามารถ &lt;font color="#ff0000"&gt;Pixel Shader 2.0&lt;/font&gt; ของการ์ดจอ &lt;/strong&gt;เหมือนกับเอฟเฟค์ภาพเบลอ แสงฟุ้งๆ ที่เราเห็นในเกมแบบนั้นเลยครับ (สังเกตประกายไฟ หรือการ Glow ของโลหะบนมอเตอร์ไซค์ในรูปด้านล่าง)&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:block;float:none;border-top-width:0px;border-bottom-width:0px;margin-left:auto;border-left-width:0px;margin-right:auto;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_04C8DFB1.png" width="550" height="310" /&gt;&lt;/p&gt;  &lt;p&gt;สำหรับการ์ดจอที่ใช้ PS2.0 ได้ คือรุ่น&amp;#160; Intel GMA900, Nvidia GeForce 5XXX ขึ้นไป หรือ ATI Radeon X300 ขึ้นไป หรือจะแปลง่ายๆ ก็คือ หาเครื่องที่ใช้ไม่ได้ยากมากครับในยุคนี้ เพราะแค่การ์ดจอ OnBoard ก็มีแล้วครับ สังเกตง่ายๆ ว่า ถ้าเครื่องไหน เปิด Aero Glass ได้ ก็ใช้ PS2.0 ได้แน่นอนครับ&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/400pxSIMD.svg_0CA03679.png"&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;margin-left:0px;border-left-width:0px;margin-right:0px;" title="400px-SIMD.svg" border="0" alt="400px-SIMD.svg" align="left" src="http://coredeveloper.net/blogs/nantcom/400pxSIMD.svg_thumb_500837DB.png" width="157" height="157" /&gt;&lt;/a&gt;&amp;#160; เนื่องจาก GPU ทำงานอะไรที่เป็น SIMD (&lt;a href="http://en.wikipedia.org/wiki/SIMD" target="_blank"&gt;Single Instruction Multiple Data&lt;/a&gt; - คำสั่งทำกับหลายๆ ข้อมูล เช่น เปลี่ยนสีทุกพิกเซลเป็นสีขาว) แบบนี้ได้เร็วมากๆ การทำเอฟเฟคบน GPU จึงแทบไม่มีผลอะไรเลยครับ เกือบจะมี Performance Hit เป็น 0 เลยก็ว่าได้ (ย้ำว่า &lt;strong&gt;เกือบ&lt;/strong&gt;) แต่สำหรับเอฟเฟคเบลอ พยายามอย่าใช้ Radius เกิน 10 ครับ เพราะเอฟเฟคเบลอ คือการไปดูพิกเซลข้างๆ แล้วเอามาหาค่าเฉลี่ย ถ้าใส่ไป 20 หมายถึงว่า การจะเรนเดอร์ 1 พิกเซล ก็จะต้องดูพิกเซลรอบๆ อีก 1600 พิกเซลเพื่อเอามาบวกกันหาค่าเฉลี่ย (20 พิกเซลจากทุกด้าน = 40x40) ต่อให้ GT200 ก็ยังอาจจะลำบากอยู่เหมือนกัน เพราะว่า WPF ไม่ได้ทำงานแบบเกม (ดูข้อ 10 ครับ) และเกมก็ไม่มีเอฟเฟคที่เบลอกระจายขนาดนี้ครับ (เขาใช้เทคนิคหลายๆ อย่างผสมกันน่ะครับ)&lt;/p&gt;  &lt;h2&gt;2. อย่าสร้าง Animation ที่เกี่ยวข้องกับ Layout&lt;/h2&gt;  &lt;p&gt;แม้ว่าเดี๋ยวนี้ CPU จะเร็วมากแล้ว แต่การสร้าง Animation ที่เกี่ยวข้องกับ Layout นั้น ใช้ CPU ล้วนๆ ครับ (และไม่มีทางทำบน GPU ได้ด้วย) นั่นก็คือ ทุกเฟรมที่โปรแกรมทุกงาน จะต้องมีการคำนวณ Layout ใหม่หมด ทุกเฟรม ดังนั้น ไม่ควรอย่างยิ่งครับ&lt;/p&gt;  &lt;p&gt;แล้วแบบไหนคือ Animation ที่ใช้ Layout? ตัวอย่างเช่น&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;ขยาย Listbox มา&lt;strong&gt;จนเต็มที่ว่างในจอ&lt;/strong&gt;พอดี &lt;/li&gt;    &lt;li&gt;เปลี่ยนขนาด Grid โดย&lt;strong&gt;ตั้งค่า Width, Height&lt;/strong&gt; &lt;/li&gt;    &lt;li&gt;ขยับวัตถุ โดยใช้ Canvas.Left, Canvas.Top (มีหลายคนบอกว่า มีผลไม่มากนะ) &lt;/li&gt;    &lt;li&gt;นึกไม่ออกละ….เพราะผมไม่เคยใช้เลย :P &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;เอาเป็นว่า ถ้าคุณทำอะไร &lt;font color="#ff8000"&gt;ที่มันทำให้ Layout ขยับ&lt;/font&gt; นั่นแปลว่า CPU ล้วนๆ ครับ ดั้งนั้น ถ้าจะทำ Animation ให้มองลงมาล่างๆ หน่อยครับ จะเจอ Category ชื่อ RenderTransform นั่นละครับ ถ้าจะใช้ Animation ให้เปลี่ยนค่าตรงนี้เท่านั้น ซึ่งคุณสามารถเล่นกับอนิเมชั่น ประมาณนี้ได้ครับ&lt;a href="http://coredeveloper.net/blogs/nantcom/image_2B76AA57.png"&gt;&lt;img style="border-right-width:0px;margin:30px 30px 20px 0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" align="left" src="http://coredeveloper.net/blogs/nantcom/image_thumb_7D1D24A9.png" width="265" height="160" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;ย่อ-ขยาย ขนาดของวัตถุ (ScaleTransform) &lt;/li&gt;    &lt;li&gt;กลับด้านวัตถุ (ScaleTransform แล้วตั้งค่าเป็น –1 เช่น Y =-1 จะได้ภาพกลับหัว เหมือนเงาสะท้อน) &lt;/li&gt;    &lt;li&gt;หมุนวัตถุ (RotateTransform) ไม่เหมือนกับการกลับด้านนะครับ ถ้าทำให้วัตถุหมุนไป จนกลับหัว มันจะกลับซ้ายเป็นขวา &lt;/li&gt;    &lt;li&gt;ขยับวัตถุไปมา เป็นจำนวน Pixel คงที่ เช่น ขยับซ้าย 10 pixel (TranslateTransform) แต่ถ้า ขยับไปจนเต็มพื้นที่ แบบนี้จะทำไม่ได้ครับ &lt;/li&gt;    &lt;li&gt;Opacity เปลี่ยนความโปร่งแสงได้ &lt;/li&gt;    &lt;li&gt;เปลี่ยนสี เนื่องจากการเปลี่ยนสีเป็นการเปลี่ยนสีบน Vertex ของวัตถุ &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Animation เหล่านี้ จะไม่กิน CPU เลย เพราะมันทำงาน หลังจากที่ Layout ทำงานแล้ว มันจึงเป็นการย่อ ขยาย หมุน เลื่อน สิ่งที่ปรากฏบนจอเท่านั้นเองครับ ไม่มีการคำนวณอะไรใหม่ทั้งสิ้น&lt;/p&gt;  &lt;h2&gt;3. ทำ Background ให้มัน Simple!&lt;/h2&gt;  &lt;p&gt;อีกหลุมหนึ่งที่เป็นปัญหามาก คือ เรามักจะเผลอวาดคอนโทรลซ้อนทับกันไป ซ้อนทับกันมา แล้วใช้มันเป็นพื้นหลังครับ ซึ่งไม่ควรอย่างยิ่ง ถ้าเป็นไปได้ ให้สร้าง DrawingBrush แทน จะดีกว่า เพราะนอกจากจะไม่ทำให้ Visual Tree ของเราใน Blend ตีกันวุ่นแล้ว มันยังเร็วขึ้นด้วย หรือถ้าจะให้ดี ทำให้มันเป็นภาพนิ่งเลยครับ จะดีที่สุด&lt;/p&gt;  &lt;p&gt;เช่นเดียวกัน ผมเคยเจอกรณีทีมีดีไซนเนอร์ท่านหนึ่ง ใส่เงาไว้ที่ตัวอักษรทุกตัวเลย นั่นเท่ากับว่า เราจะต้องสร้างเงาขึ้นมาสำหรับตัวอักษรแต่ละตัว ซึ่งผลลัพธ์ที่ได้ ก็มีค่าเท่ากับการใส่เงาที่ Canvas หรือ Grid ที่คลุมตัวอักษรนั้นอยู่&lt;/p&gt;  &lt;h2&gt;4. Reset Property ทุกครั้ง ถ้าจะไม่เปลี่ยนมัน&lt;/h2&gt;  &lt;p&gt;ลองดูโค๊ดของสี่เหลี่ยมนี้เทียบกันครับ&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;img style="border-right-width:0px;display:block;float:none;border-top-width:0px;border-bottom-width:0px;margin-left:auto;border-left-width:0px;margin-right:auto;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_7CB0F1B4.png" width="550" height="460" /&gt; &lt;/p&gt;  &lt;p&gt;จะเห็นว่า ทั้งสองสี่เหลี่ยมนี่ แสดงผลเหมือนกันทุกประการ แต่ว่าอันที่ Hilight อยู่โค๊ด XAML ยาวกว่า นั่นก็เพราะเวลาคุณเซ็ทค่าบางอย่างด้วย Blend แม้ว่าค่านั้น จะเป็นค่า Default เจ้า Blend ก็่ยังจะเซ็ทค่าให้ครับ (เช่น Margin 0, HorizontalAlignment=Stretch&lt;/p&gt;  &lt;p&gt;อย่าลืมว่า XAML นั้น มันโดนแปลงเป็นโค๊ดอีกทีหนึ่ง การที่มีการตั้งค่าต่างๆ ก็เหมือนกับการมีโค๊ดที่เซ็ทค่าเหล่านั้นทำงานอยู่ด้วยนั่นละครับ แถมเสียเวลาทำแล้ว ก็ยังไม่ได้ผลอะไร (เพราะมีคือค่า Default อยู่แล้ว) ดังนั้น ถ้าจะไม่ตั้งค่าอะไร ให้ Reset ค่าเสมอครับ ซึ่งเป็นเรื่องน่ายินดีที่ Blend 3 มันรีเซ็ทให้เอง ถ้าพบว่าการตั้งค่านั้นเป็นการตั้งค่าด้วยค่า Default&lt;/p&gt;  &lt;h2&gt;5. อย่าเปิดหลายฟอร์ม&lt;/h2&gt;  &lt;p&gt;เพราะว่าการเปิด Form ใหม่ คือการสร้าง Render Surface เพิ่มครับ เนื่องจาก WPF นั้น Render ด้วย DirectX ซึ่งปกติเขาจะ Render กันอยู่ในหน้าต่างเดียว (สังเกตง่ายๆ ว่า โปรแกรมเดโมของ Microsoft นั้น ทำงานด้วยหน้าต่างเดียวเกือบทั้งหมด)&lt;/p&gt;  &lt;h2&gt;6. อย่าเขียนโค๊ดแทนระบบ Layout&lt;/h2&gt;  &lt;p&gt;อย่าลืมว่า WPF มีระบบ Layout อยู่นะครับ ถ้าต้องเขียนโค๊ดใน อะไรซํกอย่าง_Resized เช่น ตั้งให้คอนโทรลอยู่ตรงกลาง Form เนี่ย แสดงว่าต้องมีอะไรผิดพลาดแน่ๆ ลองจัด Layout ใหม่ ดีๆ ครับ และที User Control กับ Window เราสามารถทดลองย่อขยายขนาดของมันได้ เพื่อดูว่า Layout ของเราขยับตามได้หรือไม่ (Fluid Layout) โดยลากที่มุมด้านล่างของคอนโทรล หรือ Form ครับ (อย่าลืมตั้ง Width/Height เป็น Auto)&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_619822A6.png"&gt;&lt;img style="border-right-width:0px;display:block;float:none;border-top-width:0px;border-bottom-width:0px;margin-left:auto;border-left-width:0px;margin-right:auto;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_1E6CA479.png" width="554" height="270" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;h2&gt;8. อย่าตกหลุมเดียวกับ HTML&lt;/h2&gt;  &lt;p&gt;นอกจากนี้ อีกหลุมนึงที่ผมเจอก็คือ มีหลายคนพยายามใช้ Grid ทำเป็นเหมือน Table เพื่อวางคอนโทรลครับ ซึ่ง HTML ก็ได้ผ่านจุดนั้นมาแล้วจากการใช้ CSS ดังนั้น อย่าพยายามไปทำความผิดซ้ำอีก การวางคอนโทรลใน Grid ที่มี Row และ Column นั้น ลำบากมากๆ ครับ ลองใช้การจัดชิดเต็ม ร่วมกับการใส่ Margin ดู อาจได้ผลเหมือนกันก็ได้ (เช่นเดียวกับการสร้างตารางที่มี 2 คอลัมน์ ที่ได้ผลเหมือนกับการวาง &lt;a href="http://www.bigbaer.com/css_tutorials/css.float.html.tutorial.htm" target="_blank"&gt;div float left&lt;/a&gt;)&lt;/p&gt;  &lt;h2&gt;9. ใช้ DataBinding และ AttachedProperty ให้มากที่สุด&lt;/h2&gt;  &lt;p&gt;จุดเปลี่ยนของ WPF เลย คือเรื่องระบบ Data Binding ที่ทำได้วิจิตรพิสดารมาก จนผมไม่สามารถกลับไปทนใช้ Windows Form ได้อีกแล้ว และระบบ Dependency Property ที่ไม่รู้ว่าคิดได้ยังไง อย่างเช่นโปรแกรม WaiV2 ของผมนั้น ใน UI เกือบจะไม่มีโค๊ดเลย เป็นการ Binding ล้วนๆ และ ระบบ Localization ก็ใช้การสร้าง Attached Property ไปแปะไว้กับคอนโทรล แทนการทำให้คอนโทรล Localize ได้ แจ่มมากครับสำหรับฟีเจอร์ทั้งสองนี้&lt;/p&gt;  &lt;p&gt;อ้อ สำหรับ Blend 3 จะมีฟีเจอร์ชื่อ Interactivity ด้วย มันคือการนำเอา AttachedProperty แปะเข้าไปนั่นเองครับ อย่างโค๊ดด้านล่างนี่ คือการแปะ Property Interaction.Triggers ลงไปบน Rectangle ทำให้ Rectangle เป็นลิงค์ เปิดหน้าเว็บได้&lt;a href="http://coredeveloper.net/blogs/nantcom/image_6338C8AD.png"&gt;&lt;img style="border-right-width:0px;margin:5px auto 0px;display:block;float:none;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_0DA4F9CB.png" width="484" height="150" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;h2&gt;10. อย่าลืมว่า นี่ไม่ใช่ Windows Form&lt;/h2&gt;  &lt;p&gt;ทันทีที่คุณใช้ WPF คุณก็ได้ก้าวเข้าสู่อีกโลกหนึ่งแล้วครับ การเขียนโปรแกรมด้วย WPF นั้น ในความคิดของผม มันคือการเขียนเกมดีๆ นี่เองครับ &lt;strong&gt;เพราะคุณจะต้องคำนึงถึงเรื่อง Performance ตลอดเวลา เพราะการวาดภาพนั้น มีราคาค่างวดที่สูงมากครับ&lt;/strong&gt; และต้องอย่าลืมว่า WPF ทำงานแบบ &lt;a href="http://en.wikipedia.org/wiki/Retained_mode" target="_blank"&gt;Retained Mode&lt;/a&gt; ซึ่งจะ&lt;strong&gt;&lt;font color="#ff8040"&gt;คงออบเจ็กต์ไว้ในแรมตลอดเวลา&lt;/font&gt;&lt;/strong&gt; ต่างจากเวลาที่เราวาดภาพด้วย GDI บน Windows Form ซึ่งเป็นแบบ Immediate Mode ที่วาดภาพลงไปเลย โดยที่ไม่คงออบเจ็กต์ไว้ ถึงเวลาจบการวาดก็ล้างออบเจ็กต์ทิ้ง (เหมือนการสร้าง Visual Tree ใหม่ ทุกเฟรม)&lt;/p&gt;  &lt;p&gt;ข้อดีของการทำงานแบบ Retained Mode คือ คุณจะมีอิสระมากกว่าในการเปลี่ยนแปลงรูปร่างหน้าตาของวัตถุ เพราะไม่ต้องคำนึงว่ามันจะวาดลงไปบนจอหรือยัง หรือวัตถุไหนจะวาดก่อน วัตถุไหนวาดทีหลัง เพียงแค่เปลี่ยนค่าตามใจชอบ แล้วเดี๋ยวระบบก็สามารถจัดการวาดภาพลงไปได้เลย เมื่อถึงเวลาที่ต้องอัพเดทหน้าจอ&lt;/p&gt;  &lt;p&gt;และเมื่อคุณรู้แล้วว่า WPF ทำงานแบบ Retained Mode &lt;strong&gt;&lt;font color="#008000"&gt;คุณก็ควรจะทำให้ Visual Tree ของคุณ ให้เป็น Tree น้อยที่สุด&lt;/font&gt;&lt;/strong&gt; คืออย่าให้แขนงของมันเยอะมากจนเกินไป (เช่นทุกอย่างเป็นวัตถุหมด ไม่มี Path หรือ Drawing Brush เลย แม้แต่รูปก็ไม่ยอมใช้) และอย่าให้มันลึกจนเกินไป (มีคอนโทรลซ้อนกันอยู่มากมายก่ายกอง) เพราะมันจะช้าครับ&lt;/p&gt;  &lt;p&gt;ผมมีโปรเจคที่ใช้กราฟฟิคเยอะมากๆ อยู่ โดยมี Canvas ซ้อนกันกว่า 500 ชั้น (ย้ำว่า ซ้อนกันนะครับ ไม่ใช้ Canvas อยู่ใน Canvas) และภายใน Canvas มีวัตถุอื่นๆ อยู่ภายใน แล้วทั้งหมดนี้ วาดอยู่บนพื้นผิวที่เป็นแผนที่อีกทอด จากการใช้งาน ก็สามารถทำงานได้เป็นที่น่าพอใจครับ ลูกค้าก็พอใจกับผลงานที่ออกมา&lt;/p&gt;  &lt;h1&gt;ตาคุณบ้าง&lt;/h1&gt;  &lt;p&gt;คุณมีข้อเสนอ หรือความเห็นอย่างไรบ้างครับ? มาคอมเม้นกันได้นะ มาแลกเปลี่ยนกันครับ&lt;/p&gt;&lt;img src="http://coredeveloper.net/aggbug.aspx?PostID=744" width="1" height="1"&gt;</description></item><item><title>ใช้ SSD หรือเปล่า? มาเร่งความเร็ว SSD กันเถอะ</title><link>http://coredeveloper.net/blogs/nantcom/archive/2009/04/29/ssd-ssd.aspx</link><pubDate>Wed, 29 Apr 2009 14:38:41 GMT</pubDate><guid isPermaLink="false">86cc649a-bf28-4d34-a9ef-d75c61f34293:678</guid><dc:creator>นันคอม</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://coredeveloper.net/blogs/nantcom/rsscomments.aspx?PostID=678</wfw:commentRss><comments>http://coredeveloper.net/blogs/nantcom/archive/2009/04/29/ssd-ssd.aspx#comments</comments><description>&lt;p&gt;SSD มีข้อจำกัดอย่างหนึ่งคือ จะทำงานได้ช้ามากในความเร็วระดับ KB แทนที่จะเป็น MB (หรือช้าลงหลายพันเท่าเลยทีเดียว) เวลาเขียนไฟล์เล็กๆ โดยเฉพาะอย่างยิ่ง SSD แบบ MLC ที่พบได้ใน Netbook ทั่วไปอย่างเช่น AspireOne A110 หรือ Dell Mini แต่ปัญหาเหล่านั้นกำลังจะหมดไป เพราะมีโปรแกรมเมอร์สมองใส คิด Filter Driver มาลงแทน Driver “Disk Drive” ธรรมดา ที่ Windows ลงให้ พร้อมสร้าง Write Buffer ขนาด 32MB ให้เราแล้วครับ!!!&lt;/p&gt;  &lt;p&gt;ผลที่ได้ ก็คือ…&lt;/p&gt;  &lt;table border="0" cellspacing="0" cellpadding="2" width="550"&gt;     &lt;tr&gt;       &lt;td valign="top" width="275"&gt;         &lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt;&lt;/p&gt;          &lt;p&gt;&lt;img style="border-bottom:0px;border-left:0px;display:inline;border-top:0px;border-right:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_238655F4.png" width="261" height="330" /&gt; &lt;/p&gt;       &lt;/td&gt;        &lt;td valign="top" width="275"&gt;&lt;strong&gt;           &lt;br /&gt;After            &lt;br /&gt;            &lt;br /&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_6D54B7D7.png"&gt;&lt;img style="border-bottom:0px;border-left:0px;display:inline;border-top:0px;border-right:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_347A5E0A.png" width="273" height="312" /&gt;&lt;/a&gt; &lt;/strong&gt;&lt;/td&gt;     &lt;/tr&gt;   &lt;/table&gt;  &lt;p&gt;นอกจากนีั ยังมีเวอร์ชั่นสำหรับ SD Card ด้วยครับ ซึ่งถ้าคุณทำตาม &lt;a href="http://coresharp.net/blogs/article/archive/2009/02/01/aspireone-windows7-tuning.aspx" target="_blank"&gt;ไกด์ที่ผมเขียนไว้&lt;/a&gt; ก็จะเห็นว่าผมได้ย้าย Profile ไปที่ Drive D ซึ่งอยู่บน SD Card ที่จำลองเป็น Harddisk&lt;/p&gt;  &lt;p&gt;อย่ารอช้าครับ ดาวน์โหลดได้เลย:&lt;/p&gt;  &lt;p&gt;For SSD: &lt;a title="http://zflashpoint.blogspot.com/2009/04/flashpoint-beta4-is-uploaded.html" href="http://zflashpoint.blogspot.com/2009/04/flashpoint-beta4-is-uploaded.html"&gt;http://zflashpoint.blogspot.com/2009/04/flashpoint-beta4-is-uploaded.html&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;For SDcard: &lt;a title="http://zflashpoint.blogspot.com/2009/04/usbfp-fixed-disk-emulation-version-is.html" href="http://zflashpoint.blogspot.com/2009/04/usbfp-fixed-disk-emulation-version-is.html"&gt;http://zflashpoint.blogspot.com/2009/04/usbfp-fixed-disk-emulation-version-is.html&lt;/a&gt;&lt;/p&gt;&lt;img src="http://coredeveloper.net/aggbug.aspx?PostID=678" width="1" height="1"&gt;</description></item><item><title>ลง Windows 7 (หรือ Vista) บน SSD + โปรแกรม แบบไม่ต้องรอข้ามคืน (เทคนิคการทำเครื่องต้นฉบับสำหรับ Ghost จาก Virtual Machine และ Ghost ผ่าน Network)</title><link>http://coredeveloper.net/blogs/nantcom/archive/2009/04/23/windows-7-vista-ssd-ghost-virtual-machine-ghost-network.aspx</link><pubDate>Wed, 22 Apr 2009 20:34:00 GMT</pubDate><guid isPermaLink="false">86cc649a-bf28-4d34-a9ef-d75c61f34293:672</guid><dc:creator>นันคอม</dc:creator><slash:comments>5</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://coredeveloper.net/blogs/nantcom/rsscomments.aspx?PostID=672</wfw:commentRss><comments>http://coredeveloper.net/blogs/nantcom/archive/2009/04/23/windows-7-vista-ssd-ghost-virtual-machine-ghost-network.aspx#comments</comments><description>&lt;p&gt;สวัสดีครับ จากบทความก่อนหน้านี้ที่ผมได้พูดคุยถึงเรื่องการ Tweak Windows 7 ให้ใช้งานบน SSD ของ Acer Aspire One ได้เป็นอย่างดี โดยไม่ช้ามาก และด้วยช่วงเวลาที่ Windows 7 จะออก RC (Release Candidate = เกือบขายได้แล้ว) เร็วๆ นี้ ผมเลยเอาเทคนิคการลง Windows 7 บน Aspire One 110L ที่ใช้ SSD สุดเต่าขนาด 8GB ซึ่งอาจจะใช้เวลาหลายวันกว่าจะลงโปรแกรมได้ครบ เทคนิคที่ผมเอามาฝากนี้ จะเอาไปประยุกต์เป็นเตรียมเครื่องที่จะเป็น Master สำหรับ Ghost ไปลงในบริษัทหรือห้องคอมก็ได้ ไม่ว่ากัน อ้อ ใช้ได้ทั้ง XP/Vista และ Windows 7 นะครับ &lt;br /&gt;&lt;/p&gt;
&lt;h1&gt;สิ่งที่ต้องใช้&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;แผ่น Windows XP SP2 หรือ SP3 เราจะใช้แผ่นนี้ในการทำแผ่นบูตเครื่องด้วย CD/USB Flash Drive ตอนหลังครับ &lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.nu2.nu/download.php?sFile=pebuilder3110a.exe" target="_blank"&gt;PE Builder&lt;/a&gt; ของนาย&lt;a href="http://www.nu2.nu/pebuilder/" target="_blank"&gt;เทพ Bart&lt;/a&gt; ครับ เอาไว้สร้างแผ่น Windows PE จาก Windows XP &lt;/li&gt;
&lt;li&gt;ไฟล์ Ghost32.exe จาก Norton Ghost ครับ ลองพยายามหาดูนะ มีเยอะแยะ &lt;/li&gt;
&lt;li&gt;สำหรับ Windows 7/Windows Vista ดาวน์โหลด &lt;a href="http://www.oldapps.com/nLite.php?old_nlite=1" target="_blank"&gt;vLite เวอร์ชั่นเก่า&lt;/a&gt; (1.1.1) และ&lt;a href="http://www.vlite.net/download.html" target="_blank"&gt;เวอร์ชั่นล่าสุด&lt;/a&gt; ใช้ทั้งสองอันครับ &lt;br /&gt;สำหรับ Windows XP ดาวน์โหลด nLite แล้วประยุกต์วิธีการตามได้ครับ &lt;/li&gt;
&lt;li&gt;&lt;a href="http://coresharp.net/files/folders/projects/entry789.aspx"&gt;ไฟล์ Setting ของ vLite&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Driver ของ Aspire One สำหรับ Windows XP โดยเฉพาะ Wireless Lan และ Card Reader และ Driver อุปกรณ์อื่นๆ ที่อาจจะนำมาต่อกับเจ้า Aspire One &lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=AB99342F-5D1A-413D-8319-81DA479AB0D7" target="_blank"&gt;.NET Framework 2.0&lt;/a&gt;++ ถ้าลง MSN Messenger เวอร์ชั่นใหม่แล้ว มันมีในตัวแล้วครับ &lt;/li&gt;
&lt;li&gt;&lt;a href="http://coresharp.net/blogs/projects/archive/2009/02/24/addon-pack-v1.aspx" target="_blank"&gt;Windows Addon Installer&lt;/a&gt; ถ้าไม่อยากลงโปรแกรมเอง หรือแผ่นโปรแกรมอื่นๆ &lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.virtualbox.org/wiki/Downloads" target="_blank"&gt;Sun VirtualBox&lt;/a&gt; โปรแกรมจำลองเครื่อง (VM) เร็ว + ฟีเจอร์ระดับ VMWare แต่ฟรี และถูกลิขสิทธิ์ &lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.google.co.th/search?hl=th&amp;amp;client=firefox-a&amp;amp;rls=org.mozilla%3Aen-US%3Aofficial&amp;amp;hs=PLi&amp;amp;q=IpsMigrationPlugin.dll&amp;amp;btnG=%E0%B8%84%E0%B9%89%E0%B8%99%E0%B8%AB%E0%B8%B2&amp;amp;meta=" target="_blank"&gt;IpsMigrationPlugin.dll&lt;/a&gt; หรือ &lt;a href="http://www.uploadtoday.com/download/?275903&amp;amp;A=838817" target="_blank"&gt;จากนี่&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Harddisk External หรือ Thumb Drive ขนาด &amp;gt; 4GB &lt;/li&gt;
&lt;li&gt;สาย LAN แบบ Cross ถ้าเป็นไปได้ หรือ สายแลนธรรมดาเนี่ยแหละ เครื่องสมัยนี้มักจะมีระบบ Auto-Sensing ได้อยู่แล้ว &lt;/li&gt;
&lt;li&gt;เวลาราว 1-2 ชั่วโมง &lt;/li&gt;&lt;/ul&gt;
&lt;h1&gt;ขั้นแรก เตรียมแผ่น Windows&lt;/h1&gt;
&lt;p&gt;เนื่องจากน้องวรรณเรามีที่่ค่อนข้างจำกัด แค่ 8GB ในการจะกระเบียดกระเสียนลง Windows 7/Vista Ultimate ลงไปแบบเต็มๆ เลย ถึงแม้จะทำได้ ก็คงเหลือที่ลงโปรแกรมอีกไม่เท่าไหร่ เราจึงจำเป็นต้องทำการตัดฟีเจอร์บางส่วนออกไป ให้มันสามารถลงได้ ซึ่งบางทีซะ เทียบกับที่เราตัดไปนี่ เหมือนลง Windows Home Basic ที่ตีตราเป็น Ultimate เลยละ แต่เอาเถอะ ไม่งั้น ที่ไม่พอครับ!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ขั้นตอนแรกเลย ลง &lt;a href="http://www.oldapps.com/nLite.php?old_nlite=1" target="_blank"&gt;vLite เวอร์ชั่นเก่า&lt;/a&gt;ก่อน ทำไมต้องใช้เวร์ชั่นเก่า? เพราะเวอร์ชั่นเก่ามี Driver ที่จำเป็นอยู่ครับ ซึ่ง Microsoft เรียกร้องให้คนทำเขาเอาออกไปในเวอร์ชั่นใหม่ &lt;/li&gt;
&lt;li&gt;ลง vLite เวอร์ชั่นใหม่ ทับไปเลยที่เดิม ไม่ต้องสนใจ &lt;/li&gt;
&lt;li&gt;ทำการ Copy ไฟล์ &lt;a href="http://coresharp.net/files/folders/projects/entry789.aspx"&gt;Works.ini&lt;/a&gt; ไปไว้ในโฟลเดอร์ C:\Program Files\vLite\Presets หรือ C:\Program Files(x86)\vLite\Presets ครับ ถ้าใช้ Windows 64-bit &lt;br /&gt;&lt;br /&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_0E4C8D93.png" width="400" height="350" /&gt; &lt;br /&gt;&lt;/li&gt;
&lt;li&gt;จากนั้น เปิดโปรแกรม vLite ขึ้นมา จะเป็นหน้าจอถามให้เราลง &lt;strong&gt;WIM Filter Driver&lt;/strong&gt; ก็กดลงเลยครับ แล้วจะได้หน้าจอแบบนี้ ก็จัดการ Mount แผ่นจากไฟล์ ISO ด้วยโปรแกรม Daemon Tools หรือ จะเอาแผ่น Windows ใส่ไปเลยก็ได้ครับ ไม่ควรเป็น Windows ที่โมมาแล้ว อย่าง Dark Edition หรืออะไรแบบนี้เป็นอันขาด อาจจะใช้ไม่ได้นะ แล้ว Browse ไปที่ตัวแผ่นเลย &lt;br /&gt;&lt;br /&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_7AFBB126.png" width="550" height="440" /&gt; &lt;br /&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_0ADAD01E.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_2EB381AB.png" width="404" height="273" /&gt;&lt;/a&gt; &lt;br /&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_16606790.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;MARGIN:0px 0px 10px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_540D4F4C.png" width="446" height="208" /&gt;&lt;/a&gt; &lt;br /&gt;กด OK แล้วเลือกโฟลเดอร์ที่จะใช้ Copy File ไปเก็บไว้ ถ้ามี HDD External แนะนำให้ใส่ไว้ในนั้นเลยครับ &lt;br /&gt;&lt;/li&gt;
&lt;li&gt;เมื่อ vLite Copy ไฟล์ให้เสร็จแล้ว จะมี Edition ให้เลือกครับ ถ้าเป็น Windows 7 ต้องเลือก Ultimate เท่านั้น เพราะ Key ฟรี เป็นของ Ultimate ครับ ส่วน Windows Vista แนะนำให้เลือก Home Premium ครับ &lt;br /&gt;&lt;br /&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_12929CF3.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;MARGIN-LEFT:0px;BORDER-LEFT-WIDTH:0px;MARGIN-RIGHT:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_1794DAA2.png" width="404" height="331" /&gt;&lt;/a&gt; &lt;br /&gt;&lt;/li&gt;
&lt;li&gt;จากนั้นกด Next แล้ว vLite จะถามว่า เราจะทำอะไรบ้าง เลือกตามตัวอย่างครับ (ถ้าเป็น nLite สามารถ Integrate พวก Addonได้เลย ในขั้นตอน Integration นะครับ) &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_54D58F69.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_5AB03302.png" width="404" height="227" /&gt;&lt;/a&gt; &lt;br /&gt;&lt;/li&gt;
&lt;li&gt;ทำการแตกไฟล์ Driver ไว้ใน Folder เดียวกัน ดังรูป แต่ &lt;strong&gt;ห้ามนำ Driver Touchpad Integrate ไปด้วยเด็ดขาดครับ!&lt;/strong&gt; &lt;br /&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_4A54BB49.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;MARGIN:10px 0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_6F05D2C0.png" width="404" height="344" /&gt;&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;ในหน้านี้ มาที่แถบ Driver กดปุ่ม Insert แล้วเลือก Multiple Driver Folder จากนั้น Browse ไปที่โฟลเดอร์ที่แตกไฟล์ไว้ จากนั้นก็เลือก Driver ที่จะ Integrate ครับ &lt;br /&gt;&lt;br /&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_60E6E3C3.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_7759824A.png" width="444" height="306" /&gt;&lt;/a&gt; &lt;br /&gt;&lt;/li&gt;
&lt;li&gt;จากนั้น ไปที่ขั้นตอน Components ได้เลย เมื่ออยู่ในหน้านั้นแล้ว กดโหลด Preset จากเมนูเลยครับ &lt;br /&gt;&lt;br /&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_0C8787F3.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_64E10BC8.png" width="358" height="215" /&gt;&lt;/a&gt; &lt;br /&gt;&lt;/li&gt;
&lt;li&gt;อย่าไปเพิ่ม หรือลบอะไรมากนักนะครับ การทำเครื่องหมายถูกคือการ “เอาออก” ครับ โดยเฉพาะพวก System ทั้งหลาย แต่สิ่งที่ไปเอาเครื่องหมายถูกออกได้คือพวก ที่อยู่ใน Accessories ทั้งหลาย และ Media Center ครับ เช่น 
&lt;ul&gt;
&lt;li&gt;Games &lt;/li&gt;
&lt;li&gt;Media Center (โปรแกรมดูหนังฟังเพลง แบบเทพมากและไฮโซมาก&amp;nbsp; ไม่ค่อยเจอคนรู้จักเล่นเท่าไหร่เลยนะ แต่ฮิตมากในบางประเทศ) &lt;/li&gt;
&lt;li&gt;Paint &lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;ไปที่หน้า Tweak เลย โหลด Preset เลยเหมือนกัน ไม่ชอบอะไรตรงไหน เปลี่ยนได้ครับ ไม่ว่ากัน สิ่งที่อาจเปลี่ยนได้ คือ 
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;UAC&lt;/strong&gt; ถ้าอยากปิดไว้แต่ต้น (แต่ผมจะปิดไว้ก่อน เวลาลงโปรแกรมจะได้ไม่ต้องถามให้รำคาญ) &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DEP&lt;/strong&gt; จะเลือก Default ก็ได้ครับ จะเป็นการเปิดการกันรอยรั่ว แต่ส่วนมากแล้วจะทำให้เกมที่ ใช้ยาแก้ไอใช้ไม่ได้ &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Paging Executive&lt;/strong&gt; คือการบังคับให้ Kernel Memory อยู่ในแรมเท่านั้น ห้าม Page ลง Disk ช่วยให้เร็วขึ้นได้หน่อยนึง &lt;br /&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_0423B29C.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;MARGIN:10px 0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_4A346FAF.png" width="404" height="194" /&gt;&lt;/a&gt; &lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;จากนั้น กด Apply ได้เลย เลือก Save Changes Only เพื่อความรวดเร็ว และไม่ต้องไป Delete อะไรครับ มันช้า Continue ผ่านไปได้เลย จากนั้น รอไม่เกิน 3-5 นาที ก็เรียบร้อย Next ต่อได้ &lt;br /&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_15B39D67.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;MARGIN:10px 0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_018A5B11.png" width="400" height="442" /&gt;&lt;/a&gt; &lt;br /&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_1C930452.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;MARGIN:10px 0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_22D9DAE0.png" width="404" height="229" /&gt;&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;จากนั้น เราจะทำให้มันเป็น ISO เพื่อใช้ติดตั้งลงใน VirtualBox ครับ เริ่มนึกภาพออกหรือยังว่าเราจะทำอะไร ;) เลือก Mode เป็น Create Image นะครับ &lt;br /&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_4F1661C4.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;MARGIN:10px 0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_07C10BD2.png" width="188" height="177" /&gt;&lt;/a&gt;&amp;nbsp; &lt;br /&gt;(ถ้ามี HDD External แนะนำให้เซฟ ISO ไว้ในเครื่องครับ แล้วค่อยเล่น VM บน HDD External มันจะได้ไม่กวนเครื่องมากนักเวลามันทำงาน แล้วลงได้เร็วกว่าด้วย) หลังจากรอสักพัก ก็ได้แล้วครับ ผลงานของเรา &lt;br /&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_406BB5DF.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;MARGIN:10px 0px 0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_71F72374.png" width="244" height="110" /&gt;&lt;/a&gt; &lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;ติดตั้งใน VirtualBox&lt;/h1&gt;
&lt;p&gt;เมื่อพร้อมแล้ว ก็ลง VirtualBox ได้เลยครับ หน้าตา VirtualBox จะเป็นประมาณนี้&lt;/p&gt;
&lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_06103FFE.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;MARGIN:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_492B985E.png" width="554" height="416" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;กด New เพื่อสร้าง VM ใหม่ได้เลยครับ เลือก OS ให้ตรงกับที่เราจะใช้ ให้แรมมันไปสัก 768MB – 1024MB ได้จะดีมากครับ&lt;/p&gt;
&lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_7EE0DDB8.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_33BDBD29.png" width="465" height="417" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;เมื่อถึงขั้นตอนการสร้าง Virtual Harddisk ก็กด New แล้วเลือก Dynamic ให้พืนที่ไป 7.51GB ก็จะเท่ากับ SSD น้องวรรณเราพอดี แต่จะให้ใหญ่กว่าก็ได้ครับ อย่างผมจะลง Visual Studio 2008+SP1 ด้วย ต้องใช้ที่เยอะกว่า 8GB เวลาลง ที่สำคัญคือ อย่าลืมเปลี่ยนที่เซฟไฟล์ ถ้าเป็นไปได้ ควรเซฟไว้ที่ HDD External ครับ &lt;br /&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;MARGIN:10px 0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_610F2D2C.png" width="554" height="284" /&gt; &lt;/p&gt;
&lt;p&gt;หลังจากจบ Wizard แล้ว มาเปลี่ยน Setting นิดหน่อยครับ&lt;/p&gt;
&lt;p&gt;อย่างแรกเลย เราจะเปลี่ยนที่เซฟ Snapshot ครับ มันควรจะอยู่ใน HDD External ไม่งั้นมันจะเซฟรวมไว้ใน My Document ครับ ถ้าเครื่องสามารถกากบาท VT-X/AMD-V, Nested Paging ได้ ก็อย่าลืมกากบาทด้วย&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_09EA1C69.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_3D8262FA.png" width="444" height="406" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;ในหน้า CD-ROM ก็ทำการ Mount แผ่น Windows ที่ทำไว้ได้เลย&lt;/p&gt;
&lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_67A1EB15.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_3AE90B6F.png" width="444" height="406" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;ในหน้า Network เปลี่ยนจาก NAT เป็น Adapter Type เป็น AMD PCNET Fast III ครับ&lt;/p&gt;
&lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_64302DA0.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_384FB3E4.png" width="444" height="406" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;อย่าลืม Enable USB ด้วยละครับ&lt;/p&gt;
&lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_2D62ACCF.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_109911ED.png" width="444" height="406" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;และตรงไหนที่เก็บไฟล์โปรแกรมไว้ ก็ Share ไว้เลยครับ&lt;/p&gt;
&lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_54209D42.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_5AA3F705.png" width="444" height="406" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;เรียบร้อยแล้วก็สั่ง Start ได้เลยครับ จากนั้น เครื่องก็จะบูตเข้า การ Setup Windows ก็ติดตั้งตามปกติเลยครับ ถ้าเอาเมาส์คลิ๊กเข้าไปในหน้าต่างแล้ว มันจะล็อคไว้ ให้กดปุ่มคอนโทรล (Ctrl) ทางขวามือ เพื่อปลดเมาส์ออกมาครับ&lt;/p&gt;
&lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_052FB216.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_6A16E307.png" width="244" height="207" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_0F342D74.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_3913E002.png" width="554" height="467" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;h1&gt;เตรียมแผ่น PE เพื่อบูต&lt;/h1&gt;
&lt;p&gt;ระหว่างที่ Windows ติดตั้งอยู่ เราก็จะทำอย่างอื่นไปด้วยระหว่างรอ นั่นก็คือ การทำแผ่น PE ครับ ถ้าเรามีแผ่น PE แล้ว เราก็สามารถเปิด Aspire One พร้อมกับเครื่อง VM แล้ว Ghost ข้าม Network ได้เลย หรือจะ Ghost ใส่ HDD External โดยการเสียบ USB กับ VM ก็ได้ แจ่มมั๊ยละครับ&lt;/p&gt;
&lt;p&gt;หลังจากได้ PEBuilder มาแล้วให้แตกไฟล์ แล้วไปดูใน Folder Plugin ครับ จะมี Folder ชื่อ ghost8 อยู่ เอาไฟล์ Ghost32 ใส่ไปในนั้น &lt;br /&gt;&amp;nbsp;&lt;img style="BORDER-RIGHT-WIDTH:0px;MARGIN:10px 0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_03E70538.png" width="554" height="499" /&gt; &lt;/p&gt;
&lt;p&gt;แล้วก็แตกไฟล์การ์ดแลนของ Aspire one ใส่ใน Folder Driver ด้วย&lt;/p&gt;
&lt;p&gt;จากนั้น เปิดไฟล์ PE Builder แล้วกดดูที่ปุ่ม Plugins ดูให้แน่ใจว่า Ghost มัน Enable แล้ว ก็ไปต่อได้เลยครับ&lt;/p&gt;
&lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_3DC62257.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_7EC47BEE.png" width="554" height="410" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;โปรแกรม Bartpe ไม่มีอะไรมากมายครับ แค่ใส่ค่าแล้วกด Build เฉยๆ ก็จะได้ ISO มาแล้ว เอาไป Burn ลงแผ่นได้เลย เราก็จะได้แผ่น Boot PE แล้วครับ&lt;/p&gt;
&lt;p&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_07F09163.png" width="511" height="433" /&gt; &lt;/p&gt;
&lt;p&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_361A6750.png" width="511" height="379" /&gt; &lt;/p&gt;
&lt;p&gt;สำหรับท่านที่ไม่มี External DVD Writer มันจะมีวีธีเอาลง USB อยู่ครับ แต่ว่าผมขอข้ามไปก่อนนะ เดี๋ยวกลับมา Update พอดีดึกมากแล้วตอนนี้ อยากเขียนให้เสร็จ&lt;/p&gt;
&lt;h1&gt;ติดตั้งเครื่อง&lt;/h1&gt;
&lt;p&gt;เมื่อ Setup เสร็จ จนเห็นปลากัดแล้ว ก็ทำการลง Virtual Machine Addition ครับ เพื่อให้การทำงานของเราง่ายขึ้น หรือถ้าอยากได้เครื่องแบบคลีนๆ เลย จะไม่ลงก็ได้ แต่เวลาใช้ก็จะลำบากขึ้นหน่อยนึง หรืออาจจะไม่มี Driver&lt;/p&gt;
&lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_38834D74.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_2E5E867C.png" width="444" height="418" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;จากนั้น หลังจากลงเสร็จแล้ว ก็เป็นเวลาที่ดีที่จะเก็บ Snapshot เครื่องไว้ครับ ถ้าเกิดว่าเราทำอะไรพังไป ก็สามารถใช้หน้า&amp;nbsp; Snapshot เพื่อย้อนเวลากลับมาได้เสมอ เพราะฉะนั้น ลองเล่น ลองลงอะไรได้ตามใจชอบครับ&lt;/p&gt;
&lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_063BB190.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_0703F1AD.png" width="554" height="453" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;อ้อแนะนำว่า จะใช้อุปกร์อะไร ก็จับมาต่อให้หมดครับ อุปกรณ์ USB สามารถต่อไปที่ VM ได้เหมือนปกติเลยครับ&lt;/p&gt;
&lt;h1&gt;เตรียม Ghost&lt;/h1&gt;
&lt;p&gt;ขั้นแรก เอาไฟล์ IpsMigrationPlugin.dll ใส่ไว้ใน &lt;strong&gt;C:\Program Files\Common Files\Microsoft Shared\ink&lt;/strong&gt; ก่อนครับ&lt;/p&gt;
&lt;p&gt;จากนั้น ก็อย่าลืม Remove Guest Addition ออกก่อนละครับ เพราะเราจะพามันไปเครื่องจริงแล้ว&lt;/p&gt;
&lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_65E0CF45.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_5EE11CC0.png" width="244" height="226" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;เสร็จ Snapshot อีกครั้ง ชื่อว่า Just Before Sysprep กันพลาด เพราะพลาดแล้ว เครื่องมักจะพังเลย ต้องทำใหม่หมด&lt;/p&gt;
&lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_67EDA841.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_2160926C.png" width="554" height="462" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;Sysprep คือกระบวนการในการเตรียมเครื่อง เพื่อสร้าง Image ครับ ระบบนี้จะทำให้วินโดวส์กลับสู่สภาพ เหมือนลงใหม่อีกครั้ง ซึ่ง Windows จะทำการ Detect Hardware ใหม่ ถาม User/Password TimeZone อะไรพวกนั้นใหม่หมดเลย แล้วสามารถนำไปลงได้ทุกเครื่้องได้อย่างไม่มีปัญหา เพราะอันที่จริง Vista/7 ตอนที่ Microsoft เขาทำแผ่นให้เรา เขาก็ใช้ไอ้ Sysprep นี่ละครับ (ถ้า Windows XP จะต้องลง Driver IDE Controller ให้ครบ จึงจะบูตได้ครับ ลองดูจาก&lt;a href="http://www.spicydog.org/forum/index.php?showtopic=398&amp;amp;mode=threaded" target="_blank"&gt;โพสนี้&lt;/a&gt; และ &lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=3E90DC91-AC56-4665-949B-BEDA3080E0F6&amp;amp;displaylang=en" target="_blank"&gt;XP ก็มี Sysprep เหมือนกัน&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;เข้าไปเช็คด้วยว่า เซอร์วิส Windows Media Sharing ไม่ได้รันอยู่ โดยการคลิ๊กขวาที่คำว่า Computer ใน Start Menu แล้วเลือก Manage ถ้ารันอยู่ กด Stop ก่อนครับ&lt;/p&gt;
&lt;p&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_573561B9.png" width="550" height="412" /&gt; &lt;/p&gt;
&lt;p&gt;เมื่อพร้อมแล้ว Start –&amp;gt; cmd แล้วรันแบบ Admin ครับ&lt;/p&gt;
&lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_63FF920A.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_2067E0E8.png" width="445" height="268" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;พิมพ์ว่า&lt;/p&gt;
&lt;p&gt;cd sysprep กด Enter&lt;/p&gt;
&lt;p&gt;sysprep กด Enter&lt;/p&gt;
&lt;p&gt;จะได้หน้าจอดังภาพ เลือกคำสั่งตามในรูป แล้วก็เริ่มได้เลยครับ&lt;/p&gt;
&lt;p&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_24FDEBA2.png" width="486" height="426" /&gt; &lt;/p&gt;
&lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_6ADEF8F5.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_57BDCC49.png" width="316" height="183" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;เมื่อเสร็จแล้ว โปรแกรมจะปิดไป เราก็ไปเปลี่ยนแผ่น CD เป็น pe.iso ที่เราสร้างรอไว้&lt;/p&gt;
&lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_0F23DD78.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_33D4F4EF.png" width="244" height="82" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;แล้วสั่ง Restart เครื่องเลยครับ เราพร้อมเริ่มขั้นต่อไปแล้ว&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_0436D663.png" width="551" height="123" /&gt; &lt;/p&gt;
&lt;h1&gt;เริ่ม Ghost กันเลย&lt;/h1&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;หลังจาก VM Restart ก็ให้กด F12 แล้วเลือกบูตจาก CD ครับ พอเข้า VM ได้ ก็จะได้คำถามว่า จะ Start Network หรือเปล่า แน่นอนครับว่า Start แล้วเลือก Manual นะครับ&lt;/p&gt;
&lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_170B5A0D.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_4B5BBAEE.png" width="554" height="416" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;ทางฝั่ง Aspire One ก็ทำแบบเดียวกัน แล้วก็เสียบสายแลนกับเครื่องที่เราทำ VM อยู่ด้วยครับ&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;หลังจากทำงานสักพัก มันจะเสนอให้ใช้ IP 192.168.0.240 ครับ เครื่อง VM ให้ตั้งเป็น 192.168.0.240 ส่วน Aspire One ตั้งเป็น 192.168.0.241 แล้วกัน (มันอาจจะบอกว่า IP ชนกัน ไม่ต้องสนใจครับ)&lt;/p&gt;
&lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_162EE024.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_26C6DB12.png" width="429" height="470" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;เรียบร้อยแล้ว ใช้โปรแกรม A43 เข้าไปหาไฟล์ Ghost32 ในแผ่นแล้วเปิดมันขึ้นมา ทั้งใน VM และ AspireOne ครับ&lt;/p&gt;
&lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_7A3DAB2B.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_5F80E945.png" width="554" height="416" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;จากนั้น ให้เครื่อง AspireOne เป็น Slave&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_11D496F8.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_1C91EE4D.png" width="283" height="173" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;ส่วน VM เป็น Master แล้วใส่ IP ของ AspireOne ลงไป&lt;/p&gt;
&lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_181B6D86.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_3BD49520.png" width="380" height="184" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;ทางฝั่ง AspireOne (Slave) จะเห็นแบบนี้&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_5059E49E.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_421B6BAE.png" width="431" height="129" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;ส่วนทางฝั่ง VM จะเห็นเหมือน Ghost ปกติ ก็เลือก Disk To Disk เลยครับ จะเป็นการ Clone จาก VM ไปยัง AspireOne ผ่านสายแลน&lt;/p&gt;
&lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_0FB7982F.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_01791F3F.png" width="213" height="123" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_2127F907.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_07BFF5CD.png" width="481" height="169" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_5966701F.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_3FFE6CE5.png" width="485" height="165" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;จัดการแบ่ง Partition ให้ลงตัว&lt;/p&gt;
&lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_5483BC63.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_5821D740.png" width="549" height="239" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_379A978E.png"&gt;&lt;img style="BORDER-RIGHT-WIDTH:0px;DISPLAY:inline;BORDER-TOP-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-LEFT-WIDTH:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_7E178796.png" width="389" height="271" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;เรียบร้อยครับ ข้อมูลก็จะโดนถ่ายจาก VM ไปยัง Aspire One แล้ว&lt;/p&gt;
&lt;p&gt;แน่นอนว่า จะใช้การทำ Image เก็บใน Shared Folder ก็ได้นะครับ เราต่อแลนได้แล้วนี่!&lt;/p&gt;
&lt;h1&gt;&lt;/h1&gt;
&lt;h1&gt;สงท้าย&lt;/h1&gt;
&lt;p&gt;เอาละครับ หวังว่าโพสนี้ น่าจะเป็นประโยชน์กับทุกๆ ท่าน ทั้งคนที่ใช้ AspireOne และไม่ได้ใช้ AspireOne ให้มีของเล่นเยอะขึ้น โดนเฉพาะคนทำงานห้องคอม (ผมเข้าใจดีมากๆ) คงจะมีอาวุธใหม่ในการทำงานให้เสร็จเร็วขึ้น สำหรับตอนนี้ ราตรีสวัสดิ์ก่อนครับ&lt;/p&gt;&lt;img src="http://coredeveloper.net/aggbug.aspx?PostID=672" width="1" height="1"&gt;</description></item><item><title>เรื่องเล่าจากเดี่ยวโปรแกรมเมอร์ (ตอนที่ 2)</title><link>http://coredeveloper.net/blogs/nantcom/archive/2009/03/25/637.aspx</link><pubDate>Tue, 24 Mar 2009 17:14:25 GMT</pubDate><guid isPermaLink="false">86cc649a-bf28-4d34-a9ef-d75c61f34293:637</guid><dc:creator>นันคอม</dc:creator><slash:comments>6</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://coredeveloper.net/blogs/nantcom/rsscomments.aspx?PostID=637</wfw:commentRss><comments>http://coredeveloper.net/blogs/nantcom/archive/2009/03/25/637.aspx#comments</comments><description>&lt;p&gt;เมื่อเกือบปีหนึ่งมาแล้ว ผมได้แปลบทความชื่อว่า เรื่องเล่าจากเดี่ยวโปรแกรมเมอร์ไว้ ในสไตล์เล่าให้เพื่อนฟังกันขำๆ และผมได้แปลไว้จนถึงตอนที่ผู้เขียนเล่าถึงประสบการณ์และคำแนะนำต่างๆ ซึ่งยังเหลืออีกกว่าครึ่ง ที่ผมยังไม่ได้ขึ้นมาสานต่อ จนกระทั่งผมเอง ก็ลืมมันไปในที่สุด (แหะๆ) จนเมื่อตอนที่ผมแก้ Layout ให้ระบบมันแสดงบทความยาวขึ้นนี่ละครับ ปรากฏว่า บทความนี้ ถูกดึงขึ้นมาเป็นบทความท้ายสุดพอดี (ซึ่งตอนนี้ ก็โดนดันลงไปอีกแล้ว) ผมจึงได้ระลึกถึงมันขึ้นมาได้ เพราะผมเอง ก็ยังอ่านไม่จบเหมือนกัน (ใช้เวลาอ่านมากว่า 7 ปีแล้วเนี่ย!!!)&lt;/p&gt;  &lt;p&gt;สำหรับบทความในตอนแรก อยู่ที่ &lt;a title="http://coresharp.net/blogs/article/archive/2008/04/10/178.aspx" href="http://coresharp.net/blogs/article/archive/2008/04/10/178.aspx"&gt;http://coresharp.net/blogs/article/archive/2008/04/10/178.aspx&lt;/a&gt; ครับ ผมกะว่า จะแบ่งเป็นทั้งหมด 4 ตอน ตอนนี้ก็จะเป็นครึ่งหลัง ของ ”เรื่องเล่า” ในภาคไม่เกี่ยวกับเทคนิค ของผู้เขียนเขาครับ&lt;/p&gt;  &lt;p&gt;---- ตอนที่ 2 ---&lt;/p&gt;  &lt;h2&gt;โปรแกรมเมอร์ที่ดี ต้องนอนแต่หัวค่ำ&lt;/h2&gt;  &lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/khonkaenlinkcom71_314D47AB.gif"&gt;&lt;img style="border-right-width:0px;margin:0px 10px 0px 0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="khonkaenlink-com-71" border="0" alt="khonkaenlink-com-71" align="left" src="http://coredeveloper.net/blogs/nantcom/khonkaenlinkcom71_thumb_0CBBBA27.gif" width="50" height="50" /&gt;&lt;/a&gt;ผู้เขียนทำงานล่วงเวลาอยู่บ่อยครั้ง บางครั้งก็ดึกมาก บางครั้งก็ยันสว่าง บางครั้ง สว่างไปสองครั้งแล้ว งานก็ยังไม่เสร็จ บางครั้ง มันก็เป็นเรื่องที่ช่วยไม่ได้จริงๆ แต่พอลองมองย้อนกลับมาดู ก็พบว่า จริงๆ แล้ว มันก็เป็นความผิดพลาดของผู้เขียนเองนั่นแหละ และถ้าผู้เขียนได้วางแผนดีๆ ตั้งแต่แรก งานล่วงเวลา(ฟรี) พวกนี้ ก็ไม่จำเป็นเลย&lt;/p&gt;  &lt;p&gt;จากการวิเคราะห์อย่างเป็นกลางที่สุดเท่าที่ผู้เขียนจะกลางได้ ก็สรุปได้ว่า ปัญหาอาการนอนไม่หลับ (เพราะหลับไม่ได้) นี้ เกิดจาก:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;ไม่ได้”สร้างความคาดหวัง” ที่ถูกต้องไว้กับลูกค้า &lt;/li&gt;    &lt;li&gt;มัวแต่”เจิม”/”ปิดทอง” (ใส่อะไรที่ไม่จำเป็น/ลองนู่นลองนี่) &lt;/li&gt;    &lt;li&gt;เทสงานไม่พอ &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;ปัญหาหนึ่งของการทำงานล่วงเวลาก็คือ มันจะกินเวลาชีวิตส่วนตัวคุณไป แถมยังทำให้เกิดนิสัยไม่ดีด้วย ไม่ดีที่ว่านี้ คือไม่ดีกับตัวเราเองนี่แหละ อย่างเช่นมีครั้งหนึ่งที่ผู้เขียนบอกกับตัวเองว่า จะต้องทำฟีเจอร์นี้ให้ทันวันจันทร์เช้า!…แล้วถ้าไม่ทำตอนกลางคืน มันจะไปเสร็จตอนเช้าได้ยังไงละ!&lt;/p&gt;  &lt;p&gt;นอกจากนี้ ปัญหาสำคัญที่สุดของการทำงานดึกๆ ก็คือ ยิ่งดึก คุณภาพก็ยิ่งด้อยลง ทั้งสาเหตุจากจิตใต้สำนึกที่อยากจะนอนเต็มที และความรีบด้วยประกอบกัน และเมื่อผู้เขียนทราบถึงปัญญาเหล่านี้แล้ว ผู้เขียนก็ลงมือจัดการกับมันทันที โดยการเปลี่ยนแปลงเหล่านี้&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;ไม่พยายามตั้งเงื่อนเวลาเป็น “ตอนเช้า” โดยเฉพาะอย่างยิ่ง วันจันทร์ &lt;em&gt;[ผู้แปล: ถ้าทำให้ทันวันจันทร์ ก็แสดงว่าต้องทำเสาร์-อาทิตย์ด้วยใช่ไหมละ] &lt;/em&gt;&lt;/li&gt;    &lt;li&gt;ไม่เอาเวลาส่วนตัว หรือเวลาตอนกลางคืน รวมเข้าไปในการวางแผน คิดตามวันทำงานจริงๆ เท่านั้น &lt;/li&gt;    &lt;li&gt;แบ่งเวลาให้ถูกต้อง ผู้เขียนจะคอยควบคุมเวลาการทานอาหารกลางวัน และเวลาเช็คเมล์ให้เหมาะสมในแต่ละวัน &lt;/li&gt;    &lt;li&gt;ผู้เขียนจะใช้เวลาตอนเช้า ในการเขียนโปรแกรม และตอนบ่าย ในการเทส/ดีบัก &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/agt_home_1059D504.png"&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;margin-left:0px;border-left-width:0px;margin-right:0px;" title="agt_home" border="0" alt="agt_home" align="right" src="http://coredeveloper.net/blogs/nantcom/agt_home_thumb_0DD0A346.png" width="62" height="62" /&gt;&lt;/a&gt;และที่สำคัญ อย่าลืมว่า เวลาส่วนตัวสำคัญเหมือนกัน แต่ก็ใช่ว่าจะเอาแต่เวลาส่วนตัวอย่างเดียว ถ้างานเร่งจริงๆ ก็พร้อมที่จะทำงานหามรุ่ง หามค่ำได้ &lt;/p&gt;  &lt;p&gt;&lt;em&gt;[ผู้แปล: แม่ผมเองก็เคยบอกว่า ทำงานล่วงเวลามันมองได้สองด้าน ด้านที่ดีก็ดีไป ด้านที่ไม่ดีก็คือ เราทำงานไม่เสร็จหรือเปล่า ถึงต้องอยู่จนเย็น &lt;strong&gt;กลางวันมัวไปทำอะไรอยู่&lt;/strong&gt;???]&lt;/em&gt;&lt;/p&gt;  &lt;h2&gt;สร้างความหวังที่เป็นไปได้…ด้วยการสื่อสารให้เข้าใจกัน&lt;/h2&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;margin-left:0px;border-left-width:0px;margin-right:0px;" title="gaim" border="0" alt="gaim" align="right" src="http://coredeveloper.net/blogs/nantcom/gaim_693F15C1.png" width="191" height="191" /&gt; ผู้เขียนได้ร่วมงานกับคนที่มีเหตุผลมากๆ หลายคน แต่การที่คนเหล่านี้ เกิดไม่พอใจในผลงานของผู้เขียน ส่วนหนึ่งก็มาจากเรื่องความคาดหวังนี่เอง&lt;/p&gt;  &lt;p&gt;มียู่ครั่งหนึ่งที่ผู้เขียนพยายามจะแสดงเดโมให้ลูกค้าดู ซึ่งยังเป็นโซลูชั่นที่ยังพัฒนาไม่เสร็จ การเดโมผ่านไปด้วยดี แต่ลูกค้าไม่ปลื้มด้วย เพราะเขาไม่เห็นฟีเจอร์ที่เขาสนใจอยากได้อยู่ในนั้น แถมยังเลยเถิดไปถึงการคิดไปถึงว่า เวลาที่ผู้เขียนใช้ไปกลับไม่ได้นำเอาไปสร้างฟีเจอร์ที่เขาต้องการ ซึ่งอันที่จริงแล้วมันก็ถูกต้อง เพราะลูกค้าเขาไม่ได้อยากเห็นโปรแกรม เขาอยากเห็นว่าเราเข้าใจปัญหาของเขา และเราสามารถแก้ปัญหาให้เขาได้มากกว่า การไปเดโมโปรแกรมให้เขาดูเร็วเกินไป ก็มีแต่จะทำให้ความคาดหวังที่เขาเคยมีในตัวเราเสียไปเท่านั้น&lt;/p&gt;  &lt;p&gt;&lt;em&gt;[ผู้แปล: ถึงตรงนี้ ผมก็นึกได้ว่า ตอนไปพบลูกค้ากับเจ้านาย แล้วผมบอกไปว่า ได้ทดลองทำเดโมมาแล้ว อยากจะโชว์หรือเปล่า เขาก็บอกผมมาว่า บางทีทำเร็วเกินไปก็ไม่ดีนะ เราจะไม่ได้ขายเอา….เพราะอะไรรู้ไหมครับ? หนึ่งในเทคนิคการขาย คือ การสร้างความคาดหวังไงครับ แล้วถ้าเรายังไม่รู้ว่าเขาต้องการอะไร แล้วไปโชว์เลย เขาก้ออาจจะเลยเถิดเข้าใจอะไรผิดไปได้]&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;และอีกครั้งหนึ่ง เจ้านายมอบหมายให้ผู้เขียนทำโปรเจคชิ้นหนึ่ง และถามว่า จะต้องใช้เวลาเท่าไหร่ถึงจะโชว์ได้ ผู้เขียนก็ประเมินไปว่า สองอาทิตย์ แต่ว่า เราทั้งคู่ต่างเข้าใจไม่ตรงกัน เจ้านายเข้าใจว่า สองอาทิตย์เสร็จ แต่ผู้เขียนคิดว่า สองอาทิตย์คือเดโม ผลที่ออกมาก็คือ เจ้านายนัดลูกค้าไปแล้วว่า จะสามารถให้ดูงานได้ในสองอาทิตย์ ในขณะที่เรามีแต่เดโม และผู้เขียนก็ต้องนั่งหลังขดหลังเข็งทำให้่มันใช้ได้ (ในระดับหนึ่ง) เพื่อให้ทันโชว์ลูกค้า ถ้าเพียงแค่เราพูดกันให้เข้าใจว่า อะไรคือ โชว์ได้ และจะโชว์อะไรบ้าง เงื่อนเวลาที่ตั้งไว้ ก็คงไม่ผูกปมกันซะแน่นขนาดนี้ และผู้เขียน ก็คงจะไม่เสียเครดิต และเสียเวลาส่วนตัวไป โดยที่ผลสุดท้ายคือ ไม่มีใครพอใจเลย ทั้งเจ้านาย ลูกค้า และตัวผู้เขียนเอง&lt;/p&gt;  &lt;h2&gt;ผมคือ Software Architect!&lt;/h2&gt;  &lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/kuser_78FEAAC5.png"&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;margin-left:0px;border-left-width:0px;margin-right:0px;" title="kuser" border="0" alt="kuser" align="left" src="http://coredeveloper.net/blogs/nantcom/kuser_thumb_5EBE41A1.png" width="128" height="128" /&gt;&lt;/a&gt;ในยุคที่เด็กๆ สามารถใส่โปรแกรม หุ่นยนต์เลโก้ได้อย่างทุกวันนี้ และ Source Code ก็มีให้ Copy อยู่ทั่วทุกหนแห่ง คำว่า “โปรแกรมเมอร์” แม้ว่ามันจะเข้าใจง่าย แ ต่คุณค่ามันก็ลดลงจากเมื่อก่อนแล้ว ผู้เขียนจึงพยายามเสาะแสวงหา ชื่อตำแหน่งที่สมศักดิ์ศรีเดี่ยวโปรแกรมเมอร์มาใช้ซักหน่อย และหนึ่งในนั้นคือ “Senior Software Architect” ซึ่งเป็นการดีด้วย เพราะมันคือการปูทางให้กับอนาคต ว่าที่สุดแล้ว คุณต้องการจะเป็นอะไร ในสายอาชีพ&lt;/p&gt;  &lt;p&gt;และอีกนัยนึง ผมก็ไม่ใช่ “Computer Guy” ด้วย การมี title เริ่ดๆ แบบนี้ ก็สามารถกันคนที่จะเดินมาที่โต๊ะ เพื่อให้ช่วยฟอร์แมตเครื่อง หรือลงวินโดวส์ให้ เพราะนั่นจะทำให้ผู้เขียนไม่สามารถทำงานให้เสร็จลุล่วงได้ และยังทำให้เสียสมาธิอีกต่างหาก&lt;/p&gt;  &lt;p&gt;&lt;em&gt;[ผู้แปล: มีคนเคยบอกผมว่า อาชีพที่ตกต่ำที่สุดของเด็ก Com Sci คือ Programmer ครับ เพราะโดยเนื่องานแล้ว Programmer แค่ Code จริงๆ แทบไม่ได้คิดอะไรเลย…ในขณะที่เราเรียนกันมามากมาย ทั้งเรื่องการ Optimize Performance เรื่อง Data Structure อีกร้อยแปด…&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;&lt;em&gt;การที่ผู้เขียนสามารถเรียกตัวเองแบบนั้นได้ ก็เพราะว่าเขาทำทุกอย่างครับ (จากที่เขาเล่านะ) ตั้งแต่ออกแบบ รวมไปถึงการคุยกับลูกค้า ซึ่งก็ไม่แปลกเลย เพราะนี่คืองาน Architect เลยละ]&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;&lt;em&gt;------&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;สำหรับวันนี้ ขอพอแค่นี้ก่อนครับ ไว้โอกาสหน้า ผมจะมาแปลให้อ่านกันต่อ ในส่วนต่อไป ซึ่งจะเป็นเรื่อง Technical แล้วละ นอกจากนี้ ผู้เขียนเขายังมีบทความน่าอ่านอีกหลายร้อยหน้าเลย (เขาเขียนอยู่กี่ปีแล้วเนี่ย!) ถ้าสนใจ ลองตามไปอ่านได้ครับ&lt;/p&gt;  &lt;p&gt;&lt;a title="http://www.codeproject.com/script/Articles/MemberArticles.aspx?amid=8976" href="http://www.codeproject.com/script/Articles/MemberArticles.aspx?amid=8976"&gt;http://www.codeproject.com/script/Articles/MemberArticles.aspx?amid=8976&lt;/a&gt;&lt;/p&gt;&lt;img src="http://coredeveloper.net/aggbug.aspx?PostID=637" width="1" height="1"&gt;</description></item><item><title>โหลดบิต ปิดเครื่อง ด้วย WiFi Router + ดูทีวี BuddyBB ได้ (ตอนที่ 1)</title><link>http://coredeveloper.net/blogs/nantcom/archive/2009/03/24/wifi-router-buddybb-1.aspx</link><pubDate>Tue, 24 Mar 2009 15:47:34 GMT</pubDate><guid isPermaLink="false">86cc649a-bf28-4d34-a9ef-d75c61f34293:636</guid><dc:creator>นันคอม</dc:creator><slash:comments>1</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://coredeveloper.net/blogs/nantcom/rsscomments.aspx?PostID=636</wfw:commentRss><comments>http://coredeveloper.net/blogs/nantcom/archive/2009/03/24/wifi-router-buddybb-1.aspx#comments</comments><description>&lt;p&gt;&lt;img style="border-bottom:0px;border-left:0px;display:inline;border-top:0px;border-right:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_4000EABE.png" width="540" height="353" /&gt; &lt;/p&gt;  &lt;p&gt;ผมว่าเรื่องที่ผมกำลังจะเขียนนี่คงจะเก่าแล้วละ เพราะเขาเล่นกันมานานแล้ว และผมเองก็เล่นมาเกินสองปีแล้วเหมือนกัน แต่ส่วนหนึ่งที่ผมตัดสินใจเขียน คือ เผื่อผมจะเก็บไว้ดูเองด้วยครับ เพราะเมื่อไม่นานนี้ ผมทดลอง Firmware ตัวใหม่ไป แล้วปรากฏว่ามันไม่ Work แล้วพอเอาตัวเก่ามาลง ก็ปรากฏว่าจำไม่ได้แล้วว่าทำอะไรไปบ้าง (เล่นนานจนลืม) ดังนั้น มาเริ่ม Set Router ใหม่ พร้อมกับผมเลยแล้วกัน :) สำหรับ Router ของผมคือ ASUS WL-500g Premium V แรก ครับ สำหรับผู้ที่ใช้รุ่นอื่น ลองดูได้ว่า &lt;a href="http://www.openwrt.org" target="_blank"&gt;Firmware ที่ผมใช้&lt;/a&gt; รุ่นของคุณ สามารถลงได้หรือไม่ ได้&lt;a href="http://wiki.openwrt.org/OpenWrtDocs/Hardware" target="_blank"&gt;จากใน WiKi&lt;/a&gt; เขาครับ&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;font color="#ff8000"&gt;* Bittorrent เป็นโปรโตคอลสำหรับแลกเปลี่ยนไฟล์ แต่ไฟล์บางไฟล์ ก็มีลิขสิทธิ์ ดังนั้น ผมไม่สามารถรับผิดชอบต่อการละเมดลิขสิทธิ์ใดๆ ที่อาจเกิดขึ้นได้ จากการใช้ขั้นตอนเหล่านี้นะครับ&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#ff8000"&gt;* การ Flash Router มีความเสี่ยงต่อการเสียหาย หรือการหมดการรับประกันได้ ถ้าทำพัง ก็ตัวใครตัวมันนะครับ อิอิ&lt;/font&gt;&lt;/p&gt;  &lt;h2&gt;สรุปความต้องการ&lt;/h2&gt;  &lt;p&gt;สำหรับสิ่งที่ผมต้องการให้ Router ทำได้ ก็มีตามนี้ครับ…สรุปเป็นตารางให้สวยงามซะเลย ซึ่งก็ต้องมีของที่ใช้คู่กันอยู่ด้วย ดาวน์โหลด และเตรียมโปรแกรมให้พร้อมเลยครับ&lt;/p&gt;  &lt;table border="0" cellspacing="0" cellpadding="2" width="550"&gt;     &lt;tr&gt;       &lt;td valign="top" width="210"&gt;&lt;strong&gt;คุณสมบัติ&lt;/strong&gt;&lt;/td&gt;        &lt;td valign="top" width="340"&gt;&lt;strong&gt;สิ่งที่ต้องใช้/ทำ&lt;/strong&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="210"&gt;1) ลงโปรแกรมได้&lt;/td&gt;        &lt;td valign="top" width="340"&gt;&amp;#160;&lt;a href="http://www.x-wrt.org" target="_blank"&gt;X-Wrt&lt;/a&gt; OpenWRT Firmware          &lt;br /&gt;&lt;a href="http://downloads.x-wrt.org/xwrt/kamikaze/8.09/brcm-2.4/extra/openwrt-brcm-2.4-squashfs.trx" target="_blank"&gt;สำหรับ ASUS WL-500gP แบบ xtra           &lt;br /&gt;&lt;/a&gt;Thumb Drive เก่าๆ 1 ตัว สำหรับลงโปรแกรม มีที่ราวๆ 256MB กำลังดี&amp;#160; &lt;br /&gt;สายแลน สำหรับต่อตรงกับ Router&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="210"&gt;2) Web Server, FTP&lt;/td&gt;        &lt;td valign="top" width="340"&gt;Web Server (มีในตัว)         &lt;br /&gt;vsftpd สำหรับ FTP&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="210"&gt;3) ต่อ Harddisk USB และแชร์ไฟล์&lt;/td&gt;        &lt;td valign="top" width="340"&gt;Samba Server (มีในตัว)         &lt;br /&gt;และ Harddisk ว่างๆ ซักลูก ที่ Format ด้วย Ext3          &lt;br /&gt;ต้องใช้แผนบูต Linux และ Virtual Machine ดีๆ สักตัวที่ต่อ USB ได้ อย่าง &lt;a href="http://www.virtualbox.org/wiki/VirtualBox" target="_blank"&gt;VirtualBox&lt;/a&gt; หรือ &lt;a href="http://www.vmware.com/download/player/" target="_blank"&gt;VMWare Player&lt;/a&gt; ในการเตรียม&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="210"&gt;4) โหลดบิต&lt;/td&gt;        &lt;td valign="top" width="340"&gt;&lt;a href="http://oleg.wl500g.info/bin/mlnet/" target="_blank"&gt;MLDonkey&lt;/a&gt; ที่รัน บน Router          &lt;br /&gt;และ &lt;a href="http://www.google.co.th/search?hl=th&amp;amp;client=firefox-a&amp;amp;rls=org.mozilla%3Aen-US%3Aofficial&amp;amp;hs=fqR&amp;amp;q=sancho-0.9.4-59-win32.exe&amp;amp;btnG=%E0%B8%84%E0%B9%89%E0%B8%99%E0%B8%AB%E0%B8%B2&amp;amp;meta=" target="_blank"&gt;Sancho&lt;/a&gt; สำหรับควบคุมจาก PC&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="210"&gt;5) ดูทีวี BuddyBB ได้&lt;/td&gt;        &lt;td valign="top" width="340"&gt;แบ่ง VLAN&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="210"&gt;6) SSH/Server&lt;/td&gt;        &lt;td valign="top" width="340"&gt;&lt;a href="http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html" target="_blank"&gt;Putty.exe&lt;/a&gt; ใช้ในการต่อเข้าไป&lt;/td&gt;     &lt;/tr&gt;   &lt;/table&gt;  &lt;p&gt;หลังจากเตรียมโหลด/ลงโปรแกรมพร้อมแล้ว ขอบอกย้ำอีกครั้ง ว่าผมต่อ ADSL ในบ้านแบบนี้นะครับ (ภาพเดียวกัน กับในหน้า &lt;a href="http://coresharp.net/blogs/projects/archive/2008/04/07/capturebuddy.aspx" target="_blank"&gt;CaptureBuddy&lt;/a&gt;) เพราะผมจะอัดรายการทีวีด้วย เลยต้องเอาคอม ไปอยู่ VLAN เดียวกับ Set Top Box (วง WAN) ครับ&lt;/p&gt;  &lt;p&gt;&lt;img style="border-bottom:0px;border-left:0px;display:inline;border-top:0px;border-right:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_37F9228F.png" width="464" height="292" /&gt; &lt;/p&gt;  &lt;p&gt;ก็ได้เวลาเริ่มกันละ&lt;/p&gt;  &lt;h2&gt;เตรียม Harddisk และ Thumb Drive&lt;/h2&gt;  &lt;p&gt;หลังจากเราได้ไฟล์ slax-6.0.9.iso มาแล้ว เราก็ตั้งให้ Virtual Machine บูตจากแผ่นนี้ เพื่อเข้า Linux ครับ อันนี้สุดแล้วแต่ว่าคุณจะใช้ VM ตัวไหน ถ้าใช้ VMWare Player สามารถไปที่เว็บไซต์ &lt;a href="http://www.easy-vmx.com"&gt;www.easy-vmx.com&lt;/a&gt; เพื่อสร้าง Virtual Machine ได้ครับ เนื่องจากตัว Player จะไม่สามารถสร้างให้เราได้ (ก็มันชื่อ Player นี่เนาะ)&lt;/p&gt;  &lt;p&gt;เมื่อบูตได้ จะเจอกับหน้าจอประมาณนี้ครับ สามารถปล่อยให้มัน Auto เปิดให้เราเลยได้ หรือถ้าขี้เกียจรอ ก็เลือกอันแรกครับ&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_0F0E0D86.png"&gt;&lt;img style="border-bottom:0px;border-left:0px;display:inline;border-top:0px;border-right:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_78F77C26.png" width="244" height="184" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;เมื่อบูตเสร็จ (สังเกตว่าเร็วมาก เหอๆ) ก็ต่อ Flash Drive และเข้ากับ VM ได้เลย อันนี้แล้วแต่ VM ที่คุณใช้ครับ แต่ยังไงซะ ก็ต้องเสียบกับเครืองเราจริงๆ เนี่ยแหละ&lt;/p&gt;  &lt;p&gt;เมื่อ VM มองเห็นแล้ว KDE มันจะขึ้นหน้าต่างคล้ายๆ Windows มาถามครับ ให้ตอบ Cancel ไปเลย &lt;/p&gt;  &lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_62552DDF.png"&gt;&lt;img style="border-bottom:0px;border-left:0px;display:inline;border-top:0px;border-right:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_4E5B9B49.png" width="270" height="284" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;img style="border-bottom:0px;border-left:0px;margin:0px 10px 0px 0px;display:inline;border-top:0px;border-right:0px;" title="image" border="0" alt="image" align="left" src="http://coredeveloper.net/blogs/nantcom/image_5EF39637.png" width="84" height="45" /&gt;&lt;img style="border-bottom:0px;border-left:0px;margin:0px 0px 0px 10px;display:inline;border-top:0px;border-right:0px;" title="image" border="0" alt="image" align="right" src="http://coredeveloper.net/blogs/nantcom/image_5A7D1570.png" width="324" height="184" /&gt;แล้วไปกดที่ปุ่ม Terminal เพื่อเปิด Terminal ขึ้นมาครับ&amp;#160;&amp;#160; เมื่อเปิดได้แล้ว พิมพ์ fdisk –l เพื่อดูว่า Flash Drive เรา มันอยู่ที่ Path ไหน ใน Slax เพื่อที่เราจะได้ใช้ Fdisk เปิดดูได้ถูก (อย่าตกใจครับ ผมไม่ได้มี Flash Drive 32GB หรอก เหอๆ เพื่อนซื้อมาฝากจากเซินเจ้นครับ ใช้ได้จริงๆ ราว 512MB :P)&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;เมื่อรู้แล้ว ก็อย่ารอช้าครับ พิมพ์ว่า &lt;strong&gt;fdisk /dev/sdb&lt;/strong&gt; ได้เลย&lt;/p&gt;  &lt;p&gt;พอได้แล้ว เราจะลบทุกสิ่งอย่างในตัวมันครับ ให้พิมพ์ o (ตัว โอ) แล้วกด Enter โปรแกรมก็จะตอบมาแบบนี้&lt;/p&gt;  &lt;p&gt;&lt;img style="border-bottom:0px;border-left:0px;display:inline;border-top:0px;border-right:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_11E3269F.png" width="320" height="133" /&gt; &lt;/p&gt;  &lt;p&gt;กด n เพื่อสร้าง Partition ใหม่&lt;/p&gt;  &lt;p&gt;ตอบว่า p เพื่อเลือก Primary Partition&lt;/p&gt;  &lt;p&gt;และตอบว่า 1 เพื่อสร้าง Primary Partition ที่ 1&lt;/p&gt;  &lt;p&gt;และตอบ 1 อีกที เพื่อสร้าง Parition ตั้งแต่ Cylinder แรก&lt;/p&gt;  &lt;p&gt;จากนั้นตอบ +128M เพื่อสร้าง Parition ขนาด 128MB สำหรับ Partition นี้ เราจะเอาไว้ทำ Swap ครับ เพราะการทำงานที่เราจะให้ Router ทำ มันกินแรมเยอะมาก มีแค่ 32MB คงจะไม่ไหว&lt;/p&gt;  &lt;p&gt;จากนั้น ทำแบบนี้ซ้ำอีกครั้ง เพื่อสร้าง Partition ที่ 2 สำหรับลงโปรแกรม สำหรับครั้งนี้ เราสามารถกด Enter ผ่านไปหมดได้เลย และใช้ค่า Default ซึ่งจะทำให้เราได้ Parition 2 ที่เต็มพื้นที่ที่เหลือพอดี&lt;/p&gt;  &lt;p&gt;จากนั้น กด w เพื่อเขียน Parition ลง Flash Drive ซึ่งก็จะได้คำตอบ ประมาณนี้ครับ&lt;/p&gt;  &lt;p&gt;&lt;img style="border-bottom:0px;border-left:0px;display:inline;border-top:0px;border-right:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_50687445.png" width="404" height="269" /&gt; &lt;/p&gt;  &lt;p&gt;เมื่อสร้าง Partition แล้ว เราก็ต้อง Format ครับ สำหรับ Parition แรก คือ Swap ให้ใช้คำสั่ง &lt;strong&gt;mkswap /dev/sdb1&lt;/strong&gt; ก็ทำได้โดยการใช้คำสั่ง ส่วน Partition ที่ใช้เก็บไฟล์ เราจะใช้เป็น ext3 ซึ่งก็คือคำสั่ง &lt;strong&gt;mke2fs –j /dev/sdb2&lt;/strong&gt; (ถ้าไม่แน่ใจว่าคือ Parition อะไรบ้าง ลองใช้ fdisk-l ดูได้ครับ)&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_5974FFC6.png"&gt;&lt;img style="border-bottom:0px;border-left:0px;display:inline;border-top:0px;border-right:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_2CEBCFE0.png" width="244" height="215" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;เมื่อเรียบร้อยแล้ว ก็ทำแบบเดียวกัน กับ Harddisk External ครับ แต่เราใช้แค่ Partition เดียวแบบ Ext3 ก็พอ&lt;/p&gt;  &lt;h2&gt;เตรียม Router&lt;/h2&gt;  &lt;p&gt;สำหรับตัว Router การที่จะ Flash Firmware ด้วยวิธีที่ปลอดภัยที่สุด ก็คือใช้โปรแกรม TFTP ครับ ถ้าใน Windows XP จะมีอยู่แล้ว แต่ถ้าใน Windows Vista/Windows 7 จะต้องไปเปิด Feature เพิ่ม ซึ่งผมว่า &lt;a href="http://www.tftp-server.com/tftp-client.html" target="_blank"&gt;โหลดของฟรีมาใช้&lt;/a&gt;จะง่ายกว่า และเร็วด้วย :)&lt;/p&gt;  &lt;p&gt;(สำหรับรุ่นอื่นๆ ลองดูใน Wiki นะครับ ว่าจะต้องทำยังไง หรือจะลอง Update โดยใช้ Web Interface ของตัว Router ก็ได้ครับ) &lt;/p&gt;  &lt;p&gt;ก่อนอื่นเลย เราต้องเปลี่ยนโหมดให้ Router มันเข้า Safe Mode ก่อนครับ (ไม่รู้จะเรียกยังไงดี) ในโหมดนี้ Router จะไม่แจก IP แต่จะตั้ง IP ตัวเองเป็น 192.168.1.1 (หรือ IP ที่เคยตั้งไว้) และรอรับการเชื่อมต่อ TFTP เพื่อ Upload Firmware อย่างเดียว และ Ethernet Port เบอร์ 1 จะเป็น Port เดียวที่ใช้ได้ครับ&lt;/p&gt;  &lt;p&gt;ในการเซ็ตให้ Router เข้าโหมดนี้ เราจะต้องถอดปลั๊ก Router ก่อน แล้วหาดินสอ/ปากกามาแท่งนึงครับ จิ้มไปตรงปุ่ม Reset แล้วจิ้มค้างไว้ ในขณะที่เสียบสายไว้เข้าเครื่องด้วย ถ้าทำถูกต้อง ไฟสัญญาณแสดงว่าเปิดเครื่อง จะกระกริบครับ&lt;/p&gt;  &lt;p&gt;ดังนั้น เราก็ต้องไปตั้ง IP ของการ์ดแลน ใน Windows ให้เป็น 192.168.1.2/255.255.255.0 (หรือ IP เดิมที่เคยใช้ได้) อย่าลืมเสียบสายแลนเข้า Port 1ด้วยละครับ&lt;/p&gt;  &lt;p&gt;เพื่อความง่าย ผมแนะนำให้ Copy ตัว Firmware ไปไว้ที่ Drive C: ก่อน (หรือ Drive อะไรก็ได้ที่มี) รวมไปถึงโปรแกรม tftp ด้วยครับ เอาไว้ที่เดียวกันเลย จากนั้นก็เข้า Command Prompt เลยครับ โดยการกดปุ่ม Windows พร้อมกับปุ่ม R แล้วพิมพ์ในช่องว่า cmd&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_6B04EA91.png"&gt;&lt;img style="border-bottom:0px;border-left:0px;display:inline;border-top:0px;border-right:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_49391200.png" width="324" height="160" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;ใน Command Prompt เราก็พิมพ์ว่า&lt;/p&gt;  &lt;p&gt;c: (หรือ d:. e: ตามแต่ Drive ของคุณ)&lt;/p&gt;  &lt;p&gt;tftp -i 192.168.1.1 put &amp;quot;openwrt.XXXX.trx&amp;quot;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_55C6BF1C.png"&gt;&lt;img style="border-bottom:0px;border-left:0px;display:inline;border-top:0px;border-right:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_3F90A3CA.png" width="421" height="144" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;หลังจากนั้น ทุกอย่างจะตกอยู่ในความสงบชัวครู่ แล้วก็จะมีข้อความว่า การส่งข้อมูลสำเร็จ ถึงตรงนี้ ไปให้ไกลๆ จาก Router เลยครับ อย่าไปมอง อย่าไปสนใจมัน อย่างน้อย 10 นาที เพื่อความชัวร์ พอกลับมาดู สัญญาณไฟที่เคยกระพริบ มันจะหยุดครับ นั่นคือ เราได้ทำการ Flash เรียบร้อยแล้ว&lt;/p&gt;  &lt;h2&gt;First Boot&lt;/h2&gt;  &lt;p&gt;เมื่อเปิด Router ขึ้นมาครั้งแรก แล้วเข้าไปที่ &lt;a href="http://192.168.1.1"&gt;http://192.168.1.1&lt;/a&gt; มันจะถามให้ใส่ Password ของ root ก็ใส่ได้ตามใจชอบเลยครับ&lt;/p&gt;  &lt;p&gt;เรียบร้อยแล้ว ไปที่หน้า System/Settings ครับ ตั้งค่า Host name ได้ตามใจชอบ เปิด Boot Wait ไว้ และให้มัน Wait นานหน่อย 5-10 วินาที เราจะได้กดปุ่ม Reset ได้ดินสอทันเหมือนเดิม ถ้าต้องการจะ Flash Firmware ใหม่&lt;/p&gt;  &lt;p&gt;ในส่วน Time Settings ก็ตั้งค่าตามปกติครับ และ NTP Server ช่องแรก ใส่เป็น &lt;strong&gt;time.navy.mi.th &lt;/strong&gt;เพื่อให้ Router Sync เวลาได้&lt;/p&gt;  &lt;p&gt;จากนั้น เราก็ต้องตั้งต่า ADSL ของเราก่อนครับ เพื่อให้ใช้เน็ตได้ ก็เลือกตามภาพด้านล่างนี้เลย สำหรับ MTU ที่เป็น 1454 นั้น เพราะว่า&lt;a href="http://www.google.co.th/search?q=MTU+1454&amp;amp;ie=utf-8&amp;amp;oe=utf-8&amp;amp;aq=t&amp;amp;rls=org.mozilla:en-US:official&amp;amp;client=firefox-a" target="_blank"&gt;เหตุผลอะไรไม่รู้ยาวเหยียด&lt;/a&gt; สรุปว่า มันคือค่าขนาดของ Packet ที่เหมาะสมสำหรับเครือข่าย ADSL ครับ (Lan คือ 1500 และ Modem นี่ หลักร้อยครับ)&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_3EB83DE0.png"&gt;&lt;img style="border-bottom:0px;border-left:0px;display:block;float:none;margin-left:auto;border-top:0px;margin-right:auto;border-right:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_3171B4CD.png" width="436" height="822" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;หลังจากแก้ไขแล้ว ต่อไปนี้ ให้กด &lt;img style="border-bottom:0px;border-left:0px;display:inline;border-top:0px;border-right:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_3AEA7343.png" width="91" height="34" /&gt; ตามด้วย &lt;img style="border-bottom:0px;border-left:0px;display:inline;border-top:0px;border-right:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_5A994D0B.png" width="138" height="29" /&gt; เสมอนะครับ ก่อนจะไปเซ็ตค่าอย่างอื่น บางทีมันตีกัน แล้วจะพาลทำ Setting เสียได้&lt;/p&gt;  &lt;p&gt;หลังจากตั้งค่า Network แล้ว ก็มาหน้าถัดไปเลยครับ ตั้ง Vlan ถ้าจะตั้งเผื่ออัดรายการด้วย ก็ใช้แบบตามภาพนี้ครับ ถ้าจะเอา Set Top Box ต่ออย่างเดียว ใช้แค่ Port 4 ก็พอ แล้วเอา Set Top Box ต่อเข้าช่อง 4 ครับ&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;img style="border-bottom:0px;border-left:0px;display:inline;border-top:0px;border-right:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_2835798C.png" width="544" height="143" /&gt; &lt;/p&gt;  &lt;p&gt;ในหน้า Wireless โดย Default มันจะปิดอยู่ครับ อย่าลืมเปิดด้วย สำหรับ Encryption type เลือกเป็น WPA+WPA2 ครับ จะใช้ได้กับอุปกรณ์เยอะกว่า (ถ้าใช้ค่านี้ เวลาเอา PSP ต่อ เลือกเป็น WPA/AES ครับ) และก็ควรใส่ระยะ Wireless ด้วยครับ ช่วยในเรื่องสัญญาณได้นิดหน่อย และป้องกันไม่ให้คนมาแอบเล่นด้วย (WPA มัน Hack ได้นะ เห็นว่า) นอกจากนี้ เรายังเล่นได้อีก โดยการเพิ่ม Virtual Interface แล้วต่อมันเข้ากับ WAN (เลือกในช่อง Network) เราก็น่าจะสามารถอัดทีวี ผ่าน WiFi ได้ครับ หรืออาจจะดูทีวีผ่าน WiFi เลย ก็น่าจะได้ ผมมีโครงการจะทดลองทำเร็วๆ นี้ละครับ :)&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_4D52C3F8.png"&gt;&lt;img style="border-bottom:0px;border-left:0px;display:inline;border-top:0px;border-right:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_649DC869.png" width="511" height="434" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;(แต่บางทีเหมือนจะมี Bug นะครับ คือรหัส PSK มันจะไม่ยอมเปลี่ยน แล้วจะกลายไปเหมือนกับรหัสของ root ที่เราตั้งไว้ (ซะงั้น)&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;เมื่อเรียบร้อย Router เราก็กลับมาเป็น Router ละ คราวนี้ ก็เตรียมลงโปรแกรมได้&lt;/p&gt;  &lt;h2&gt;ลงโปรแกรม&lt;/h2&gt;  &lt;p&gt;ในการลงโปรแกรม พวก Linux เขาจะเรียกโปรแกรมพวกนี้ว่า Package ครับ (หรือเราควรจะเปลี่ยนใหม่ว่า ลง Package ดี) เอาเป็นว่า ไปที่หน้า System/Packagesแล้วกด Update Package Lists ทีนึงก่อน เมื่อมันไปดาวน์โหลดลิสต์มาแล้ว ก็เริ่มลง Package ตามนี้ครับ (ตามลำดับ)&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;kmod-usb2&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;kmod-usb-storage&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;kmod-nls-utf8&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;kmod-fs-ext3&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;swap-utils&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;samba-server&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;vsftpd&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;เมื่อลงเสร็จแล้ว Restart ซะที ก็จะเป็นการดีครับ ลิงค์ Reboot อยู่ด้านบนนี่เอง&lt;/p&gt;  &lt;h2&gt;เซ็ต Storage ให้ใช้งานได้&lt;/h2&gt;  &lt;p&gt;หลังจากนั้น เราก็ต้องมาตั้งค่านิดหน่อย เพื่อ Mount เจ้า Flash Drive กับ USB Harddisk นี้ บน Router ตรงนี้ ต้องใช้ SSH ช่วยครับ ก็เปิด Putty ขึ้นมาเลย โปรแกรมนี้เวลาใช้งานจะงงนิดนึงนะครับ แต่ก็คือ พิมพ์ IP ลงในช่อง พิมพ์ชื่อลงในช่อง Saved Session แล้วกด Save ครับ ครั้งต่อไป เปิดขึ้นมาก็ Double Click ที่ลิสต์ข้างล่างได้เลย&lt;/p&gt;  &lt;p&gt;&lt;img style="border-bottom:0px;border-left:0px;display:inline;border-top:0px;border-right:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_6E1686DF.png" width="496" height="514" /&gt; &lt;/p&gt;  &lt;p&gt;ตอนต่อ มันจะถามเรื่อง Certificate ก็กด Yes ไปครับ แล้วจากนั้น ใส่ Username ว่า root และพาสเวิร์ด ก็ตามที่ตั้งไว้ครับ&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_781B023D.png"&gt;&lt;img style="border-bottom:0px;border-left:0px;display:inline;border-top:0px;border-right:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_5C96003A.png" width="551" height="428" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;พิทม์ว่า mkdir /opt ก่อนเลย เพื่อสร้างโฟลเดอร์ /opt เอาไว้ Mount ตัว Flash Drive&lt;/p&gt;  &lt;p&gt;พิมพ์ว่า dmesg แล้วย้อนไปดู จะเจอข้อความประมาณนี้&lt;/p&gt;  &lt;p&gt;Attached scsi disk sda at scsi0, channel 0, id 0, lun 0   &lt;br /&gt;SCSI device sda: 117210240 512-byte hdwr sectors (60012 MB)    &lt;br /&gt;Partition check:    &lt;br /&gt;&amp;#160;&lt;strong&gt;/dev/scsi/host0/bus0/target0/lun0&lt;/strong&gt;: p1    &lt;br /&gt;WARNING: USB Mass Storage data integrity not assured    &lt;br /&gt;USB Mass Storage device found at 2    &lt;br /&gt;hub.c: new USB device 01:03.2-2, assigned address 3    &lt;br /&gt;scsi1 : SCSI emulation for USB Mass Storage devices    &lt;br /&gt;&amp;#160; Vendor: Kingston&amp;#160; Model: DataTravelerMini&amp;#160; Rev: PMAP    &lt;br /&gt;&amp;#160; Type:&amp;#160;&amp;#160; Direct-Access&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; ANSI SCSI revision: 02    &lt;br /&gt;Attached scsi removable disk sdb at scsi1, channel 0, id 0, lun 0    &lt;br /&gt;device wl0 entered promiscuous mode    &lt;br /&gt;wl0: attempt to add interface with same source address.    &lt;br /&gt;br-lan: port 2(wl0) entering learning state    &lt;br /&gt;br-lan: port 2(wl0) entering forwarding state    &lt;br /&gt;br-lan: topology change detected, propagating    &lt;br /&gt;SCSI device sdb: 977664 512-byte hdwr sectors (501 MB)    &lt;br /&gt;sdb: Write Protect is off    &lt;br /&gt;&amp;#160;&lt;strong&gt;/dev/scsi/host1/bus0/target0/lun0&lt;/strong&gt;: p1 p2&lt;/p&gt;  &lt;p&gt;นั้นคือ Path ของ Harddisk/Flash Drive ของเครื่องผมครับ ของคุณอาจจะต่างไปได้ ก็เปิดหน้านี้ไว้ แล้วกลับไปที่ System/Mountpoints บนเว็บครับ จัดการตั้งค่าตามนี้ สังเกตว่ามี&lt;strong&gt; /part1 /part2 ต่อท้าย&lt;/strong&gt; ด้วยนะครับ โดยให้ Harddisk Mount ไปที่ /mnt และ Flash Drive ให้ Mount ไปที่ /opt&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_05BD9879.png"&gt;&lt;img style="border-bottom:0px;border-left:0px;display:inline;border-top:0px;border-right:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_4F8BFA5C.png" width="753" height="458" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;สำหรับ async หมายถึง ให้มันทำงานแบบ Asynchronous จะได้ไม่หน่วงมาก (แต่ระวังไฟตก ถ้าไฟตกตอนเขียน ข้อมูลอาจหายได้ แต่โครงสร้างยังโอเคอยู่ เพราะเราใช้ ext3) noatime คือ ไม่ต้องเก็บเวลา Access ไฟล์ล่าสุด และ nodiratime ก็เหมือนกัน แต่สำหรับไดเรกทอรี ทั้งสองอันนี้ จะทำให้ Flash Drive เราไม่พังเร็วเกินไป เพราะโดนเขียนซ้ำไปซ้ำมาบ่อยๆ และลดการใช้ Disk ด้วยครับ&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;เมื่อ Apply แล้ว ให้ไปที่หน้า System/Startup แล้วใส่คำสั่ง&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;/etc/init.d/fstab start &amp;amp;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;ลงในบรรทัดสุดท้าย เพื่อให้ Router มัน Mount Disk ทั้งสองตัว ตอนเริ่มทำงาน เมื่อ Save changes แล้ว/Apply Changes แล้ว ก็ Reboot อีกครั้งครับ&lt;/p&gt;  &lt;p&gt;ลองเปิด Putty ใหม่ (ถ้ามันค้างไป เพราะ Connection โดนตัด คลิ๊กขวาตรง Title Bar เลือก Restart Session ได้ครับ) แล้ว Login ถ้าพิมพ์ &lt;strong&gt;df –h&lt;/strong&gt; และ &lt;strong&gt;free&lt;/strong&gt; ดู ควรจะได้ผลประมาณนี้ครับ ก็คือ Partition ที่ 2 ของ Flash Drive ถูก Mount ไปที่ /opt และ Harddisk ถูก Mount ไปที่ /mnt ส่วน Free ก็บอกว่า เรามีที่ Swap เข้ามา 100MB&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_751577BD.png"&gt;&lt;img style="border-bottom:0px;border-left:0px;display:inline;border-top:0px;border-right:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_7DB5D049.png" width="551" height="428" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;h2&gt;ลง MLDonkey&lt;/h2&gt;  &lt;p&gt;สำหรับการลง MLDonkey ต้องทำผ่าน Command line ครับ ยุ่งยากหน่อย แต่พิมพ์ไม่เยอะครับ ตามด้านล่างนี้เลย&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;mkdir /opt/tmp&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;opkg --tmp-dir /opt/tmp install &lt;/strong&gt;&lt;a href="http://ipkg.nslu2-linux.org/feeds/optware/oleg/cross/unstable/uclibc-opt_0.9.28-13_mipsel.ipk"&gt;&lt;strong&gt;http://ipkg.nslu2-linux.org/feeds/optware/oleg/cross/unstable/uclibc-opt_0.9.28-13_mipsel.ipk&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;opkg --tmp-dir /opt/tmp install &lt;/strong&gt;&lt;a href="http://ipkg.nslu2-linux.org/feeds/optware/oleg/cross/unstable/ipkg-opt_0.99.163-10_mipsel.ipk"&gt;&lt;strong&gt;http://ipkg.nslu2-linux.org/feeds/optware/oleg/cross/unstable/ipkg-opt_0.99.163-10_mipsel.ipk&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;opkg --tmp-dir /opt/tmp install &lt;/strong&gt;&lt;a href="http://ipkg.nslu2-linux.org/feeds/optware/oleg/cross/unstable/bzip2_1.0.5-1_mipsel.ipk"&gt;&lt;strong&gt;http://ipkg.nslu2-linux.org/feeds/optware/oleg/cross/unstable/bzip2_1.0.5-1_mipsel.ipk&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;opkg --tmp-dir /opt/tmp install &lt;/strong&gt;&lt;a href="http://ipkg.nslu2-linux.org/feeds/optware/oleg/cross/unstable/ncurses_5.7-1_mipsel.ipk"&gt;&lt;strong&gt;http://ipkg.nslu2-linux.org/feeds/optware/oleg/cross/unstable/ncurses_5.7-1_mipsel.ipk&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;mkdir /opt/config&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;touch /opt/config/mlnetstart&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;chmod +x /opt/config/mlnetstart&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;cd /opt/bin&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;wget &lt;/strong&gt;&lt;a title="http://oleg.wl500g.info/bin/mlnet/292/mlnet.lite.gz" href="http://oleg.wl500g.info/bin/mlnet/292/mlnet.lite.gz"&gt;&lt;strong&gt;http://oleg.wl500g.info/bin/mlnet/292/mlnet.lite.gz&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;gunzip mlnet.lite.gz&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;เมื่อทำครบทุกขั้นตอนแล้ว กลับไปที่หน้าเว็บ แล้วไปที่ System/File Editor แล้ว Browse เข้าไปดูใน &lt;strong&gt;/opt/config/&lt;/strong&gt; จะเห็นว่ามีไฟล์ชื่อ &lt;strong&gt;mlnetstart&lt;/strong&gt; อยู่ กดปุ่มดินสอ Edit เลยครับ จากนั้น ใส่ Script นี้ลงไป&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font size="2" face="Courier New"&gt;#!/bin/sh &lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font size="2" face="Courier New"&gt;set backuphome=$HOME       &lt;br /&gt;set backuptmp=$TMPDIR        &lt;br /&gt;export HOME=/mnt/        &lt;br /&gt;export TMPDIR=/mnt/.mldonkey/temp        &lt;br /&gt;export PATH=/opt/bin:/opt/sbin:/opt/usr/bin:/opt/usr/sbin:/bin:/sbin:/usr/bin:/usr/sbin        &lt;br /&gt;export LD_LIBRARY_PATH=/opt/usr/lib:/opt/lib:/lib:/usr/lib &lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font size="2" face="Courier New"&gt;rm -f /mnt/.mldonkey/*.tmp       &lt;br /&gt;nice -n 19 /opt/bin/mlnet.lite -allowed_ips &amp;#39;127.0.0.1/8 192.168.1.0/24&amp;#39; &amp;amp; &lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font size="2" face="Courier New"&gt;export HOME=$backuphome       &lt;br /&gt;export TMPDIR=$backuptmp        &lt;br /&gt;export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/opt/bin:/opt/sbin:/opt/usr/bin:/opt/usr/sbin        &lt;br /&gt;export LD_LIBRARY_PATH=/lib:/usr/lib:/opt/usr/lib:/opt/lib&lt;/font&gt;&lt;/strong&gt; &lt;/p&gt;  &lt;p&gt;Script นี้ คือคำสั่งสำหรับเริ่มการทำงานของ MLDonkey ครับ (ถ้าอยากจะต่อเข้ามาดู Status ผ่านเน็ต เปลี่ยน ip เป็น 0.0.0.0/0 ครับ )&lt;/p&gt;  &lt;p&gt;พร้อมแล้วใช่ไหมครับ ลองรันดูได้เลย พิมพ์ว่า&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;/opt/config/mlnetstart&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;จะได้ผลประมาณนี้ครับ&lt;/p&gt;  &lt;p&gt;&lt;img style="border-bottom:0px;border-left:0px;display:inline;border-top:0px;border-right:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_14BA27AD.png" width="671" height="428" /&gt; &lt;/p&gt;  &lt;p&gt;จากนั้น เราก็สามารถ เข้าไปยัง &lt;a title="http://192.168.1.1:4080/" href="http://192.168.1.1:4080/"&gt;http://192.168.1.1:4080/&lt;/a&gt; เพื่อดูหน้าตาเจ้า MLDonkey ได้แล้ว ซึ่งในครั้งแรก มันจะให้เราพิมพ์คำสั่ง useradd admin… เข้าไปในช่องที่มุมบนด้านขวามือ เพื่อตั้ง User ครับ password ก็ตามที่ต้องการครับ&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_636B3D4C.png"&gt;&lt;img style="border-bottom:0px;border-left:0px;display:inline;border-top:0px;border-right:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_3C30F417.png" width="468" height="35" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;เมื่อตั้งเรียบร้อยแล้ว ทีนี้ เราก็ใช้ Sancho ต่อกับ MLDonkey ได้แล้วครับ ตอนเปิดครั้งแรก มันจะให้เราใส่ค่าต่าง ก็ใส่ตามที่มันแนะนำครับ&lt;/p&gt;  &lt;p&gt;&lt;img style="border-bottom:0px;border-left:0px;display:inline;border-top:0px;border-right:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_70D15052.png" width="536" height="680" /&gt; &lt;/p&gt;  &lt;p&gt;เท่านี้ก็เรียบร้อย! โยนไฟล์ .torrent มาใส่ในหน้าโปรแกรมได้เลย Router ก็จะเริ่มดาวน์โหลดให้เองครับ&lt;/p&gt;  &lt;h2&gt;&lt;/h2&gt;  &lt;p&gt;&lt;/p&gt;  &lt;h2&gt;ขอพอก่อน!!!&lt;/h2&gt;  &lt;p&gt;ทำไปทำมา ตอนนี้ก็ปาเข้าไปห้าทุ่มแล้วครับ นั่งเขียนมาแล้ว 2 ชั่วโมงรวด ผมว่าผมไปพักผ่อนก่อนดีกว่า งานก็ยังคั่งค้างอยู่ เอาไว้มาเขียนต่อวันหลังละกันครับ &lt;img src="http://coredeveloper.net/emoticons/emotion-1.gif" alt="Smile" /&gt;&lt;/p&gt;&lt;img src="http://coredeveloper.net/aggbug.aspx?PostID=636" width="1" height="1"&gt;</description></item><item><title>Source Control : ใช้งานพร้อมกัน และแก้ Conflict</title><link>http://coredeveloper.net/blogs/nantcom/archive/2009/03/16/source-control-2.aspx</link><pubDate>Mon, 16 Mar 2009 07:23:19 GMT</pubDate><guid isPermaLink="false">86cc649a-bf28-4d34-a9ef-d75c61f34293:621</guid><dc:creator>นันคอม</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://coredeveloper.net/blogs/nantcom/rsscomments.aspx?PostID=621</wfw:commentRss><comments>http://coredeveloper.net/blogs/nantcom/archive/2009/03/16/source-control-2.aspx#comments</comments><description>&lt;p&gt;สวัสดีครับ หายไปนานเลยจาก&lt;a href="http://coresharp.net/blogs/article/archive/2008/12/28/version-control.aspx"&gt;ภาคแรก ซึ่งผมทิ้งท้ายไว้ที่การตั้ง Source Control อย่างงายๆ สำหรับใช้เองในเครื่อง ด้วย SVN&lt;/a&gt; รวมไปถึงการใช้งาน SVN อย่างคร่าวๆ ไม่ว่าจะเป็นการสร้าง Repository ใหม่ การนำไฟล์เข้า Source Control, การ Update และ Commit แต่เรื่องที่ผมยังไม่ได้กล่าวถึง คือการทำให้ SVN สามารถใช้งานร่วมกันได้ ภายในทีม และการจัดการกับ Conflict ซึ่งจะเกิดขึ้นได้ เมื่อมีผู้พัฒนาหลายๆ คน ทำการแก้ไขไฟล์พร้อมๆ กัน ในส่วนเดียวกัน&lt;/p&gt;  &lt;h2&gt;อย่าใช้ Shared Folder!&lt;/h2&gt;  &lt;p&gt;สำหรับการทำให้ SVN ใช้งานได้พร้อมกันหลายๆ คน บางคนอาจจะนึกได้ว่า ทำไมเราไม่ทำการทำให้โฟลเดอร์ที่เป็น Repository นั้น ให้กลายเป็น Shared Folder แล้วให้ผู้พัฒนาทุกคน ต่อเข้ามาผ่านโปรแกรม Tortoise SVN ล่ะ? คำตอบคือ เป็นสิ่งที่สามารถทำได้ครับ แต่อย่าลืมว่า ในการแก้ไขข้อมูล SVN จะต้องทำการ Lock/Unlock ไฟล์ เหมือนกับการเขียนไฟล์ลงเครื่องทั่วๆ ไป แต่ปัญหาไม่ได้อยู่ที่การที่เราจะ Lock ไฟล์ ไม่ได้ครับ ปัญหามันอยู่ที่ การ Lock ไฟล์ผ่าน Shared Folder ยังเป็นเรื่องที่อันตรายมากๆ ในทางปฏิบัติ จึงเป็นสิ่งที่ไม่สมควรทำเป็นอย่างยิ่ง (ทีนี้รู้หรือยังว่า ไม่ควรเปิดไฟล์ Word/Excel จาก Shared Folder แล้วแก้ไขมันตรงๆ) โดยเฉพาะกับข้อมูลสำคัญอย่าง Source Code ของเรา และเป็นที่มาของส่วนหนึ่งของความไม่ค่อยจะน่าเชื่อถือของ Visual Source Safe นั่นเองครับ&lt;/p&gt;  &lt;p&gt;แล้วทีนี้ จะทำยังไงดีละ?&lt;/p&gt;  &lt;h2&gt;Visual SVN Server&lt;/h2&gt;  &lt;p&gt;โดยปกติแล้ว เมื่อเราติดตั้ง SVN เราจะได้ SVN Server มาพร้อมกันด้วยครับ ตัวโปรแกรมมันชื่อว่า SVNServe ซึ่งเราสามารถเปิดโปรแกรมนี้ไว้ แล้วให้เครื่องอื่นๆ ใช้ Tortoise SVN ต่อเข้ามาได้เลย แต่ผมจะแนะนำโปรแกรมอีกตัวหนึ่ง ให้ใช้แทน SVNServe ที่ชื่อว่า Visual SVN Server ครับ&lt;/p&gt;  &lt;p&gt;Visual SVN Server นี้เป็นผลิตภัณฑ์ของบริษัทแห่งหนึ่ง ซึ่งทำ Plug-in เพื่อให้ Visual Studio สามารถติดต่อกับ SVN ได้ ออกขายครับ แต่ว่าเขาใจดี เปิดให้เราใช้ตัว SVN Server ซึ่งเขาทำการโมมาให้เรียบร้อยแล้วได้ฟรี ถ้าจะใช้จริงๆ จังๆ ก็อย่าลืมอุดหนุนเขาบ้างนะครับ ที่ &lt;a href="http://www.visualsvn.com"&gt;http://www.visualsvn.com&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;สาเหตุที่ผมคิดว่า Visual SVN Server นั้นดีกว่า SVN Serve ที่เราได้มาพร้อมกันกับการลง SVN นั่นก็คือ…&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;b&gt;จัดการได้ง่าย &lt;/b&gt;เพราะว่า ทีมที่พัฒนา Visual SVN Server นั้น เขาได้ทำ GUI มาเป็น MMC ไว้เลยครับ การสร้าง Repository, จัดการ Permission หรือ จัดการ User ทำได้ง่ายกว่า SVNServe ซึ่งต้องใช้การแก้ Text File ด้วยมือ &lt;/li&gt;    &lt;li&gt;&lt;b&gt;รองรับ Active Directory/Windows Authentication&lt;/b&gt; ถ้าเรามีระบบ AD หรือ ที่เครื่อง Server มีการสร้าง User เพื่อจัดการการแชร์ไฟล์ หรืออะไรอื่นๆ อยู่แล้ว เราสามารถให้ Visual SVN ไป Authen กับ AD หรือ Windows Authentication ได้เลยครับ ไม่ต้องสร้าง User หลายๆ ที่ ให้วุ่นวาย &lt;/li&gt;    &lt;li&gt;&lt;b&gt;มี HTTP/HTTPS ให้เลย &lt;/b&gt;โดยปกติแล้ว ถ้าเราใช้ SVNServe ตัว Tortoise SVN จะต้องติดต่อผ่านโปรโตคอลของ SVN เอง ซึ่งมันมักจะโดน Firewall บล็อคครับ ทำให้การติดต่อกับ Server SVN ผ่านเน็ต จะต้องมีการคอนฟิก Firewall เพิ่มมาด้วย SVN จึงมีการพัฒนาให้รองรับการใช้ Apache เป็นหน้าด่าน เพื่อทำงานกับ Repository ผ่านโปรโตคอล HTTP ได้ด้วย ติดตรงที่ถ้าเราเอามาทำเอง มันจะวุ่นวายอยู่พอสมควร แต่ Visual SVN นี่ จะเป็น Server SVN แบบ HTTP/HTTPS โดยตัวมันเองอยู่แล้วครับ &lt;/li&gt;    &lt;li&gt;&lt;b&gt;รองรับการทำงานกับระบบอื่นๆ ได้อีก&lt;/b&gt; ถ้าหากเราจะเล่น SVN ให้มัน Advance ไปอีก ซึ่งขั้นต่อไป ก็คือทำงานร่วมกับระบบ Project Management อย่างเช่น Trac ที่โปรเจค Open Source ทั้งหลายนิยมใช้กัน&amp;#160; Visual SVN เขาก็เปิดช่องไว้ให้เราแล้วด้วยเหมือนกันครับ โดยคุณสามารถตั้งค่าได้จากตัว GUI โดยตรงเลย แต่ผมยังไม่ขอพูดถึงเรื่องนี้นะครับ อาจจะเป็นในภาค 3 ของเรื่องนี้ ถ้าสนใจ ลอง Search หาเรื่อง Commit Hook ดูครับ &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;การติดตั้งนั้น ง่ายดายมากครับ เปิดตัว Setup ขึ้นมา กด Next ลูกเดียว ก็เสร็จแล้วครับ สำหรับการ Authentication ผมแนะนำให้ใช้ Windows Authentication ครับ เพื่อที่ Username/Password ทั้งหลาย จะได้ไม่ไปกระจายอยู่หลายที่ หลังจากติดตั้ง ตัว Visual SVN Server จะเป็น Windows Service ครับ มันจะทำงานโดนอัตโนมัติเมื่อเปิดเครื่อง&lt;/p&gt;  &lt;h2&gt;การใช้งานเบื้องต้น&lt;/h2&gt;  &lt;p&gt;สำหรับการใช้งาน Visual SVN Server นั้น สามารถใช้ GUI จัดการได้เลยครับ โดยหลักๆ แล้ว สิ่งที่เราต้องจัดการ ก็มีแค่สร้าง Repository กับการเปลี่ยน Permission เท่านั้นเองครับ &lt;/p&gt;  &lt;h2&gt;ปัญหาจากการทำงานร่วมกัน&lt;/h2&gt;  &lt;p&gt;อย่างที่ผมได้เกริ่นไปแล้ว ว่า SVN นั้น เป็นการทำงานแบบ Copy-Modify-Merge ซึ่งจะทำให้ทุกคนสามารถทำงานพร้อมๆ กันได้ อย่างสะดวก แต่อย่างไรก็ดี ปัญหาที่อาจเกิดขึ้นได้ คือขั้นตอนของการ Merge นี่เองครับ เพราะว่า…&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;b&gt;ผู้พัฒนา อาจจะทำงานในบริเวณเดียวกันของไฟล์&lt;/b&gt; ที่ผมใช้คำว่า บริเวณเดียวกัน นั่นก็เพราะว่า อัลกอริธึมในการ Merge ที่ SVN ใช้นั้น สามารถติดตามได้ว่า Source Code แต่ละบรรทัด ถูกย้ายไปยังตำแหน่งไหนบ้าง มีอะไรที่ต่างกันบ้าง แต่ว่า การเปลี่ยนแปลงที่เกิดขึ้นในบรรทัดเดียวกัน หรือการเปลี่ยนแปลงที่มากจนทำให้อัลกอริธึมสับสน ก็ไม่สามารถ Merge ได้ครับ แต่ข้อดีของ SVN ก็คือ Source Code ที่อยู่ใน Repository จะไม่มีทางอยู่ในสภาพที่เสียหายโดยเด็ดขาด เพราะว่า ก่อนการ Commit Code เข้าไปยัง Repository ตัว SVN จะตรวจว่า มีการเปลี่ยนแปลงที่ไฟล์เดียวกันหรือไม่ ถ้ามี ผู้พัฒนา จะต้องทำการ Update เพื่อดาวน์โหลด Source Code มาทำการ “ทดลอง Merge” ในเครื่องก่อน ถ้าเกิดว่า Merge ไม่ผ่าน ก็จะไม่สามารถ Commit ได้ &lt;/li&gt;    &lt;li&gt;&lt;b&gt;การ Merge ไม่สามารถการันตีเรื่อง Semantic/Logic ได้&lt;/b&gt; แต่ทีนี้ ถึงแม้การ Merge จะสำเร็จ เราก็ไม่สามารถการันตีได้ว่า Source Code ที่ Merge สำเร็จแล้ว จะคอมไพล์ผ่าน และทำงานได้ถูกต้อง (ซึ่งก็เป็นปัญหาเดียวกัน กับ Source Control ในทุกรูปแบบ รวมไปถึง แบบที่ไม่มี Source Control ด้วย) ดังนั้น ทีมพัฒนาบางแห่ง จึงจะมี Server อีกตัว เรียกว่า Continuous Integration Server ที่จะคอยดาวน์โหลด Source Code จาก Repository มาเป็นระยะๆ เพื่อคอมไพล์ และรัน Test (บางที อาจจะทั้ง Unit และ Integrated) เพื่อให้มั่นใจว่า Source Code ยังอยู่ในสภาพดีเสมอ และจะได้ชี้นิ้วถูก ว่า ใครคือคนสุดท้าย ที่ Commit Code เข้าไป แล้วทำพัง &lt;img src="http://coredeveloper.net/emoticons/emotion-1.gif" alt="Smile" /&gt; &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;สำหรับปัญหาแรก นักพัฒนาจะถูกบังคับให้แก้ไข โดยการ Merge Source Code ด้วยมือ เรียกอย่างเป็นทางการ ก็คือ Conflict Resolution หรือการแก้ Conflict ครับ ส่วนปัญหาที่สอง มีทางเดียวคือต้องใช้ CI ครับ&lt;/p&gt;  &lt;p&gt;การแก้ Conflict&lt;/p&gt;  &lt;p&gt;ก่อนอื่น ลองมาดูกันว่า Conflict จะเกิดขึ้นได้อย่างไรครับ เริ่มจาก ผมมี Repository ที่มีไฟล์อยู๋ไฟล์หนึ่ง&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coresharp.net/blogs/article/image_3529D856.png"&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_thumb_13CA32BA.png" width="518" height="144" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;และผมมีนักพัฒนาอยู่สองคน ชื่อ Nant และ John นะครับ ซึ่งทั้งคู่ ต่างก็ Checkout Repo นี่ไว้ในโฟลเดอร์ของตัวเอง&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_596EBCD8.png" width="466" height="514" /&gt; &lt;/p&gt;  &lt;p&gt;จากนั้น Nant ก็ทำการแก้ไขไฟล์ โดยการเพิ่มฟังก์ชั่น ไปที่บรรทัดที่ 50&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_314BE7EC.png" width="426" height="96" /&gt; &lt;/p&gt;  &lt;p&gt;ส่วน John ก็ไปเพิ่มฟังก์ชั่น Test ไว้ที่ด้านล่างของไฟล์&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_7EE8146C.png" width="292" height="56" /&gt; &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;จากนั้น John ก็ทำการ Commit ก่อน&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coresharp.net/blogs/article/image_57ADCB37.png"&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_thumb_163318DE.png" width="402" height="150" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;แล้วถ้า Nant จะ Commit บ้าง ก็จะ Commit ไม่ได้ และ SVN ก็จะแนะนำว่า ให้ผมสั่ง Update ก่อน เพราะว่าไฟล์มัน Out-of-Date ไปแล้ว&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_3FC6E411.png" width="376" height="205" /&gt; &lt;/p&gt;  &lt;p&gt;จากนั้น เมื่อผมสั่ง Update แล้ว ผมก็จะได้ไฟล์ AutoCompleteBox.cs ที่ Merge สำเร็จ แต่มีฟังก์ชั่น Test สองครั้ง ทำให้คอมไพล์ไม่ผ่าน ก็จะเป็นหน้าที่ของผม ที่จะต้องไปสื่อสารกับคุณ John เขาว่า เราจะจัดการยังไง และก็สั่ง Lock ไฟล์นี้ไว้ก่อน เพื่อป้องกันใครมาแก้ไขเพิ่ม ระหว่างเราตกลงกันอยู่ เมื่อเรียบร้อย ผมจึงจะสั่ง Commit ครับ แล้วค่อยปลดล็อค แล้วคุณ John ก็สั่ง Update อีกครั้ง เพื่อให้ไฟล์ของเขา เป็นตัวล่าสุด ที่แก้ไขให้คอมไพล์ได้แล้ว&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_1E673E75.png" width="376" height="141" /&gt; &lt;/p&gt;  &lt;p&gt;ทีนี้ ผมก็แก้ไขฟังก์ชั่น Test ต่อไป แล้วสั่ง Commit&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_5D58BF10.png" width="307" height="130" /&gt; &lt;/p&gt;  &lt;p&gt;แต่คุณ John ก็แก้ไขเหมือนกัน แล้วพอจะ Commit คุณ John ก็จะต้อง Update ก่อน&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_58E23E49.png" width="483" height="131" /&gt; &lt;/p&gt;  &lt;p&gt;แต่เนื่องจากเป็นการแก้ไข ที่บริเวณเดียวกัน (บางครั้ง แก้ไขในบรรทัดเดียวกัน ก็ยังสามารถ Merge ได้นะครับ) คือ true กับ false ก็จะเกิดการ Conflict ครับ&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_2A88B89C.png" width="375" height="155" /&gt;&lt;/p&gt;  &lt;p&gt;สังเกตว่า จะมีไอคอนแสดงเลยว่า ไฟล์นี้ กำลัง Conflict พร้อมกับมีไฟล์ต้นฉบับ เพื่อใช้ในการแก้ Conflict มาให้เห็นด้วย&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_0276097D.png" width="530" height="276" /&gt; &lt;/p&gt;  &lt;p&gt;เนื่องจากว่าในการแก้ไข หรือ Commit แต่ละครั้ง SVN จะไม่ได้จำ Revision เฉพาะไฟล์ใด ไฟล์หนึ่ง แต่จะเป็นการจำการเปลี่ยนแปลงของทั้ง Repository ดังนั้น ถ้าเกิดเรา Commit หลายๆ ไฟล์พร้อมกัน ในการ Update ก็เป็นไปได้ที่จะเกิดการ Conflict หลายๆ ไฟล์พร้อมกัน ซึ่งเราก็ควรจะแก้ให้หมดก่อน แล้วค่อย Commit ครับ&lt;/p&gt;  &lt;p&gt;ทีนี้ บางท่านอาจจะบอกว่า ถ้าอย่างนั้น ไล่ Commit ทีละไฟล์จะดีกว่าหรือเปล่า ผมขอแนะนำว่า อย่าครับ เพราะบางครั้ง การเปลี่ยนแปลงที่เราทำ อาจจะกระทบไปหลายๆ ไฟล์ และถ้าเราพบว่า การเปลี่ยนแปลงที่เราทำ มันไปทำให้เกิดปัญหา แล้วต้องการ Revert (ย้อนเวลา) กลับไป Revision ก่อน เราก็จะต้องจำให้ได้ ว่า Commit ไฟล์ไหนไปบ้าง แล้วไล่ Revert ให้ครบ แทนที่จะเป็น Revert การเปลี่ยนแปลงที่เกิดขึ้นทั้งหมด ซึ่งนี่คือจุดดีของ SVN ด้วยครับ&lt;/p&gt;  &lt;p&gt;ทีนี้ การแก้ Conflict ก็ทำได้โดยการคลิ๊กขวาที่ไฟล์ แล้วเลือก Edit Conflict &lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_01F9B0BB.png" width="470" height="375" /&gt; &lt;/p&gt;  &lt;p&gt;จากนั้น เราก็จะพบหน้าต่างแบบนี้ครับ เรียกว่า TortoiseMerge&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_755BDDD1.png" width="550" height="340" /&gt; &lt;/p&gt;  &lt;p&gt;สิ่งที่เราต้องทำก็คือ กด Ctrl+ลูกศรขึ้น หรือ ลง เพื่อไล่ดูจุดที่มัน Conflict กันทั้งหมด ซึ่งบางครั้ง อาจจะไม่ได้เป็นแค่บรรทัดเดียว แต่เป็นทั้งบล็อค หลายๆ บรรทัด แล้วจัดการแก้ไขครับ โดยการดูจากหน้าต่างซ้ายมือ (ไฟล์นี้ ใน SVN) กับทางขวามือ (ไฟล์ในเครื่องเรา) แล้วเลือกว่า จะให้ผลลัพธ์การ Merge มาจากด้านไหน โดยการกด Use “Theirs” หรือ Use “Mine” บนทูลบาร์ ตรงนี้ เราก็คงต้องเอาคุณ John มานั่งคุยกับผมครับ ว่าจะ Merge กันอย่างไรดี&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coresharp.net/blogs/article/image_08D917A6.png"&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_thumb_086CE4B1.png" width="231" height="54" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;แล้วตรงที่ Conflict ในหน้าต่าง Merged ก็จะกลายเป็นสีเขียวครับ&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_56091131.png" width="550" height="258" /&gt; &lt;/p&gt;  &lt;p&gt;หรือถ้าไม่สามารถเลือกข้างได้ จะพิมพ์ลงไปเลยก็ได้เหมือนกันครับ เมื่อเรียบร้อยแล้ว กด Save แล้วทำการ Flag บอก SVN ว่า ไฟล์นี้แก้ไขเรียบร้อยแล้ว ขั้นตอนนี้ขึ้นอยู่กับดุลยพินิจของเรานะครับ ว่าจะให้มันผ่านหรือเปล่า เพราะยังไง SVN ก็บังคับเราไมได้ จะให้มันผ่านเลย โดยทียังไม่แก้ Conflict ก็ยังได้ครับ&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_4AAFD727.png" width="432" height="372" /&gt; &lt;/p&gt;  &lt;p&gt;เมื่อถึงจุดนี้แล้ว ก็สามารถ Commit ได้ตามปกติครับ (ยกเว้น มีคน Commit ตัดหน้ามาก่อน ก็ต้อง Update อีกรอบ เพราะส่วนใหญ่ จะแก้ Conflict กันนานเหมือนกัน)&lt;/p&gt;  &lt;h2&gt;แต่ยังไง…VSTS ก็คือคำตอบ&lt;/h2&gt;  &lt;p&gt;จะเห็นว่า SVN นั้น ช่วยให้การทำงานเป็นทีมนั้น ราบรื่นขึ้นมากเลยทีเดียว เพราะตอนนี้ ทุกคนก็สามารถพร้อมกันได้ อย่างเป็นสุข โดยไม่ต้องมานั่งรอ Lock ไฟล์ หรือ Copy ไฟล์กันไปมาอีกแล้ว และอย่าลืมว่า โดยส่วนใหญ่ เรามักจะทำงานกันคนละไฟล์อยู่แล้ว ปัญหาด้าน Conflict นี้ จะเกิดขึ้นได้น้อยมาก&lt;/p&gt;  &lt;p&gt;แต่ปัญหาที่จะเกิดขึ้นตามมาได้ ก็คือ Human Error ครับ นั่นก็เพราะว่า SVN ไม่ได้ทำงานร่วมกับ IDE อย่างสมบูรณ์ เหมือนกับ Visual Studio Team System เราจึงไม่สามารถควบคุมคุณภาพของ Source Code ใน Repository ได้ และการใช้ CI ก็เป็นการแก้ปัญหาที่ปลายเหตุ เนื่องจากเราจะพบปัญหาได้ ก็ต่อเมื่อ&amp;#160; Source Code ใน Repository มันเสียหายไปแล้ว &lt;/p&gt;  &lt;p&gt;มาดูกันว่า SVN ยังให้อะไรเราไม่ได้บ้าง&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;b&gt;การควบคุม คุณภาพ Source Code ที่จะเข้าไปยัง Repository&lt;/b&gt; อย่างที่เกริ่นไปแล้วว่า SVN นั้น เป็นแค่ Source Control เฉยๆ ด้วยตัว SVN เอง จึงสามารถรับรู้ได้แค่การเปลี่ยนแปลงของ Source Code เท่านั้น แต่ VSTS เราสามารถคุมคุณภาพได้ ตั้งแต่ก่อนที่ผู้พัฒนา จะ Commit โค๊ดเข้าไปใน Repository       &lt;br /&gt;      &lt;br /&gt;ใน VSTS นี่ Project Manager สามารถควบคุมคุณภาพได้จากส่วนกลางเลย โดยการตั้งเงื่อนไขการ Check-in ตั้งแต่เงื่อนไขง่ายที่สุดเช่น &lt;b&gt;จะต้อง Compile ผ่าน&lt;/b&gt; ซึ่งผมเองก็พบอยู่หลายครั้งว่า หลายๆ คนก็ Commit โค๊ดไปเลย โดยที่ไม่ได้ทดลอง Compile ดูก่อน ซึ่งจะไปสร้างปัญหากับคนอื่นๆ ในทีม แทบจะในทันที โดยเฉพาะ ทีมใหญ่ๆ ที่ทำงานกันอยู่หลายๆ ออฟฟิศ คนที่เป็น Admin ก็ต้องคอย Monitor ว่า CI มีการรายงานมาหรือไม่ว่า Code คอมไพล์ไม่ผ่าน แล้วก็ต้องคอย Revert กลับให้       &lt;br /&gt;      &lt;br /&gt;หรือถ้าต้องการเงื่อนไขซับซ้อนกว่านั้น ก็มีให้เลือกครับ เช่น จะต้องรัน Unit Test ผ่าน หรือ มี Performance ในระดับที่พอใจ หรือแม้กระทั่ง จะต้องทดสอบผ่าน Static Analysis Tool ก่อน (FxCop)       &lt;br /&gt;&lt;/li&gt;    &lt;li&gt;&lt;b&gt;การทำงานจากภายใน Visual Studio&lt;/b&gt; แม้ว่า Tortoise SVN จะช่วยให้เรา Commit/Update ได้จากใน Explorer แต่ชีวิตประจำวันของผู้พัฒนาจริงๆ ก็ยังอยู่ใน Visual Studio ครับ แม้ว่า SVN จะมี Plugin ของ Visual Studio อยู่ แต่เท่าที่ผมทดลองใช้ดู ก็ยังทำงานได้ไม่สมบูรณ์เท่าที่ควร และผมก็ยังไม่อยากเสี่ยงครับ       &lt;br /&gt;&lt;/li&gt;    &lt;li&gt;&lt;b&gt;ต้องอาศัยระบบอื่นช่วย &lt;/b&gt;เป็นเรื่องธรรมดามากอยู่แล้ว สำหรับโปรเจค Open Source ทั่วไป เพราะเหมือนว่าเขาจะมี Philosophy กันว่า จะไม่ทำอะไรที่มีคนเคยทำมาแล้ว อย่าง SVN ก็จะทำได้แค่ Source Control อย่างเดียว ถ้าอยากได้อะไรเพิ่ม ก็ต้องลงเพิ่มเอง อย่างเช่น Trac เพื่อใช้เป็น Portal, Apache เพื่อให้ติดต่อผ่าน HTTP ได้, NUnit ทำ Test, Cruise Control ทำ Continuous Integration, FxCop ไว้ทำ Static Analysis ซึ่งกว่าจะทำให้มันขึ้นมาได้ และใช้งานได้สมบูรณ์ ก็ซับซ้อนอยู่พอสมควร       &lt;br /&gt;&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;สำหรับการใช้งานด้าน Source Control จริงๆ SVN ก็สามารถช่วยเราได้เยอะแล้วครับ แต่หลังจากใช้งานเขาจริงๆ จังๆ เราก็จะพบว่า มันยังไม่สามารถตอบความต้องการเราได้ซะทีเดียว โดยเฉพาะอย่างยิ่ง กับการทำงานเป็นทีม และความสามารถในการใช้งานด้าน Project Management เอาไว้เมื่อผมมีเวลาว่าง ผมจะหาโอกาสเขียนเรื่อง VSTS เพิ่มเติม หรืออาจจะชวนให้คุณมี่ เว็บ greatfriends ให้เขียนลง GreatFriends ก้อได้ครับ เพราะขานั้น เขาเทพ VSTS มาเลยทีเดียว :)&lt;/p&gt;  &lt;p&gt;เจอกันคราวหน้าครับ&lt;/p&gt;&lt;img src="http://coredeveloper.net/aggbug.aspx?PostID=621" width="1" height="1"&gt;</description></item><item><title>Source Control : เรื่องจำเป็นที่ไม่ควรมองข้าม</title><link>http://coredeveloper.net/blogs/nantcom/archive/2009/03/16/version-control.aspx</link><pubDate>Mon, 16 Mar 2009 07:22:43 GMT</pubDate><guid isPermaLink="false">86cc649a-bf28-4d34-a9ef-d75c61f34293:620</guid><dc:creator>นันคอม</dc:creator><slash:comments>5</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://coredeveloper.net/blogs/nantcom/rsscomments.aspx?PostID=620</wfw:commentRss><comments>http://coredeveloper.net/blogs/nantcom/archive/2009/03/16/version-control.aspx#comments</comments><description>&lt;p&gt;สำหรับโปรแกรมเมอร์ หรือทีมพัฒนาเล็กๆ แล้ว สิ่งสำคัญสิ่งหนึ่งที่เรามักจะมองข้ามไป คือการสำรองข้อมูล และการจัดการเรื่องการใช้งานไฟล์โปรเจคร่วมกัน เชื่อหรือไม่ว่า บางที ถ้าลองสังเกตดูแล้วว่า เวลาที่เสียไปกับการบริหารจัดการเรื่องจิปาถะเหล่านี้ อาจจะกินเวลาของโปรเจค มากกว่าที่คุณคิดก็ได้&lt;/p&gt;  &lt;p&gt;ลองมาสังเกตดูกระบวนการแชร์ไฟล์โปรเจค ที่คนส่วนใหญ่มักจจะทำกันก่อนดีกว่าครับ&lt;/p&gt;  &lt;h2&gt;วิธี “ตัวใครตัวมัน”&lt;/h2&gt;  &lt;p&gt;ลองนึกย้อนไปตอนสมัยเรียนนนะครับ เมื่อเราจะต้องทำโปรเจคใหญ่ๆ อย่าง Senior Project หรือ โปรเจคจบของรายวิชาต่างๆ ที่ต้องร่วมมือกันหลายคน วิธีที่เราใช้ในตอนนั้น ก็มักจะเป็นตามภาพด้านล่างนี้ ก็คือ วางโครงโปรเจคกันก่อน จากนั้นต่างคนก็ต่างไปทำกันมา ส่วนของใครของมัน แล้วพอใกล้ๆ เวลาส่ง เราก็จะนำไฟล์ของทุกคนมารวมกัน ที่บ้านใครซักคน แล้วก็ภาวนาให้ทุกอย่างผ่านไปได้ด้วยดี &lt;img src="http://coredeveloper.net/emoticons/emotion-1.gif" alt="Smile" /&gt;&lt;/p&gt;  &lt;p&gt;แต่เราก็รู้ดีอยู่แล้วว่า มันเป็นเรื่องที่เป็นไปไม่ได้ อย่างน้อย ถ้าเกิดใครเพิ่มไฟล์ใหม่เข้ามา ที่ชื่อดันเหมือนกัน ก็ยุ่งแล้วครับ แต่ปัญหาที่ยุ่งเหยิงกว่านั้นก็คือ เราไม่มีทางรู้ได้เลยว่า เมื่อทุกส่วนรวมกันแล้ว มันจะใช้งานได้ อย่างที่เราคิดไว้ จริงอยู่ที่ มันอาจจะคอมไพล์ผ่าน แต่ก็ไม่มีใครการันตีถึง ความถูกต้องได้ จริงไหมครับ?&lt;/p&gt;  &lt;h2&gt;แชร์ไฟล์ไว้ที่ File Server&lt;/h2&gt;  &lt;p&gt;วิธีหนึ่งที่ดู Advance ขึ้นมาหน่อย ก็คือเอาไฟล์ ใส่ไว้ใน Shared Folder แล้วทุกคนก็มารวมตัวกันที่บ้านใครซักคน เพื่อจะได้ทำงานไปพร้อมๆ กัน โดยการ Map Drive มา แล้วก็เปิดโปรเจคทำไปพร้อมกันเลย&lt;/p&gt;  &lt;p&gt;วิธีนี้ แน่นอนว่า เราสามารถมั่นใจเรื่องความถูกต้องได้มากขึ้น เพราะเราทำงานกับไฟล์โปรเจคที่สมบูรณ์จริงๆ แต่ปัญหาก็อาจะเกิดขึ้นได้ ถ้าเกิดมีความจำเป็นที่ต้องใช้งานไฟล์เดียวกัน พร้อมๆ กัน ลองดูจากภาพด้านล่างนี้ครับ (ภาพจากคู่มือ TortoiseSVN)&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coresharp.net/blogs/article/image_4EAFFFFB.png"&gt;&lt;img style="border-right-width:0px;display:block;float:none;border-top-width:0px;border-bottom-width:0px;margin-left:auto;border-left-width:0px;margin-right:auto;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_thumb_24CF8BC6.png" width="376" height="355" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;นั่นก็คือ ถ้าต่างคนต่างลืมบอกกันว่า เฮ้ย เราจะใช้ไฟล์นี้นะ ก็จะเกิดเหตุการณ์ หัวเราะทีหลังดังกว่า เพราะใครเซฟท้ายสุด ก็จะเป็นคนที่สามารถบันทึกการเปลี่ยนแปลงของตัวเองลงในไฟล์ได้ โดยที่การเปลี่ยนแปลงของอีกคน ก็จะหายไปเลย&lt;/p&gt;  &lt;p&gt;จึงเป็นที่มาของระบบ Source Control แบบแรก คือ&lt;/p&gt;  &lt;h2&gt;Lock-Modify-Unlock&lt;/h2&gt;  &lt;p&gt;ระบบนี้เป็นระบบง่ายๆ นั่นก็คือ ใครที่ต้องการจะแก้ไฟล์นี้ ก็จะต้องทำการ “จอง” ไฟล์ไว้ก่อน ถ้าใครจะเข้ามาแก้ไขไฟล์นี้ ก็จะแก้ไม่ได้ คล้ายกับระบบ Lock ไฟล์ที่มีใน OS ทั่วไปครับ อันที่จริงแล้ว ระบบนี้ก็ไม่ได้ต่างอะไรกับการแชร์ไฟล์โดยปกติ แตกต่างกันแค่ เราถูกบังคับให้ต้องสื่อสารกันว่า ใครจะใช้ไฟล์ นั่นเอง&lt;/p&gt;  &lt;p&gt;แต่สมมุติว่า ถ้าเราไม่ได้นั่งทำงานที่เดียวกัน เพราะพอมีระบบแล้ว ต่างคนก็ต่างนั่งทำงานที่บ้านตัวเอง แล้วผมเกิดล็อคไฟล์ไว้เพื่อแก้ไข แล้วออกไปข้างนอกดูหนังคลายเครียด เพื่อนๆ คนอื่นที่อยู่ที่บ้าน ก็หมดสิทธ์ใช้ไฟล์ไปโดยปริยาย จนกว่าผมจะดูหนังจบ และกลับถึงบ้าน ไม่อย่างนั้น ก็อาจจะต้องให้คนที่เป็นแอดมิน ปลดล็อคให้ เพื่อจะได้ใช้ไฟล์ได้&lt;/p&gt;  &lt;p&gt;จะเห็นว่า ระบบนี้ แม้จะช่วยเรื่องการแชร์ไฟล์ได้ แต่ก็ยังสร้างปัญหาอยู่ดี ซึ่งขอสรุปให้เป็นข้อๆ ดังนี้&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;&lt;strong&gt;ทำงานได้ช้าลง เพราะต้องแย่งไฟล์กัน        &lt;br /&gt;&lt;/strong&gt;แน่นอนว่า โดยทั่วไปแล้วแม้เราจะสามารถแยกโปรเจคเป็นไฟล์ย่อยๆ ไม่เกี่ยวข้องกันเลยได้ แต่โอกาสที่จะมีการใช้ไฟล์ร่วมกัน ก็มีอยู่ อย่างเช่น ไฟล์ที่เป็น Web Service, ไฟล์ที่เก็บ Enum หรือ Constant เป็นต้น       &lt;br /&gt;      &lt;br /&gt;นอกจากนี้ การแก้ไขส่วนใหญ่ มีโอกาสน้อยมาก ที่เราจะแก้ไขที่จุดเดียวกัน แต่ระบบนี้จะทำให้คนสองคน ซึ่งจำเป็นต้องแก้ใขไฟล์เดียวกัน แต่คนละส่วน ก็ไม่สามารถทำงานได้พร้อมกันอยู่ดี       &lt;br /&gt;&lt;/li&gt;    &lt;li&gt;&lt;strong&gt;แอดมินปวดหัว&lt;/strong&gt;       &lt;br /&gt;เนื่องจากทุกคนสามารถ Lock ไฟล์กันได้อย่างอิสระเสรี จึงเกิดเหตุการข้างต้นที่ผมยกตัวอย่างไปแล้วได้ นั่นคือ ล็อคตาย คือ คนล็อค ล็อคเสร็จแล้ว มันก็ตายไปเลย ไม่ยอมมาปลดล็อคให้ ซึ่งจากที่ผมได้ทำงานร่วมกับระบบ Source Control แบบนี้ บ่อยครั้งมาก ที่เราจะต้องตะโกนบอกให้เพื่อนอีกคน คืนไฟล์ให้ หรือถามว่า ใช้เสร็จหรือยัง ถ้าเกิดวันนั้นเจ้าตัวคนล็อค ไม่ได้มาทำงาน ก็ต้องเสี่ยงให้แอดมิน ปลดล็อคให้ ซึ่งก็จะทำให้เกิดปัญหาที่อีกข้อ ตามมา นั่นก็คือ       &lt;br /&gt;&lt;/li&gt;    &lt;li&gt;&lt;strong&gt;เกิดปัญหางานหายได้ง่าย        &lt;br /&gt;&lt;/strong&gt;ระบบ Source Control ลักษณะนี้ ก็ยังคงทำงานในแบบไฟล์อยู่ ไม่ได้ดูแลการแก้ไขตัวเนื้อไฟล์จริงๆ ซึ่งถ้าผู้ที่เคยใช้ระบบ Visual Source Safe มาก่อน น่าจะทราบดี ถ้าหากว่าเราทำการแก้ไขไฟล์ในเครื่อง แล้วสั่ง Get Latest Version ออกมา ระบบก็จะแค่ตรวจสอบว่า ไฟล์ในเครื่อง กับไฟล์บน Source Safe นั้น เหมือนกันหรือไม่ ถ้าไม่เหมือนกัน ก็มีแค่สองทางเลือกคือ Lock ไฟล์ เพื่อแก้ไข หรือ เอาไฟล์จากบน Source Safe ทับไฟล์ของเราเลย&amp;#160; (โดยที่ไม่ได้บอกด้วยว่า ไฟล์ของเรา กับไฟล์บน Server ต่างกันอย่างไร) บางที ถ้าเราเผลอไปกดให้ทับไฟล์ของเราในเครื่อง ก็เท่ากับจบกัน &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;ถ้าลองมองดีๆ ปัญหาแบบเดียวกันนี้ ก็เกิดขึ้นกับระบบฐานข้อมูลครับ แต่ระบบ DBMS เขาจัดการด้วย Transaction นั่นคือ แต่ละคน ก็จะเห็นมุมมองของ Database ในชั่วขณะที่คนนั้น ติดต่อเข้ามา โดยที่การเปลี่ยนแปลงของอีกคน จะไม่ส่งผลกระทบข้ามมาเลย และถ้าต้องการบันทึกการเปลี่ยนแปลงที่เกิดขึ้น ก็ต้องสั่ง Commit Transaction เพื่อให้ระบบ DBMS ตรวจหา Conflict ว่ามีการพยายามแก้ไขข้อมูลจุดเดียวกันหรือไม่ และเกิดขั้นตอน Conflict Resolution ต่อไป&lt;/p&gt;  &lt;p&gt;ด้วยแนวคิดเดียวกันนี้ ก็มี Source Control อีกแบบเกิดขึ้น นั่นก็คือ…&lt;/p&gt;  &lt;h2&gt;Copy-Modify-Merge&lt;/h2&gt;  &lt;p&gt;ระบบแบบนี้ จะทำงานคล้ายกัยฐานข้อมูลครับ นั่นคือ แต่ละคนสามารถติดต่อเข้ามา เพื่อดูข้อมูลล่าสุด แล้วแก้ไขเปลี่ยนแปลงได้ตามใจชอบ จนกว่าจะพอใจ แล้วสั่ง Commit เพื่อเซฟการเปลี่ยนแปลง เพื่อความเข้าใจ ผมจะอธิบายตามภาพด้านล่างนี้ครับ&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:block;float:none;border-top-width:0px;border-bottom-width:0px;margin-left:auto;border-left-width:0px;margin-right:auto;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image1_42D54BCD.png" width="350" height="176" /&gt; &lt;/p&gt;  &lt;p&gt;แรกเริ่มเลย ทั้งแฮรี่ และแซลลี่ สามารถเปิดดูไฟล์ A พร้อมกันได้ แล้วจะทำการแก้ไขเปลี่ยนแปลงอย่างไรก็ได้ ตามใจชอบ ในเครื่องของตัวเอง โดยที่ไม่ต้องสั่ง Lock ก่อน&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coresharp.net/blogs/article/image7_64C0AE51.png"&gt;&lt;img style="border-right-width:0px;display:block;float:none;border-top-width:0px;border-bottom-width:0px;margin-left:auto;border-left-width:0px;margin-right:auto;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image7_thumb_66910418.png" width="350" height="169" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;ทีนี้ แซลลี่แก้ไขจนเสร็จแล้ว ก็ทำการเซฟไฟล์ กลับเข้าไปยังระบบ และถ้าแฮรี่ ต้องการจะเซฟบ้าง ก็จะโดนปฏิเสธโดยระบบ เพราะว่าตอนนี้ ไฟล์ในระบบนั้น ไม่เหมือนกับไฟล์ที่แฮรี่เคยอ่านมาดูตั้งแต่ครั้งแรกแล้ว&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coresharp.net/blogs/article/image12_58C14ECE.png"&gt;&lt;img style="border-right-width:0px;display:block;float:none;border-top-width:0px;border-bottom-width:0px;margin-left:auto;border-left-width:0px;margin-right:auto;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image12_thumb_6CDA6B57.png" width="350" height="179" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;ดังนั้น แฮรี่ก็จะต้องอ่านไฟล์ที่ถูกแก้ไขโดยเซลลี่ เข้ามาดูในเครื่องก่อน และระบบ ก็จะจัดการ “Merge” หรือ รวมการเปลี่ยนแปลง ที่แฮรี่ทำไว้ในเครื่อง (A’) เข้ากับไฟล์ที่ได้ออกมาจากระบบ (A”) โดยอัตโนมัติ ซึ่งในขั้นตอนนี้ ถ้าเกิดว่าแซลลี่ และแฮรี่ ไม่ได้แก้ไขไฟล์ที่จุดเดียวกันเลย ระบบก็จะสามารถรวมไฟล์ให้ได้โดยอัตโนมัติ&lt;/p&gt;  &lt;p&gt;แต่ถ้าหากว่าแซลลี่ และแฮรี่ เกิดแก้ตรงจุดเดียวกันขึ้นมา ไฟล์ในเครื่องแฮรี่ ก็จะอยู่ในสถานะ&lt;strong&gt; “Conflict” &lt;/strong&gt;ซึ่งแฮรี่ ก็จะต้องไปเรียกแซลลี่ มานั่ง Merge ด้วยกัน ว่าเธอแก้ตรงไหน ฉันแก้ตรงไหน ต้องเก็บอะไรไว้ เอาอะไรออก&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image17_3593E41C.png" width="580" height="400" /&gt;&lt;/p&gt;  &lt;p&gt;จากนั้น เมื่อทั้งคู่ Merge กันได้แล้ว แฮรี่ก็จะสามารถเซฟไฟล์ กลับลงไปในระบบได้ และเมื่อแซลลี่ โดนกลับไปถึงโต๊ะ ก็สามารถอ่านไฟล์ที่ Merge กันได้แล้ว มาเก็บไว้ในเครื่อง เพื่อทำการแก้ไขต่อไป&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:block;float:none;border-top-width:0px;border-bottom-width:0px;margin-left:auto;border-left-width:0px;margin-right:auto;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image24_30FFA86C.png" width="351" height="178" /&gt; &lt;/p&gt;  &lt;h2&gt;แล้วประโยชน์สำหรับนักพัฒนาคนเดียวละ?&lt;/h2&gt;  &lt;p&gt;แน่นอนว่า สำหรับคนที่ฉายเดี่ยว เรื่องการแชร์ไฟล์นี่ไม่ใช่ปัญหาอยู่แล้ว แต่ปัญหาที่ผมเชื่อว่า ทุกคนน่าจะเคยพบเหมือนกัน ก็น่าจะหนีไม่พ้น…&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;เมื่อวานยังใช้ได้นี่นา ทำไมวันนี้ใช้ไม่ได้แล้ว &lt;/li&gt;    &lt;li&gt;วันก่อนเราแก้อะไรไปบ้างวะเนี่ย??? &lt;/li&gt;    &lt;li&gt;ปัญหานี้ เวอร์ชั่นก่อน ไม่เคยเป็นนี่นา!?!? &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;แต่ถ้าคนที่มี Backup ปัญหาก็คงจะทุเลาไปได้บ้าง แต่อย่างไรก็ตาม เรามักจะ Backup กันแบบไม่จริงจัง เช่น Zip เอาไว้เฉยๆ บ้าง บางคนเป็นระเบียบหน่อย ก็จัดเป็นวันๆ ไว้ แต่ถึงอย่างไร Backup ก็ข่วยได้แค่เก็บไฟล์ก่อนหน้านี้ไว้ได้ แต่ไม่ได้ช่วยบอกเราอยู่ดีว่า เราแก้อะไรไปบ้าง&lt;/p&gt;  &lt;h2&gt;ขอแนะนำ Subversion&lt;/h2&gt;  &lt;p&gt;สำหรับระบบ Source Control ในท้องตลาดก็มีเยอะแยะมากมาย แต่ตัวหนึ่งที่มีความน่าสนใจเป็นพิเศษ เนื่องจากเป็น Free Software และมีการใช้งานอย่างแพร่หลาย ก็คงจะหนีไม่พ้นเจ้า Subversion นี่ละครับ เนื่องจากความที่มันเป็น Free Software จึงทำให้มีคนพัฒนาระบบและโปรแกรมต่างๆ มาติดต่อกับมันเป็นจำนวนมาก จนเมื่อเอาทั้งหมดมารวมกันเนี่ย สามารถทำระบบ Life Cycle Management อย่าง Team System ได้เลยทีเดียว&lt;/p&gt;  &lt;p&gt;สำหรับ Subversion นั้น มีคุณสมบัติที่ดีกว่าระบบอื่นดังนี้ครับ&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;strong&gt;ทำงานได้ทั้งแบบ Copy-Modify-Merge และ Lock-Modify-Unlock        &lt;br /&gt;&lt;/strong&gt;เพราะว่าไฟล์บางประเภท เราไม่สามารถทำการ Merge ได้ครับ อย่างเช่นไฟล์รูป หรือไฟล์ PDF เป็นต้น Subversion ก็เลยสนับสนุนทั้งสองแบบ       &lt;br /&gt;&lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Subversion มอง Version แบบทั้งโปรเจค        &lt;br /&gt;&lt;/strong&gt;ระบบของยี่ห้ออื่นบางทีจะมีมุมมองของโปรเจคที่เพี้ยนไป นั่นคือระบบจะมองเวอร์ชั่นเป็นตามไฟล์ แต่ว่าถ้าเราคิดดูจริงๆ แล้ว โปรเจคทั้งโปรเจค แค่มีไฟล์โดนแก้ไปเพียงไฟล์เดียว ก็ทำให้เวอร์ชั่นเปลี่ยนแล้ว จริงไหมครับ?       &lt;br /&gt;&lt;/li&gt;    &lt;li&gt;&lt;strong&gt;การกระทำหลายๆ อย่าง สามารถรวมกันได้ ในการบันทึก 1 ครั้ง (Atomicity)        &lt;br /&gt;&lt;/strong&gt;ลองนึกดูว่า ในการทำงานจริงๆ เช่นการแก้บัก หรือเพิ่มฟีเจอร์ เราไม่ได้มีการแก้ไขไฟล์ แค่ไฟล์เดียว แต่อาจจะต้องมีการแก้ไขไฟล์หลายๆ ไฟล์ หรือแม้แต่การเพิ่มไฟล์ใหม่ เพิ่มโฟลเดอร์ใหม่เพื่อใส่รูป เป็นต้น การกระทำทั้งหมดนี้ เราสามารถ บันทึกได้ โดยการ Commit เพียงครั้งเดียว ซึ่งจะทำให้เวลาเราไปดูรายงานการเปลี่ยนแปลง ก็สามารถมองเห็นได้อย่างชัดเจนว่า การเพิ่มฟีเจอร์นั้นๆ หรือแก้บักตัวนี้ ได้เกิดการเปลี่ยนแปลงอะไรบ้างในภาพรวม &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;สามารถทำงานร่วมกับระบบ Bug Tracking หรือ Project Management ใดๆ ก็ได้        &lt;br /&gt;&lt;/strong&gt;เนื่องจากตัว Subversion สามารถกำหนด Field เพิ่มเติม ตอน Commit ได้ เราจึงสามารถนำมันมาใช้ร่วมกับระบบ Project Management ได้ โดยไม่ต้องเปลี่ยนระบบใหม่ทั้งหมด ซึ่งเอาไว้ผมจะมาเล่าให้ฟังในโอกาสต่อไปครับ &lt;/li&gt; &lt;/ul&gt;  &lt;h2&gt;ต้องลงอะไรบ้าง&lt;/h2&gt;  &lt;p&gt;สำหรับการสรรหา Subversion มาติดตั้งในเครื่องนั้น ง่าย และถูกลิขสิทธิ์ด้วยครับ โปรแกรมหลักๆ ที่เราจะใช้ ก็มีแค่&lt;a href="http://tortoisesvn.net/downloads" target="_blank"&gt;TortoiseSVN&lt;/a&gt; เท่านั้นเอง ซึ่งเป็นทั้งระบบ Subversion ในตัว และ GUI สำหรับทำงานร่วมกับระบบ Subversion ด้วย (ถ้าใช้ Windows 64-bit ก็ต้องดาวน์โหลดแบบ 64-bit มาด้วยนะครับ)&lt;/p&gt;  &lt;p&gt;แต่ถ้าในการทำงานเป็นทีม ก็จะต้องมีโปรแกรมเพิ่มเติมด้วยครับ ดังนี้&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;a href="http://winmerge.org/downloads/" target="_blank"&gt;WinMerge&lt;/a&gt; จริงๆ แล้วตัว Subversion มันสามารถ Merge ให้เราเองได้อยู่แล้ว แต่ว่าโปรแกรมนี้ จะใช้ในกรณีที่เกิด Conflict ขึ้นครับ แน่นอนว่า ถ้าทำงานคนเดียว โปรแกรมนี้ไม่ต้องใช้เลย เพราะยังไงก็ไม่มีวันเกิด Conflict       &lt;br /&gt;&lt;/li&gt;    &lt;li&gt;&lt;a href="http://www.visualsvn.com/server/" target="_blank"&gt;Visual SVN Server&lt;/a&gt; ชื่อก็บอกอยู่แล้ว ว่าเป็นตัว Server ครับ ใช้สำหรับลงในเครื่อง Server เท่านั้นครับ ในเว็บจะมี Plugin ของ Subversion สำหรับ Visual Studio ด้วย แต่ว่ามีค่าใช้จ่ายครับ เราไม่ชอบ อิอิ &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;หรือถ้าคนที่อยากจะทำงานจาก Visual Studio อย่างเดียวเลย (ซึ่งผมเองไม่ค่อยชอบ) ก็มีครับ นั่นคือ &lt;a href="http://ankhsvn.open.collab.net/" target="_blank"&gt;AnkhSvn&lt;/a&gt; ซึ่งได้รับการพัฒนาอย่างต่อเนื่องมาน่าจะเกิน 5 ปีแล้ว&lt;/p&gt;  &lt;p&gt;สำหรับโพสนี้ ผมขอแนะนำการใช้งานทั่วไป สำหรับทำงานคนเดียวก่อนนะครับ ซึ่งจะเป็นพื้นฐาน ก่อนจะต่อยอดไปเป็นการทำงานไปทีมในโพสต่อไป อ้อ ลงแล้วอย่าลืม Restart เครื่องก่อนนะครับ&lt;/p&gt;  &lt;h2&gt;การใช้งาน&lt;/h2&gt;  &lt;p&gt;หลังการติดตั้ง TortoiseSVN แล้ว จะไม่มีโปรแกรมอะไรใหม่ในเครื่องเลยครับ เพราะว่าการทำงานของ TortoiseSVN นั้น จะเป็นแบบ Shell Extension ซึ่งฝังลงไปในตัว Windows Explorer เลย &lt;/p&gt;  &lt;p&gt;สมมุติว่า ตอนนี้เรามีโปรเจรอยู่แล้ว และต้องการเพิ่มโปรเจคนี้ เข้าไปยัง Subversion สามารถทำได้ตามนี้ครับ&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;&lt;a href="http://coresharp.net/blogs/article/image_705D5BFC.png"&gt;&lt;img style="border-right-width:0px;margin:0px 0px 0px 10px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" align="right" src="http://coresharp.net/blogs/article/image_thumb_11DF1C3D.png" width="300" height="225" /&gt;&lt;/a&gt;อย่างแรกเลย เราจะต้องมีฐานข้อมูล ที่จะเก็บตัวโปรเจคของเรา เรียกว่า Repository ก่อน และเนื่องจาก Subversion มอง Version เป็นแบบภาพรวม เราจึงควรจะมี Repository แยกกันเป็นโปรเจคไปด้วย แต่ถ้าไม่สะดวก หรือไม่ได้ต้องการทราบเลขเวอร์ชั่นที่ละเอียดมาก ก็สามารถจับทุกโปรเจค มารวมกันได้ครับ แต่ต้องจำไว้ว่า การ Commit ครั้งหนึ่ง จะทำให้ Subversion มองว่าทั้ง Repository ได้ขยับเวอร์ชั่นไป เป็นเวอร์ชั่นใหม่ ซึ่งจะส่งผลกับทุกโปรเจคที่อยู่ใน Repository เดียวกันครับ       &lt;br /&gt;      &lt;br /&gt;วิธีสร้าง Repository ในเครื่องก็ง่ายมากครับ แค่สร้างโฟลเดอร์ใหม่ แล้วก็เข้าไปใน Folder นั้น คลิ๊กขวา เลือก TortoiseSVN –&amp;gt; Create Repository Here…       &lt;br /&gt;      &lt;br /&gt;ก็จะพบว่า ด้านในโฟลเดอร์ มีไฟล์กับโฟลเดอร์ขึ้นมาเยอะเลย       &lt;br /&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_614E2F35.png" width="534" height="159" /&gt; &lt;/li&gt;    &lt;li&gt;&lt;a href="http://coresharp.net/blogs/article/image_1BC92F0A.png"&gt;&lt;img style="border-right-width:0px;margin:0px 0px 0px 10px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" align="right" src="http://coresharp.net/blogs/article/image_thumb_103371CB.png" width="240" height="131" /&gt;&lt;/a&gt;ทีนี้ ถ้าเราต้องการจะเข้าไปดู Repository (ต่อไปนี้ขอเรียกสั้นๆ ว่า Repo…จะเรียกมันว่าลิโพเลยดีมั๊ยนะ) ที่เราเพิ่งสร้างขึ้นมา ก็สามารถใช้คำสั่ง RepoBrowser ได้ โดยการคลิ๊กขวาที่ใดก็ได้ใน Windows Explorer หรือ Desktop       &lt;br /&gt;      &lt;br /&gt;จากนั้น ก็กรอก URL ของ Repo ลงไปครับ เนื่องจากว่าเป็นการเข้าถึง Repo ในเครื่อง ก็ต้องใช้ว่า       &lt;br /&gt;      &lt;br /&gt;&lt;a title="file:///E:/Repositories/TestProject"&gt;file:///E:/Repositories/TestProject&lt;/a&gt;       &lt;br /&gt;      &lt;br /&gt;สังเกตว่า เป็นเครื่องหมาย / ธรรมดา และมี 3 ตัวนะครับ ที่อยู่ด้านหลัง file:       &lt;br /&gt;      &lt;br /&gt;ถ้าเกิดว่า อยากจะให้สามารถแชร์ Repo ข้ามเครื่องได้ด้วย ก็สามารถใช้เป็น Share Folder ได้ครับ แต่ด้วยระบบของ Subversion แล้ว เป็นสิ่งที่ไม่ควรทำอย่างยิ่ง ถ้าต้องการจะใช้ร่วมกันจริงๆ ควรจะลง Server SVN จะดีที่สุดครับ       &lt;br /&gt;&lt;/li&gt;    &lt;li&gt;&lt;a href="http://coresharp.net/blogs/article/image_2B6BCACC.png"&gt;&lt;img style="border-right-width:0px;margin:0px 0px 0px 10px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" align="right" src="http://coresharp.net/blogs/article/image_thumb_6DDE6D02.png" width="240" height="148" /&gt;&lt;/a&gt; จากนั้น ตามหลักสากล เราควรจะมีโฟลเดอร์ด้วยกัน 3 โฟลเดอร์ครับ คือ trunk branches tags ซึ่งเราสามารถใช้ RepoBrowser สร้างขึ้นมาได้เลย       &lt;br /&gt;      &lt;br /&gt;สำหรับทั้งสามโฟลเดอร์นั้น มีจุดประสงค์ดังนี้ครับ       &lt;br /&gt;      &lt;br /&gt;&lt;strong&gt;trunk &lt;/strong&gt;เอาไว้เก็บตัวโปรเจค ที่เรากำลังพัฒนาอยู่ในปัจจุบัน       &lt;br /&gt;      &lt;br /&gt;&lt;strong&gt;branches &lt;/strong&gt;เอาไว้เก็บ Branch ในกรณีที่เราต้องการจะลองเล่นอะไรใหม่ๆ แต่ไม่อยากจะให้กระทบกับตัวหลักใ trunk       &lt;br /&gt;      &lt;br /&gt;&lt;strong&gt;tags &lt;/strong&gt;สำหรับเอาไว้เก็บ Build หรือจะเอาไว้ Backup เมื่อเราทำงานเสร็จเป็นระยะๆ ก็ได้ครับ เช่น Beta 1, RC1, V1.1 เป็นต้น       &lt;br /&gt;      &lt;br /&gt;อันที่จริงแล้ว เราไม่มีกฏตายตัวนะครับ ว่าจะต้องมีโฟลเดอร์เหล่านี้ เราสามารถจะจัดมันยังไงก็ได้ ตามใจชอบ แต่โฟลเดอร์ที่ผมแนะนำนี้ คือรูปแบบทั่วไป ที่ใช้กันอยู่ครับ       &lt;br /&gt;&lt;/li&gt;    &lt;li&gt;เมื่อเรามี Repo พร้อมแล้ว ก็เหลือแค่การเอาไฟล์ ใส่มาใน Repo ของเรา ซึ่งทำได้ง่ายมากครับ จากใน Windows Explorer อีกแล้ว      &lt;br /&gt;      &lt;br /&gt;สมมุติว่า ตอนนี้ผมมีโปรเจคอยู่แล้ว และต้องการนำมันไปไว้ใน SVN ที่ผมจะต้องทำก็คือ ไปที่โฟลเดอร์ที่เก็บไฟล์โปรเจค แล้วคลิ๊กขวา เลือก SVN Checkout ครับ       &lt;br /&gt;      &lt;br /&gt;จากนั้น URL ก็ใส่เป็น URL ของ Repo เรา พร้อมกับโฟลเดร์ trunk เช่น &lt;a title="file:///E:/Repositories/TestProject/trunk"&gt;file:///E:/Repositories/TestProject/trunk&lt;/a&gt; และโฟลเดอร์ที่จะ Checkout ก็ตรวจให้แน่ใจว่าเป็นโฟลเดอร์ของโปรเจค เพราะตัว Tortoise SVN มันอาจจะเติมชื่อ Folder ใน SVN ให้ครับ       &lt;br /&gt;      &lt;br /&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_0916C604.png" width="539" height="324" /&gt;       &lt;br /&gt;เรียบร้อยแล้ว ก็กด OK เราจะถูกถามว่า โฟลเดอร์มันมีไฟล์อยู่แล้วนะ แน่ใจหรือเปล่า ก็ตอบ Yes ครับ       &lt;br /&gt;      &lt;br /&gt;&lt;img style="border-right-width:0px;display:block;float:none;border-top-width:0px;border-bottom-width:0px;margin-left:auto;border-left-width:0px;margin-right:auto;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_17E38C8D.png" width="377" height="134" /&gt;&amp;#160; &lt;br /&gt;      &lt;br /&gt;จากนั้น จะมีอีกหน้าต่างเปิดขึ้นมา เพื่อรายงานสถานะ ทุกอย่างควรจะเรียบร้อยดีครับ       &lt;br /&gt;      &lt;br /&gt;&lt;a href="http://coresharp.net/blogs/article/image_052E92D6.png"&gt;&lt;img style="border-right-width:0px;display:block;float:none;border-top-width:0px;border-bottom-width:0px;margin-left:auto;border-left-width:0px;margin-right:auto;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_thumb_32461055.png" width="531" height="240" /&gt;&lt;/a&gt;       &lt;br /&gt;&lt;img style="border-right-width:0px;margin:0px 0px 0px 10px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" align="right" src="http://coresharp.net/blogs/article/image_2ABD3199.png" width="166" height="189" /&gt; ถ้าเราเปิดดูโฟลเดอร์ ก็จะพบว่า มีเครื่องหมายถูกในวงกลมสีเขียวอยู่ด้วย แปลว่า โฟลดอร์ของเรา ได้กลายเป็น Working Copy ของ โฟลเดอร์ trunk ใน Repo ของเราแล้วครับ นั่นก็คือ โฟลเดอร์นี้ จะเป็นโฟลเดร์ที่เราใช้ทำงาน ซึ่งมันจะลิงค์กับโฟลเดร์ trunk ในลิโพ เอ้ย repo ของเราครับ       &lt;br /&gt;      &lt;br /&gt;จากนั้นสำหรับครั้งแรก เราก็จะต้องเพิ่มไฟล์ให้เข้าไปอยู่ในลิโพซะก่อน โดยการใช้คำสั่ง Commit ครับ ซึ่งก็จะมีหน้าต่างใหม่เปิดขึ้นมา       &lt;br /&gt;      &lt;br /&gt;&lt;a href="http://coresharp.net/blogs/article/image_01B5234E.png"&gt;&lt;img style="border-right-width:0px;margin:0px 5px 0px 0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_thumb_08F90B1B.png" width="360" height="286" /&gt;&lt;/a&gt;       &lt;br /&gt;แต่ก่อนจะเลือก Select All แล้วกด OK ผ่านไปเฉยๆ ขอให้ลองดูซักนิดครับ ว่ามีไฟล์อะไรที่ไม่เกี่ยวข้องกับโปรเจคของเราหรือเปล่า อย่างเช่น       &lt;br /&gt;      &lt;br /&gt;- โฟลเดอร์ bin/obj ซึ่งเปลี่ยนทุกครั้งเมื่อ Compile โปรแกรม       &lt;br /&gt;- ไฟล์ .suo ซึ่งเป็นไฟล์ Setting ของ Solution       &lt;br /&gt;- ไฟล์ .user ซึ่งเป็นไฟล์ Setting ของ Project       &lt;br /&gt;      &lt;br /&gt;ถ้าพบ ก็เอาออกซะก่อน ไฟล์/โฟลเดอร์เหล่านี้ จะได้ไม่มากวนเลขเวอร์ชั่น (Revision) ของเรา เมื่อเสร็จเรียบร้อย SVN ก็จะรายงานว่า ตอนนี้ Revision เราอยู่ที่หมายเลข 6 นั่นคือ ตั้งแต่สร้างลิโพนี้มา เราได้คอมมิทไปแล้ว&amp;#160; 6 ครั้งครับ ถ้าในขั้นตอนที่ผ่านมา ใครสร้างโฟลเดอร์แล้วลบหลายๆ ครั้ง ก็จะได้เลขที่เยอะกว่านี้ ส่วนผมเอง ก็สร้างแล้วลบไปครั้งนึงครับ       &lt;br /&gt;      &lt;br /&gt;&lt;a href="http://coresharp.net/blogs/article/image_60E8ECAC.png"&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_thumb_55E40DB7.png" width="532" height="242" /&gt;&lt;/a&gt;       &lt;br /&gt;      &lt;br /&gt;เท่านี้ก็เรียบร้อยครับลิโพเราพร้อมใช้งานแล้ว &lt;/li&gt; &lt;/ol&gt;  &lt;h2&gt;การใช้งานประจำวัน&lt;/h2&gt;  &lt;p&gt;ทีเรา ต่อไปพอเราทำงานไปเรื่อยๆ เราก็สามารถบันทึกการเปลี่ยนแปลงเอาไว้ช่วยเตือนตัวเอง และช่วยให้เราคืนชีพโปรเจคได้ ถ้าเราเกิดไปทำมันพังขึ้นมา สิ่งที่เราจะต้องทำเป็นประจำ กับตัว SVN ก็มีลักษณะการทำงาน ตามนี้ครับ&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;strong&gt;ทำงาน เซฟ แล้ว Commit        &lt;br /&gt;&lt;/strong&gt;หลังจากทำงานไปซักพัก และรู้สึกว่า ทุกอย่างดูดี วันนี้จะพักนอนเล่นแล้ว คุณก็ควรจะสั่ง Commit สักครั้งครับ เพื่อบันทึกข้อมูลลง SVN และจะได้มีโอกาสเขียน Log ไว้เตือนตัวเองด้วย คำสั่งและหน้าจอ Commit ผมได้แสดงให้ดูไปแล้วในหัวข้อก่อนหน้านี้       &lt;br /&gt;&lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Update ดู Log แล้วทำงานต่อ        &lt;br /&gt;&lt;/strong&gt;เมื่อเริ่มงานใหม่ในแต่ละวัน เราก็อาจจะกด Update สักครั้งหนึ่ง เพื่อให้แน่ใจว่า ข้อมูลใน Working Folder เรา ตรงกับใน SVN แล้วอาจจะเปิด Log มาดู เพื่อเช็คว่า คราวก่อนๆ เราได้ทำอะไรไปบ้าง ซึ่งวิธีดู Log นั้นง่ายมากครับ คลิ๊กขวา แล้วเลือก Show Log เท่านั้น       &lt;br /&gt;      &lt;br /&gt;&lt;img style="border-right-width:0px;display:block;float:none;border-top-width:0px;border-bottom-width:0px;margin-left:auto;border-left-width:0px;margin-right:auto;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_46D2503F.png" width="532" height="367" /&gt;       &lt;br /&gt;แต่ Log ของ SVN จะต่างกับของ VSS ตรงที่เราจะเห็น Log ของการ Commit ไม่ได้เป็น Log แยกตามไฟล์ เพราะอย่างที่ผมได้บอกไปแล้วว่า SVN มองการ Commit เป็นแบบ Atomic หรือมองเป็นการแก้ไขในภาพรวม ถ้าอยากจะดู Log เป็นแต่ละไฟล์ ก็สามารถทำได้ โดยการไล่ Commit ทีละไฟล์ครับ แต่ว่าการทำแบบนั้น จะเป็นการทำลายจุดเด่นเรื่องนี้ของ SVN ที่ช่วยให้เราติดตามการเปลี่ยนแปลงแบบภาพรวมไป       &lt;br /&gt;      &lt;br /&gt;นอกจากนี้ เรายังเอา SVN มาใช้คำนวณ Man Hour ได้ด้วย โดยดูจากอายุของตัวลิโพ และปริมาณการคอมมิทที่เกิดขึ้น โดยการกดปุ่ม Statistics       &lt;br /&gt;      &lt;br /&gt;&lt;a href="http://coresharp.net/blogs/article/image_0A3F7304.png"&gt;&lt;img style="border-right-width:0px;display:block;float:none;border-top-width:0px;border-bottom-width:0px;margin-left:auto;border-left-width:0px;margin-right:auto;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_thumb_47CF617E.png" width="509" height="379" /&gt;&lt;/a&gt;       &lt;br /&gt;&lt;/li&gt;    &lt;li&gt;&lt;strong&gt;ลบ/เปลี่ยนชื่อโฟลเดอร์        &lt;br /&gt;&lt;/strong&gt;การลบหรือเปลี่ยนชื่อโฟลเดอร์ ผมแนะนำว่า ควรจะใช้ RepoBrowser เพราะว่าเชื่อถือได้มากกว่า การทำจากใน Explorer ครับ หลังจากทำเสร็จแล้ว ก็แค่สั่ง Update เพื่อปรับให้ตรงกับใน SVN       &lt;br /&gt;&lt;/li&gt;    &lt;li&gt;&lt;strong&gt;เปลี่ยนใจ&lt;/strong&gt;       &lt;br /&gt;บางที เราแก้อะไรไปแล้ว แล้วเกิดเปลี่ยนใจ ไม่อยากจะเก็บไว้แล้ว ก็สามารถทำได้ โดยใช้คำสั่ง Revert ครับ       &lt;br /&gt;&lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Tag        &lt;br /&gt;&lt;/strong&gt;เมื่องานสำเร็จลุล่วงตามเป้าหมายที่วางไว้ อย่างพวก Milestone หรือ Build เราก็สามารถ Backup เก็บไว้ได้ครับ เกิดวันหน้าวันหลัง ทำอะไรพังขึ้นมา อย่างน้อย ก็ยัง ย้อนเวลากลับมาเอาของเดิมได้ เราเรียกการ Backup นี้ว่า Tag ครับ       &lt;br /&gt;      &lt;br /&gt;การ Tag สามารถทำได้จากหน้า RepoBrowser ครับ (หรือจะเลือก Click ขวาแล้ว Branch/Tag ก็ได้ แต่ผมไม่แนะนำ) ซึ่งจริงๆ แล้วก็ไม่ได้มีขึ้นตอนยุ่งยากอะไร มันคือการ Copy Trunk ไปเก็บไว้นั่นเอง ซึ่งทำได้โดยการ Click ขวาที่ trunk เลือก Copy To.. แล้วใส่ URL ว่า &lt;a title="file:///E:/Repositories/TestProject/tags/M1"&gt;file:///E:/Repositories/TestProject/tags/M1&lt;/a&gt; โดย M1 จะเป็นอะไรก็ได้ครับ มันก็คือ Folder ธรรมดานั่นเอง อย่าลืมใส่ Log ไว้ด้วยละว่า เรา Tag เพราะอะไร       &lt;br /&gt;      &lt;br /&gt;&lt;a href="http://coresharp.net/blogs/article/image_142985D1.png"&gt;&lt;img style="border-right-width:0px;display:block;float:none;border-top-width:0px;border-bottom-width:0px;margin-left:auto;border-left-width:0px;margin-right:auto;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_thumb_58D8B0C3.png" width="346" height="183" /&gt;&lt;/a&gt; &lt;/li&gt; &lt;/ul&gt;  &lt;h2&gt;ส่งท้าย&lt;/h2&gt;  &lt;p&gt;สำหรับการใช้งาน SVN ในขั้นต้น คงจะมีเท่านี้ครับ แต่ยังมีอีกหลายเรื่องมากมาย ที่ผมไม่ได้พูดถึงในโพสนี้ เช่น&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;แก้ Conflict เวลาคนใช้พร้อมกันหลายคน &lt;/li&gt;    &lt;li&gt;Branch &lt;/li&gt;    &lt;li&gt;Merge Revision, Branch &lt;/li&gt;    &lt;li&gt;การ Backup ลิโพ &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;อ่านต่อได้จากภาคสอง ที่ &lt;a title="http://coresharp.net/blogs/article/archive/2009/03/08/source-control-2.aspx" href="http://coresharp.net/blogs/article/archive/2009/03/08/source-control-2.aspx"&gt;http://coresharp.net/blogs/article/archive/2009/03/08/source-control-2.aspx&lt;/a&gt;&lt;/p&gt;&lt;img src="http://coredeveloper.net/aggbug.aspx?PostID=620" width="1" height="1"&gt;</description></item><item><title>MapOnMobile 2/x : เรื่องของการวาดแผนที่…</title><link>http://coredeveloper.net/blogs/nantcom/archive/2009/03/16/maponmobile2.aspx</link><pubDate>Mon, 16 Mar 2009 07:20:39 GMT</pubDate><guid isPermaLink="false">86cc649a-bf28-4d34-a9ef-d75c61f34293:618</guid><dc:creator>นันคอม</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://coredeveloper.net/blogs/nantcom/rsscomments.aspx?PostID=618</wfw:commentRss><comments>http://coredeveloper.net/blogs/nantcom/archive/2009/03/16/maponmobile2.aspx#comments</comments><description>&lt;p&gt;หลังจากใน &lt;a href="http://coresharp.net/blogs/article/archive/2008/12/24/maponmobile.aspx"&gt;MapOnMobile ภาคแรก&lt;/a&gt; ผมได้แนะนำถึงหลักการต่างๆ รวมไปถึงสูตรคำนวณแล้ว ผมเชื่อว่าด้วยข้อมูลพวกนั้น หลายๆ คน อาจจะไปทำ Map มาสำเร็จแล้วก็ได้ แต่ไม่เป็นไรครับ ในตอนที่สองนี้ ผมจะมาแนะนำถึงเรื่องการวาดแผนที่ก็แล้วกันครับ :)&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_26E8A189.png"&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;margin-left:0px;border-left-width:0px;margin-right:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_43057242.png" width="550" height="353" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;h2&gt;การวาดแผนที่&lt;/h2&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;margin:0px 10px 0px 0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" align="left" src="http://coredeveloper.net/blogs/nantcom/image_1F688243.png" width="217" height="236" /&gt;ผมขออ้างอิงจากภาพเก่าในภาคแรกมานิดนึงนะครับ สมมุติว่า เราต้องการจะวาดภาพของแผนที่ ในระดับ 2 ซึ่งมีแผนที่ทั้งหมด 16 แผ่น (4 แถว 4 คอลัมน์) ถ้าลองมองข้ามตัวเลขไป มันก็คือการไล่สร้างตาราง จากซ้ายไปขวา บนลงล่างธรรมดานั่นเอง ซึ่งโค๊ด ก็คงหนีไม่พ้น For ซ้อนกัน 2 For แบบนี้ &lt;/p&gt;  &lt;div class="cf"&gt;   &lt;p class="cl"&gt;&lt;span class="cb1"&gt;for&lt;/span&gt; (&lt;span class="cb1"&gt;int&lt;/span&gt; &lt;span class="cb2"&gt;row&lt;/span&gt; &lt;span class="cb3"&gt;=&lt;/span&gt; &lt;span class="cb4"&gt;0&lt;/span&gt;; &lt;span class="cb2"&gt;row&lt;/span&gt; &lt;span class="cb3"&gt;&amp;lt;&lt;/span&gt; &lt;span class="cb4"&gt;4&lt;/span&gt;; &lt;span class="cb2"&gt;row&lt;/span&gt;&lt;span class="cb3"&gt;++&lt;/span&gt;)&lt;/p&gt;    &lt;p class="cl"&gt;{&lt;/p&gt;    &lt;p class="cl"&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span class="cb1"&gt;for&lt;/span&gt; (&lt;span class="cb1"&gt;int&lt;/span&gt; &lt;span class="cb2"&gt;column&lt;/span&gt; &lt;span class="cb3"&gt;=&lt;/span&gt; &lt;span class="cb4"&gt;0&lt;/span&gt;; &lt;span class="cb2"&gt;column&lt;/span&gt; &lt;span class="cb3"&gt;&amp;lt;&lt;/span&gt; &lt;span class="cb4"&gt;4&lt;/span&gt;; &lt;span class="cb2"&gt;column&lt;/span&gt;&lt;span class="cb3"&gt;++&lt;/span&gt;)&lt;/p&gt;    &lt;p class="cl"&gt;&amp;#160;&amp;#160;&amp;#160; {&lt;/p&gt;    &lt;p class="cl"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span class="cb5"&gt;// Draw&lt;/span&gt;&lt;/p&gt;    &lt;p class="cl"&gt;&amp;#160;&amp;#160;&amp;#160; }&lt;/p&gt;    &lt;p class="cl"&gt;}&lt;/p&gt; &lt;/div&gt;  &lt;p&gt;แต่ปัญหาก็คือ จะวาดยังไง และเอาแผนที่มาจากไหน มากกว่า จริงไหมครับ :)&lt;/p&gt;  &lt;p&gt;ถ้าเราลืมเรื่องการคำนวณไปก่อน วาดแผนที่มันทั้งโลกเลย ที่ Level 1 (ซึ่งจะมีแค่ 4 แผ่น) URL ของแผนที่ทั้ง 4 ก็คือ&lt;/p&gt;  &lt;p&gt;&lt;a title="http://a2.ortho.tiles.virtualearth.net/tiles/a10.jpeg?g=45&amp;#13;&amp;#10;" href="http://a2.ortho.tiles.virtualearth.net/tiles/a0.jpeg?g=45"&gt;http://a2.ortho.tiles.virtualearth.net/tiles/a0.jpeg?g=45      &lt;br /&gt;&lt;/a&gt;&lt;a title="http://a2.ortho.tiles.virtualearth.net/tiles/a10.jpeg?g=45&amp;#13;&amp;#10;" href="http://a2.ortho.tiles.virtualearth.net/tiles/a1.jpeg?g=45"&gt;http://a2.ortho.tiles.virtualearth.net/tiles/a1.jpeg?g=45&lt;/a&gt;     &lt;br /&gt;&lt;a title="http://a2.ortho.tiles.virtualearth.net/tiles/a10.jpeg?g=45&amp;#13;&amp;#10;" href="http://a2.ortho.tiles.virtualearth.net/tiles/a2.jpeg?g=45"&gt;http://a2.ortho.tiles.virtualearth.net/tiles/a2.jpeg?g=45&lt;/a&gt;     &lt;br /&gt;&lt;a title="http://a2.ortho.tiles.virtualearth.net/tiles/a10.jpeg?g=45&amp;#13;&amp;#10;" href="http://a2.ortho.tiles.virtualearth.net/tiles/a3.jpeg?g=45"&gt;http://a2.ortho.tiles.virtualearth.net/tiles/a3.jpeg?g=45&lt;/a&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;(a2 ด้านหน้าคือ Server นะครับ)&lt;/p&gt;  &lt;p&gt;อย่างแรกเลย คือ การวาดภาพนั้น มีเรื่องของ Performance เข้ามีเกี่ยวข้อง ถ้าเราวาดภาพไป อ่านไฟล์ไป คงไม่ดีแน่ ดังนั้นแล้ว เราควรจะมี Cache เพื่อเก็บภาพไว้ในแรมก่อนครับ และสำหรับการแสดงภาพแผนที่ เราก็ต้องดาวน์โหลดมาด้วย ซึ่งเน็ต 2.5G บ้านเราก็ใช่ว่าจะเร็ว ยังไงซะ ก้อต้องมีที่เซฟภาพไว้ก่อนจะเอาขึ้นมาวาด ดังนั้น ปัญหาแรกเลย คือ เซฟภาพที่ไหน…&lt;/p&gt;  &lt;p&gt;โค๊ดด้านล่างนี้ ใช้หาว่า โฟลเดอร์ปัจจุบัน ที่โปรแกรมเรากำลังรันอยู่เนี่ย มันโฟลเดอร์อะไร (ซึ่งเป็นหนึ่งในคำถามยอดฮิตที่พบบ่อยมาก) และผมต่อท้ายให้เป็น&amp;#160; Folder ชื่อ Cache ด้วย จะได้เป็นระเบียบ (จริงๆ ควรจะใส่ SD Card นะ…) อ้อ โค๊ดนี้ของ Compact Framework นะครับ ของ Full Framework ใช้ AppDomain.CurrentDomain.BaseDirectory ได้เลย&lt;/p&gt;  &lt;div class="cf"&gt;   &lt;p class="cl"&gt;&lt;span class="cb1"&gt;// Cache Folder&lt;/span&gt;&lt;/p&gt;    &lt;p class="cl"&gt;&lt;span class="cb2"&gt;private&lt;/span&gt; &lt;span class="cb2"&gt;string&lt;/span&gt; &lt;span class="cb3"&gt;_CachePath&lt;/span&gt; &lt;span class="cb4"&gt;=&lt;/span&gt; &lt;span class="cb5"&gt;Path&lt;/span&gt;&lt;span class="cb4"&gt;.&lt;/span&gt;&lt;span class="cb3"&gt;GetDirectoryName&lt;/span&gt;(&lt;/p&gt;    &lt;p class="cl"&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span class="cb5"&gt;Assembly&lt;/span&gt;&lt;span class="cb4"&gt;.&lt;/span&gt;&lt;span class="cb3"&gt;GetExecutingAssembly&lt;/span&gt;()&lt;span class="cb4"&gt;.&lt;/span&gt;&lt;span class="cb3"&gt;GetName&lt;/span&gt;()&lt;span class="cb4"&gt;.&lt;/span&gt;&lt;span class="cb3"&gt;CodeBase&lt;/span&gt;) &lt;span class="cb4"&gt;+&lt;/span&gt; &lt;span class="cb6"&gt;@&amp;quot;\Cache\&amp;quot;&lt;/span&gt;;&lt;/p&gt; &lt;/div&gt;  &lt;p&gt;จากนั้น ก็ต้องมีฟังก์ชั่นสำหรับให้เราดึงภาพได้ แต่ว่าภาพจะโดนอ่านจากไฟล์ในครั้งแรก และเราจะเก็บในแรมไว้ สำหรับครั้งต่อไป&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_3B55A33C.png"&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_77DA29B4.png" width="550" height="367" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;สำหรับผู้อ่านตาดี คงจะสังเกตเห็นแล้วว่า เอ๊ะ ทำไม Dictionary มันถึงเป็น WeakReference แทนที่จะเป็น Bitmap…&lt;/p&gt;  &lt;p&gt;นั่นก็เพราะว่า ถ้าเราใส่ Bitmap ไปใน Dictionary เลย จะเป็นการสร้าง Strong Reference (ซึ่งตรงข้ามกับ Weak Reference) เข้ากับ Bitmap ครับ ซึ่งถ้านึกภาพของการใช้งาน เวลาเราซูมเข้า ซูมออก ก็จะมีแผ่นแผนที่จำนวนมาก ที่เราไม่ต้องการใช้แล้ว ถ้าเราใช้ Weak Reference ก็จะมีค่าเท่ากับการที่เรา ไม่ได้ถือ Reference ไปยัง Object นั้นไว้จริงๆ ทำให้ CLR สามารถนับ Bitmap ที่ถูกทิ้งพวกนั้นเป็นขยะได้เอง โดยที่เราไม่ต้องจัดการ Remove ออกจาก Dictionary เองครับ&lt;/p&gt;  &lt;p&gt;ส่วนการวาด เราจะใช้การ Override ฟังก์ชั่น OnPaintBackground ของคอนโทรลครับ&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_528C6DE1.png" width="550" height="276" /&gt; &lt;/p&gt;  &lt;p&gt;จะเห็นว่า โค๊ดในการวาด ก็คือวาดตามแถว ตามหลักกันดื้อๆ เลยนั่นละครับ แต่ที่ Advance กว่าหน่อย ก็คือ เราทำการแปลแถว และหลัก มาเป็น Quad Key สำหรับ Virtual Earth แล้วก็ขอภาพจาก Cache เอาออกมาวาดนั่นเองครับ ผลลัพทธ์ที่ได้ ก็ตามภาพครับ :)&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_55118BA0.png" width="298" height="461" /&gt; &lt;/p&gt;  &lt;h2&gt;เจาะช่อง มองแผนที่ (ViewPort)&lt;/h2&gt;  &lt;p&gt;แน่นอนว่า นี่แค่ Level 1 แผนที่มันเองก็ทะลุจอไปไกลแล้ว (ยกเว้นคนใช้ Touch HD) นั่นก็เพราะว่า แผนที่ 1 แผ่น ก็มีขนาด 256x256 Pixel ต่อกัน 4 แผ่น ก็เข้าไป 512x512 แล้ว ทั้งที่จอเราเองก็มีกันแค่ 240 Pixel เป็นอย่างมาก เราจึงต้องอาศัยแนวคิดของ Viewport มาช่วยครับ&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_74139B3F.png" width="401" height="265" /&gt; &lt;/p&gt;  &lt;p&gt;ลองนึกภาพถึง เรามีหน้าต่างกรอบสีส้มอยู่บนผนัง (ที่ขยับได้) ที่เราใช้มองไปยังวิวภายนอกที่เป็นแผ่นสีฟ้า ถ้าเราอยากเปลี่ยนวิว ไปมองส่วนอื่นบ้าง โดยที่เรายืนมองจากตำแหน่งเดิม ไม่ก้าวไปข้างหน้า หรือถอยหลัง (มองแบบ 2 มิตินั่นเอง) เราก็สามารถทำได้โดยการ ขยับหน้าต่างไปมาตามที่เราต้องการ เพื่อจะมองส่วนอื่นๆ บ้าง&lt;/p&gt;  &lt;p&gt;แล้วเราขยับหน้าต่างไม่ได้ละ?…เราก็ขยับวิวภายนอกสิครับ :)&lt;/p&gt;  &lt;p&gt;ปกติแล้ว เวลาเราวาดภาพด้วยการเขียนโปรแกรม จุด Origin (0.0) ก็จะอยู่ที่มุมบนซ้ายเสมอ ถ้าเราเริ่มวาดแผนที่แผ่นแรกที่ตำแหน่ง (0, 0) ซึ่งได้จากสูตร column x 256, row x 256 วิวของเราก็จะเริ่มที่ Origin ในเมื่อเราอยากจะ &lt;strong&gt;ขยับวิว&lt;/strong&gt; เราก็แค่&lt;strong&gt;เปลี่ยนจุด Origin&lt;/strong&gt; เท่านั้นเองครับ ซึ่งการทำแบบนี้ เรียกว่า Translation (หรือ Translate Transform ใน WPF/Silverlight นั่นเอง) และตามการใช้งานส่วนมาก เรามักจะให้จุดสนใจอยู่ที่กลาง Viewport ครับ ดังนั้น Translation ของเราก็ควรจะทำตามหลักการนี้ด้วย ดังโค๊ดดัานล่างนี้ครับ&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_4E79F811.png" width="550" height="204" /&gt; &lt;/p&gt;  &lt;p&gt;จากโค๊ด จะเห็นว่า ผมทำการ ย้ายแกน โดยย้ายจุดสนใจไปในทางลบ ให้มันชิดมุมซ้ายบนเลย จากนั้น ก็ค่อยขยับมันกลับมาตรงกลางมุมมองอีกที จากการทดสอบกับจุดกรุงเทพ ที่ผมคำนวณให้ดูไว้ในตอนแรก ก็ได้ผลดังภาพครับ&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_48FB61A0.png" width="299" height="459" /&gt; &lt;/p&gt;  &lt;p&gt;นอกจากนี้ ไม่ว่าจะเป็นการวาดจุดต่างๆ บนแผนที่ ก็สามารถใช้หลักการเดียวกันได้ครับ เช่น ถ้าผมต้องการวาดจุดสีแดง แสดงตำแหน่งของกรุงเทพ ผมก็ทำการคำนวณตำแหน่งของกรุงเทพ ออกมาเป็น Pixel ก่อน แล้วค่อยย้ายแกน เพื่อขยับมันมาอยู่ตรงกลาง&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_25BB4070.png" width="550" height="41" /&gt; &lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;margin-left:0px;border-left-width:0px;margin-right:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_28A07F56.png" width="300" height="457" /&gt; &lt;/p&gt;  &lt;p&gt;ที้นี้ ก็ใกล้ความจริงมากแล้วละครับ จะวาดแผนที่ก็ได้แล้ว วาดจุดก็ได้แล้ว ยังจะขาดก็แต่ ทำให้แผนที่มันลากได้เนี่ยละ ซึ่งเดี๋ยวผมขออุบไว้ก่อน ว่าจะทำยังไง อีกแป๊บผมจะมา Update เฉลยให้ครับ&lt;/p&gt;  &lt;h2&gt;ขั้นต่อไป?&lt;/h2&gt;  &lt;p&gt;สำหรับตอนต่อไป ก็จะต้องเป็นการทำให้โปรแกรม สามารถดาวน์โหลดภาพ ในระหว่างที่เรากำลังเล่นโปรแกรมอยู่ ได้โดยที่โปรแกรมไม่ค้าง ซึ่งต้องใช้เทคนิคเรื่อง Threading เข้ามาช่วย รวมไปถึงการคำนวณแผนที่ใหม่ ซึ่งจะเปลี่ยนไปเวลาที่เราขยับดูแผนที่ ทั้งสองหัวข้อนี้ผมจะมาแนะนำในตอนหน้าครับ และหลังจากเราสร้างให้มันใช้งานได้แล้ว ค่อยเข้าเรื่องการ Refactor มันให้มีโครงร่างสวยๆ ใช้งานง่ายๆ กันต่อครับ&lt;/p&gt;&lt;img src="http://coredeveloper.net/aggbug.aspx?PostID=618" width="1" height="1"&gt;</description></item><item><title>MapOnMobile : รู้จักกับแนวคิด</title><link>http://coredeveloper.net/blogs/nantcom/archive/2009/03/13/maponmobile.aspx</link><pubDate>Fri, 13 Mar 2009 07:21:00 GMT</pubDate><guid isPermaLink="false">86cc649a-bf28-4d34-a9ef-d75c61f34293:619</guid><dc:creator>นันคอม</dc:creator><slash:comments>2</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://coredeveloper.net/blogs/nantcom/rsscomments.aspx?PostID=619</wfw:commentRss><comments>http://coredeveloper.net/blogs/nantcom/archive/2009/03/13/maponmobile.aspx#comments</comments><description>&lt;p&gt;สวัสดีครับ และยินดีต้อนรับสู่บทความแรกในชุดของ MapOnMobile ซึ่งไอเดียของซีรี่ส์นี้ มาจากการที่เมื่อเร็วๆ นี้ ผมได้พัฒนา Application ให้กับบริษัท ซึ่งต้องการแสดงผลแผนที่บน WPF โดยที่ไม่ใช้ Browser เพื่อที่จะแสดงผลข้อมูลต่างๆ ตามใจชอบอย่างเต็มที่โดยไม่ต้องพึ่ง API และข้อจำกัดของตัว Browser ซึ่งผมคิดว่า แนวคิดนี้ น่าจะเอามาใช้กับการแสดงผลแผนที่บน Windows Mobile ได้เป็นอย่างดีเลยทีเดียว เพราะเรามีข้อจำกัดในการแสดงผลอย่างมาก และแน่นอนว่าการพึ่งพาให้ Browser บน Windows Mobile ทำการแสดงผลแผนที่ให้เรานั้น คงอาจจะเป็นไปไม่ได้เลย&lt;/p&gt;  &lt;p&gt;สำหรับบทความในชุดนี้ผมจะพยายามเขียนให้ครอบคลุมตั้งแต่แนวคิดของแผนที่ การออกแบบโปรแกรม และการใช้ Thread และการจัดการบริหารแรม ซึ่งมีเพียงน้อยนิดบน WM&amp;#160; ซึ่งหวังว่าจะเป้นประโยชน์กับทั้งผู้เริ่มต้น และผู้ที่เขียนเป็นแล้วครับ&lt;/p&gt;  &lt;p&gt;แต่ก่อนที่เราจะไปกันต่อถึงการเขียนโค๊ด ในบทความแรกนี้ มีเรื่องที่เราจะต้องเข้าใจให้ตรงกันก่อนครับ นั่นคือหลักการต่างๆ ที่เกี่ยวของกับโปรเจคของเรา นั่นคือแนวคิดและหลักการการนำแผนที่มาแสดงผลบนโปรแกรมครับ&lt;/p&gt;  &lt;h2&gt;รู้จักกับ Mecrator Projection&lt;/h2&gt;  &lt;p&gt;&lt;a href="http://coresharp.net/blogs/article/image_6A00C823.png"&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_thumb_4F542C0A.png" width="560" height="245" /&gt;&lt;/a&gt;     &lt;br /&gt;ภาพจาก &lt;a href="http://en.wikipedia.org/wiki/Map_projection" target="_blank"&gt;WikiPedia&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;เราทราบกันดีว่าโลกเรานั้นกลม แต่ว่าหน้าจอเรามันแบน จะทำอย่างไรจึงจะสามารถแสดงผลโลกกลม บนหน้าจอแบน หรือแผ่นกระดาษได้? ก็คงหนีไม่พ้นกระบวนการที่เรียกว่า Projection นั่นเองครับ หลักการของ Mecrator Projection นั้น มีการใช้กันมาตั้งแต่ปี 1569 เพื่อประโยชน์ในการเดินเรือ เพราะว่ามันสามารถแสดงเส้นทางการเดินเรือที่สำคัญ เป็นเส้นตรงได้ ต่างจากวิธีอื่นๆ โดยแนวคิดของ Mecrator Projection นั้น คือ การเอาแผนที่ ซึ่งเป็นกระดาษแผ่นใหญ่ๆ ม้วนรอบโลกเอาไว้ โดยให้กึ่งกลางของแผนที่แผ่นนี้ แตะกับเส้นศุนย์สูตรพอดี และดึงเอาภาพจากบนโลก มาแสดงผลบนแผนที่แผ่นนี้ครับ&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;margin:0px 10px 5px 0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" align="left" src="http://coresharp.net/blogs/article/image_34A78FF1.png" width="221" height="205" /&gt; &lt;/p&gt;  &lt;p&gt;ลองนึกถึงการพยายามพิมพ์ภาพแผนที่โลกบนแผนพลาสติกที่มันยืดๆ ได้และมีความสูงเท่ากับลูกโลกในห้องสมุด เพื่อเอาไปแปะลงบนลูกโลกที่ว่า แน่นอนว่า เราไม่สามารถพิมพ์ภาพด้วยสัดส่วนที่ถูกต้องได้ครับ เพราะตอนที่เราเอาแผนพลาสติกนาบลงไป โดยเริ่มจากบริเวณเส้นศุนย์สูตร เราก็ต้องยืดแผ่นพลาสติกทั้งด้านบนและด้านล่าง เพื่อให้ขอบด้านบน และขอบด้านล่าง สามารถไปปิดขั้วโลกเหนือ และขั้วโลกใต้ได้มิด&lt;/p&gt;  &lt;p&gt;การใช้ Mecrator Projection จึงทำให้เกิดความผิดเพี้ยนไปบ้างในเรื่องของขนาดของวัตถุต่างๆ วงกลมสีแดงในภาพด้านข้างนี้ แสดงให้เห็นถึงอัตราความยืด ของวัตถุโดยเฉลี่ลยครับ&lt;/p&gt;  &lt;p&gt;ข้อจำกัดอีกอย่างหนึ่ง คือแผนที่แบบ Mecrator นี้ ไม่สามารถแสดงผลบริเวณขั้วโลกได้ครบครับ เนื่องจากความยืดของวัตถในบริเวณดังกล่าวจะเพิ่มเข้าใกล้ค่าอนันต์ (มีลิมิตเป็นอินฟินิตี้) ทำให้เราไม่สามารถวาดแผนที่ออกมาได้ จึงไม่เหมาะกับการใช้งานด้านการบินครับ เพราะว่าไม่ครอบคลุมทั้งโลก และมีหลายสายการบินที่มีเส้นทางบินผ่านขั่วโลกเหนือครับ&lt;/p&gt;  &lt;h2&gt;ระบบแกนของแผนที่โลก&lt;/h2&gt;  &lt;p&gt;&lt;a href="http://upload.wikimedia.org/wikipedia/commons/1/1e/World_map.png" target="_blank"&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_765EC57F.png" width="570" height="517" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;ผมขอสรุปเป็นข้อๆ ตามนี้ครับ จะได้ไม่เยิ่นเย้อ ดูภาพประกอบได้นะครับ&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;หมายเหตุ: โลกไม่ได้กลมดิก แต่ผมจะขอเรียกว่า โลกกลมนะครับ      &lt;br /&gt;&lt;/li&gt;    &lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Longtitude" target="_blank"&gt;Longtitude&lt;/a&gt; คือ แกนนอน หรือแกน X ครับ ถ้าผมบอกว่าโลกกลม แน่นอนว่าทุกคนคงจำได้ว่า วงกลมมี 360 องศา แต่ว่าค่าของ Lon. นั้น จะไม่ได้อิงค่าเป็นวงกลมแบบนั้น เนื่องจากเราจะแบ่งโลกเป้นวองซีกตามที่เราชอบเรียกกันว่า ตะวันออก กับ ตะวันตกครับ โดย –180 องศา คือ ตะวันตกสุด ของโลก (เลยอเมริกาไปหน่อย) และ 180 คือตะวันออกสุดของโลก (ซึ่งก็จะไปชนกับฝั่งตะวันตกสุดของโลกพอดี) โดยจุดกึ่งกลางของโลกคือ 0 องศาครับ       &lt;br /&gt;      &lt;br /&gt;ในภาษาพูด เราจะพูดว่า องศาตะวันตก หรือ องศาตะวันออกครับ ถ้าผมบอกว่า 64 องศาตะวันตก ก็คือ –64 และ 86 องศาตะวันออก ก็คือ 86 องศานั่นเองครับ       &lt;br /&gt;&lt;/li&gt;    &lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Latitude" target="_blank"&gt;Latitude&lt;/a&gt; คือแกนตั้ง หรือแกน Y ครับ ถ้าลองนึงภาพโลกในมุมตัดขวาง โลกก็จะกลมเหมือนกัน แต่ว่าผิวของโลกในแนวตั้ง จะมีแค่ครึ่งเดียวเท่านั้น นั่นคือ 180 องศา ต้องลองนึกภาพตอนเราเอาแผนที่พลาสติกห่อโลกครับ แผนที่ที่เราพิมพ์มา มันสามารถหุ้มโลกได้รอบแล้วในแนวนอน พอเราห่อโลกไปแล้ว ตัวแผนที่ก็จะคลุมโลกได้มิดพอดี เหมือนเอากระดาษไปห่อกระป๋องน้ำอัดลมนั่นเอง       &lt;br /&gt;      &lt;br /&gt;เช่นเดียวกับ Latitude ครับ เราได้แบ่งโลกเป็นซีกโลกเหนือ ซีกโลกใต้เหมือนกัน นั่นก็คือ –90 ถึง 90 และภาษาพูดก็จะเรียกว่า องศาเหนือ องศาใต้เช่นเดียวกัน แต่ด้วยระบบ Mecrator Projection เราจะสามารถแสดงผลได้แค่ ±85.05113 องศาเท่านั้น หรือถ้าต้องการคำนวณ สามารถใช้สูตรนี้ได้ครับ       &lt;br /&gt;      &lt;br /&gt;&lt;a href="http://coresharp.net/blogs/article/image_0CA0F2A0.png"&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_thumb_5A3D1F20.png" width="439" height="48" /&gt;&lt;/a&gt;       &lt;br /&gt;&lt;/li&gt;    &lt;li&gt;การเขียนพิกัดในภาษาเขียน เรามักจะเขียนในรูปของ DMS หรือ องศา นาที วินาที&amp;#160; แทนที่จะเขียนเป็นจุดทศนิยมตามปกติ พร้อมกับมี NSWE กำกับเพื่อบอกซีกโลกครับ เช่น 10”10’10N 10”12’19E หมายถึง 10”10’10 องศาเหนือ 10”12’19 องศาตะวันออก      &lt;br /&gt;      &lt;br /&gt;แน่นอนว่าผู้ใช้ทั่วไป มักจะได้พิกัดมาในรูปนี้ ดังนั้น ในการคำนวณ เราจะต้องแปลงเลขพวกนี้ให้อยู่ในรูปทศนิยมเสียก่อน ซึ่งมีสูตรการแปลงดังนี้ครับ       &lt;br /&gt;      &lt;br /&gt;      &lt;div style="font-family:consolas;background:white;color:black;font-size:12pt;-moz-background-clip:-moz-initial;-moz-background-origin:-moz-initial;-moz-background-inline-policy:-moz-initial;"&gt;       &lt;div style="font-family:consolas;background:white;color:black;font-size:12pt;-moz-background-clip:-moz-initial;-moz-background-origin:-moz-initial;-moz-background-inline-policy:-moz-initial;"&gt;         &lt;div style="font-family:consolas;background:white;color:black;font-size:12pt;-moz-background-clip:-moz-initial;-moz-background-origin:-moz-initial;-moz-background-inline-policy:-moz-initial;"&gt;           &lt;p style="margin:0px;"&gt;&lt;span style="color:blue;"&gt;public&lt;/span&gt; &lt;span style="color:blue;"&gt;double&lt;/span&gt; ToDecimal()&lt;/p&gt;            &lt;p style="margin:0px;"&gt;{&lt;/p&gt;            &lt;p style="margin:0px;"&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color:blue;"&gt;return&lt;/span&gt; &lt;span style="color:blue;"&gt;this&lt;/span&gt;.Degree + (&lt;span style="color:blue;"&gt;this&lt;/span&gt;.Minute / 60d) + (&lt;span style="color:blue;"&gt;this&lt;/span&gt;.Second / 3600);&lt;/p&gt;            &lt;p style="margin:0px;"&gt;}&lt;/p&gt;         &lt;/div&gt;       &lt;/div&gt;     &lt;/div&gt;   &lt;/li&gt; &lt;/ul&gt;  &lt;h2&gt;ระบบ Tile System (อ้างอิงจาก Virtual Earth)&lt;/h2&gt;  &lt;p&gt;เนื่องจากว่า ขนาดของแผนที่โลกนั้น มีขนาดใหญ่มาาาากกก จนไม่น่าจะมีคอมพิวเตอร์เครื่องไหนในโลก สามารถแสดงผลแผนที่โลกทั้งแผ่นที่ความละเอียดสูงสุด ได้ในการอ่านข้อมูลครั้งเดียว เราจึงต้องทำการซอยแผนที่เป็นแผ่นย่อยๆ เรียกว่า Tile ครับ โดย Tile แต่ละแผ่นนั้น จะมี 256x256px ซึ่งจริงๆ จะมีขนาดเท่าใดก็ได้ แต่ทั้งสามระบบ (Virtual Earth/GoogleMap/Yahoo Map) นั้นใช้ 256px เท่านั้นหมดครับ ยกเว้น GoogleMap Mobile ที่จะใช้ 64px เพื่อให้ดาวน์โหลดได้เร็ว ซึ่งจริงๆ แล้วเทคโนโลยี DeepZoom ของ Silverlight นั้นก็ใช้วิธีการซอยแบบนี้เช่นเดียวกันครับ&lt;/p&gt;  &lt;p&gt;สำหรับไอเดียของ Tile โดยแริ่มแรกเลย แผนที่โลกทั้งแผ่น จะถูกย่อให้เหลือขนาด 1 Tile ก่อน จากนั้น เมื่อเราทำการซูมเข้าไป 1 ระดับ Tile 1 แผ่นนั้น ก็จะโดนแยกเป็น 4 ส่วน (4 Tile) ดังภาพ โดยแต่ละระบบ ก็จะมีระดับต่างกันไป ซึ่งของ Virtial Earth อยู่ในช่วง 1-23 ครับ&lt;a href="http://coresharp.net/blogs/article/image_79EBF8E8.png"&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_thumb_41D9DF38.png" width="580" height="340" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;และนอกจากนี้ การคำนวณหา Tile ที่เราต้องการนั้น เพียงแค่ใช้การยกกำลังเท่านั้นเอง จึงทำให้การประมวลผล สามารถทำได้อย่างรวดเร็วมาก และการจัดเก็บ Tile ที่ต้องการ ก็ง่ายอีกด้วย&lt;/p&gt;  &lt;h2&gt;ขนาดของแผนที่ในแต่ละระดับการซูม&lt;/h2&gt;  &lt;p&gt;เพื่อให้การคำนวณหาพิกัด โดยอ้างอิงจากพิกัดของพิกเซลบนจอภาพสามารถทำได้ เราจำเป็นจะต้องรู้ขนาดของแผนที่ของแต่ละระดับการซูมด้วยครับ ซึ่งสามารถคำนวณได้ตามสูตรนี้ ต้องระวังด้วยว่า GoogleMap แบบ Street View นั้น ใช้ zoomLevel แบบผกผันนะครับ นั่นคือ เลขมาก ซูมน้อย ส่วนของเจ้าอื่น เป็นค่าตามปกติครับ&lt;/p&gt;  &lt;div style="font-family:consolas;background:white;color:black;font-size:12pt;-moz-background-clip:-moz-initial;-moz-background-origin:-moz-initial;-moz-background-inline-policy:-moz-initial;"&gt;   &lt;p style="margin:0px;"&gt;&lt;span style="color:blue;"&gt;public&lt;/span&gt; &lt;span style="color:blue;"&gt;long&lt;/span&gt; MapSize(&lt;span style="color:blue;"&gt;int&lt;/span&gt; zoomLevel)&lt;/p&gt;    &lt;p style="margin:0px;"&gt;{&lt;/p&gt;    &lt;p style="margin:0px;"&gt;&lt;/p&gt;    &lt;p style="margin:0px;"&gt;&lt;span style="color:green;"&gt;&lt;/span&gt;&lt;/p&gt;    &lt;p&gt;&amp;#160;&lt;/p&gt;    &lt;p style="margin:0px;"&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color:blue;"&gt;return&lt;/span&gt; (&lt;span style="color:blue;"&gt;long&lt;/span&gt;)&lt;span&gt;Math&lt;/span&gt;.Pow(2, zoomLevel) * TileSize;&lt;/p&gt;    &lt;p style="margin:0px;"&gt;}&lt;/p&gt; &lt;/div&gt;  &lt;p&gt;ลองดูจากตารางด้านล่างนี้นะครับ ว่าในแต่ละระดับการซูม แผนที่จะมีขนาดเปลี่ยนแปลงไปอย่างไรบ้าง&lt;/p&gt;  &lt;table border="0" cellspacing="0" cellpadding="2" width="499"&gt;     &lt;tr&gt;       &lt;td valign="top" width="122"&gt;&lt;b&gt;ซูม&lt;/b&gt;&lt;/td&gt;        &lt;td valign="top" width="195"&gt;&lt;b&gt;ขนาดแผนที่&lt;/b&gt;&lt;/td&gt;        &lt;td valign="top" width="180"&gt;&lt;b&gt;จำนวน Tile&lt;/b&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="123"&gt;0&lt;/td&gt;        &lt;td valign="top" width="195"&gt;256x256&lt;/td&gt;        &lt;td valign="top" width="180"&gt;1&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="123"&gt;1&lt;/td&gt;        &lt;td valign="top" width="195"&gt;512x512&lt;/td&gt;        &lt;td valign="top" width="180"&gt;4&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="123"&gt;         &lt;p&gt;10&lt;/p&gt;       &lt;/td&gt;        &lt;td valign="top" width="195"&gt;262144 x 262144&lt;/td&gt;        &lt;td valign="top" width="180"&gt;1048576&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="123"&gt;23&lt;/td&gt;        &lt;td valign="top" width="195"&gt;         &lt;p&gt;2,147,483,648 x 2,147,483,648&lt;/p&gt;       &lt;/td&gt;        &lt;td valign="top" width="180"&gt;16777216&lt;/td&gt;     &lt;/tr&gt;   &lt;/table&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;จะเห็นได้ว่า เรากำลังจะต้องต่อกรกับข้อมูล (ภาพ) ปริมาณมากมายมหาศาลขนาดไหน เพราะฉะนั้น เราจะต้องมีระบบจัดการแรมที่ดีด้วยนะครับ&lt;/p&gt;  &lt;h2&gt;การหาพิกัดของโลก บนแผนที่&lt;/h2&gt;  &lt;p&gt;สมมุติว่า ถ้าเรารู้แล้วว่า เราต้องการดูแผนที่ ที่พิกัด &lt;a href="http://stable.toolserver.org/geohack/geohack.php?pagename=Bangkok&amp;amp;params=13_45_8_N_100_29_38_E_type:city" target="_blank"&gt;13° 45′ 8″ N, 100° 29′ 38″ E&lt;/a&gt; (พิกัดของกรุงเทพ) เราจะทราบได้อย่างไรว่า พิกัดดังกล่าว คือพิกเซลไหนบนแผนที่? การคำนวณดังกล่าว ก็คือการแปลงแกนนั่นเองครับ&lt;/p&gt;  &lt;p&gt;ในการแปลงแกนแบบนี้ โดยมาก เราจะทำการ Normalize ค่าที่เราจะแปลง ให้อยู่ในช่วง 0 ถึง 1 ก่อน (คล้ายกับการเทียบบัญญัตไตรยางค์ [rule of three in arithmetic] เขียนถูกมั๊ยเนี่ย) เพื่อที่เราจะได้รู้ว่า ค่าที่เราต้องการ อยู่ในช่วงไหน ของแกนเก่า จากนั้นถึงเอาไปเทียบกับแกนใหม่อีกทีหนึ่ง&lt;/p&gt;  &lt;p&gt;สำหรับค่า Longtitude นั้น ง่ายมากครับ เนื่องจากเป็นค่า Linear และเรารู้อยู่แล้วว่า โลกกลม นั่นก็คือมี 360 องศาถ้าต้องการจะหาว่า องศาที่ต้องการ อยู่ในช่วงไหน ก็ใช้ว่า&lt;/p&gt;  &lt;div style="font-family:consolas;background:white;color:black;font-size:12pt;-moz-background-clip:-moz-initial;-moz-background-origin:-moz-initial;-moz-background-inline-policy:-moz-initial;"&gt;   &lt;p style="margin:0px;"&gt;&lt;span style="color:blue;"&gt;double&lt;/span&gt; x = (longtitude + 180) / 360;&lt;/p&gt; &lt;/div&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;ซึ่งจะเห็นว่า ผมได้แปลงค่า longtitude ให้กลับมาในช่วง 0-360 ก่อน (จากเดิมอยู่ที่ –180 ถึง 180) แล้วหาค่า x ออกมาครับ ส่วนค่า lontitude นั้น ยากหน่อย ตรงที่เราต้องใช้สูตรของ Mercator Projection มาช่วยครับ    &lt;br /&gt;&lt;img style="border-right-width:0px;margin:10px 0px 0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_6D9A0D5A.png" width="213" height="208" /&gt; &lt;/p&gt;  &lt;p&gt;ซึ่งสูตรที่สามารถแปลงเป็นโค๊ดได้ง่ายที่สุด ก็คือสูตรที่สองครับ เนื่องจากใช้เพียงฟังก์ชั่น Sin กับ Log เท่านั้น โดยสามารถแปลงเป็นโค๊ดได้ว่า&lt;/p&gt;  &lt;div style="font-family:consolas;background:white;color:black;font-size:12pt;-moz-background-clip:-moz-initial;-moz-background-origin:-moz-initial;-moz-background-inline-policy:-moz-initial;"&gt;   &lt;p style="margin:0px;"&gt;&lt;span style="color:blue;"&gt;double&lt;/span&gt; sinLatitude = &lt;span&gt;Math&lt;/span&gt;.Sin(coord.Latitude * &lt;span&gt;Math&lt;/span&gt;.PI / 180);       &lt;br /&gt;&lt;span style="color:blue;"&gt;double&lt;/span&gt; y = 0.5 - &lt;span&gt;Math&lt;/span&gt;.Log((1 + sinLatitude) / (1 - sinLatitude)) / (4 * &lt;span&gt;Math&lt;/span&gt;.PI);&lt;/p&gt; &lt;/div&gt;  &lt;p&gt;แต่ผมขอยอมรับตามตรงว่า ผมจำไม่ได้แล้วว่าทำไมถึงแปลงได้แบบนี้ เคยพยายามทำความเข้าใจอยู่พักใหญ่ๆ แต่ตอนนี้ลืมไปแล้วละ ถ้าใครคล่อง Math มาบอกกันบ้างก็ดีนะครับ ส่วนที่ใช้ฟังก์ชั่น Math.Log เฉยๆ เพราะว่า Math.Log จะใช้ฐานเป็น e ถ้าไม่ได้กำหนดฐานครับ ซึ่งก็คือ ln นั่นเอง จากสุตรนี้ ค่าของ x, y ก็จะโดน Normalize ให้อยู่ในช่วงขอพิกัดของแผนที่แบบ Mecrator Projection เรียบร้อยแล้ว&lt;/p&gt;  &lt;p&gt;จากนั้น เราก็นำค่าที่ได้ มาหาพิกัดเป็น Pixel โดยเทียบกับจนาดของแผนที่ปัจจุบัน ซึ่งในส่วนที่แล้ว เราได้สร้างฟังก์ชั่น MapSize ขึ้นมาเรียบร้อยแล้ว ก็เพียงแต่นำฟังก์ชั่นนั้นมาใช้ครับ เช่น พิกัดของกรุงเทพ ก็จะคำนวณได้เป็น&lt;/p&gt;  &lt;p&gt;&lt;b&gt;x=0.779149691666667, y=0.461427218750532&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;จากนั้น เราก็ดูว่า ขนาดของแผนที่ในระดับที่เราต้องการ มีขนาดเท่าไหร่ เช่น ถ้าเรากำลังดูแผนที่โลกที่ระดับไกลที่สุด (ระดับ 1) ของ VE แผนที่โลกก็จะมีขนาด 512x512 pixel (2^1 * 256) ซึ่งก็คือ&lt;/p&gt;  &lt;p&gt;&lt;b&gt;x=0.779149691666667*512 = 398.924642133333504      &lt;br /&gt;y=0.461427218750532*512 = 236.250736000272384&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;เห็นแล้วใช่มั๊ยครับ ว่าทำไม double จึงจำเป็น &lt;img src="http://coredeveloper.net/emoticons/emotion-1.gif" alt="Smile" /&gt; ถ้าเราลองพลอตจุดนี้ลงในแผนที่โลก ขนาด 512x512px (แผนที่จาก Virtual Earth) ก็จะพบว่า จุดที่เราได้นั้น อยู่บริเวณกรุงเทพพอดีเลยครับ&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coresharp.net/blogs/article/image_3F4087AD.png"&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_thumb_52BDC181.png" width="512" height="512" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;h2&gt;หา Tile ที่พิกัดนั้น ตั้งอยู่&lt;/h2&gt;  &lt;p&gt;หลังจากที่เราทราบพิกัดบนแผนที่่เรียบร้อยแล้ว ปัญหาต่อไปก็คือการหา Tile ที่พิกัดนั้นตั้งอยู่ เพื่อที่เราจะได้สามารถโหลด Tile ที่ถูกต้องออกมาได้ ซึ่งการหา Tile นั้น ทำได้ง่ายมาก เนื่องจากขนาดของแผนที่นั้น จะมีขนาดตายตัว และเป็นภาพสี่เหลี่ยมจัตุรัส การหา tile นั้น ก็เพียงแค่นำเอาพิกัด หารด้วยขนาดของ Tile แบบไม่เอาเศษนั่นเอง เช่น&lt;/p&gt;  &lt;p&gt;&lt;b&gt;x=398.924642133333504 / 256 = 1      &lt;br /&gt;y=236.250736000272384 / 256 = 0&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;เมื่อเราทราบแล้วว่า เราจะต้องใช้ Tile ชิ้นไหน เหลือก็เพียงแค่ การหา URL ที่จะโหลด Tile เหล่านี้มาใช่ นั่นเองครับ&lt;/p&gt;  &lt;h2&gt;URL ของ Tile&lt;/h2&gt;  &lt;p&gt;สำหรับ URL ของผู้ให้บริการแต่ละแห่ง ในการดาวน์โหลดแผนที่ ก็จะแตกต่างกันไป ซึ่งผมขอยกตัวอย่างเจ้าใหญ่ๆ สองที่ คือ Microsoft และ Google ครับ แต่ก่อนจะนำ URL เหล่านี้ไปใช้ ผมอยากจะให้ท่านที่สนใจจะนำไอเดียเหล่านี้ไปใช้ เช็คกับ License ของผู้ให้บริการแผนที่เสียก่อนว่า เราสามารถนำแผนที่เขามาใช้ได้อย่างไรบ้างนะครับ&lt;/p&gt;  &lt;h3&gt;Virtual Earth&lt;/h3&gt;  &lt;p&gt;รูปแบบ URL:    &lt;br /&gt;http://&lt;font color="#ff0080"&gt;[type]&lt;/font&gt;&lt;font color="#0080c0"&gt;[server]&lt;/font&gt;.ortho.tiles.virtualearth.net/tiles/&lt;font color="#ff0080"&gt;[type]&lt;/font&gt;&lt;font color="#ff8000"&gt;[location]&lt;/font&gt;.&lt;font color="#400080"&gt;[format]&lt;/font&gt;?g=45&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;b&gt;&lt;font color="#0080c0"&gt;Server:&lt;/font&gt;&lt;/b&gt; เลข Server ที่จะดาวน์โหลด มี 4 Server คือ 0, 1, 2, 3. &lt;/li&gt;    &lt;li&gt;&lt;b&gt;&lt;font color="#ff0080"&gt;Type&lt;/font&gt;:&lt;/b&gt; r = แผนที่ธรรมดา, a = แผนที่ดาวเทียม, h = แผนที่ดาวเทียมแบบมีเส้นทาง. &lt;/li&gt;    &lt;li&gt;&lt;b&gt;&lt;font color="#400080"&gt;Formats:&lt;/font&gt;&lt;/b&gt; png สำหรับ r และ&amp;#160; jpeg สำหรับที่เหลือ &lt;/li&gt;    &lt;li&gt;&lt;b&gt;&lt;font color="#ff8000"&gt;Location:&lt;/font&gt;&lt;/b&gt; จากฟังก์ชั่น TileXYToQuadKey ซึ่ง Microsoft มีโค๊ดให้ครับ &lt;/li&gt; &lt;/ul&gt;  &lt;div style="font-family:consolas;background:white;color:black;font-size:10pt;-moz-background-clip:-moz-initial;-moz-background-origin:-moz-initial;-moz-background-inline-policy:-moz-initial;"&gt;   &lt;p style="margin:0px;"&gt;&lt;span style="color:gray;"&gt;///&lt;/span&gt;&lt;span style="color:green;"&gt; &lt;/span&gt;&lt;span style="color:gray;"&gt;&amp;lt;summary&amp;gt;&lt;/span&gt;&lt;/p&gt;    &lt;p style="margin:0px;"&gt;&lt;span style="color:gray;"&gt;///&lt;/span&gt;&lt;span style="color:green;"&gt; Converts tile XY coordinates into a QuadKey at a specified level of detail.&lt;/span&gt;&lt;/p&gt;    &lt;p style="margin:0px;"&gt;&lt;span style="color:gray;"&gt;///&lt;/span&gt;&lt;span style="color:green;"&gt; &lt;/span&gt;&lt;span style="color:gray;"&gt;&amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;/p&gt;    &lt;p style="margin:0px;"&gt;&lt;span style="color:gray;"&gt;///&lt;/span&gt;&lt;span style="color:green;"&gt; &lt;/span&gt;&lt;span style="color:gray;"&gt;&amp;lt;param name=&amp;quot;tileX&amp;quot;&amp;gt;&lt;/span&gt;&lt;span style="color:green;"&gt;Tile X coordinate.&lt;/span&gt;&lt;span style="color:gray;"&gt;&amp;lt;/param&amp;gt;&lt;/span&gt;&lt;/p&gt;    &lt;p style="margin:0px;"&gt;&lt;span style="color:gray;"&gt;///&lt;/span&gt;&lt;span style="color:green;"&gt; &lt;/span&gt;&lt;span style="color:gray;"&gt;&amp;lt;param name=&amp;quot;tileY&amp;quot;&amp;gt;&lt;/span&gt;&lt;span style="color:green;"&gt;Tile Y coordinate.&lt;/span&gt;&lt;span style="color:gray;"&gt;&amp;lt;/param&amp;gt;&lt;/span&gt;&lt;/p&gt;    &lt;p style="margin:0px;"&gt;&lt;span style="color:gray;"&gt;///&lt;/span&gt;&lt;span style="color:green;"&gt; &lt;/span&gt;&lt;span style="color:gray;"&gt;&amp;lt;param name=&amp;quot;levelOfDetail&amp;quot;&amp;gt;&lt;/span&gt;&lt;span style="color:green;"&gt;Level of detail, from 1 (lowest detail)&lt;/span&gt;&lt;/p&gt;    &lt;p style="margin:0px;"&gt;&lt;span style="color:gray;"&gt;///&lt;/span&gt;&lt;span style="color:green;"&gt; to 23 (highest detail).&lt;/span&gt;&lt;span style="color:gray;"&gt;&amp;lt;/param&amp;gt;&lt;/span&gt;&lt;/p&gt;    &lt;p style="margin:0px;"&gt;&lt;span style="color:gray;"&gt;///&lt;/span&gt;&lt;span style="color:green;"&gt; &lt;/span&gt;&lt;span style="color:gray;"&gt;&amp;lt;returns&amp;gt;&lt;/span&gt;&lt;span style="color:green;"&gt;A string containing the QuadKey.&lt;/span&gt;&lt;span style="color:gray;"&gt;&amp;lt;/returns&amp;gt;&lt;/span&gt;&lt;/p&gt;    &lt;p style="margin:0px;"&gt;&lt;span style="color:blue;"&gt;public&lt;/span&gt; &lt;span style="color:blue;"&gt;static&lt;/span&gt; &lt;span style="color:blue;"&gt;string&lt;/span&gt; TileXYToQuadKey(&lt;span style="color:blue;"&gt;int&lt;/span&gt; tileX, &lt;span style="color:blue;"&gt;int&lt;/span&gt; tileY, &lt;span style="color:blue;"&gt;int&lt;/span&gt; levelOfDetail)&lt;/p&gt;    &lt;p style="margin:0px;"&gt;{&lt;/p&gt;    &lt;p style="margin:0px;"&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span&gt;StringBuilder&lt;/span&gt; quadKey = &lt;span style="color:blue;"&gt;new&lt;/span&gt; &lt;span&gt;StringBuilder&lt;/span&gt;();&lt;/p&gt;    &lt;p style="margin:0px;"&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color:blue;"&gt;for&lt;/span&gt; (&lt;span style="color:blue;"&gt;int&lt;/span&gt; i = levelOfDetail; i &amp;gt; 0; i--)&lt;/p&gt;    &lt;p style="margin:0px;"&gt;&amp;#160;&amp;#160;&amp;#160; {&lt;/p&gt;    &lt;p style="margin:0px;"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color:blue;"&gt;char&lt;/span&gt; digit = &lt;span&gt;&amp;#39;0&amp;#39;&lt;/span&gt;;&lt;/p&gt;    &lt;p style="margin:0px;"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color:blue;"&gt;int&lt;/span&gt; mask = 1 &amp;lt;&amp;lt; (i - 1);&lt;/p&gt;    &lt;p style="margin:0px;"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color:blue;"&gt;if&lt;/span&gt; ((tileX &amp;amp; mask) != 0)&lt;/p&gt;    &lt;p style="margin:0px;"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; {&lt;/p&gt;    &lt;p style="margin:0px;"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; digit++;&lt;/p&gt;    &lt;p style="margin:0px;"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }&lt;/p&gt;    &lt;p style="margin:0px;"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color:blue;"&gt;if&lt;/span&gt; ((tileY &amp;amp; mask) != 0)&lt;/p&gt;    &lt;p style="margin:0px;"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; {&lt;/p&gt;    &lt;p style="margin:0px;"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; digit++;&lt;/p&gt;    &lt;p style="margin:0px;"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; digit++;&lt;/p&gt;    &lt;p style="margin:0px;"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }&lt;/p&gt;    &lt;p style="margin:0px;"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; quadKey.Append(digit);&lt;/p&gt;    &lt;p style="margin:0px;"&gt;&amp;#160;&amp;#160;&amp;#160; }&lt;/p&gt;    &lt;p style="margin:0px;"&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color:blue;"&gt;return&lt;/span&gt; quadKey.ToString();&lt;/p&gt;    &lt;p style="margin:0px;"&gt;}&lt;/p&gt; &lt;/div&gt;  &lt;h3&gt;GoogleMap&lt;/h3&gt;  &lt;p&gt;ของ GoogleMap นั้นจะง่ายกว่า ตรงที่เราสามารถใส่เลข Tile ที่เราคำนวณได้ทันที โดยไม่ต้องแปลงค่าเพิ่มครับ โปรดสังเกตว่า Zoom ของ GoogleMap นั้น เริ่มที่ 17 นั่นคือมุมกว้างที่สุด ไปหา 1 ซึ่งคือมุมแคบที่สุด (ซูมมากที่สุด) สำหรับ Url นี้เป็น URL ของ Street Map ครับ&lt;/p&gt;  &lt;p&gt;รูปแบบ URL:    &lt;br /&gt;&lt;a href="http://%5Bserver%5D.google.com/mt?v=%5Bversion%5D&amp;amp;x=%5Bcolumn%5D&amp;amp;y=%5Brow%5D&amp;amp;z=%5Bzoom"&gt;http://&lt;font color="#ff8000"&gt;[server]&lt;/font&gt;.google.com/mt?v=&lt;font color="#0000a0"&gt;[version]&lt;/font&gt;&amp;amp;x=&lt;font color="#008000"&gt;[column]&lt;/font&gt;&amp;amp;y=&lt;font color="#008000"&gt;[row]&lt;/font&gt;&amp;amp;z=&lt;font color="#ff0000"&gt;[zoom&lt;/font&gt;]&lt;/a&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;font color="#ff8000"&gt;&lt;b&gt;Servers:&lt;/b&gt;&lt;/font&gt;&lt;font color="#666666"&gt; mt0, mt1, mt2, mt3&lt;/font&gt; &lt;/li&gt;    &lt;li&gt;&lt;b&gt;&lt;font color="#0000a0"&gt;Version:&lt;/font&gt;&lt;/b&gt; w2.89 แต่ผมแนะนำให้ลองใช้ Fiddler เช็คดูว่า ตอนเราเล่น GoogleMap ตัว Browser นั้นไปโหลดแผนที่จากไหนมา แล้วใช้เลขเวอร์ชั่นตามนั้นครับ &lt;/li&gt;    &lt;li&gt;&lt;b&gt;&lt;font color="#008000"&gt;Row, Column&lt;/font&gt;:&lt;/b&gt; tile ที่ได้จากการคำนวณ &lt;/li&gt;    &lt;li&gt;&lt;b&gt;&lt;font color="#ff0000"&gt;Zoom:&lt;/font&gt;&lt;/b&gt; 1-17 &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;สำหรับ Tile ที่มีพิกัดของกรุงเทพอยู่ ก็คือ    &lt;br /&gt;&lt;a title="http://r3.ortho.tiles.virtualearth.net/tiles/r1.png?g=45" href="http://r3.ortho.tiles.virtualearth.net/tiles/r1.png?g=45"&gt;http://r3.ortho.tiles.virtualearth.net/tiles/r1.png?g=45&lt;/a&gt; ดังภาพครับ&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coresharp.net/blogs/article/image_1789E5B6.png"&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_thumb_2FAD5011.png" width="240" height="240" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;ส่วนของ GoogleMap จะเป็นภาพนี้ครับ    &lt;br /&gt;&lt;a title="http://mt1.google.com/mt?v=w2.89&amp;amp;hl=th&amp;amp;x=1&amp;amp;y=0&amp;amp;z=1&amp;amp;s=zz" href="http://mt1.google.com/mt?v=w2.89&amp;amp;hl=th&amp;amp;x=1&amp;amp;y=0&amp;amp;z=1&amp;amp;s=zz"&gt;http://mt1.google.com/mt?v=w2.89&amp;amp;hl=th&amp;amp;x=1&amp;amp;y=0&amp;amp;z=1&amp;amp;s=zz&lt;/a&gt;&amp;#160; &lt;br /&gt;(จะเห็นว่า Url มีพารามิเตอร์ s ด้วย ซึ่งเอาไว้ใช้หลอกไม่ให้ Browser Cache ภาพไว้)&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coresharp.net/blogs/article/image_7B98B0BD.png"&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coresharp.net/blogs/article/image_thumb_7AC04AD3.png" width="240" height="240" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;h1&gt;สรุป&lt;/h1&gt;  &lt;p&gt;ในบทความนี้ ผมได้ครอบคลุมถึงหลักการแสดงผลแผนที่ ตลอดจนการคำนวณต่างๆ ที่เกี่ยวข้องไว้ ซึ่งผมเชื่อว่า น่าจะเป็นจุดเริ่มต้น ให้คุณสามารถทดลองเขียนโค๊ด เพื่อดาวน์โหลดแผนที่จากผู้ให้บริการต่างๆ ได้แล้ว และในบทความต่อไป ผมจะแนะนำถึงการออกแบบตัวโปรเจคนี้ เพื่อให้สามารถทำงานได้กับระบบแผนที่หลายๆ เจ้าได้ครับ&lt;/p&gt;  &lt;p&gt;สำหรับการวาดแผนที่ ดูต่อได้ภาคสองเลยครับ    &lt;br /&gt;&lt;a title="http://coresharp.net/blogs/article/archive/2009/03/15/maponmobile2.aspx" href="http://coresharp.net/blogs/article/archive/2009/03/15/maponmobile2.aspx"&gt;http://coresharp.net/blogs/article/archive/2009/03/15/maponmobile2.aspx&lt;/a&gt;&lt;/p&gt;&lt;img src="http://coredeveloper.net/aggbug.aspx?PostID=619" width="1" height="1"&gt;</description></item><item><title>แก้ปฏิทินภาษาไทยบน Silverlight</title><link>http://coredeveloper.net/blogs/nantcom/archive/2009/03/09/silverlight.aspx</link><pubDate>Sun, 08 Mar 2009 17:32:50 GMT</pubDate><guid isPermaLink="false">86cc649a-bf28-4d34-a9ef-d75c61f34293:592</guid><dc:creator>นันคอม</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://coredeveloper.net/blogs/nantcom/rsscomments.aspx?PostID=592</wfw:commentRss><comments>http://coredeveloper.net/blogs/nantcom/archive/2009/03/09/silverlight.aspx#comments</comments><description>&lt;p&gt;หลังจากเล่นๆ Silverlight อีกสักพัก ผมก็พบว่า ปฏิทินของ Silverlight ก็ไม่สามารถแสดงวันที่ภาษาไทยได้ถูกต้องครับ ผมได้พยายามจะ Feedback เรื่องนี้ไปที่ทีม Silverlight แล้ว ในงาน MVP Summit ก้อไม่ได้รับคำตอบที่น่าพอใจเท่าที่ควรครับ&lt;/p&gt;  &lt;p&gt;&lt;object data="data:application/x-silverlight," type="application/x-silverlight-2" width="550px" height="350px"&gt; &lt;param name="source" value="http://files.coresharp.net/CoreSharp.SLCalendar.xap" /&gt; &lt;param name="onerror" value="onSilverlightError" /&gt; &lt;param name="background" value="white" /&gt; &lt;param name="minRuntimeVersion" value="2.0.31005.0" /&gt; &lt;param name="autoUpgrade" value="true" /&gt; 			&lt;a href="http://go.microsoft.com/fwlink/?LinkID=124807" style="text-decoration:none;"&gt;      			&lt;img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style:none;" /&gt; 			&lt;/a&gt; 		&lt;/object&gt;&lt;/p&gt;  &lt;p&gt;จะเห็นว่า ปฏิทินทางขวา ของ Silverlight จะแสดงปี เป็นปี 2009 แทนที่จะเป็นปี 2552 อย่างที่มันควรจะเป็น ซึ่งตามที่ผมเข้าใจ ปฏิทินไทย เริ่มใช้วันที่ตามแบบสากล ตั้งแต่ปี 1 มกราคม ปี 1941 (พ.ศ. 2484) โดยจอมพลแปลก พิบูลย์สงคราม (อ้างอิงจาก: &lt;a title="http://en.wikipedia.org/wiki/Thai_solar_calendar" href="http://en.wikipedia.org/wiki/Thai_solar_calendar"&gt;http://en.wikipedia.org/wiki/Thai_solar_calendar&lt;/a&gt;) ซึ่งทำให้ปฏิทินไทย กับปีสากล (Gregorian Calendar) ตรงกัน ตั้งแต่วันนั้น ยกเว้นแต่ว่า เราเลือกใช้ พุทธศักราช แทน คริสต์ศักราช ซึ่งมันขัดกับประเทศเราโดยธรรมชาติอยู่แล้ว จากการเปลี่ยนแปลงครั้งนั้น ทำให้เราสามารถใช้ พ.ศ. ลบ 543 เปลี่ยนเป็น ค.ศ. ได้ทันที และวันที่ก็ตรงกันหมด ตามหลักสากล&lt;/p&gt;  &lt;p&gt;แต่ จากความพยายามอธิบายของผมอยู่นาน &lt;a title="http://silverlight.net/forums/p/76896/182957.aspx" href="http://silverlight.net/forums/p/76896/182957.aspx"&gt;http://silverlight.net/forums/p/76896/182957.aspx&lt;/a&gt; ก็เหมือนว่าจะไม่เป็นผลเท่าไหร่ และก็คงได้รับการยืนยันว่า ปฏิทินไทย ไม่ใช่ Gregorian Calendar อยู่อย่างนั้น ผมก็คงต้องทำใจ และทำปฏิทินของผมเอาเองครับ&lt;/p&gt;  &lt;p&gt;เรื่องน่าแปลกก็คือ ผมได้ลองกับ Culture อื่นๆ เช่น จีน (ซึ่งมีปีที่มี 13 เดือนได้) ไต้หวัน ญี่ปุ่น ก็พบว่า ปฏิทินเขาเป็น Gregorian ทั้งหมด แต่ของเราดันมีอัลกอริธึมแปลกๆ ออกมาแทน&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_7F609952.png"&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_130D82E7.png" width="561" height="137" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;และที่น่าสังเกต คือ อย่างไต้หวัน จะใช้ Gregorian เป็นหลัก และมี Optional Calendar เป็นปฏิทินไต้หวันอยู่ด้วย (สังเกตว่ามันมี Min/Max Supported Date Time)&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_63DB974F.png"&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_498B085E.png" width="554" height="390" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;แต่ของไทย กลับใช้ปฏิทินหลัก เป็น ปฏิทินไทย แล้วมี Gregorian เป็น Optional แทน&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_4DF16358.png"&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_0C76B0FF.png" width="554" height="309" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;ผมก็ต้องขอบอกว่า ผมเองก็ไม่ได้ดู BCL (Base Class Library) ของ .NET อย่างชัดแจ้ง ขนาดที่จะรู้ว่า เวลามัน Format Date/Time มันทำงานอย่างไรนะครับ แต่ถ้าเอาตามความคิด จากที่เห็น th.DateTimeFormat ควรจะเป็นคนที่จัดการ Format วันที่ให้ ซึ่งถ้าเป็นตามนั้น การสร้าง CultureInfo ของประเทศไทยจริงๆ ก็น่าจะสามารถสร้างคลาส ThaiGregorianCalendar ที่ Inherit จาก Gregorian Calendar แต่มี Min Supported Date/Time เป็น 1/1/1941 และจัดการแปลง 2009-&amp;gt;2552 ให้ ตอน Format จากนั้นก็มี ThaiBuddhistCalendar เป็น Optional Calendar สำหรับ Format วันที่ก่อนวันที่ 1/1/1941 ถึงจะถูกต้องมากกว่า และทำให้ Cultureinfo ของไทย เป็นสากลไปด้วยในตัว&lt;/p&gt;  &lt;p&gt;และปฏิทิน Silverlight ก็จะ Support ปฏิทินไทย ไปโดยปริยาย…รวมไปถึงอีกหลายๆ คอนโทรล ในอนาคต ซึ่งผมคิดว่า จะต้องมีปัญหาอีกแน่นอน&lt;/p&gt;  &lt;h2&gt;บ่นพอละ&lt;/h2&gt;  &lt;p&gt;สำหรับ Calendar ที่ผมทำขึ้นนะครับ ผมลองกับ Culture หลายๆ อันแล้ว ก็ใช้ได้ดี (แต่ไม่รู้ว่ามีปัญหาหรือเปล่านะ) เพราะอย่างที่เห็นว่า Culture อื่นๆ เขากำหนดเป็น Gregorian Calendar ไว้หมดแล้ว แล้วผมก็ทำให้ Calendar ผม Resize ได้ ตามใจชอบ และรูปแบบเหล่านี้เป็นเทมเพลตทั้งหมดนะครับ สามารถใช้ Blend ไปแก้ Control Template ได้ ซึ่งก็ไม่มีอะไรมากครับ มี Grid ด้านล่าง และปุ่ม 3 ปุ่มเท่านั้น&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_7587B9B5.png" width="510" height="322" /&gt;&lt;/p&gt;  &lt;p&gt;ส่วน Style ของปุ่ม ก็สามารถเซ็ตได้ด้วย Property ต่างๆ ตามด้านล่างนี้ครับ แต่ถ้าเทมเพลตของผมเลย ผมใช้ใส่เอาไว้ใน Resource ของ Grid ครับ ลองแก้ Control Template ดูน่าจะมองเห็น&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/image_6D901753.png"&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_thumb_1B113717.png" width="280" height="151" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;การทำงานก็จะคล้ายกับปฏิทินใน Vista นะครับ แต่ผมยังไม่ได้เพิ่ม Animation เข้าไป สามารถแสดงมุมมองแบบ เดือน แบบปี แบบทศวรรษ ได้ เพื่อให้เลือกได้สะดวก&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/image_1AA50422.png" width="510" height="321" /&gt;&lt;/p&gt;  &lt;p&gt;ถ้าพบบัก ก็มาโพสรายงานได้ในฟอรั่ม หรือในคอมเมนต์นี้นะครับ &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Download:&lt;/strong&gt; &lt;a title="http://coresharp.net/files/folders/projects/entry654.aspx" href="http://coresharp.net/files/folders/projects/entry654.aspx"&gt;http://coresharp.net/files/folders/projects/entry654.aspx&lt;/a&gt;&lt;/p&gt;&lt;img src="http://coredeveloper.net/aggbug.aspx?PostID=592" width="1" height="1"&gt;</description></item><item><title>Textbox บน Silverlight รองรับภาษาไทยได้แล้ว!</title><link>http://coredeveloper.net/blogs/nantcom/archive/2009/03/01/textbox-silverlight.aspx</link><pubDate>Sun, 01 Mar 2009 09:49:33 GMT</pubDate><guid isPermaLink="false">86cc649a-bf28-4d34-a9ef-d75c61f34293:588</guid><dc:creator>นันคอม</dc:creator><slash:comments>4</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://coredeveloper.net/blogs/nantcom/rsscomments.aspx?PostID=588</wfw:commentRss><comments>http://coredeveloper.net/blogs/nantcom/archive/2009/03/01/textbox-silverlight.aspx#comments</comments><description>&lt;p&gt;ถ้าใครเคยลองใช้ Silverlight จะพบว่า&amp;#160; Silverlight มีข้อจำกัดนิดหน่อย นั่นก็คือ มันไม่สามารถพิมพ์ภาษาไทยได้ครับ ไม่ใช่เพราะเรื่องฟอนต์นะครับ แต่ว่า เป็นเพราะว่า หลังจากเราตั้งฟอนต์ให้เป็นภาษาไทยแล้ว และทำการพิพม์ข้อความลงไป มันจะ Crash หลังจากเราพิมพ์สระครับ หน้าจอตอน Crash ก็เป็นแบบนี้&lt;/p&gt;  &lt;p&gt;&lt;a href="http://coredeveloper.net/blogs/nantcom/error_57E96D8A.png"&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="error" border="0" alt="error" src="http://coredeveloper.net/blogs/nantcom/error_thumb_4A630EEA.png" width="554" height="358" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;หลังจากนั่งงมหาวิธีอยู่นาน ผมก็พบกับทางที่จะให้ Silverlight สามารถรองรับการพิมพ์ภาษาไทยใน TextBox ได้ครับ &lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="screenshot" border="0" alt="screenshot" src="http://coredeveloper.net/blogs/nantcom/screenshot_36595687.png" width="550" height="267" /&gt;&lt;/p&gt;  &lt;p&gt;&lt;script type="text/javascript"&gt;&lt;/script&gt;&lt;/p&gt;  &lt;p&gt;ตอนนี้ ผมได้เริ่มแก้ไข ให้มันสามารถป้องกันสระลอย และเพิ่มอื่นๆ อีกหลายอย่างเข้าไป อย่าง แสดงธงภาษาไทย ภาษาอังกฤษ (เพราะไม่ได้ใช้ Setting จากเครื่อง) ไว้เมื่อมันพร้อมกว่านี้ ผมจะแวะมาอัพเดทครับ&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;margin-left:0px;border-left-width:0px;margin-right:0px;" title="shot2" border="0" alt="shot2" src="http://coredeveloper.net/blogs/nantcom/shot2_2F29F442.png" width="248" height="107" /&gt;&lt;/p&gt;&lt;img src="http://coredeveloper.net/aggbug.aspx?PostID=588" width="1" height="1"&gt;</description></item><item><title>เปลี่ยน Font ของ UI บนวินโดวส์</title><link>http://coredeveloper.net/blogs/nantcom/archive/2008/08/31/font-ui.aspx</link><pubDate>Sun, 31 Aug 2008 01:25:17 GMT</pubDate><guid isPermaLink="false">86cc649a-bf28-4d34-a9ef-d75c61f34293:539</guid><dc:creator>นันคอม</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://coredeveloper.net/blogs/nantcom/rsscomments.aspx?PostID=539</wfw:commentRss><comments>http://coredeveloper.net/blogs/nantcom/archive/2008/08/31/font-ui.aspx#comments</comments><description>&lt;p&gt;ตอนนี้ ผมใช้ Windows Vita Ultimate อยู่ครับ ซึ่งทุกคนก้อน่าจะยอมรับว่า มันก้อดูสวยดี แต่ที่ยังไม่ถูกใจผมก็คือ เรื่อง Font ครับ&lt;/p&gt; &lt;p&gt;บน Vista จะใช้ Font Segoe UI เป็นหลัก ซึ่งภาษาอังกฤษมันก้อสวยดีหรอก แต่ว่าภาษาไทยนี่ ตัวอักษรมันจะแบนๆ แล้วก้อไม่คมด้วย ผมเลยอยากจะเปลี่ยนให้ทุกที่ มันใช้ Font โปรดของผม ที่ชื่อว่า Leelawadee ให้หมด (ถ้าคุณเข้าเว็บนี้ด้วย Vista มันก้อจะเป็นฟอนต์นี้ละครับ) แน่นอนว่า ความพยายามแรกของผมคือ การใช้หน้าจอของวินโดวส์เปลี่ยนมันดูครับ&lt;/p&gt; &lt;p&gt;&lt;img style="border-right-width:0px;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/WindowsLiveWriter/FontUI_74AB/image_3.png" width="575" height="491" /&gt; &lt;/p&gt; &lt;p&gt;แต่หลังจากลองเปลี่ยนแล้ว ก้อพบว่า บางที่มันก้อเปลี่ยน บางที่มันก้อไม่เปลี่ยน อย่างเช่นขอความในหน้า Personalize เนี่ยแหละ มันก้อยังไม่ยอมเปลี่ยนอยู่ดี ผมจึงเริ่มเข้าใจว่า ที่คนบอกว่า OSX มี UI ที่มัน Consistent มันเป็นอย่างนี้นี่เอง ขนาดฟอนต์บนวินโดวส์นี่ คนละที่ยังใช้ไม่เหมือนกันเลย เหอๆ&lt;/p&gt; &lt;p&gt;เอาละ ไม่เป็นไร ผมใช้ท่ายากก็ได้ครับ! ถ้าลองเปิด Registry ขึ้นมาดู จะพบว่า Font ในเครื่อง มันจะมี Registry เก็บอยู่ว่า ฟอนต์นี้ ชื่อไฟล์อะไร แต่ผมเกลียด Segoe UI แล้ว ผมก้อจัดการไปลบชื่อไฟล์มันทิ้งซะเลย (อย่าลืม Export ไว้ก่อนทำละครับ)&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;img border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/WindowsLiveWriter/FontUI_74AB/image_af5a4e82-57f2-483b-b04f-1aa1731a93e9.png" width="573" height="280" /&gt; &lt;/p&gt; &lt;p&gt;และด้วยกลไกของวินโดวส์ ถ้ามันหาฟอนต์ไม่เจอ มันจะตามมาดูที่นี่ครับ FontSubstitutes&lt;/p&gt; &lt;p&gt;&lt;img border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/WindowsLiveWriter/FontUI_74AB/image_11777a0e-52b6-4545-98b0-1cf68125c599.png" width="577" height="313" /&gt; &lt;/p&gt; &lt;p&gt;ผมก้อแค่เพิ่ม String Value อันใหม่เข้าไป 3 อัน ตั้งชื่อว่า &amp;quot;Segoe UI&amp;quot;, &amp;quot;Segoe UI,0&amp;quot;, &amp;quot;Segoe UI,222&amp;quot; อย่าถามนะครับว่า 0 กะ 222 คืออะไร ผมก้อไม่รู้เหมือนกัน ผมทำตามอันอื่นๆ ในนี้ครับ เหอๆๆ แต่ดูเหมือนว่า 0 หมายถึง Charset มาตรฐานของตัวฟอนต์ และ 222 คือภาษาไทยครับ อย่าลืม Export ไว้ด้วยละครับ&lt;/p&gt; &lt;p&gt;หลังจากแก้เรียบแล้ว (อาจจะดู Font อื่นๆ ด้วยก็ได้ เช่น MS Dialog, MS Sans) ก็ Restart เครื่องครับ จะพบว่า Font ในวินโดวส์สวยสมใจละ ตัวอักษรของ Leelawadee เขาจะคมกว่า Segoe มากครับ&lt;/p&gt; &lt;p&gt;ลองเทียบของผม กะของเครื่องคุณด็ได้เลย อิอิ&lt;/p&gt; &lt;p&gt;&lt;img border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/WindowsLiveWriter/FontUI_74AB/image_a58d2be0-1d01-4535-8d34-4d32353faa6e.png" width="580" height="468" /&gt; &lt;/p&gt; &lt;p&gt;&lt;img style="border-right-width:0px;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/WindowsLiveWriter/FontUI_74AB/image_51aac0ff-b716-4c61-836f-dce514c48b27.png" width="567" height="344" /&gt; &lt;/p&gt; &lt;p&gt;&lt;img style="border-right-width:0px;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/WindowsLiveWriter/FontUI_74AB/image_98e946ec-fbbe-4286-beb1-321825b46f51.png" width="584" height="448" /&gt; &lt;/p&gt; &lt;p&gt;ถ้ายังคมไม่พอ ผมมีอีกโปรแกรม ที่จะช่วยให้มันตักษระยิ่งคมเข้มกว่าเดิมได้ครับ คือ ClearTweak เอาไว้ปรับ Contrast ของระบบ ClearType ในวินโดวส์&lt;/p&gt; &lt;p&gt;&lt;img border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/WindowsLiveWriter/FontUI_74AB/image_005bae09-92e4-4557-83d0-209a72112585.png" width="505" height="305" /&gt; &lt;/p&gt; &lt;p&gt;ตอนนี้ผมใช้ที่ 1000 เลยครับ คือเข้มสุดๆ ซึ่งค่าปกติจะอยู่ที่ 1400 ที่ผมคิดว่ามันบางเกินไป ลองเทียบกันดูครับ (ต้องจอ LCD นะครับ ถึงจะเห็นผลชัดเจน)&lt;/p&gt; &lt;p&gt;1400:&lt;img border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/WindowsLiveWriter/FontUI_74AB/image_a9fd4364-ab0a-4b67-9f1c-96d9daf83747.png" width="551" height="141" /&gt;&lt;/p&gt; &lt;p&gt;1200:&lt;br /&gt;&lt;img border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/WindowsLiveWriter/FontUI_74AB/image_416a26ff-ac7d-4e68-bda9-8b2f15f8fcb9.png" width="549" height="141" /&gt; &lt;/p&gt; &lt;p&gt;1000:&lt;br /&gt;&lt;img border="0" alt="image" src="http://coredeveloper.net/blogs/nantcom/WindowsLiveWriter/FontUI_74AB/image_42c009cb-0f10-4e34-977b-ccb40606fc42.png" width="539" height="140" /&gt; &lt;/p&gt; &lt;p&gt;ขอให้สนุกกับการ Tweak ครับ ! &lt;img src="http://coredeveloper.net/emoticons/emotion-1.gif" alt="Smile" /&gt; ตอนนี้ผมยังติดนิดนึงที่ ตักษรเวลากรอกรหัสผ่านมันเป็นสี่เหลี่ยม แต่ยังไม่มีเวลานั่งลองแก้ครับ เพราะแก้ที ต้อง Restart ที ถึงจะเห็นผล &lt;img src="http://coredeveloper.net/emoticons/emotion-6.gif" alt="Sad" /&gt;&lt;/p&gt;&lt;img src="http://coredeveloper.net/aggbug.aspx?PostID=539" width="1" height="1"&gt;</description></item></channel></rss>