פתרון תחילת תוכנית והטוען¶
ה־breakpoint בבנאי הגלובלי מגיע לפני main. המחסנית אמורה להראות את מסלול ה־CRT שמפעיל את האתחולים שהמהדר אסף. השמות המדויקים תלויים בגרסת הכלים, ולכן הדוח צריך לשמור את ה־PDB ואת ה־image ששימשו לניסוי.
הלקוח משתמש בחתימות הבאות אחרי פתרון exports. הן חייבות להתאים לרוחב size_t ולמוסכמת הקריאה של אותה ארכיטקטורה.
using CreateContext = int (__cdecl*)(std::size_t, void**) noexcept;
using DestroyContext = void (__cdecl*)(void*) noexcept;
הקטע הבא משתלב בלקוח מהשיעור 3.5 אחרי טעינה מוצלחת. יש לכלול גם <cstddef>. הוא מחליף את הקריאה ל־scale_checked.
const auto create = reinterpret_cast<CreateContext>(
GetProcAddress(module, "context_create"));
const auto destroy = reinterpret_cast<DestroyContext>(
GetProcAddress(module, "context_destroy"));
if (!create || !destroy) {
FreeLibrary(module);
return 1;
}
void* context = nullptr;
const int status = create(4096, &context);
if (status != 0) {
FreeLibrary(module);
return 1;
}
destroy(context);
context = nullptr;
FreeLibrary(module);
הקיבולת אפס נדחית לפני הקצאה, והפלט מאופס. קיבולת מעל המגבלה נדחית באותו אופן. קיבולת חוקית עדיין יכולה להיכשל בגלל מחסור בזיכרון, ולכן אין להניח שהבדיקה המבנית מבטיחה הצלחה.
במעגל ההמתנה, ה־thread הראשון מחזיק את loader lock בזמן DllMain ומחכה ל־thread השני. ה־thread השני אינו יכול להשלים את מסלול האתחול שנדרש לפני קוד המשתמש שלו כל עוד הטוען חסום. הפתרון אינו timeout ארוך יותר, אלא הוצאת יצירת העבודה וההמתנה לשלב מפורש שאחרי הטעינה.
ממשק תצורה משופר יכול לקבל bytes שכבר נקראו בידי הלקוח, או נתיב ולבצע את הקריאה בתוך context_create. הלקוח מקבל קוד שגיאה לפני שיש מופע שמיש. כל משאב זמני מוחזק ב־RAII, והבעלות על המופע מועברת רק אחרי השלמת כל השלבים.
הבחירה מונעת מהטעינה עצמה להפוך לפעולה ארוכה עם כשלים עסקיים שקשה להציג. היא גם מאפשרת ליצור שני מופעים עם תצורות שונות ולבדוק כשל בלי להפעיל מחדש את כל התהליך.