Home

สร้าง USB Boot ง่ายๆ กับ ventoy

นอกจากลง windows ผ่าน usb ที่ microsoft จะทำตัวติดตั้งให้โหลดจากเว็บตัวเองแล้วลง usb ไม่ต้องใช้ cd dvd เหมือนสมัยก่อนแล้ว แต่ถ้าจะลง windows หลาย version หรือจะลง linux จะลงตัวสแกนไวรัส ปกติมันก็ใช้ flash drive ตัวละอัน รกกว่า dvd หลายอีก

หลังจากลองมาหลายตัวอย่าง Rufus, YUMI จนได้เจอกับตัวที่ถูกใจ ใช้ง่ายมากๆ คือ ventoy ที่ตอบโจทย์มากๆ คือ ลงได้หลาย os หลาย iso โดยใช้แค่ usb อันเดียว เพิ่มลด iso แค่ลบ วาง ไม่ต้องทำอะไรพิเศษ จะทำใน windows ก็ได้ windows พังก็ใช้ linux ทำก็ได้

  1. โหลดตัวติดตั้งได้จากเว็บ https://www.ventoy.net/en/download.html โดยมีให้เลือก windows / linux / live cd
  2. โหลด windows 10, xp, 7 ตัวที่ชอบ, linux, Hiren’s BootCD PE, ตัวแสกนไวรัส อะไรก็แล้วแต่ไปพลางๆ ขอแค่เป็นไฟล์ iso เป็นอันใช้ได้
  3. หา usb flash drive sd card หรือ harddisk ที่ชะตาขาดเพราะว่ามันจะต้องโดนลบข้อมูลข้างในทั้งหมด เลือกเอาที่มันจุหน่อย ตอนนี้ตัว 32GB ไม่กี่ร้อยละ จะได้ลงได้หลายๆ ตัว เสียบเข้าเครื่องรอเอาไว้
  4. แตก zip ไฟล์ออกมาทั้ง folder
  5. ดับเบิลคลิกตรง Ventoy2Disk จะเห็นโปรแกรมหน้าตาบ้านๆ ไม่มีอะไรเลยจริงๆ เพราะมันใช้ง่ายมาก
  6. เลือก device ( usb ) อันโชคร้ายของเรา ( เลือกให้ถูกนะเพราะมันจะโดนลบเกลี้ยง ) ถ้าไม่เห็นคลิกวงเขียวๆ ด้านหลัง
  7. กด Install แค่นี้ละ
  8. หลายคนคงจะงงแล้วที่โหลด windows เอาไว้ละ เปิดดูใน File Explorer จะเห็น drive ใหม่ชื่อ Ventoy เพิ่มขึ้นมาให้เอา iso ที่โหลดเอาไว้ทั้งหมด วางไว้ในนี้เป็นอันเสร็จ

ทดสอบโดยการตั้งให้เครื่องของเรา boot จาก usb อันที่ทำไว้ จะเห็นชื่อไฟล์ iso ที่เราโหลดมา เลือกตัวที่ต้องการจะใช้ติดตั้งได้เหมือน usb แยกเฉพาะตัวเลย

ความดีงามของ Ventoy คือเวลา windows ออกเวอร์ชั่นใหม่ขึ้นมา ก็แค่โหลด iso ตัวใหม่มาวางใน dirve เดิม ถ้ามันเก่าแล้วหรือพื้นที่ไม่พอแล้วก็แค่ลบอันที่ไม่ใช้ออก ไม่ต้องทำอะไรเป็นพิเศษ ไม่ต้องใช้ Ventoy มาทำอะไรด้วยซ้ำไป

C#: Error CS0579 Duplicate ‘System.Reflection.AssemblyCompanyAttribute’

เขียน c# อยู่ๆเจอ error Error CS0579 จริงๆ ไม่ใช่อยู่ๆ หรอก ทุกอย่างมันมีเหตุปัจจัยมันถึงแจ้ง error มาให้ดู โดยที่เจอคือ

  • Error CS0579 Duplicate ‘System.Reflection.AssemblyConfigurationAttribute’
  • Error CS0579 Duplicate ‘System.Reflection.AssemblyConfigurationAttribute’
  • Error CS0579 Duplicate ‘System.Reflection.AssemblyFileVersionAttribute’
  • Error CS0579 Duplicate ‘System.Reflection.AssemblyProductAttribute’
  • Error CS0579 Duplicate ‘System.Reflection.AssemblyTitleAttribute’
  • Error CS0579 Duplicate ‘System.Reflection.AssemblyVersionAttribute’

ดูโดยรวมแล้วมันคือไฟล์ที่บันทึกเวอร์ชั่นของโปรเจคที่เราได้เขียนเอาไว้ สาเหตุก็น่าจะง่ายๆ ไปแก้ไขมันแล้วพังนั่นเอง แต่ไม่น่าพังนะ ก็ไปแก้ตามวิธีปกติเองนี่น่า ไปไล่ๆ ดูจนพบวิธีแก้ คือ

  1. ไปที่โฟลเดอร์ของ project ที่มีปัญหา
  2. ค้นหาไฟล์ *.Assemblyinfo.cs
  3. จะเห็นว่ามันมีไฟล์ตัวนี้อยู่ 5 ตัวเลย ลบทิ้งทั้งหมด
  4. ลอง clearn แล้ว run project ดูใหม่ ถ้ายัง error ไปบรรทัดที่มันเตือนอยู่แล้วลบทิ้งไปเลย

Cr. Duplicate AssemblyVersion Attribute

c#: Multiple Document Interface (MDI)

ตัวอย่างเขียน app แบบมีหลายฟอร์มย่อยอยู่ใน window หลัก หรือที่ฝรั่งเรียกไว้ว่า Multiple Document Interface (MDI)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace PrototypesCSharp
{
    public partial class FormMDI : Form
    {
        public FormMDI()
        {
            InitializeComponent();
        }

        public Form TryGetFormByName(string frmName)
        {
            if (panelMDI.Controls.Count > 0)
            {
                panelMDI.Controls.RemoveAt(0);
            }

            var formType = Assembly.GetExecutingAssembly().GetTypes()
                .Where(a => a.BaseType == typeof(Form) && a.Name == frmName)
                .FirstOrDefault();

            if (formType == null)
            {//if no form
                return null;
            }

            Form frm = (Form)Activator.CreateInstance(formType);

            //https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.form.acceptbutton?view=net-5.0

            frm.AutoSize = true;
            frm.AutoSizeMode = AutoSizeMode.GrowAndShrink;
            frm.Height = panelMDI.Height;
            frm.TopLevel = false;
            frm.TopMost = true;
            frm.WindowState = FormWindowState.Maximized;
            frm.Width = panelMDI.Width;

            frm.Show();

            panelMDI.Controls.Add(frm);

            return frm;
        }

        //on menu click
        private void menuStrip1_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            try
            {
                String formName = e.ClickedItem.Name.Split(new[] { "toolStripMenuItem" }, StringSplitOptions.None)[1];

                Form frm = TryGetFormByName(formName);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        //resize form inside when main windows resize
        private void panelMDI_Resize(object sender, EventArgs e)
        {
            foreach (Form frm in panel1.Controls)
            {
                frm.WindowState = FormWindowState.Normal;

                frm.WindowState = FormWindowState.Maximized;
            }
        }

    }
}

code ชุดนี้ทำงานโดยเมื่อ user click บน menuStrip (ไม่ใช้ตัว item นะ) จะอ่าน item name แล้วตัด toolStripMenuItem ออกเพื่อเรียกฟอร์ม ต้องขอขอบคุณ nemesv สำหรับคำตอบในเรื่อง Winforms, create form instance by form name ทำให้เขียน code สะอาดขึ้นเยอะเลย ไม่ต้องมา set ให้ item ทีละตัวแล้ว

c#: build .exe with version

ต้องเก็บไฟล์ที่ build ไฟล์เป็น version ต่างๆ ให้ user เอาไว้เทส ถ้านานๆ ทำทียังพอจะเปลี่ยนชื่อให้ได้ แต่ถ้าทำบ่อยๆ คงจะน่าเบื่อ ให้คอมทำให้ซิ

เปิดไฟล์ *.csproj ใน folder ของ project ออกมาแก้ ปกติก็ใส่ไว้ท้าย ๆ ไฟล์

  <Target Name="GetAssmeblyVersion" AfterTargets="Build">
    <GetAssemblyIdentity AssemblyFiles="$(TargetPath)">
      <Output TaskParameter="Assemblies" ItemName="MyAssemblyIdentities" />
    </GetAssemblyIdentity>
    <Message Text="Assmebly Version: %(MyAssemblyIdentities.Version)" />
  </Target>
  <Target Name="PostBuild" AfterTargets="GetAssmeblyVersion">
    <Exec Command="copy /Y "$(ProjectDir)$(OutDir)\$(TargetName).exe" "$(ProjectDir)$(OutDir)\$(TargetName) v.%(MyAssemblyIdentities.Version)_$(ConfigurationName).exe"

" />
  </Target>

</Project>

หลังจาก build code ชุดนี้จะทำตามคำสังใน PostBuild ในที่นี้คือใช้ command copy .exe ตามปกติแล้วเพิ่ม version เข้าไปให้

ขอขอบคุณ

c#: แบ่งงานช่วยกันรุมให้เสร็จโดย Tasks

cpu รุ่นใหม่ๆ สามารถทำงานได้พร้อมๆ กันหลายๆ งานในเวลาเดียวกัน ทำไมจะไม่เขียนโปรแกรมให้ทำงานพร้อมๆ กันจะได้เสร็จไว้ๆ ละ

ตัวอย่าง c# Console App

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    internal class Program
    {

        private static void Main(string[] args)
        {
            Dictionary<string,
            string> months = new Dictionary<string,
            string>() {
        {"01","January"},
        {"02","February"},
        {"03","March"},
        {"04","April"},
        {"05","May"},
        {"06","June"},
        {"07","July"},
        {"08","August"},
        {"09","September"},
        {"10","October"},
        {"11","November"},
        {"12","December"},
            };
            var reultsts = new List<List<string>>();
            Console.WriteLine("Start Main");
            reultsts = RunAsync(months).Result;
            Console.WriteLine("End Main get reultsts " + reultsts.Count() + " items");

            Console.ReadKey();
        }

        public static async Task<List<List<string>>> RunAsync(Dictionary<string, string> months)
        {
            var tasks = new List<Task>();
            var reultsts = new List<List<string>>();
            Console.WriteLine("Start RunAsync");
            foreach (KeyValuePair<string, string> item in months)
            {
                tasks.Add(Task.Run(() =>
                {
                    reultsts.Add(TaskRun(item.Key, item.Value).Result);
                }));
            }
            Task t = Task.WhenAll(tasks);
            try
            {
                t.Wait();
            }
            catch { }
            Console.WriteLine("End RunAsync");
            return reultsts;
        }

        public static async Task<List<string>> TaskRun(string key, string value)
        {
            Random random = new Random();
            string message;
            int sleep;
            var results = new List<string>();
            for (int i = 0; i < 2; i++)
            {
                sleep = random.Next(1, 10) * 1000;
                System.Threading.Thread.Sleep(sleep);
                message = "TaskRun month " + key + " " + i + " " + sleep;
                results.Add(message);
                Console.WriteLine("\t" + message);
            }
            return results;
        }
    }
}

จะเห็นว่าไม่ได้ทำงานตามลำดับเดือน แถมบางเดือนจะทำครั้งที่ 1 และครั้งที่ 2 ห่างกันมากด้วยซ้ำไปเพราะว่าการจำลองการทำงานด้วย sleep ที่สุ่มไว้ได้เวลาต่างกันมาก อันไหนทำเสร็จแล้วมันก็จะไปหยิบงานอื่นมาทำก่อนโดยไม่ต้องรอให้งานเสร็จตามลำดับ

Apache: redirect

บางครั้งเว็บก็ทำเรื่องเฉพาะกิจอย่างมีกิจกรรมพิเศษช่วงปีใหม่ ก็ใช้วิธี redirct ไป subdomain ก่อนชั่วคราว หรือเปลี่ยนเพิ่มลด URL ทำได้โดยแก้ config

  1. sudo nano /etc/apache2/sites-available/example.com.conf
  2. พิมพ์เพิ่มเติมตามตัวอย่าง โดย
    • 301 Moved permanently : การย้าย URL ไปอยู่ที่ตำแหน่งใหม่อย่างถาวร ให้ GOOGLE จำไว้ว่าทุกลิงค์ที่เคยใช้อยู่เดิม ให้เปลี่ยนเป็นตัวใหม่
    • 302 Found : ใช้ชั่วคราว
    • 307 – Temporary redirect : ใช้ชั่วคราวชั่วคราวตอนนี้  คล้ายกับ 302 แต่จะกลับไปใช้ URL เก่านะ
    • 410 – Content deleted : ลิงค์นี้ ลบออกไปแล้วนะ
    • 451 – Content unavailable for legal reasons :URL นี้ไม่แสดงภายใต้เหตุผลทางกฎหมาย

ดูความหมายประเภทของ Redirect เพิ่มเติมได้ที่ http://Which redirect should I use?

<VirtualHost *:80>
    Redirect 302 / "http://newyear.example.com/"
    ServerName www.pexample.com
</VirtualHost>
  1. รีสตาร์ apache
    sudo systemctl restart apache2
  2. เรียก url ดูถ้าไม่ทำงานให้เช็ดดูตรง ServerAlias และ ServerName ใน config ทั้งหมด เพราะว่าอาจจะทำให้ apache สับสนไปทำงานผิดที่ได้ ให้ใส่ # ไปหน้าบรรทัดเดิมที่คิดว่าเป็นสาเหตุเพื่อ comment ออกไปชั่วคราว restart apache แล้วดูผลอีกครั้ง

Apache: VirtualHost

ถ้าต้องการเพิ่มเว็บเข้าไปใน host โดยที่แยกคนละ site คนละโดแมนออกจากกันเพื่อความสะดวกในการดูแล

  1. เพิ่ม directory สำหรับเก็บข้อมูลก่อน
    sudo mkdir -p /var/www/example.com/public_html
  2. ตั้งสิทธิให้ apache เข้าไปวิ่งเล่นเขียนอ่านได้
    sudo chown -R $USER:$USER /var/www/example.com/public_html
    sudo chmod -R 755 /var/www
  3. สร้างไฟล์ configuration แยกออกมาจากตัวเดิม
    sudo nano /etc/apache2/sites-available/example.com.conf
  4. พิมพ์ตามตัวอย่าง
<VirtualHost *:80>
    CustomLog ${APACHE_LOG_DIR}/access.log combined
    DocumentRoot /var/www/example.com/public_html
    ErrorLog ${APACHE_LOG_DIR}/error.log
    ServerAdmin [email protected]
    ServerAlias www.example.com
    ServerName example.com
    <Directory "/var/www/example.com/public_html">
        Allow from all
        AllowOverride All
        Options FollowSymLinks
        Order Allow,Deny
    </Directory>
</VirtualHost>
  1. เปิดใช้งาน configuration
    sudo a2ensite example.com.conf
  2. รีสตาร์ apache
    sudo systemctl restart apache2

ทดลองใส่ไฟล์เข้าไปใน /var/www/example.com/public_html แล้วเรียก url ที่ใส่เพิ่มเข้าไปดูว่าสามารถเรียกได้รึเปล่า

เซ็ต SSL เร็วและฟรีใน 5 นาที

ตั้งแต่ google ใช้ SSL ออกมาเป็นเงื่อนไขในการทำ SEO ทำให้ทุกเว็บต้องมาเปิดใช้ SSL ไม่งั้นอันดับจะตกลง

สามารถขอ SSL ฟรีได้ง่ายๆ โดยใช้บริการของ http://Let’s Encrypt’s เปิดให้ใช้ฟรีหรือจะบริจาคเล็กน้อยตามศรัทธาก็ได้ การติดตั้งง่ายๆ โดย

  • Web Hosting who support Let’s Encrypt จะเป็น host ที่ support อยู่แล้วสามารถเปิดใช้ได้เลยอย่าง hostgator.com
  • ใช้ certbot instructions เลือกว่าใช้ Apache, Haproxy, Nginx, Plesk และ os ที่ใช้อย่าง Arch Linux, CentOS 6, Debian, Fedora, FreeBSD, Gentoo, macOS, OpenBSD 6.0+, openSUSE, RHEL, Ubuntu, Windows มันจะบอกวิธิติดตั้งมาให้ภายใน 5 นาทีเว็บก็ใช้ SSL ได้เลยทุก domain !!!

อย่าง server ผมก็ทำแค่ SSH เข้าไปที่ host แล้วพิมพ์ตาม

sudo snap install core
sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
sudo certbot --apache
sudo certbot certonly --apache

แล้วทดลองเรียกเว็บโดยใช้ SSL เท่านั้นเอง ง่ายจริงๆ

apache: port

สาเหตุที่ต้องเปลี่ยน port apache จากมาตราฐาน 80, 443 เพราะว่า server ลง iis เอาไว้และต้องการใช้งานทั้งคู่ไปพร้อมๆ กันเลย แต่ที่ลงไว้มันจะชนกันใช้พร้อมกันไม่ได้

วิธีเปลียนก็ไม่ยากแค่หา config ที่มันเขียนเลข port อยู่แล้วใส่เลข port อื่นที่ไม่มีใครใช้ firewall ไม่ block เข้าไปแทน อย่างจะเปลี่ยนจากมาตราฐาน http port 80 เป็น 84 และ https 443 เป็น 85

  1. เปิดไฟล์ C:\xampp\apache\conf\httpd.conf แล้วแก้บรรทัด
    1. Listen 80 แก้เป็น
      Listen 84
    2. ServerName localhost:80 แก้เป็น
      ServerName localhost:84
  2. เปิดไฟล์ C:\xampp\apache\conf\extra\httpd-ssl.conf แล้วแก้บรรทัด
    1. Listen 443 แก้เป็น
      Listen 85
    2. <VirtualHost _default_:443> แก้เป็น
      <VirtualHost _default_:85>
    3. ServerName www.example.com:443 แก้เป็น
      ServerName www.example.com:85
  3. restart apache
  4. ทดสอบโดยการเปิดเว็บเช่น http://localhost:84 และ https://localhost:85

จริง ๆ แล้วมีผู้ได้กรุณาเขียนวิธีเปลี่ยน apache port ไว้ละเอียดมาก How to change XAMPP apache server port? และดู port ที่ว่างได้จากกิสต์ Well-known ports