1. ตัวชี้ฟังก์ชัน
AddressOf ได้รับตัวชี้ฟังก์ชันภายใน VB เราสามารถส่งตัวชี้ฟังก์ชันนี้ไปยัง API ที่ต้องการเรียกฟังก์ชันนี้กลับได้ ฟังก์ชันคือการอนุญาตให้โปรแกรมภายนอกเรียกใช้ฟังก์ชันภายใน VB
อย่างไรก็ตาม การใช้พอยน์เตอร์ฟังก์ชันใน VB นั้นไม่ครอบคลุมเท่ากับใน C เนื่องจากเอกสาร VB แนะนำวิธีการส่งพอยน์เตอร์ฟังก์ชันไปยัง API เพื่อดำเนินการเรียกกลับเท่านั้น และไม่ได้ชี้ให้เห็นฟังก์ชันมหัศจรรย์มากมายของพอยน์เตอร์ฟังก์ชัน เนื่องจาก VB ไม่สนับสนุน เมื่อใช้พอยน์เตอร์ พอยน์เตอร์ฟังก์ชันก็ไม่มีข้อยกเว้น
อันดับแรก เรามาจำแนกวิธีการใช้พอยน์เตอร์ของฟังก์ชันกันก่อน
1. โทรกลับ. นี่เป็นฟังก์ชันพื้นฐานและสำคัญที่สุด ตัวอย่างเช่น แกนหลักของเทคโนโลยีการสืบทอดคลาสย่อยที่นำมาใช้ในเอกสาร VB คือ API สองตัว: SetWindowLong และ CallWindowPRoc
เราสามารถใช้ SetWindowLong API เพื่อแทนที่ตัวชี้ฟังก์ชันหน้าต่างต้นฉบับด้วยตัวชี้ฟังก์ชันของเราเอง และบันทึกตัวชี้ฟังก์ชันหน้าต่างต้นฉบับ ด้วยวิธีนี้ ข้อความหน้าต่างสามารถส่งไปยังฟังก์ชันของเราเองได้ และเราสามารถใช้ CallWindowProc เพื่อเรียกตัวชี้หน้าต่างที่บันทึกไว้ก่อนหน้านี้เมื่อใดก็ได้เพื่อเรียกใช้ฟังก์ชันหน้าต่างต้นฉบับ ด้วยวิธีนี้ เราสามารถจัดการกับข้อความที่ติดยาเสพติดได้โดยไม่ทำลายฟังก์ชันการทำงานของหน้าต่างเดิม
เราควรจะคุ้นเคยกับการประมวลผลเฉพาะ และเอกสาร VB ก็อธิบายอย่างชัดเจนเช่นกัน สิ่งที่ต้องให้ความสนใจที่นี่คือ CallWindowProc API เราจะเห็นการใช้งานที่ยอดเยี่ยมของมันในภายหลัง
ที่นี่เราเรียกการโทรกลับเพื่ออนุญาต "การเรียกภายนอกไปยังตัวชี้ฟังก์ชันภายใน"
2. สำหรับการใช้งานภายในโปรแกรม ตัวอย่างเช่น ในภาษา C เราสามารถส่งตัวชี้ฟังก์ชัน C เป็นพารามิเตอร์ไปยังฟังก์ชัน C ที่ต้องใช้ตัวชี้ฟังก์ชัน เช่น qsort ฟังก์ชันไลบรารี C ที่จะกล่าวถึงในภายหลัง
ต้องใช้ตัวชี้ฟังก์ชันประเภท COMPARE เพื่อเปรียบเทียบขนาดของตัวแปรสองตัว เพื่อให้ฟังก์ชันการเรียงลำดับสามารถเรียกตัวชี้ฟังก์ชันนี้เพื่อเปรียบเทียบตัวแปรประเภทต่างๆ ได้ ดังนั้น qsort จึงสามารถจัดเรียงอาร์เรย์ตัวแปรประเภทต่างๆ ได้
ลองเรียกแอปพลิเคชันนี้ว่า "การเรียกตัวชี้ฟังก์ชันภายในจากภายใน"
3. เรียกใช้ฟังก์ชันภายนอก
คุณอาจถามว่าไม่ได้ใช้ API เพียงเรียกใช้ฟังก์ชันภายนอกใช่ไหม ใช่ แต่บางครั้งเรายังจำเป็นต้องรับพอยน์เตอร์ของฟังก์ชันภายนอกโดยตรง ตัวอย่างเช่น การโหลด DLL แบบไดนามิกผ่าน LoadLibrary จากนั้นรับตัวชี้รายการฟังก์ชันที่เราต้องการผ่าน GetProcAddress จากนั้นเรียกใช้ฟังก์ชันภายนอกผ่านตัวชี้ฟังก์ชันนี้ เทคโนโลยีการโหลด DLL แบบไดนามิกนี้ช่วยให้เราเรียกใช้ฟังก์ชันภายนอกได้อย่างยืดหยุ่นมากขึ้น
เราเรียกวิธีนี้ว่า "การเรียกตัวชี้ฟังก์ชันภายนอกจากภายใน"
4. ไม่จำเป็นต้องพูด เรายังสามารถควบคุม "การเรียกตัวชี้ฟังก์ชันภายนอกจากภายนอก" ได้ ไม่ ตัวอย่างเช่น เราสามารถโหลด DLL หลายตัวและส่งผ่านตัวชี้ฟังก์ชันใน DLL หนึ่งไปยังฟังก์ชันใน DLL อื่นได้
การแบ่งประเภท "ภายใน" และ "ภายนอก" ข้างต้นเป็นคำศัพท์ที่เกี่ยวข้องกันทั้งหมด (จริงๆ แล้ว DLL ยังอยู่ในกระบวนการ) การจำแนกประเภทนี้จะช่วยให้เราหารือเกี่ยวกับปัญหาต่างๆ ในอนาคต โปรดจำการจำแนกประเภทข้างต้นของฉัน เนื่องจากบทความในอนาคตจะใช้การจำแนกประเภทนี้ด้วย เพื่อวิเคราะห์ปัญหา
การใช้พอยน์เตอร์ฟังก์ชันไม่มีอะไรมากไปกว่าสี่วิธีข้างต้น แต่ในการใช้งานจริงจะมีความยืดหยุ่นและเปลี่ยนแปลงได้ ตัวอย่างเช่น การสืบทอดและความหลากหลายใน C และอินเทอร์เฟซใน COM ล้วนเป็นแอปพลิเคชั่นที่ชาญฉลาดของตารางตัวชี้ฟังก์ชันที่เรียกว่า vTable การใช้พอยน์เตอร์ฟังก์ชันสามารถทำให้การประมวลผลโปรแกรมมีประสิทธิภาพและยืดหยุ่นมากขึ้น
ยกเว้นวิธีแรก เอกสาร VB ไม่ได้แนะนำวิธีการอื่น และยังระบุอย่างชัดเจนว่าตัวชี้ฟังก์ชัน "พื้นฐานถึงพื้นฐาน" ไม่ได้รับการสนับสนุน (นั่นคือ วิธีที่สองที่กล่าวถึงข้างต้น) สามารถทำได้ทั้งสี่วิธีข้างต้น วันนี้เรามาดูวิธีการนำวิธีที่สองไปใช้กันดีกว่า เรามาเริ่มกันด้วยวิธีง่ายๆ กันก่อน สำหรับวิธีการเรียกใช้พอยน์เตอร์ฟังก์ชันภายนอกใน VB และวิธีการใช้งานที่ชาญฉลาดของพอยน์เตอร์ฟังก์ชันต่างๆ โดยการประมวลผลตารางกระโดดตัวชี้ฟังก์ชันอินเทอร์เฟซ vTable ใน VB เนื่องจากสิ่งนี้จะเกี่ยวข้องกับหลักการภายในของ COM ฉันจะอธิบายอย่างละเอียดในบทความอื่น .
ในความเป็นจริง เอกสาร VB ก็ไม่ผิด VB ไม่รองรับตัวชี้ฟังก์ชัน "Basic to Basic" แต่เราสามารถสร้างวิธีวงเวียนเพื่อให้บรรลุได้ นั่นคือ "Basic to API" แรก จากนั้นใช้วิธีแรก "ภายนอก เรียกตัวชี้ฟังก์ชันภายใน" เพื่อเปลี่ยนจาก "API เป็น BASIC" จึงบรรลุวัตถุประสงค์ของวิธีที่สองจาก "Basic to Basic" เราสามารถเรียกเทคโนโลยีนี้ว่า "forced callback" ซึ่งมีเฉพาะใน VB เท่านั้น แปลกมาก เทคโนโลยี.
มันซับซ้อนเล็กน้อย แต่ลองคิดให้รอบคอบเกี่ยวกับ CallWindowProc ในเทคโนโลยีการสืบทอดคลาสย่อยของหน้าต่าง เราสามารถใช้ CallWindowProc เพื่อบังคับให้ระบบปฏิบัติการภายนอกเรียกใช้ตัวชี้ฟังก์ชันหน้าต่างดั้งเดิมที่บันทึกไว้ของเรา ตัวชี้ฟังก์ชัน
ฮ่าๆ ฉันบอกไปแล้วว่าเราควรพูดถึงหลักการให้น้อยลง แต่มาเริ่มเรียนรู้ท่ากันดีกว่า!
พิจารณาว่าเราใช้ qsort ใน VB ที่รองรับการเปรียบเทียบหลายคำหลักเช่นเดียวกับ C คุณสามารถดูซอร์สโค้ดที่สมบูรณ์ได้ในโค้ดสนับสนุนของบทความนี้ เฉพาะโค้ดที่เกี่ยวข้องกับแอปพลิเคชันตัวชี้ฟังก์ชันเท่านั้นที่ได้รับที่นี่
มาดูสุดท้ายที่คำสั่ง qsort สุดท้ายของเรา
ArrayPtr ด้านบนเป็นตัวชี้ไปยังองค์ประกอบแรกของอาร์เรย์ที่ต้องเรียงลำดับ nCount คือจำนวนองค์ประกอบในอาร์เรย์ nElemSize คือขนาดของแต่ละองค์ประกอบ และ pfnCompare คือตัวชี้ฟังก์ชันการเปรียบเทียบของเรา การประกาศนี้คล้ายกับ qsort ในฟังก์ชันไลบรารี C มาก
เช่นเดียวกับ C เราสามารถส่งตัวชี้ฟังก์ชันของ Basic ไปยังฟังก์ชัน qsort ของ Basic ได้
วิธีใช้:
เพื่อนฉลาดคุณเคยเห็นความลึกลับที่นี่แล้วหรือยัง? จากการทดสอบ ตอนนี้คุณช่วยบอกวิธีใช้ตัวชี้ฟังก์ชันใน qsort ให้ฉันได้ไหม ตัวอย่างเช่น ตอนนี้เราจำเป็นต้องเปรียบเทียบขนาดขององค์ประกอบ i-th และองค์ประกอบ j-th ของอาร์เรย์โดยการเรียกตัวชี้ฟังก์ชัน
ใช่ แน่นอน คุณต้องใช้ Compare API ที่ประกาศไว้ก่อนหน้านี้ (จริงๆ แล้วคือ CallWindowProc) เพื่อดำเนินการบังคับเรียกกลับ
การใช้งานเฉพาะมีดังนี้:
มีการแนะนำการเคลื่อนไหวแล้วคุณเข้าใจไหม? ให้ฉันอธิบายสั้น ๆ เกี่ยวกับความหมายของการเปรียบเทียบด้านบน มันใช้ CallWindowProc API อย่างชาญฉลาดมาก API นี้ต้องการพารามิเตอร์ห้าตัว พารามิเตอร์แรกคือตัวชี้ฟังก์ชันปกติ API นี้สามารถเรียกกลับตัวชี้ฟังก์ชันได้ทันทีและส่งพารามิเตอร์ประเภทยาวสี่ตัวสุดท้ายของ API นี้ไปยังฟังก์ชันที่ชี้โดยตัวชี้ฟังก์ชัน นี่คือสาเหตุที่ฟังก์ชันการเปรียบเทียบของเราต้องมีสี่พารามิเตอร์ เนื่องจาก CallWindowProc API ต้องการให้ตัวชี้ฟังก์ชันที่ส่งผ่านไปจะต้องสอดคล้องกับต้นแบบฟังก์ชัน WndProc ต้นแบบของ WndProc เป็นดังนี้:
LRESULT, HWND, UINT, WPARAM และ LPARAM ข้างต้นทั้งหมดสามารถสอดคล้องกับประเภท Long ใน VB ได้ ซึ่งดีมากเพราะประเภท Long สามารถใช้เป็นตัวชี้ได้!
ลองดูที่เวิร์กโฟลว์อีกครั้ง เมื่อเราเรียก qsort โดยใช้ AddressOfCompareSalaryName เป็นพารามิเตอร์ตัวชี้ฟังก์ชัน พารามิเตอร์อย่างเป็นทางการ pfnCompare ของ qsort ถูกกำหนดให้เป็นตัวชี้ฟังก์ชันของพารามิเตอร์จริง CompareSalaryName ในขณะนี้ การเรียก Compare เพื่อบังคับการเรียกกลับไปยัง pfnCompare จะเทียบเท่ากับการเรียกคำสั่ง VB ต่อไปนี้:
สิ่งนี้จะไม่ทำให้เกิดข้อผิดพลาดประเภทพารามิเตอร์ที่ไม่ตรงกันใช่หรือไม่ พารามิเตอร์สองตัวแรกของ CompareSalaryName ประเภท TEmployee ไม่ใช่ใช่หรือไม่ อันที่จริงการโทรดังกล่าวไม่สามารถทำได้ใน VB เนื่องจากการตรวจสอบประเภทของ VB จะไม่อนุญาตให้มีการโทรดังกล่าว อย่างไรก็ตาม การเรียกนี้จริงๆ แล้วเป็นการเรียกกลับโดย API และเป็นไปไม่ได้ที่ VB จะตรวจสอบว่าประเภทพารามิเตอร์ของฟังก์ชันการเรียกกลับ API นั้นเป็นประเภทตัวเลขแบบยาวธรรมดาหรือตัวชี้โครงสร้าง ดังนั้นจึงอาจกล่าวได้ว่าเรามี ข้ามการควบคุมพารามิเตอร์ฟังก์ชันของ VB สำหรับการตรวจสอบประเภท เราสามารถประกาศพารามิเตอร์แบบยาวนี้ให้เป็นตัวชี้ประเภทใดก็ได้ ดังนั้นเราจึงต้องใช้เทคนิคนี้อย่างระมัดระวัง ตัวอย่างเช่น พารามิเตอร์ "ArrayPtr (i-1)*nElemSize" ที่ในที่สุดจะถูกส่งไปยังฟังก์ชัน CompareSalaryName ด้านบนนี้เป็นเพียงที่อยู่เท่านั้น จะไม่ตรวจสอบที่อยู่นี้เสมอไป ที่อยู่จะถือเป็นพอยน์เตอร์ประเภท TEmployee หากถูกใช้โดยไม่ได้ตั้งใจเป็น "ArrayPtr i*nElemSize" ดังนั้น เมื่อ i เป็นองค์ประกอบสุดท้าย เราจะทำให้เกิดข้อผิดพลาดในการเข้าถึงหน่วยความจำ ดังนั้นเราจึงต้องใส่ใจกับปัญหาขอบเขต เช่นเดียวกับเมื่อต้องจัดการกับพอยน์เตอร์ใน C
คุณสามารถดูแอปพลิเคชันที่ชาญฉลาดของฟังก์ชันพอยน์เตอร์ได้ที่นี่ แต่วิธีที่แนะนำที่นี่ยังคงมีข้อจำกัดอย่างมาก ฟังก์ชันของเราต้องมีสี่พารามิเตอร์ แนวทางที่สะอาดกว่าคือการเขียน DLL ใน VC หรือ Delphi และสร้าง API ที่สอดคล้องมากขึ้นเพื่อใช้ฟังก์ชันต่างๆ คล้ายกับ CallWindowProc ฉันได้ติดตามการใช้งานภายในของ CallWindowProc ซึ่งทำงานหลายอย่างที่เกี่ยวข้องกับข้อความในหน้าต่าง ซึ่งซ้ำซ้อนในแอปพลิเคชันของเรา ในความเป็นจริง หากต้องการใช้ API การเรียกกลับแบบบังคับ คุณจะต้องพุชพารามิเตอร์สองสามตัวสุดท้ายลงบนสแต็ก จากนั้นจึงเรียกพารามิเตอร์แรก ซึ่งเป็นเพียงคำสั่งประกอบบางส่วนเท่านั้น
เป็นเพราะข้อจำกัดของ CallWindowProc ทำให้เราไม่สามารถใช้มันเพื่อเรียกตัวชี้ฟังก์ชันภายนอกเพื่อใช้วิธีการเรียกตัวชี้ฟังก์ชันที่สามที่กล่าวถึงข้างต้นได้ หากต้องการใช้วิธีที่สาม Master MattCurland ได้จัดเตรียมวิธีแฮ็กที่เหมือนฝันร้ายไว้ เราจำเป็นต้องสร้างอินเทอร์เฟซ IUnknown จากอากาศบาง ๆ ใน VB และเพิ่มรายการใหม่หลังจากสามรายการดั้งเดิมของ vTable ของอินเทอร์เฟซ IUnknown รายการใหม่ ใส่รหัสเครื่องลงไป รหัสเครื่องนี้จะต้องจัดการกับตัวชี้นี้ก่อนที่จะเรียกตัวชี้ฟังก์ชันที่เราให้ไปในที่สุด ไม่มีปัญหาไม่ว่าจะเป็นภายในหรือภายนอก ฉันจะกลับมาที่วิธีนี้เมื่อเราเจาะลึกเกี่ยวกับ COM ภายใน
นอกจากนี้ อัลกอริธึมการเรียงลำดับยังเป็นเรื่องของความคิดเห็น เดิมทีฉันต้องการนำเสนออัลกอริธึมที่หลากหลายที่สุดพร้อมประสิทธิภาพที่ดีที่สุดในบทความนี้ แม้ว่าแนวคิดนี้จะดี แต่ก็ไม่มีอัลกอริธึมที่ "ดีที่สุด" ในทุกสถานการณ์ วิธีการเรียงลำดับอย่างรวดเร็วที่นำมาใช้โดยใช้เทคโนโลยีตัวชี้ต่างๆ ที่ให้ไว้ในบทความนี้ควรจะเร็วกว่ามาก และใช้หน่วยความจำน้อยกว่าการใช้เทคโนโลยีอ็อบเจ็กต์เพื่อใช้งานฟังก์ชันเดียวกัน แต่ถึงแม้อัลกอริธึมการเรียงลำดับอย่างรวดเร็วนี้ ซึ่งฉันปรับให้เหมาะสมแล้ว แต่ก็ยังไม่ดีเท่า ShellSort เพราะ ShellSort ใช้งานง่าย ตามทฤษฎีแล้ว qsort ควรมีประสิทธิภาพโดยเฉลี่ยที่ดีกว่า ShellSort แต่ไม่จำเป็นเสมอไปใน VB (คุณสามารถดูโค้ดสนับสนุนของบทความนี้ ซึ่งยังมีโค้ดสนับสนุนของคอลัมน์ VBPJ ที่เรียกว่า ShellSort ซึ่งก็คือ ดีมาก และแนวคิดของบทความนี้ก็นำมาจาก This ShellSort)
อย่างไรก็ตาม ควรชี้ให้เห็นว่าทั้ง Quick Sort และ ShellSort ที่นี่สามารถปรับปรุงได้อย่างมาก เนื่องจากการนำไปใช้งานต้องใช้ CopyMemroy อย่างกว้างขวางในการคัดลอกข้อมูล (นี่คือหนึ่งในข้อบกพร่องของการใช้พอยน์เตอร์ใน VB) ที่จริงแล้ว เรามีวิธีที่ดีกว่า นั่นคือการแฮ็กโครงสร้างอาร์เรย์ของ VB ซึ่งก็คือ SafeArray ในระบบอัตโนมัติของ COM เราสามารถใส่พอยน์เตอร์ของแต่ละองค์ประกอบอาร์เรย์ใน SafeArray ลงในอาร์เรย์แบบยาวได้ในคราวเดียว โดยไม่ต้องใช้ CopyMemroy เพื่อแลกเปลี่ยนองค์ประกอบในอาร์เรย์แบบยาวเพื่อให้บรรลุวัตถุประสงค์ในการแลกเปลี่ยนตัวชี้องค์ประกอบอาร์เรย์ SafeArray แบบเรียลไทม์ ข้อมูลจะไม่ถูกย้าย มีเพียงตัวชี้เท่านั้นที่ถูกย้าย คุณสามารถจินตนาการได้ว่ามันเร็วแค่ไหน
-