Ketika pdfium.dll menolak untuk dimuat di mesin pelanggan, Komponen PDFium untuk Delphi dan Lazarus mengubah kesalahan asli yang samar menjadi penyebab spesifik. Ratin CheckLoadLibrary membaca ERROR_BAD_EXE_FORMAT (193) as a 32/64-bit architecture mismatch, and CheckGetProcAddress reports a missing export as a pdfium.dll that is older than the binding. Kedua pesan tersebut menyebutkan perbaikannya daripada membiarkan Anda menebak-nebak
Hal itu penting karena kegagalan DLL asli terjadi dalam tiga cara berbeda, dan teks sistem operasi mentah menggabungkan semuanya. Arsitekturnya bisa salah, biner yang diterapkan bisa terlalu lama untuk pengikatan Pascal yang memanggilnya, atau dua thread dapat saling mendahului (race condition) untuk mengikatnya pada saat yang sama. Masing-masing memiliki perbaikan yang berbeda, dan tujuan utama dari diagnostik yang ditambahkan dalam pengerjaan siklus hidup pemuatan v2.11.0 adalah untuk memberi tahu Anda mana yang sebenarnya sedang Anda hadapi
Mengapa pdfium.dll gagal dimuat dengan kesalahan format EXE yang buruk?
Kesalahan Windows 193, ERROR_BAD_EXE_FORMAT, berarti file ditemukan dan dibuka tetapi jenis mesin PE-nya tidak cocok dengan proses host. Executable 64-bit tidak dapat memuat pdfium.dll 32-bit, dan executable 32-bit tidak dapat memuat yang 64-bit. Karena file tersebut ada, insting untuk berburu DLL yang hilang justru akan mengirim Anda ke arah yang salah
Komponen PDFium mencegat kasus ini di CheckLoadLibrary: ketika LoadLibrary mengembalikan handel null dan GetLastError adalah 193, ia menambahkan petunjuk bahwa DLL ditemukan tetapi arsitekturnya tidak cocok dengan proses host, dan ia menunjuk ke build DLLs/Win32 atau DLLs/Win64 yang cocok. Subdirektori dipilih oleh BuildDllSubDir, yang mengembalikan Win64 ketika IsWin64 bernilai true dan Win32 jika sebaliknya. Terapkan DLL di bawah pohon direktori yang sebenarnya dicari oleh pengikatan, dan ketidakcocokan tersebut akan hilang
uses
SysUtils, PDFium;
procedure OpenDocument(const AFileName: string);
var
Pdf: TPdf;
begin
// Deploy pdfium.dll next to the executable, matched to the build target:
// <AppDir>\DLLs\Win32\pdfium.dll for a 32-bit host process
// <AppDir>\DLLs\Win64\pdfium.dll for a 64-bit host process
Pdf := TPdf.Create(nil);
try
Pdf.FileName := AFileName;
try
Pdf.Active := True; // first use lazily binds pdfium.dll
except
on E: EPdfError do
// The message already names the real cause: a 32/64-bit mismatch
// (ERROR_BAD_EXE_FORMAT) or a pdfium.dll older than this binding
raise Exception.CreateFmt('PDF engine did not start: %s', [E.Message]);
end;
if Pdf.Active then
RenderFirstPage(Pdf);
finally
Pdf.Free;
end;
end;
Apa yang ditunjukkan oleh ekspor PDFium yang hilang kepada Anda?
Kelas kegagalan kedua adalah perbedaan versi (version skew). PDFium mengirimkan ekspor baru dari waktu ke waktu, dan pengikatan Pascal menyelesaikan setiap fungsi yang dibutuhkannya melalui CheckGetProcAddress selama LoadLibrary. Jika ekspor yang diperlukan tidak ada, pengikatan tersebut lebih baru daripada pdfium.dll yang ada di disk, dan diocese yang jujur adalah bahwa DLL yang diterapkan sudah usang dan bukan rusak. Fungsi CheckGetProcAddress menyatakan hal itu secara tepat: ia memunculkan EPdfError yang melaporkan bahwa pdfium.dll yang diterapkan lebih lama dari build pengikatan PDFiumPas ini, dan ia menyebutkan jalur DLLs/<subdir> tempat biner yang cocok berada
Dua detail membuat jalur tersebut kokoh. Pertama, CheckGetProcAddress memanggil UnloadLibrary sebelum memunculkan kesalahan, sehingga pengikatan parsial tidak pernah meninggalkan penunjuk fungsi yang setengah terselesaikan untuk mengacaukan upaya berikutnya. Kedua, nama DLL yang dilaporkannya berasal dari BuildDllName, yang mengembalikan pdfium.dll secara normal dan pdfium.v8.dll ketika bendera global EnableV8Engine diaktifkan. Jika aplikasi Anda mengaktifkan engine V8 JavaScript untuk formulir XFA atau formulir berbasis skrip, teks kesalahan akan menunjuk ke build V8, bukan yang biasa, sehingga Anda dapat mengganti file yang tepat pada kesempatan pertama
Apakah DLL PDFium aman untuk dimuat dari thread latar belakang?
Memuat pustaka sekarang aman dari thread mana pun; memanggil API PDFium dari beberapa thread masih belum aman. Itu adalah dua jaminan terpisah, dan menjaga keduanya tetap terpisah adalah perbedaan antara kumpulan pekerja (worker pool) yang stabil dan crash yang terjadi sesekali. Perenderan latar belakang biasanya menjalankan setiap render halaman melalui pekerja TPdfFuture<T>, dan future pertama yang menyentuh TPdf is what triggers the lazy bind. Tanpa perlindungan, dua pekerja dapat mengamati pustaka yang belum dimuat, keduanya menjalankan urutan pengikatan, menimpa handel modul, dan membocorkan pemuatan pertama
Pekerjaan v2.11.0 menutup celah tersebut dengan PDFiumLoadLock, sebuah TRTLCriticalSection global proses yang membungkus entri ke LoadLibrary dan UnloadLibrary. Bagian kritis tersebut membuat urutan periksa-lalu-muat menjadi atomik, sehingga tidak ada dua thread yang dapat melihat Loaded=False dan melakukan pengikatan ganda, dan tidak ada thread yang dapat membebaskan DLL saat thread lain sedang berada di tengah-tengah pengikatan. Kunci (lock) dibuat di bagian initialization unit dan dilepaskan di finalization, dijaga oleh bendera PDFiumLoadLockReady sehingga pemasangan tersebut aman bahkan selama penutupan sistem. Jika Anda memerlukan pola pekerja-dan-balasan lengkap dengan pembatalan, artikel pendamping tentang perenderan latar belakang dengan cancellable futures membahasnya dari ujung ke ujung
uses
PDFium, FPdfAsync;
// Worker method runs on a background thread. The first future to bind
// pdfium.dll is serialised by PDFiumLoadLock, so a second concurrent
// worker cannot double-bind or leak the module handle.
function TReportForm.RenderThumbnail(
const AToken: IPdfCancellationToken): TBitmap;
var
Pdf: TPdf;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'report.pdf';
Pdf.Active := True; // safe concurrent bind
AToken.ThrowIfCancelled;
Result := Pdf.Thumbnail; // this TPdf is owned by one thread only
finally
Pdf.Free;
end;
end;
procedure TReportForm.StartRender;
begin
TPdfFuture<TBitmap>.Run(RenderThumbnail, ThumbnailReady);
end;
procedure TReportForm.ThumbnailReady(
const AResult: TPdfFutureResult<TBitmap>);
begin
if AResult.IsSuccess then
Preview.Picture.Assign(AResult.Value);
end;
Apa yang tidak dilindungi oleh kunci pemuatan (load lock)
Batasan di sini layak untuk dinyatakan secara jelas, karena mudah untuk salah menafsirkan perbaikan tersebut secara berlebihan. PDFiumLoadLock hanya menserialisasikan status pengikatan global: handel modul dan penetapan penunjuk fungsi FPDF_*. API C PDFium yang mendasarinya masih belum thread-safe, persis seperti yang dinyatakan oleh header upstream, sehingga memanggil fungsi FPDF_* atau berbagi satu FPDF_DOCUMENT di seluruh thread tetap menjadi tanggung jawab Anda untuk menserialisasikannya. Bentuk yang aman adalah yang di atas: setiap pekerja memiliki TPdf-nya sendiri dan tidak pernah menyerahkan dokumen aktif ke thread lain. Untuk pembahasan yang lebih mendalam tentang batasan tersebut dan aturan ABI di sekitarnya, lihat memperkeras pengikatan PDFium VCL terhadap kegagalan ABI dan memori
Mengapa finalisasi dan FPDF_DestroyLibrary itu penting
Celah yang lebih halus berada di jalur penutupan (shutdown path). Unit PDFium tidak memiliki bagian finalization, yang berarti FPDF_DestroyLibrary tidak pernah berjalan saat keluar dari proses; sistem operasi mereklamasi memori DLL dan melewatkan pembersihan milik PDFium sendiri, yaitu penggabungan thread pekerja (worker-thread join), pembuangan isolat V8 ketika EnableV8Engine diaktifkan, serta pembongkaran font dan cache. Pada beban kerja perenderan biasa, itu adalah kebocoran yang tidak bersuara saat pembongkaran, tetapi dengan engine V8 diaktifkan, ia membocorkan isolat JavaScript setiap kali dijalankan, yang muncul dengan cepat di bawah otomatisasi berulang
Bagian finalization sekarang memanggil UnloadLibrary, yang memanggil FPDF_DestroyLibrary dan membebaskan modul. Ini berada di lingkup unit karena suatu alasan: TPdf.Destroy hanya mengatur Active := False untuk menutup dokumen saat ini, dan ia sengaja tidak membongkar pustaka global, sehingga aplikasi multi-instansi mempertahankan satu pengikatan PDFium bersama tetap hidup selama masa pakai seluruh proses. Menempatkan pembongkaran di finalization berarti pustaka bersama dirilis tepat sekali, ketika unit dibongkar, dan UnloadLibrary memeriksa bendera Loaded-nya terlebih dahulu sehingga panggilan tersebut merupakan no-op yang tidak berbahaya jika PDFium tidak pernah digunakan
Daftar periksa penerapan singkat
Tiga kebiasaan mencegah hampir setiap kegagalan pemuatan di lapangan. Cocokkan arsitektur DLL dengan proses host dan kirimkan di bawah DLLs/Win32 atau DLLs/Win64; jaga agar pdfium.dll tetap sejalan dengan versi pengikatan sehingga tidak ada ekspor yang diperlukan yang hilang; dan jangan pernah membagikan TPdf atau handel dokumen di seluruh thread, meskipun pengikatan itu sendiri sekarang bersifat atomik. Jika Anda membangun sumber yang sama untuk Delphi dan Lazarus, perbedaan pengemasan yang mengacaukan penerapan lintas-target dibahas dalam catatan tentang jebakan kompiler silang Delphi dan FPC. Diagnostik, kunci pemuatan, dan pembersihan finalisasi yang dijelaskan di sini semuanya didistribusikan di PDFium Component untuk Delphi and C++Builder