การเชื่อมโยงข้อมูล (binding) ของ Pascal กับไลบรารี C มีลักษณะเหมือนโค้ด Pascal ทั่วไป คุณเรียกใช้เมธอด ได้รับเรกคอร์ดกลับมา และปลดปล่อยหน่วยความจำที่คุณจองไว้ แต่ปัญหาคือ PDFium เป็นไลบรารีภาษา C และ C++ ที่มีข้อตกลงในการเรียกใช้งาน (calling convention) ความกว้างของจำนวนเต็ม และกฎเกี่ยวกับการเป็นเจ้าของหน่วยความจำและผู้ปลดปล่อยหน่วยความจำเป็นของตัวเอง สิ่งเหล่านี้ไม่มีส่วนใดที่สามารถข้ามขอบเขตระหว่างภาษาได้โดยอัตโนมัติ ข้อตกลงทุกอย่างเหล่านี้ต้องถูกประกาศใหม่ด้วยตนเองในส่วนการประกาศของ Pascal และคำที่เขียนผิดเพียงคำเดียวอาจทำให้การเรียกใช้งานที่ดูปกติกลายเป็นการทำลายกองซ้อนข้อมูล (stack corruption) การตัดทอนข้อมูลออฟเซต (truncated offset) หรือการปลดปล่อยหน่วยความจำซ้ำ (double free) การตรวจสอบทางบัญชีของเวอร์ชัน v1.61.0 ของตัวเชื่อมโยง PDFium VCL พบข้อบกพร่องดังกล่าวในทุกประเภท ปัญหานี้ควรค่าแก่การอธิบายเนื่องจากไม่ได้จำกัดเฉพาะตัวเชื่อมโยงนี้เท่านั้น แต่ยังเป็นความเสี่ยงทั่วไปที่เกิดขึ้นทุกครั้งเมื่อนำ API ภาษา C มาครอบใน Delphi หรือ Lazarus
cdecl คือส่วนหนึ่งของประเภทฟังก์ชันไม่ใช่ส่วนตกแต่ง
PDFium ถูกคอมไพล์มาจากภาษา C บนระบบ Win32 ฟังก์ชันส่งออก และที่สำคัญยิ่งกว่านั้นคือคอลแบ็ก (callbacks) ที่มันเรียกใช้จะใช้ข้อตกลงการเรียกใช้งานแบบ cdecl ภายใต้ cdecl ตัวเรียก (caller) จะเป็นผู้ล้างข้อมูลในสแต็กหลังจากที่การเรียกคืนค่ากลับมา ค่าเริ่มต้นของ Delphi คือ register และมาตรฐาน C บน Win32 สำหรับคอลแบ็กในบางไลบรารีคือ stdcall ซึ่งตัวรับสาย (callee) จะเป็นผู้ล้างข้อมูลแทน เมื่อโครงสร้างข้อมูลส่งตัวชี้ฟังก์ชันให้แก่ PDFium และคุณลืมระบุคำว่า cdecl ในประเภทของตัวชี้นั้น ทั้งสองฝ่ายจะเห็นต่างกันว่าใครควรเป็นคนปรับค่าตัวชี้สแต็ก (stack pointer) ไม่ว่าจะแก้ไขทั้งคู่หรือไม่มีใครแก้เลยก็ตาม ตัวชี้สแต็กจะเบี่ยงเบนไปตามขนาดของอาร์กิวเมนต์ในทุกครั้งที่มีการเรียกใช้งาน
เหตุผลที่ตรวจพบข้อบกพร่องนี้ได้ยากคือความเสียหายที่เกิดขึ้นไม่ได้แสดงตรงจุดที่ผิดพลาด การเรียกใช้งานที่เสียหายจะคืนค่ากลับมาและดูปกติ แต่การจัดตำแหน่งที่คลาดเคลื่อนจะไปแสดงผลในภายหลัง ในฟังก์ชันอื่นที่ไม่เกี่ยวข้องซึ่งตอนนี้ทำงานอยู่บนตัวชี้สแต็กที่เบี่ยงเบนไปสองสามไบต์ และมันจะแสดงผลในรูปแบบของการอ่านค่าหน่วยความจำผิดปกติ ที่อยู่ส่งกลับผิดพลาด หรือการหยุดทำงานโดยที่ข้อมูลการดึงค่าฟังก์ชันย้อนหลัง (backtrace) ชี้ไปยังตำแหน่งอื่นที่ไม่ใกล้เคียงกับคอลแบ็กที่เขียนผิดเลย ระบบการกรอกฟอร์ม (Form-fill) คือจุดคลาสสิกที่ปัญหานี้สร้างความเสียหาย เนื่องจากอินเตอร์เฟซการกรอกฟอร์มคือเรกคอร์ดที่เต็มไปด้วยคอลแบ็กที่ PDFium จะเรียกใช้ ตัวหนึ่งในนั้นคือ FFI_OpenFile จะส่งมอบฟังก์ชันที่มันจะใช้เรียกเพื่อเปิดไฟล์ภายนอกแก่ PDFium โดยมีการประกาศเป็น function(pThis: PFPDF_FORMFILLINFO; fileFlag: Integer; wsURL: FPDF_WIDESTRING; mode: PAnsiChar): PFPDF_FILEHANDLER; cdecl การระบุ cdecl ด้านท้ายคือสิ่งที่คุณจำเป็นต้องใส่ไว้ หากตัดมันออก โค้ดจะยังคงคอมไพล์ ลิงก์ และรันได้ปกติไปจนกระทั่ง PDFium เรียกใช้ฟังก์ชันนั้น ข้อตกลงนี้เป็นของประเภทฟังก์ชันในตัวเอง มันไม่ใช่การตกแต่งเพิ่มเติม และตัวคอมไพล์จะไม่เตือนใด ๆ เมื่อมันหายไปเนื่องจากประเภทฟังก์ชันทั่วไปเป็นประเภทข้อมูลที่ถูกต้องตามกฎหมายของ Pascal แนวทางตั้งรับเดียวคือการกำหนดให้ข้อตกลงการเรียกใช้งานเป็นฟิลด์บังคับในทุกลายเซ็นการนำเข้าและคอลแบ็กที่คุณส่งออกภายนอก
size_t มีความกว้างเท่าตัวชี้ และบน FPC Win64 หมายถึงขนาด 64 บิต
ข้อบกพร่องที่สองคือความไม่สอดคล้องของความกว้างของจำนวนเต็มซึ่งจะปรากฏบนแพลตฟอร์มเป้าหมายเดียวเท่านั้น ตัวแปร size_t ของภาษา C ถูกกำหนดให้กว้างพอที่จะเก็บขนาดของอ็อบเจกต์ใด ๆ ได้ ซึ่งบนแพลตฟอร์ม 64 บิตจะหมายถึงจำนวนเต็มแบบไม่มีเครื่องหมายขนาด 64 บิต อินเตอร์เฟซการโหลดแบบต่อเนื่อง (progressive-loading) ของ PDFium จะระบุข้อมูลออฟเซตไบต์ในรูปแบบ size_t โดยเรกคอร์ด FX_FILEAVAIL ของตัวจัดหาข้อมูลจะเก็บคอลแบ็ก IsDataAvail ที่ PDFium จะเรียกใช้พร้อมค่าออฟเซตและขนาดข้อมูล และคอลแบ็ก AddSegment ของเรกคอร์ด FX_DOWNLOADHINTS ก็รับค่าพารามิเตอร์แบบเดียวกัน ทั้งสองพารามิเตอร์นี้เป็นประเภท size_t
IsDataAvail = function(
pThis : PFX_FILEAVAIL;
offset, size: size_t): FPDF_BOOL; cdecl;
AddSegment = procedure(
pThis : PFX_DOWNLOADHINTS;
offset, size: size_t); cdecl;
หากคุณประกาศฟิลด์ออฟเซตเหล่านั้นเป็นประเภทขนาด 32 บิต ตัวเชื่อมโยงจะยังคงทำงานได้ดีบนระบบ Win32 และบน Delphi Win64 แต่จะทำงานล้มเหลวโดยไม่แจ้งเตือนทันทีบน FPC และ Lazarus Win64 สาเหตุเกิดจากความแตกต่างเล็กน้อย: บน FPC Win64 ตัวแปร NativeUInt คือประเภทข้อมูลขนาด 64 บิตที่มีความกว้างเท่าตัวชี้จริง และ size_t ถูกตั้งเป็นชื่อนามแฝง (alias) ให้มัน ในตัวเชื่อมโยงจะมีคำอธิบายในส่วนประเภทข้อมูลเพื่อเตือนถึงกรณีการประกาศซ้ำของ NativeUInt บน FPC เนื่องจากหากเขียนซ้ำให้เป็นนามแฝงขนาด 32 บิต จะส่งผลให้ size_t ถูกบีบให้มีขนาดเหลือ 32 บิตและทำลายพารามิเตอร์ size_t ทุกตัวที่ส่งผ่านหรือบันทึกโดยไลบรารี ค่าออฟเซตขนาด 64 บิตเมื่อวิ่งเข้าสู่พารามิเตอร์ขนาด 32 บิตจะสูญเสียข้อมูลครึ่งบนไป สำหรับไฟล์ขนาดเล็ก ค่าออฟเซตทุกตัวจะยังคงใส่ลงในขนาด 32 บิตได้และไม่มีปัญหาใด ๆ แต่สำหรับไฟล์ขนาดใหญ่ ทันทีที่ออฟเซตมีขนาดเกินเส้นแบ่งสี่กิกะไบต์ ค่าที่ถูกตัดทอนจะชี้ไปยังตำแหน่งอื่นโดยสิ้นเชิง PDFium จะส่งคำถามตรวจสอบช่วงข้อมูลไบต์ที่ผิดพลาด ส่งผลให้การโหลดแบบต่อเนื่องเกิดหยุดชะงักหรืออ่านข้อมูลขยะเข้ามา ข้อบกพร่องนี้จะไม่แสดงอาการจนกว่าไฟล์จะมีขนาดใหญ่เพียงพอและรันบนระบบเป้าหมายที่ size_t ถูกปรับขยายขนาดจริง
ข้อยกเว้นใน Pascal จะต้องไม่คลายข้อมูลผ่านเฟรมภาษา C เป็นอันขาด
ประเภทข้อบกพร่องที่สามเป็นเรื่องเกี่ยวกับระบบจัดการข้อยกเว้น (exception model) ซึ่งในภาษา C ไม่มีระบบนี้ เมื่อ PDFium เรียกใช้คอลแบ็กตัวหนึ่งของคุณ โค้ด Pascal จะทำงานอยู่ภายใต้ลำดับเฟรมสแต็กของภาษา C และ C++ ซึ่งไม่มีทางรู้จักกลไกการจัดการข้อยกเว้นของ Delphi เลย หากคอลแบ็กมีการสร้างข้อผิดพลาดและปล่อยให้ข้อยกเว้นนั้นวิ่งผ่านออกไป มันจะคลายสแต็กข้อมูล (unwind) ผ่านเฟรมที่ไม่ได้ถูกออกแบบมาเพื่อรองรับเรื่องนี้ การล้างข้อมูลของตัว PDFium เองจะไม่ทำงาน ตัวแปรคงที่ภายในจะถูกอัปเดตค้างไว้ครึ่ง ๆ กลาง ๆ และส่งผลให้กระบวนการทำงานเข้าสู่สภาวะที่ไลบรารีไม่เคยคาดคิด ข้อตกลงสำหรับคอลแบ็กเหล่านี้คือการคืนรหัสกลับ (return code) ไม่ใช่การโยนข้อยกเว้น
มีสองคอลแบ็กที่ทำให้เรื่องนี้เห็นภาพชัดเจนคือ FPDF_FILEWRITE ซึ่งเป็นส่วนปลายทางที่ PDFium ใช้บันทึกเอกสาร และ FPDF_FILEACCESS ซึ่งเป็นแหล่งข้อมูลต้นทางที่ใช้อ่านเอกสารนำเข้า ทั้งสองตัวนี้ถูกสร้างขึ้นบนโครงสร้าง TStream ของ Delphi และสามารถทำงานล้มเหลวได้ในทัศนะเดียวกันกับที่สตรีมทั่วไปล้มเหลว: เช่น ดิสก์เต็ม สตรีมถูกปิดทำงานในขณะประมวลผล หรือการอ่านข้อมูลเกินช่วงข้อมูลที่มีอยู่ คอลแบ็กการเขียนจะครอบกระบวนการเขียนสตรีมเอาไว้เพื่อแปลงข้อผิดพลาดใด ๆ ให้เป็นรหัสรายงานความล้มเหลวของ PDFium แทนที่จะปล่อยให้ข้อยกเว้นหลุดออกไป
function WriteBlock(
pThis: PFPDF_FILEWRITE;
pData: Pointer;
Size : LongWord): Integer; cdecl;
begin
// PDFium treats any non-1 return as a write failure. A Pascal exception
// must not unwind through this cdecl/C++ frame, so trap it and report
// failure instead.
Result := 0;
try
PPdfWrite(pThis).Stream.WriteBuffer(pData^, Size);
Result := 1;
except
end;
end;
ส่วนการอ่านข้อมูลก็ทำเช่นเดียวกัน: หากอ่านล้มเหลวจะรายงานค่าศูนย์กลับไปเพื่อให้สอดคล้องกับข้อกำหนดของ FPDF_FILEACCESS แทนที่จะสร้างข้อยกเว้นข้ามขอบเขต การใช้บล็อก except เปล่า ๆ โดยไม่มีการโยนข้อยกเว้นซ้ำ (re-raise) อาจดูเหมือนเป็นวิธีที่ผิดสำหรับโปรแกรมเมอร์ Pascal ที่ถูกสอนมาว่าไม่ควรปิดบังข้อผิดพลาด และหากเขียนในโค้ด Pascal ทั่วไปมันย่อมผิดจริง ๆ แต่บนขอบเขตของ ABI นี่คือโครงสร้างที่ถูกต้องที่สุด เนื่องจากค่าที่ปลอดภัยเพียงอย่างเดียวที่จะส่งกลับไปยังผู้เรียกใช้ภาษา C คือรหัสสถานะที่มันรู้จักวิธีตีความ ข้อความแสดงความผิดพลาดยังคงส่งผ่านออกไปได้โดยผ่านทางรหัสผลลัพธ์ และโค้ดเรียกใช้งานในระดับบนเหนือไลบรารีจะนำมาสร้างเป็นข้อยกเว้น EPdfError เมื่อกระบวนการควบคุมระบบกลับมาอยู่ฝั่งของ Pascal แล้ว
การคืนหน่วยความจำซ้ำที่ซ่อนอยู่ในพาธจัดการข้อผิดพลาด
ข้อบกพร่องประเภทที่สี่เกี่ยวข้องกับการระบุสิทธิ์ความเป็นเจ้าของ ตัวจัดการเอกสาร (document handle) ของ PDFium จะถูกเปิดใช้โดยตัวไลบรารีและจำเป็นต้องถูกปิดใช้งานเพียงครั้งเดียวเท่านั้นผ่านคำสั่ง FPDF_CloseDocument อันตรายจะอยู่ที่พาธการประมวลผลข้อผิดพลาดซึ่งอาจคืนหน่วยความจำตัวจัดการตัวเดียวกับที่ตัวล้างข้อมูลอีกตัวถือครองความเป็นเจ้าของอยู่ ลองนึกถึงรูทีนที่สร้างอ็อบเจกต์ครอบ (wrapper object) มอบหมายสิทธิ์ตัวจัดการเอกสารที่เพิ่งเปิดใหม่ให้มัน และหลังจากนั้นดำเนินการติดตั้งค่าอื่น ๆ ซึ่งอาจทำงานล้มเหลวได้ หากการติดตั้งค่าดังกล่าวโยนข้อผิดพลาดออกมา ตัวจัดการข้อยกเว้นที่สั่งยุติการทำงานก่อนกำหนดที่เรียกใช้ FPDF_CloseDocument กับตัวจัดการดิบจะสั่งปิดมัน และหลังจากนั้นตัวทำลาย (destructor) ของอ็อบเจกต์ครอบเองก็จะปิดมันซ้ำอีกครั้งเมื่อปลดปล่อยอ็อบเจกต์ ตัวจัดการจะถูกส่งคืนหน่วยความจำสองครั้ง ซึ่งส่งผลให้เกิดพฤติกรรมที่ไม่สามารถคาดเดาได้ (undefined behavior) และมักจะทำให้ระบบหยุดทำงานทันที
การตรวจสอบบัญชีพบปัญหานี้ในพาธการนำเข้าเพื่อการจัดหน้า (imposition-style import path) ที่สร้าง TPdf ครอบตัวจัดการที่ถูกเปิดใช้งานไว้แล้ว วิธีแก้ไขคือการกำหนดให้การส่งต่อสิทธิ์การเป็นเจ้าของเป็นแหล่งข้อมูลความจริงเพียงแหล่งเดียว (single source of truth) เมื่อตัวจัดการถูกกำหนดสิทธิ์ให้กับฟิลด์ของอ็อบเจกต์ครอบแล้ว อ็อบเจกต์ครอบจะถือครองความเป็นเจ้าของแต่เพียงผู้เดียว และการล้างข้อมูลอย่างเดียวในพาธจัดการข้อผิดพลาดคือการปลดปล่อยอ็อบเจกต์ครอบเท่านั้น ตัวทำลายของอ็อบเจกต์ครอบจะเรียกใช้ FPDF_CloseDocument ให้คุณโดยอัตโนมัติ ดังนั้นการสั่งปิดอีกครั้งตรง ๆ จะทำให้เกิดการปลดปล่อยเอกสารเดิมซ้ำ ตัวจัดการข้อผิดพลาดที่ได้รับการแก้ไขแล้วจะปลดปล่อยอ็อบเจกต์และทำการโยนข้อผิดพลาดซ้ำ ส่งผลให้มีพาธสั่งปิดที่แท้จริงเพียงพาธเดียว
Result := TPdf.Create(nil);
try
Result.FDocument := NewDoc; // Result now owns the handle
Result.InitializeFormFill;
Result.ReloadPage;
except
// Result.Free closes the handle. A second FPDF_CloseDocument(NewDoc)
// here would double-free the same PDFium document.
Result.Free;
raise;
end;
เรกคอร์ดที่มีการจัดการและไลบรารีที่เต็มไปด้วยฟังก์ชันส่งออก ล้วนจำเป็นต้องมีขั้นตอนการยกเลิกการติดตั้งที่ชัดเจน
ประเภทสุดท้ายเป็นเรื่องเกี่ยวกับหน่วยความจำที่ตัวคอมไพล์ช่วยจัดการให้คุณ ซึ่งความคุ้นเคยจากการเขียนภาษา C อาจส่งผลเสียเงียบ ๆ ฟังก์ชันอำนวยความสะดวกจำนวนมากในตัวเชื่อมโยงนี้จะส่งคืนค่าเป็นเรกคอร์ดที่เก็บสตริงประเภท WideString หรืออาร์เรย์แบบไดนามิก ฟิลด์เหล่านี้คือฟิลด์ข้อมูลที่มีการนับการอ้างอิง (reference-counted) และตัวคอมไพล์จะแอบรันคำสั่งเบื้องหลังเพื่ออัปเดตจำนวนการอ้างอิงนั้น สัญชาตญาณที่คุ้นชินจากภาษา C คือการล้างข้อมูลเรกคอร์ดใหม่ด้วยคำสั่ง FillChar(Result, SizeOf(Result), 0) ซึ่งการทำเช่นนั้นจะเขียนค่าศูนย์ทับตัวชี้ข้อมูลที่มีการจัดการในเรกคอร์ดโดยที่ไม่ได้ทำการลดค่าการอ้างอิงลงก่อน ตัวคอมไพล์จะนำตัวแปรชั่วคราวเบื้องหลังกลับมาใช้งานใหม่สำหรับการคืนค่าฟังก์ชันในการวนซ้ำรอบต่าง ๆ ดังนั้นในการวนซ้ำรอบที่สอง FillChar จะเขียนทับตัวชี้สตริงที่ยังมีข้อมูลอยู่และไม่เคยถูกปล่อยคืนหน่วยความจำ ส่งผลให้สตริงที่มันชี้ไปเกิดปัญหาหน่วยความจำรั่วไหล (memory leak) หากเรียกใช้ฟังก์ชันนี้ในการวนซ้ำกับคำอธิบายประกอบหนึ่งพันรายการ สตริงก็จะรั่วไหลไปถึงหนึ่งพันเส้น
การแก้ไขคือการปล่อยให้ตัวภาษาล้างข้อมูลเรกคอร์ดด้วยวิธีที่มันคุ้นเคย นั่นคือการใช้คำสั่ง Default(T) ซึ่งจะปลดปล่อยฟิลด์ข้อมูลที่มีการจัดการใด ๆ ก่อนที่จะทำการเคลียร์ข้อมูลเป็นศูนย์
// Default() instead of FillChar: the compiler reuses one hidden temp for
// the function result across loop iterations, so FillChar would zero live
// WideString pointers without releasing them.
Result := Default(TPdfAnnotation);
ปัญหาเรื่องสิทธิ์ความเป็นเจ้าของที่เกี่ยวข้องยังพบตรงขอบเขตของการโหลดไลบรารีด้วย ตัวเชื่อมโยงนี้จะทำการแปลงค่าตัวชี้ฟังก์ชันหลายร้อยรายการจากไฟล์ PDFium DLL ด้วยคำสั่ง GetProcAddress หลังจากโหลดด้วย LoadLibrary หากมีฟังก์ชันส่งออกที่จำเป็นแม้เพียงตัวเดียวขาดหายไป สภาวะเชื่อมโยงบางส่วน (partially bound state) จะมีความเสี่ยงสูง: ตัวชี้ส่วนใหญ่ใช้งานได้ในขณะที่ตัวชี้ส่วนที่เหลือเป็นค่า nil หรือข้อมูลค้างเดิม และการเรียกใช้งานครั้งใด ๆ หลังจากนั้นผ่านตัวชี้ดังกล่าวจะกระโดดเข้าไปในโมดูลที่อาจจะถูกสั่งยกเลิกการโหลดไปเรียบร้อยแล้ว ตัวเชื่อมโยงจะจัดการเรื่องนี้โดยการปลดการโหลดไลบรารีและเรียกใช้ฟังก์ชัน ClearAllBindings ทั้งหมดเพื่อรีเซ็ตตัวชี้การนำเข้าข้อมูลทุกตัวกลับเป็น nil ทุกครั้งที่ขั้นตอนการแปลงฟังก์ชันส่งออกที่จำเป็นล้มเหลว หลังจากนั้นจะไม่มีตัวชี้ฟังก์ชันใด ๆ ค้างอยู่ในโมดูลที่สั่งปิดการโหลดไปแล้ว และการเรียกใช้หลังจากนั้นจะรายงานข้อผิดพลาดอย่างหมดจดด้วยการตรวจสอบตัวชี้เป็น nil แทนที่จะเป็นการแยกคำสั่งเข้าไปในโค้ดที่คืนหน่วยความจำไปแล้ว
ตัวครอบคือจุดที่ข้อตกลงสี่ประการถูกประกาศใหม่ด้วยตนเอง
ข้อบกพร่องทั้งห้าจุดนี้ไม่ใช่เรื่องประหลาดใด ๆ แต่มันเป็นสภาวะความล้มเหลวที่สามารถคาดการณ์ได้อยู่แล้วเมื่อเขียนโค้ด Pascal ครอบ API ภาษา C บาง ๆ และข้อผิดพลาดจะมากระจุกตัวรวมกันเนื่องจากชั้นครอบบาง ๆ นี้คือจุดที่ต้องเขียนประกาศข้อตกลงที่แยกเฉพาะสี่ประการด้วยตนเอง ข้อตกลงการเรียกใช้งานจะต้องระบุคำว่า cdecl ในคอลแบ็กทุกตัว ความกว้างของจำนวนเต็มต้องตรงกับ size_t บนระบบเป้าหมายเฉพาะที่ข้อมูลขยายขนาดจริง ระบบจัดการข้อยกเว้นจะต้องแปลงเป็นรหัสส่งกลับค่าในทุกคอลแบ็กที่วิ่งผ่านพ้นจาก Pascal และสิทธิ์ความเป็นเจ้าของในตัวจัดการและฟิลด์ข้อมูลที่มีการจัดการทุกตัวจะต้องประกาศชัดเจนและปฏิบัติตามในทุกเส้นทางประมวลผล รวมถึงพาธรับมือข้อผิดพลาดที่ไม่มีใครรันการทดสอบเลยจนกระทั่งผลิตชิ้นงานจริง หากพลาดจุดใดไปแม้เพียงจุดเดียว คุณจะได้ข้อบกพร่องที่แสดงอาการห่างไกลจากจุดที่เป็นสาเหตุจริง ๆ ซึ่งส่งผลให้ข้อบกพร่องประเภทนี้มีราคาแพง คุณค่าของการตรวจสอบทางบัญชีจึงไม่ใช่เรื่องของการแก้ไขเพียงจุดเดียว แต่เป็นการกำหนดให้แต่ละประเด็นข้างต้นเป็นระเบียบวิธีตรวจสอบในทุกส่วนของตัวเชื่อมโยง
หากคุณต้องการดูการทำงานจริงของตัวเชื่อมโยงมากกว่าการตรวจสอบขอบเขตความปลอดภัย เทคนิคแคชการแสดงผลและการซูมใน บันทึกของเราเกี่ยวกับแคชการแสดงผลและประสิทธิภาพการซูม จะแสดงให้เห็นขั้นตอนการแสดงผล และบทความขั้นตอนการแปลคอมไพเลอร์ข้ามแพลตฟอร์มใน การสร้างโปรแกรมแสดงผลบน Lazarus และ FPC คือจุดที่พฤติกรรม size_t ของ Win64 ที่อธิบายไว้ในบทความนี้มีความสำคัญจริง ทั้งคู่ถูกพัฒนาขึ้นบนการทำงานด้านความปลอดภัยของหน่วยความจำและระบบ ABI แบบเดียวกันที่มีพร้อมใช้งานใน PDFium Component สำหรับ Delphi, Lazarus และ C++Builder ร่วมกับ API การแสดงผล การแยกข้อความ และการกรอกฟอร์มที่ระบุไว้ในบล็อกนี้