גשר COM: אפליקציית 32-bit קוראת ל-DLL של 64-bit

· עודכן בתאריך: · · COM, פיתוח Windows, 32bit, 64bit

היסטוריית עדכונים (גרסה ראשונה, פורסמה בתאריך 25 Jan 2026)
פרסום ראשון
לצטט את המאמר הזה(DOI: 10.5281/zenodo.22173251)

מאמר זה מאוחסן בארכיון Zenodo. להלן גם ה-DOI שתמיד מפנה לגרסה האחרונה וגם ה-DOI המקובע לגרסה שאתם קוראים.

Go Komura (2026). גשר COM: אפליקציית 32-bit קוראת ל-DLL של 64-bit. KomuraSoft LLC. https://doi.org/10.5281/zenodo.22173251 https://comcomponent.com/he/blog/com-case-study-32bit-to-64bit/

DOI (הגרסה האחרונה)
10.5281/zenodo.22173251
DOI (הגרסה הזו)
10.5281/zenodo.22173252

הדרישה לקרוא מאפליקציית 32-bit ל-DLL של 64-bit היא די טיפוסית ב-Windows. במיוחד כשרוצים להשתמש רק בפונקציונליות של צד 64-bit תוך שמירה על legacy קיים, מבנה של גשר COM נוטה להיות פתרון מעשי.

קהל היעד: מי שמתחזק אפליקציית Windows קיימת של 32-bit ורוצה להשתמש ב-DLL או בספרייה של צד 64-bit. כתוב כך שאפשר לקרוא גם אם רק “שמעתם על COM אבל מעולם לא בניתם דבר בעצמכם”.

סביבה נדרשת: גרסת 64-bit של Windows (x64), וסביבת פיתוח שיכולה לכתוב C# (כמו Visual Studio). רישום שרת COM עבור כל המחשב (תחת HKEY_LOCAL_MACHINE) דורש הרשאות Administrator. את התפיסה הבסיסית של COM סיכמנו במאמר “מה זה COM — למה התכנון של Windows COM עדיין מחזיק”.

תוכן עניינים

  1. התרחיש
  2. שיטת הפתרון
  3. זרימת העיבוד (sequence diagram)
  4. קוד לדוגמה (רעיון)
  5. קוד לדוגמה מלא
  6. סיכום
  7. מקורות

ב-diagram, solid line מציינת relation שתמיד מתקיים ו-dashed line מציינת relation מותנה (התנאים מופיעים בהסבר של כל relation ב-detail page). הרשימה המלאה של ה-relations (סה”כ 19, כולל evidence ו-certainty) וההגדרות של ה-concepts המרכזיים נמצאות ב-detail page של ה-knowledge map (ביפנית). Data: JSON-LD / Turtle

1. התרחיש

מקרה שבו רוצים להשאיר את האפליקציה הקיימת של 32-bit כמות שהיא, אבל להשתמש בעיבוד של DLL בן 64-bit. אלא ש-process של 32-bit לא יכול לטעון DLL של 64-bit. זו מגבלה ברמת ה-OS, לא משהו שניתן לפתור בטריק.

מצבים נפוצים:

  • האפליקציה הקיימת של 32-bit היא נכס גדול, ואי אפשר להעביר אותה במהירות
  • יש פונקציונליות חדשה בצד ה-DLL של 64-bit, או שספריות התלות הן 64-bit בלבד
  • רוצים לקרוא מצד ה-32-bit בצורה typed

בשילוב הזה, הדרך לקרוא באותו process חסומה מלכתחילה.

התרחיש הנתוןאפליקציית 32-bit קיימת רוצה להשתמש בפונקציונליות של DLL בן 64-bit, אבל process של 32-bit לא יכול לטעון DLL של 64-bit בגלל מגבלה ברמת ה-OS, כך שהקריאה באותו process חסומה.אפליקציית 32-bit קיימתרוצה להשתמש ב-DLL של 64-bitאי אפשר לטעון באותו processמגבלת OS, אין עקיפה

איור 1: process של 32-bit לא יכול לטעון DLL של 64-bit, ולכן אין מלכתחילה דרך לקרוא באותו process.

2. שיטת הפתרון

מכאן ואילך מופיע מינוח מקצועי, אז נסדר קודם כמה מילים מינימליות.

מונח משמעות
In-proc COM (DLL server) טוענים את רכיב ה-COM באותו process כמו ה-caller. מהיר, אבל לא נטען אם ה-bitness לא תואם
Out-of-proc COM (EXE server) מפעילים את רכיב ה-COM כ-process נפרד. אפשר לשתף פעולה גם כשה-bitness שונה
LocalServer שרת COM שרץ כ-process נפרד באותו מחשב. רושמים את הנתיב ל-EXE במפתח LocalServer32 ב-Registry
IDL / TypeLib ההגדרה (IDL) של צורת ה-interface (שמות methods, טיפוסי ארגומנטים), וגרסתו הבינארית (TypeLib). משמשים כדי ששני הצדדים יראו את אותו “חוזה”
Marshaling packing מחדש של ארגומנטים וערכים מוחזרים לצורה שאפשר לשלוח, כדי לחצות גבול process. ההפך הוא unmarshaling
Proxy / Stub קוד ייצוג שמבצע בפועל את ה-marshaling. Proxy עומד בצד ה-caller, Stub עומד בצד השרת
WOW6432Node המיקום הפיזי שבו מאוחסן תוכן ה-Registry עבור אפליקציות 32-bit ב-Windows של 64-bit. אותו שם מפתח מכיל תוכן שונה בצד 32-bit ובצד 64-bit

יסוד הפתרון הוא הפרדה עם Out-of-proc COM (EXE server). קוראים ל-DLL של 64-bit מתוך שרת COM (EXE) של 64-bit, ואפליקציית ה-32-bit משתמשת בו דרך COM.

המבנה הבסיסי של גשר COMאפליקציית 32-bit קוראת דרך COM לשרת COM של 64-bit בתור EXE, והשרת קורא פנימית ל-DLL של 64-bit.קורא דרך COMקורא פנימיתאפליקציית 32-bitשרת COM של 64-bit (EXE)DLL של 64-bit

איור 2: את ה-DLL של 64-bit ממקמים בתוך שרת EXE של 64-bit, ואפליקציית 32-bit משתמשת בשרת הזה דרך COM.

הזרימה היא כדלקמן.

  1. מכינים COM LocalServer (EXE) של 64-bit, שקורא פנימית ל-DLL של 64-bit
  2. משתפים COM interface (IDL/TypeLib) וחושפים את הטיפוסים
  3. אפליקציית ה-32-bit קוראת ל-COM בצורה typed (תקשורת דרך Proxy/Marshal)

עם זאת, יש גם נקודות לתשומת לב.

  • הרישום של 32-bit ו-64-bit נפרד (כולל WOW6432Node)
  • structs מותאמים דורשים תכנון marshaling
  • יש תקורת IPC, ולכן צריך להיזהר מקריאות בתדירות גבוהה

במילים אחרות, הדרך הסטנדרטית היא “להעביר את העיבוד של 64-bit ל-process נפרד, ולגשר עם COM”.

שלושת שלבי בניית הגשרמכינים COM LocalServer של 64-bit, חושפים את הטיפוסים ב-IDL/TypeLib, ואפליקציית 32-bit קוראת בצורה typed — שלושה שלבים שמרכיבים את הגשר.הכנת COM LocalServer של 64-bitחשיפת טיפוסים ב-IDL / TypeLibאפליקציית 32-bit קוראת בצורה typedהתקשורת דרך Proxy / Marshal

איור 3: שלושת השלבים — הכנת ה-LocalServer, חשיפת הטיפוסים, והקריאה ה-typed — יוצרים יחד את הגשר.

אם מה שרוצים להשתמש בו בצד ה-64-bit הוא מלכתחילה שרת COM מסוג in-proc (DLL שנרשם ב-InprocServer32), לפעמים אפשר לוותר על כתיבת שרת EXE. מצמידים ל-CLSID ערך AppID, וכותבים תחת מפתח ה-AppID הזה ערך DllSurrogate של מחרוזת ריקה — ואז ה-DLL נטען ב-surrogate process שמגיע עם Windows (עבור DLL של 64-bit זה System32\dllhost.exe), ומבחינת client 32-bit הוא נראה כשרת COM מסוג out-of-proc שרץ ב-process נפרד (לא רושמים כאן נתיב של EXE ב-LocalServer32 — להפך, אם LocalServer32 קיים, ה-surrogate לא בשימוש). גם בכיוון ההפוך (שימוש ב-COM DLL של 32-bit מתוך אפליקציית 64-bit) המנגנון זהה, וה-bitness של ה-surrogate שעולה נקבע לפי ה-DLL ולא לפי ה-client. אבל אם מה שרוצים לקרוא לו הוא סתם DLL native, כמו במאמר הזה, אין מלכתחילה שרת COM שאפשר לטעון ב-surrogate, ולכן צריך את שיטת שרת ה-EXE שמתוארת מכאן והלאה. את שלבי הרישום סיכמנו ב-3.5 של Registration and Bitness Pitfalls in COM/OCX/ActiveX Development (שם ההנחה היא DLL של 32-bit, אז צריך רק להחליף את ה-view שאליו כותבים את ערך ה-AppID), ואת קו ההפרדה בין surrogate שמספיק לבין כתיבת EXE משלכם סיכמנו ב-5.2 של ActiveX / OCX: להשאיר, לעטוף או להחליף.

3. זרימת העיבוד (sequence diagram)

להלן הזרימה כאשר אפליקציית 32-bit קוראת לעיבוד של DLL בן 64-bit.

תשתית ה-marshaling של COM הרשומה מטפלתDLL של 64-bitשרת COM של 64-bit(EXE)COM Stub(צד 64-bit)RPC/IPC(IPC)COM Proxy(צד 32-bit)client 32-bitDLL של 64-bitשרת COM של 64-bit(EXE)COM Stub(צד 64-bit)RPC/IPC(IPC)COM Proxy(צד 32-bit)client 32-bitmarshaling לפרמטריםunmarshaling לפרמטריםmarshaling לערך המוחזרunmarshaling לערך המוחזרICalcService.Add(1, 2)נתונים serializedמועברים דרך גבול ה-processAdd(1, 2)קריאה לפונקציה nativeתוצאה: 3תוצאה: 3תוצאה serializedמועברת דרך גבול ה-processתוצאה: 3

איור 4: הקריאה מאפליקציית 32-bit עוברת דרך Proxy, IPC ו-Stub אל שרת ה-64-bit וה-DLL, והתוצאה חוזרת באותו מסלול.

נקודות מרכזיות:

  • אפליקציית 32-bit יכולה לקרוא type-safe דרך ה-interface ICalcService
  • ה-COM runtime חוצה את גבול ה-process עם DLL רשום של Proxy/Stub, TypeLib marshaler, marshaler סטנדרטי ועוד
  • בגלל תקורה של IPC, עדיף עיבוד באצווה על פני קריאות קטנות
עקרון גודל הקריאהל-IPC יש תקורה, כך שקריאות קטנות וחוזרות מצטברות, ואילו ריכוז לעיבוד באצווה מקטין את ההשפעה — וזו הגישה המועדפת.תקורת IPCקריאות קטנות חוזרות מצטברותריכוז לעיבוד באצווה מקטין השפעהזו הגישה המועדפת

איור 5: לכל חציית גבול process יש עלות, ולכן עדיף לרכז את הקריאות לגודל גס.

4. קוד לדוגמה (רעיון)

4.1. interface משותף, שרת ו-client

זהו רעיון עקרוני בלבד. כדי שזה יעבוד בפועל, יידרש גם הרישום שמתואר בהמשך בסעיף 4.2.

// interface משותף (שקול ל-IDL)
[ComVisible(true)]
[Guid("7A4B5B23-0A2F-4D2B-9D4D-8A2A92B8B001")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ICalcService
{
    int Add(int a, int b);
}

// COM LocalServer של 64-bit (צד ה-EXE)
[ComVisible(true)]
[Guid("1C9B6F4D-1E9A-4E61-9A4F-6A0F1D2D9A11")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("KomuraSoft.CalcService")]
public class CalcService : ICalcService
{
    public int Add(int a, int b)
    {
        // כאן קוראים ל-DLL של 64-bit
        return a + b;
    }
}

// צד אפליקציית 32-bit (ה-client)
Type t = Type.GetTypeFromProgID("KomuraSoft.CalcService");
var calc = (ICalcService)Activator.CreateInstance(t);
int result = calc.Add(1, 2);

בצורה הזו, צד ה-32-bit יכול לטפל בזה בצורה typed. COM משתמש פנימית ב-Proxy/Stub וקורא דרך IPC בשבילכם.

הסיבה שמוסיפים [ProgId("KomuraSoft.CalcService")] היא כדי שה-client יוכל למצוא אותו עם Type.GetTypeFromProgID("KomuraSoft.CalcService"). ProgID הוא רק “כינוי קריא לבני אדם”, ומה שבאמת מוצא את השרת הוא רישום ה-CLSID שיוסבר בהמשך.

מ-ProgID עד הגעה לשרתה-ProgID שה-client מציין הוא רק כינוי קריא לבני אדם, שממנו נגזר CLSID, ורישום ה-CLSID הוא מה שמאתר בפועל את השרת.ProgID (כינוי קריא לבני אדם)רישום CLSIDהשרת נמצא

איור 6: ProgID הוא כינוי הכניסה, אבל מה שמצביע בפועל על השרת הוא רישום ה-CLSID.

4.2. שלבי הרישום המינימליים

COM הוא מנגנון שבו “ה-COM runtime שולף CLSID רשום ב-Registry ומפעיל אותו”, ולכן קוד שלא נרשם לעולם לא יעבוד (Type.GetTypeFromProgID יחזיר null, או ש-CreateInstance יחזיר REGDB_E_CLASSNOTREG). הרישום הדרוש עבור שרת EXE (LocalServer) מצטמצם בסופו של דבר לשלושה מפתחות בלבד.

מה נרשם מפתח ערך
התאמת ProgID → CLSID HKEY_CLASSES_ROOT\KomuraSoft.CalcService\CLSID {1C9B6F4D-1E9A-4E61-9A4F-6A0F1D2D9A11}
CLSID → נתיב ה-EXE HKEY_CLASSES_ROOT\CLSID\{1C9B6F4D-…}\LocalServer32 הנתיב המלא ל-EXE של שרת COM בן 64-bit
CLSID → חיפוש הפוך ל-ProgID HKEY_CLASSES_ROOT\CLSID\{1C9B6F4D-…}\ProgID KomuraSoft.CalcService

כאן מסתתרת המלכודת שהיא ממש נושא המאמר הזה. התיעוד של Microsoft קובע במפורש ש-HKEY_LOCAL_MACHINE\SOFTWARE\Classes משותף בין אפליקציות 32-bit ל-64-bit, בעוד שתת-המפתח CLSID שתחתיו (וגם Interface וכדומה) נפרד בין צד 32-bit לצד 64-bit (הישות הפיזית בצד 32-bit היא WOW6432Node). כלומר, מפתח ה-ProgID נראה משני הצדדים לאחר כתיבה אחת בלבד, אבל אם רישום ה-CLSID לא נכתב גם ב-32-bit view וגם ב-64-bit view, client 32-bit לא ימצא את השרת.

החלק המשותף והחלק הנפרד ב-Registryמפתח ה-ProgID תחת Classes נראה משני הצדדים אחרי כתיבה אחת, בעוד תת-המפתח CLSID נפרד בין 32-bit view ל-64-bit view ודורש כתיבה לשתיהן, וכשחסר צד 32-bit השרת לא נמצא מ-client 32-bit.מפתח ProgID (תחת Classes)נראה משני הצדדיםרישום תחת CLSIDנכתב ב-64-bit viewנכתב ב-32-bit viewבפועל נמצא ב-WOW6432Nodeחסר = לא נראה מ-32-bit

איור 7: מפתח ה-ProgID משותף, אבל תת-המפתח CLSID נפרד לפי 32-bit/64-bit view, ולכן צריך לרשום בשתיהן.

הדרך הבטוחה היא להשתמש בדגלים /reg:32 ו-/reg:64 של הפקודה reg בשורת פקודה עם הרשאות Administrator (Microsoft ממליצה שלא לכתוב את Wow6432Node בעצמכם בנתיב).

:: הריצו בשורת פקודה עם הרשאות Administrator
set CLSID={1C9B6F4D-1E9A-4E61-9A4F-6A0F1D2D9A11}
set PROGID=KomuraSoft.CalcService
set SERVER=C:\Program Files\KomuraSoft\CalcServer.exe

:: 1) ProgID → CLSID (HKLM\SOFTWARE\Classes ישירות משותף בין 32/64)
reg add "HKLM\SOFTWARE\Classes\%PROGID%\CLSID" /ve /d "%CLSID%" /f

:: 2) CLSID → LocalServer32 ו-ProgID (CLSID נפרד בין 32/64, לכן כותבים לשניהם)
::    בערך של LocalServer32 יש להכניס את הנתיב לקובץ ההרצה כולל המרכאות.
::    אם כותבים /d "%SERVER%", המרכאות נעלמות בניתוח הארגומנטים של reg.exe, והערך
::    נשמר כ-C:\Program Files\... בלבד. COM מפרש זאת כשורת פקודה
::    ולכן הוא מחפש קודם את C:\Program.exe, שנחתך לפני הרווח
reg add "HKLM\SOFTWARE\Classes\CLSID\%CLSID%\LocalServer32" /ve /d "\"%SERVER%\"" /f /reg:64
reg add "HKLM\SOFTWARE\Classes\CLSID\%CLSID%\ProgID"        /ve /d "%PROGID%"     /f /reg:64
reg add "HKLM\SOFTWARE\Classes\CLSID\%CLSID%\LocalServer32" /ve /d "\"%SERVER%\"" /f /reg:32
reg add "HKLM\SOFTWARE\Classes\CLSID\%CLSID%\ProgID"        /ve /d "%PROGID%"     /f /reg:32

:: בדקו את הערך שנשמר. אם מוצג "C:\Program Files\..." עם מרכאות, זה תקין
reg query "HKLM\SOFTWARE\Classes\CLSID\%CLSID%\LocalServer32" /ve /reg:64

המרכאות ב-LocalServer32 אינן רק עניין של סדר וניקיון. בלי מרכאות, C:\Program Files\... ניתן לפירוש כ”C:\Program עם Files\... כארגומנט”. כתוצאה מכך, בסביבה שבה מישהו יכול ליצור קובץ C:\Program.exe, הוא עלול להיות מופעל תחילה. אם הנתיב מכיל רווחים, יש להוסיף מרכאות תמיד.

איך הפרשנות משתנה לפי מרכאות ב-LocalServer32אחסון הנתיב בלי מרכאות ב-LocalServer32 מאפשר פרשנות שנחתכת לפני הרווח, כך שקובץ בשם C:\Program.exe עלול לרוץ קודם, בעוד אחסון עם המרכאות מפעיל את קובץ ה-EXE המיועד.אוחסן בלי מרכאותאפשרית פרשנות שנחתכת לפני הרווחC:\Program.exe עלול לרוץ קודםאוחסן עם המרכאותה-EXE המיועד רץ

איור 8: רישום נתיב עם רווח בלי מרכאות פותח פתח להפעלת קובץ הרצה אחר.

ביטול הרישום מתבצע פשוט על ידי מחיקת אותם מפתחות.

set CLSID={1C9B6F4D-1E9A-4E61-9A4F-6A0F1D2D9A11}
set PROGID=KomuraSoft.CalcService

reg delete "HKLM\SOFTWARE\Classes\CLSID\%CLSID%" /f /reg:64
reg delete "HKLM\SOFTWARE\Classes\CLSID\%CLSID%" /f /reg:32
reg delete "HKLM\SOFTWARE\Classes\%PROGID%" /f

בנוסף, ה-EXE צריך להצהיר בפני COM, בזמן ההפעלה, שהוא “האחראי ל-CLSID הזה”. ב-C/C++ זה נעשה עם CoRegisterClassObject, וב-.NET Framework עם RegistrationServices.RegisterTypeForComClients. רישום ה-Registry מטפל רק עד שלב הפעלת ה-EXE על ידי COM, ולכן אם ההצהרה הזו חסרה, מתקבל כישלון מבלבל שבו ה-EXE מופעל אבל אי אפשר ליצור אובייקט.

רישום ב-Registry מול הצהרת ה-EXEרישום ה-Registry מטפל רק עד הפעלת ה-EXE על ידי COM, ורק כשה-EXE שהופעל מצהיר שהוא אחראי ל-CLSID אפשר ליצור אובייקט — כשההצהרה חסרה מתקבל כישלון מבלבל של EXE שרץ אבל לא יוצר אובייקט.אם ההצהרה חסרהרישום ב-RegistryCOM מפעיל את ה-EXEה-EXE מצהיר שהוא אחראי ל-CLSIDאפשר ליצור אובייקטרץ אבל אי אפשר ליצור

איור 9: רישום ה-Registry מטפל רק עד הפעלת ה-EXE; בלי הצהרת ה-EXE עצמו, אי אפשר ליצור אובייקט.

לשם השוואה, אם רק בודקים במהלך הפיתוח, אפשר לכתוב באותו מבנה תחת HKCU\SOFTWARE\Classes במקום HKLM, ולהירשם בלי הרשאות Administrator (HKEY_CLASSES_ROOT הוא view מאוחד של HKLM ו-HKCU). עם זאת, גם HKCU\SOFTWARE\Classes\CLSID מטופל בנפרד עבור 32-bit/64-bit, כך שעדיין צריך לכתוב לשתי ה-views.

4.3. השיטה שונה בין .NET Framework ל-.NET (מגרסה 5 ואילך)

הקוד למעלה כתוב ב-C#, אבל השלבים משתנים מאוד בהתאם לגרסת .NET שבה משתמשים. אם מערבבים בין השתיים, נתקעים.

  .NET Framework .NET (Core 3.0 / 5 ומעלה)
כלי רישום קיים RegAsm.exe (אבל הוא יוצר רישום InprocServer32 עבור in-proc, כך שבסופו של דבר צריך לכתוב את LocalServer32 בעצמכם) אין כלי מקביל ל-RegAsm
חשיפת COM סטנדרטית מוסיפים attributes ל-assembly ומריצים RegAsm מגדירים <EnableComHosting>true</EnableComHosting> ליצירת *.comhost.dll ורושמים עם regsvr32 (in-proc בלבד)
יצירת TypeLib (.tlb) אפשר ליצור עם TlbExp / RegAsm /tlb לא נתמך. כותבים IDL ידנית ומקמפלים ב-MIDL (מ-.NET 6 ואילך אפשר להטמיע את ה-.tlb שנוצר בתוך comhost)
ציון CLSID אופציונלי עבור מחלקה שנוצרת על ידי COM, חובה לציין CLSID במפורש
טיפול ב-AnyCPU ניתן לשימוש מ-clients 32-bit ו-64-bit כאחד קובץ ה-*.comhost.dll הנלווה הוא בברירת מחדל 64-bit, ולכן ניתן לשימוש רק מ-clients 64-bit

המבנה במאמר הזה (שרת EXE) חורג מהטווח הסטנדרטי של EnableComHosting ב-.NET (מגרסה 5 ואילך), ולכן תצטרכו לכתוב את לוגיקת הרישום בעצמכם. יש דוגמה רשמית של Microsoft בשם OutOfProcCOM, ואם בונים בצד .NET, זו נקודת ההתחלה המומלצת.

הדרך לבניית שרת EXE ב-.NET (מגרסה 5 ואילך)שרת ה-EXE של המאמר הזה נמצא מחוץ לטווח EnableComHosting הסטנדרטי ב-.NET 5 ואילך, ולכן צריך לכתוב את הרישום בעצמכם, כשהדוגמה הרשמית OutOfProcCOM משמשת נקודת פתיחה.שרת EXE ב-.NET (מגרסה 5 ואילך)מחוץ לטווח EnableComHosting הסטנדרטיכותבים את הרישום בעצמכםהדוגמה הרשמית OutOfProcCOM כנקודת פתיחה

איור 10: שרת EXE ב-.NET (מגרסה 5 ואילך) חורג מהפונקציונליות הסטנדרטית, ולכן הרישום נכתב באופן עצמאי כברירת מחדל.

5. קוד לדוגמה מלא

פרסמנו ב-GitHub דוגמה שמממשת את הרעיון שלמעלה בצורה שרצה בפועל.

Call64bitDLLFrom32bitProc - GitHub

המאגר הזה כולל:

  • Call64bitDLLFrom32bitProc/ - COM LocalServer של 64-bit (EXE)
  • X64DLL/ - DLL של 64-bit (העיבוד בפועל)
  • X86App/ - client 32-bit (WinForms)
  • scripts/ - סקריפטים לרישום/ביטול רישום של שרת COM

אם בונים ורושמים לפי השלבים שמתוארים ב-README, אפשר לוודא בפועל שהקריאה מ-process 32-bit ל-DLL של 64-bit אכן עובדת.

6. סיכום

גשר COM אינו פתרון-על, אלא מבנה שבו מובחן בבירור בין משימות שמתאימות למשימות שלא. לפני שמחליטים לאמץ אותו, נסו להתאים את המקרה שלכם לטבלה הבאה.

מקרים מתאימים מקרים לא מתאימים
אי אפשר לבנות מחדש את גוף אפליקציית ה-32-bit (עלות השינוי לא משתלמת) אפשר לבנות מחדש את צד ה-32-bit ל-64-bit מלכתחילה (זה הפתרון הקצר ביותר)
הקריאות בגודל גס (למשל תמונה אחת בקריאה, קובץ אחד בקריאה) קריאות קטנות ותכופות של אלפי-עשרות אלפי פעמים (התקורה של IPC הופכת דומיננטית)
הנתונים המועברים הם מספרים, מחרוזות, מערכים — טיפוסים קלים ל-marshaling מעבירים בכמות גדולה pointers גולמיים או structs מותאמים מורכבים
רוצים שגוף האפליקציה יישאר בחיים גם אם העיבוד בצד 64-bit קורס (הפרדת process היא יתרון) לא רוצים לכתוב לוגיקת recovery שמטפלת ב-crash או restart של השרת
רוצים לשמור על קריאות typed (IntelliSense ובדיקה בזמן קומפילציה) מספיק עיבוד באצווה חד-פעמי, והעברה דרך stdin/stdout או קובץ מספיקה

כתמונת המראה של השורה האחרונה, שווה תמיד לשקול גם את החלופה הפשוטה של “להפוך את העיבוד בצד 64-bit ל-EXE קונסולי רגיל, ולתקשר דרך ארגומנטים וקבצים”. גשר COM משתלם כאשר רוצים לשמור על קריאות typed, וכאשר רוצים לקרוא שוב ושוב לשרת ששומר state.

נקודת ההפרדה בין גשר COM לחלופה הפשוטהאם נדרשת קריאה typed או שמירת state על ידי שרת שנקרא שוב ושוב, גשר COM עוזר; אם מספיק עיבוד באצווה חד-פעמי, שווה לשקול חלופה פשוטה של EXE קונסולי עם ארגומנטים וקבצים.כןלאנדרשת קריאה typed או שמירת state?גשר COMEXE קונסולי עם ארגומנטים וקבצים

איור 11: הצורך בקריאה typed ובשמירת state הוא מה שמפריד בין גשר COM לחלופה הפשוטה.

כפעולות הבאות, מומלץ לפעול בסדר הזה:

  1. קודם כול לשכפל (clone) את מאגר הדוגמה מסעיף 5, לבנות ולרשום לפי ה-README, וליצור אצלכם מצב אחד שרץ בפועל.
  2. לבחור פונקציה אחת בלבד מה-DLL בן 64-bit שלכם, ולהוסיף לה method אחת ב-interface המקביל ל-ICalcService בדוגמה, ולוודא שהיא עוברת.
  3. אחרי שזה עובד, למדוד את מספר הקריאות ואת כמות הנתונים בכל קריאה. אם מקבלים כאן מראש את ההחלטה התכנונית “לרכז לגודל גס” (איחוד כמה קריאות לאחת), חוזרים אחורה פחות בהמשך.

7. מקורות

  • סקירה כללית של Component Object Model (COM) https://learn.microsoft.com/en-us/windows/win32/com/component-object-model–com–portal
  • רישום COM LocalServer32 https://learn.microsoft.com/en-us/windows/win32/com/localserver32
  • יסודות ממשקי COM https://learn.microsoft.com/en-us/windows/win32/com/the-component-object-model
  • COM Interop (שימוש מ-.NET) https://learn.microsoft.com/en-us/dotnet/standard/native-interop/cominterop
  • WOW64 Registry Redirector (HKLM\SOFTWARE\Classes משותף, תת-המפתח CLSID נפרד בין 32/64) https://learn.microsoft.com/en-us/windows/win32/winprog64/shared-registry-keys
  • חשיפת רכיבי .NET (Core / 5 ומעלה) ל-COM https://learn.microsoft.com/en-us/dotnet/core/native-interop/expose-components-to-com

מאמרים עדכניים עם אותן תגיות, להעמקה בנושאים קרובים.

העמודים האלה ממקמים את הנושא בהקשר רחב יותר של שירותים והחלטות.

המאמר קשור ישירות לשירותים הבאים.

שאלות נפוצות

שאלות נפוצות בפניות בנושא המאמר.

אפשר לקרוא ישירות מאפליקציית 32-bit ל-DLL של 64-bit?
לא. process של 32-bit לא יכול לטעון DLL של 64-bit — זו מגבלה ברמת ה-OS, לא משהו שאפשר לעקוף בטריק. כי הקריאה באותו process חסומה מלכתחילה, צריך מבנה שמפריד את העיבוד בצד 64-bit ל-process נפרד.
איך משתמשים בפונקציונליות של DLL בן 64-bit מתוך אפליקציית 32-bit?
הדרך הסטנדרטית היא הפרדה עם Out-of-proc COM (EXE server). קוראים ל-DLL של 64-bit מתוך COM LocalServer (EXE) של 64-bit, ואפליקציית ה-32-bit משתמשת בו בצורה typed דרך COM interface. חושפים את הטיפוסים עם שיתוף COM interface (IDL/TypeLib), וה-COM runtime חוצה את הגבול עם Proxy/Stub ו-IPC.
מה נקודות תשומת הלב במבנה של גשר COM?
שלוש עיקריות: הרישום של 32-bit ו-64-bit נפרד (כולל WOW6432Node), structs מותאמים דורשים תכנון marshaling, ובגלל תקורה של IPC צריך להיזהר מקריאות תכופות וקטנות. עדיף לרכז לעיבוד באצווה מאשר לשלוח כמות גדולה של קריאות קטנות.
יש קוד לדוגמה שבאמת רץ?
כן. במאגר Call64bitDLLFrom32bitProc ב-GitHub מפורסמת ערכת דוגמה מלאה, כולל COM LocalServer (EXE) של 64-bit, DLL של 64-bit, client 32-bit (WinForms) וסקריפטים לרישום/ביטול רישום של שרת COM. אם בונים ורושמים לפי ההוראות ב-README, אפשר לוודא שהקריאה מ-process 32-bit ל-DLL של 64-bit אכן עובדת.

פרופיל הכותב

עמוד היכרות עם כותב המאמר.

Go Komura

מנהל KomuraSoft LLC

מתמחה בפיתוח תוכנה עבור Windows, ייעוץ טכני וחקירת תקלות, בעיקר בפרויקטים עם מערכות קיימות ובאגים שקשה לשחזר.

קישורים ציבוריים

חזרה לבלוג