Win32API : At the Root

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

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

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

    Windows ทุกรุ่นก็มี API ให้โปรแกรมเมอร์เขียนเข้าไปติดต่อครับ ตั้งแต่ Windows 95 เป็นต้นมา API ของ Windows ก็มีชื่อว่า Win32API ซึ่งก็มีการเพิ่มความสามารถมาทุกรุ่น สิ่งที่ Win32API ให้บริการนั้นก็คือ User Interface, File System, Process, I/O และอื่นๆ อีกมากครับ

    ถ้าใครเคยเขียนโปรแกรมง่ายๆ อย่าง Hello, World ซึ่ง Popup ขึ้นมาแสดงนั้น โดยใช้ Win32API ล้วนๆ คงเข้าใจถึงความซับซ้อนครับ วุ่นวายมาก ปัจจุบันคงไม่มีคนทำแล้วครับ ทุกวันนี้เขียนกันง่ายๆ โดยเฉพาะอย่างยิ่ง Visual Basic นี้ เขียนกันสั้นๆ เพียงบรรทัดเดียวก็ได้แล้ว

    คราวนี้วกกลับมาที่ .NET Framework ซึ่งเคยคุยไว้ว่าโปรแกรมเมอร์จะสามารถทำงานได้ลึกเท่ากับ Win32 API ซึ่งผมก็รอดูอยู่ แต่พอออกมาเป็น Version 1.0 ผมก็ผิดหวังเล็กๆ อยู่เหมือนกัน ผมคงไม่ได้ใส่ใจ ว่ามันเทียบเท่าหรือไม่ แต่งานง่ายๆ กลับทำไม่ได้ สรุปก็คือ .NET Framework ไม่สามารถบังเราจาก Win32API ได้ 100% ครับ ดังนั้น เราอาจจะเลี่ยงไม่ได้ที่จำเป็นต้องเรียกใช้งานจาก Win32API

    ตัวอย่างที่ .NET Framework ไม่รองรับ ก็คือ Serial Port Programming ครับ ผมถือว่าเรื่องนี้เป็นเรื่องใหญ่ทีเดียว ถ้า Microsoft จะทำให้ .NET Framework มีความ Portable ที่จะนำเอาไปใช้บนเครื่องคอมพิวเตอร์อะไรก็ได้นั้น ถ้าไม่รองรับ Serial Port Programming แล้วละก็ สอบตกแน่นอนครับ เรื่องของ Serial Port Programming ผมจะคุยให้ฟังในบทต่อไปครับ

    ภาษา C# ก็ยังดียังยอมให้เราเขียนติดต่อกับ Win32API โดยตรงได้ เรามาลองดูตัวอย่างกันครับ ตัวอย่างนี้เป็นตัวอย่างที่ดึงเอาเวลาในระบบมาแสดง ใน .NET Framework ก็มีบริการนี้อยู่ไม่ใช่ว่าไม่มี แต่มันเป็นตัวอย่างง่ายๆ ของการเรียกใช้ Win32API เลยเอามาแสดงให้ดูครับ

win32intro.cs

using System;
using System.Runtime.InteropServices;

class win32intro
{
	struct SYSTEMTIME {
		public short wYear;
		public short wMonth;
		public short wDayOfWeek;
		public short wDay;
		public short wHour;
		public short wMinute;
		public short wSecond;
		public short wMilliseconds;
	}
	[DllImport("kernel32.dll",EntryPoint="GetLocalTime")]
	private static extern void GetLocalTime(out SYSTEMTIME lpSystemTime);


	static void Main(string[] args)
	{
		SYSTEMTIME tm;
		GetLocalTime(out tm);
		Console.WriteLine("{0:0000}/{1:00}/{2:00} {3:00}:{4:00}:{5:00}:{6:0000}", 
			tm.wYear, tm.wMonth, tm.wDay, tm.wHour, tm.wMinute, tm.wSecond, tm.wMilliseconds);
	}
}

เมื่อรันโปรแกรมแล้วจะได้ผลลัพธ์ทำนองนี้ครับ

DOS Prompt

C:\CS> win32intro
2002/04/09 11:51:24:0963

C:\cs>_

 

อธิบายโปรแกรม

    ก่อนที่เราสามารถใช้ Win32API ได้นั้น เราต้อง using System.Running.InteropServices; เสียก่อนเพื่อให้เราสามารถทำ Interop กับ Win32API หรือ COM ได้ จากนั้นเราใช้ Attribute DllImport เพื่อระบุ .dll ที่เราต้องการ ในที่นี้คือ kernel32.dll ซึ่งภายในมีฟังก์ชัน GetLocalTime อยู่ เราจึงใช้ Attribute EntryPoint เพื่อระบุชื่อฟังก์ชันที่เราต้องการเรียก จากนั้นบรรทัดต่อมา เราทำการ Map เข้ากับฟังก์ชันของ C# ครับ ผลลัพธ์ที่ได้ เราจะได้เป็น External Function ซึ่งสังเกตได้ว่า GetLocalTime() นั้นไม่มี Code เมื่อเราเรียกใช้ มันก็จะส่งต่อ Interop ไปเรียกใช้ GetLocalTime ที่อยู่ใน Kernel32.dll ครับ

    คราวนี้มาถึงคำถามที่สำคัญว่า รู้ได้อย่างไรว่า มี dll อะไรให้เรียกใช้บ้าง ภายใน .dll มีฟังก์ชันอะไรบ้าง และ แต่ละฟังก์ชันมีพารามิเตอร์อะไร คำตอบก็ไม่ยากครับ คือต้องเปิดคู่มือดู คู่มือที่ว่าก็ไม่จำเป็นต้องเสียเงินซื้อครับ ลอง Search ดูแฟ้มข้อมูล win32api.txt ใน directory ของคุณดู ถ้าคุณลง Visual Studio รุ่นที่ไม่ใช่ .NET หรือ Office มันจะมีแฟ้มนี้มาให้ แต่ถ้าหาไม่เจอ ก็ไป download ได้ครับ ที่  http://ep0.hypermart.net/vb/api/Win32api.exe ภายในมันจะแสดงเป็น Code Visual Basic ซึ่งเราต้องเอามาปรับหน่อย อย่างตัวอย่างนี้ ใน Win32api.txt จะแสดงดังนี้ครับ

Declare Sub GetLocalTime Lib "kernel32" Alias "GetLocalTime" (lpSystemTime As SYSTEMTIME)

คงแปลงเป็น C# ไม่ยากใช่ไหมครับ

ฟังก์ชันชื่อ GetLocalTime
อยู่ที่ Lib ชื่อ Kernel32
มีพารามิเตอร์ 1 ตัวคือ SYSTEMTIME

ลองค้นหาดูในแฟ้ม win32api.txt ตัวของ SYSTEMTIME มีรายละเอียดดังนี้ครับ

Type SYSTEMTIME
    wYear As Integer
    wMonth As Integer
    wDayOfWeek As Integer
    wDay As Integer
    wHour As Integer
    wMinute As Integer
    wSecond As Integer
    wMilliseconds As Integer
End Type

    Type ใน Visual Basic ก็คือ struct ในภาษา C# แต่ต้องระวังหน่อยครับ Integer ของ VB นั้นมีค่า 16 บิต ส่วน int ของ C# นั้นมีค่า 32 บิต ดังนั้น integer ของ VB จึงตรงกับ short ของ C# ครับ

    เท่านั้นเราก็สามารถเรียกใช้ Win32API ได้แล้วครับ ในบทต่อไปเรามาดู Serial Port กัน

09-APR-02