מה זה Reg-Free COM: COM בלי רישום ב-registry
· עודכן בתאריך: · Go Komura · COM, Reg-Free COM, Registration-Free COM, פיתוח Windows, legacy
היסטוריית עדכונים (גרסה ראשונה, פורסמה בתאריך 16 Mar 2026)
- פרסום ראשון
לצטט את המאמר הזה(DOI: 10.5281/zenodo.22173587)
מאמר זה מאוחסן בארכיון Zenodo. להלן גם ה-DOI שתמיד מפנה לגרסה האחרונה וגם ה-DOI המקובע לגרסה שאתם קוראים.
Go Komura (2026). מה זה Reg-Free COM: COM בלי רישום ב-registry. KomuraSoft LLC. https://doi.org/10.5281/zenodo.22173587 https://comcomponent.com/he/blog/what-is-reg-free-com/
- DOI (הגרסה האחרונה)
- 10.5281/zenodo.22173587
- DOI (הגרסה הזו)
- 10.5281/zenodo.22173588
בפרויקטים של COM / ActiveX / OCX, אותן בעיות חוזרות בכל פעם שמפיצים או מעדכנים.
- צריך
regsvr32 - בדרך כלל צריך הרשאות Administrator
- מתנגש עם גרסה אחרת שאפליקציה אחרת התקינה
- uninstall ששובר גם מוצר אחר
- עובד על מחשב הפיתוח, לא עובד בסביבה נקייה
Reg-Free COM מקטין את זה משמעותית. למרות השם, זה לא קסם שמעלים את כל בעיות COM. מה שנעלם הוא בעיקר הכאבים שמגיעים מרישום גלובלי. bitness, dependent DLL-ים, type library, וקושי ה-threading model לא נעלמים.
flowchart TB
accTitle: מה נעלם ומה נשאר ב-Reg-Free COM
accDescr: תרשים שמראה שב-Reg-Free COM מה שנעלם בעיקר הוא הכאב שנגרם מרישום גלובלי, ואילו bitness, dependent DLL-ים, type library וקושי ה-threading model לא נעלמים.
rf1["Reg-Free COM"] --> rf2["פחות כאב של רישום גלובלי"]
rf1 --> rf3["קשיים שנשארים"]
rf3 -.-> rf4["bitness / dependent DLL / type library"]
איור 1: לא קסם. מצפים בנפרד למה שנעלם ולמה שנשאר.
המאמר מתמקד ב-Reg-Free COM בהקשר של שימוש ב-COM DLL / OCX כ-private לאפליקציה אחת, באפליקציית desktop ל-Windows.
למי זה מיועד, ומה מניחים
המאמר מיועד למפתחים שמפיצים אפליקציית desktop ל-Windows שמשתמשת ב-COM DLL / OCX קיימים, ורוצים להיפטר מ-regsvr32 ומהרשאות Administrator. ההנחה היא שכבר נתקלתם ביסודות COM (CLSID, ProgID, CoCreateInstance, in-proc server). אם זה עדיין לא חד, מהיר יותר לקרוא קודם את “מה זה COM / ActiveX / OCX - הסבר על ההבדלים והקשרים”.
חלק ההליכים משתמש ב-mt.exe וב-sxstrace מ-Windows SDK, ולכן ההנחה היא סביבה עם Visual Studio או Windows SDK מותקנים.
1. המסקנה בקצרה
בניסוח גס אבל שימושי:
- Reg-Free COM היא דרך שבה מידע הרישום של COM יושב ב-manifest במקום ב-registry
- בזמן ריצה, ב-resolution של
CoCreateInstanceאוCLSIDFromProgID, נבדק קודם activation context - לכן אפשר להחזיק COM DLL / OCX כ-private לכל אפליקציה
- היתרונות העיקריים: קל להפיץ ב-XCOPY, קל יותר להימנע מהתנגשות גרסאות, uninstall שפחות נשבר
- עם זאת, בעיית 32bit / 64bit לא נעלמת. אי אפשר לעקוף את זה בכתיבת manifest
- בנוסף צריך לטפל בנפרד ב-dependent DLL-ים, type library, design-time references, ותלות ברישום לא סטנדרטי
- בפועל זה מתאים מאוד כשרוצים לשים בצד קומפוננטת COM ייעודית לאפליקציה
בקיצור, Reg-Free COM הוא מנגנון שמחזיר את ה-activation של COM לרמת האפליקציה.
flowchart TB
accTitle: מקום מידע הרישום משתנה
accDescr: תרשים שמראה ש-Reg-Free COM מחזיק את מידע הרישום של COM ב-manifest במקום ב-registry, ומכיוון שב-resolution של CoCreateInstance וכדומה נבדק קודם ה-activation context, אפשר להחזיק קומפוננטות COM כ-private לכל אפליקציה.
mg1["מידע הרישום ב-manifest"] --> mg2["ב-resolution, activation context נבדק קודם"]
mg2 --> mg3["אפשר להחזיק COM כ-private לאפליקציה"]
mg3 -.-> mg4["קל יותר XCOPY ופחות התנגשות"]
איור 2: המהות היא שנקודת הכניסה ל-resolution עוברת מ-registry ל-manifest.
ב-diagram, solid line מציינת relation שתמיד מתקיים ו-dashed line מציינת relation מותנה (התנאים מופיעים בהסבר של כל relation ב-detail page). הרשימה המלאה של ה-relations (סה”כ 26, כולל evidence ו-certainty) וההגדרות של ה-concepts המרכזיים נמצאות ב-detail page של ה-knowledge map (ביפנית). Data: JSON-LD / Turtle
2. מה הכוונה ב-Reg-Free COM במאמר הזה
Reg-Free COM הוא קיצור של Registration-Free COM. לפעמים כותבים גם “COM בלי צורך ברישום”.
“בלי רישום” כאן אומר לא להסתמך לגמרי על רישום גלובלי ב-registry דרך HKCR / CLSID / InprocServer32 וכדומה, כדי להשתמש ב-COM. זה לא אומר ש-COM עצמו נעלם, וגם לא שה-GUID כבר לא נחוץ.
המאמר מתמקד בעיקר באלה:
- native COM DLL
- COM server מבוסס ATL
- ActiveX / OCX
- COM Interop מבוסס .NET Framework
- חשיפה דרך COM host של .NET 5+ / .NET 8
לעומת זאת, שתי הנקודות שחשוב להדגיש כאן:
- Reg-Free COM הוא עניין של activation
- הפצה של type information והגדרת design-time references עלולות להישאר כנושא נפרד
אם מערבבים בין השניים, הדיון נהיה מבלבל.
flowchart TB
accTitle: שני נושאים שאסור לערבב
accDescr: תרשים שמראה ש-Reg-Free COM הוא עניין של activation, ושהפצת type information והגדרת design-time references נשארות כנושא נפרד, ולכן ערבוב ביניהם מבלבל את הדיון.
pt1["Reg-Free COM"] --> pt2["עניין של activation"]
pt3["הפצת types / design-time reference"] --> pt4["נשאר כנושא נפרד"]
pt2 -.-> pt5["ערבוב מבלבל את הדיון"]
pt4 -.-> pt5
איור 3: runtime ו-design-time מטופלים בנפרד מלכתחילה.
3. קודם כל, תמונה כללית
לפני שנכנסים לתרשים, נגדיר 4 מונחים שחוזרים לאורך המאמר.
| מונח | משמעות |
|---|---|
| activation context | מבנה נתונים ב-runtime ששומר “איזו גרסה של איזה assembly ה-thread הזה משתמש בה עכשיו”. CoCreateInstance בודק את זה לפני ה-registry |
| side-by-side assembly | מנגנון ב-Windows שמאפשר לקומפוננטה עם אותו שם, בגרסאות שונות, לחיות יחד על אותה מכונה. מזוהה דרך manifest, ו-assemblyIdentity הוא התג המזהה |
| application manifest | XML שמצמידים לצד ה-EXE, וכותבים בו “באיזה side-by-side assembly זה תלוי” |
| component manifest (assembly manifest) | XML שמצמידים לצד הקומפוננטה, וכותבים בו “אילו קבצים ה-assembly הזה כולל, ואילו COM classes הוא חושף”. מידע שבמקור היה ב-registry עובר לכאן |
ה-activation context נשמר לכל thread בנפרד. COM מעביר את ה-activation context של ה-thread היוצר אל ה-thread המארח לפני שהוא קורא ל-LoadLibrary או ל-DllGetClassObject, ולכן מצד הקורא לא צריך הכנה מיוחדת.
עם זאת, מהיר יותר לראות קודם את התמונה הכללית בתרשים אחד.
flowchart LR
accTitle: התמונה הכללית של Reg-Free COM
accDescr: תרשים שמראה שהאפליקציה מפנה דרך application manifest ו-component manifest אל ה-DLL, ובמקביל האפליקציה משתמשת ב-activation context כדי ל-resolve את קריאת ה-COM ולהגיע לאותו DLL בלי registry.
APP["MyApp.exe"] --> AM["application manifest"]
AM --> DEP["dependentAssembly"]
DEP --> CM["component manifest"]
CM --> META["file / comClass / typelib"]
META --> DLL["VendorControl.dll / .ocx"]
APP --> ACTX["Activation Context"]
ACTX --> COM["CLSIDFromProgID / CoCreateInstance"]
COM --> DLL
איור 4: עוקבים מ-application manifest ל-component manifest, ומגיעים ל-DLL בלי registry.
ב-COM רגיל, כשקוראים ל-CoCreateInstance, עוברים דרך ה-registry כדי לקבוע איזה DLL לטעון.
ב-Reg-Free COM, לפני זה נבדק ה-activation context הפעיל כרגע, ומתוכו נעשה resolve לפי מידע ה-manifest שכתוב שם.
בגלל זה, קל יותר שאפליקציה A ואפליקציה B על אותה מכונה יחזיקו גרסאות שונות של אותה משפחת קומפוננטות COM. זה מחזיר קצת את מודל ה-sharing של COM לכיוון private לאפליקציה.
4. למה הפצה רגילה של COM יוצאת כבדה
הפצה רגילה של COM כבדה לא כי COM עצמו רע, אלא בגלל ההנחה שצריך רישום גלובלי.
כדי להשתמש ב-COM class, בערך צריך את המידע הבא.
| מידע | תפקיד |
|---|---|
| CLSID | GUID שמזהה את ה-class באופן ייחודי |
| ProgID | שם נוח לבני אדם |
| InprocServer32 | איזה DLL לטעון |
| ThreadingModel | הנחה כמו Apartment / Both |
| TypeLib | type information |
כשזה נכנס ל-registry, זה נוח ברמת המכונה: קל לשתף בין כמה אפליקציות.
בפועל, ה-sharing הזה עובד נגדך.
- ההתקנה של מוצר אחד דורסת את רישום ה-COM של מוצר אחר
- ה-uninstaller “חושב” שהוא מוחק רק את שלו, ושובר COM משותף
- registration שקיים במקרה במחשב הפיתוח, לא קיים במכונת הייצור
- רישום 32bit ו-64bit לא תואמים, ורק הסימפטום נראה מוזר
כלומר, מודל ההפצה, ולא COM עצמו, הוא מה שמטריד אנשים לעיתים קרובות. Reg-Free COM הוא מנגנון שמקטין את הכאב הזה במודל ההפצה.
flowchart TB
accTitle: איך sharing גלובלי עובד נגדך
accDescr: תרשים שמראה שמידע רישום COM שנכנס ל-registry נוח לשיתוף ברמת המכונה, אבל בפועל ה-sharing הזה עובד נגדך דרך overwrite ממוצרים אחרים, uninstall ששובר אחרים, ופער בין סביבות, כך שמודל ההפצה מטריד יותר מ-COM עצמו.
gs1["מידע רישום ברמת המכונה"] --> gs2["נוח לכמה אפליקציות"]
gs1 --> gs3["בפועל ה-sharing עובד נגדך"]
gs3 -.-> gs4["overwrite / uninstall / פער סביבות"]
gs3 --> gs5["מודל ההפצה מטריד"]
איור 5: הבעיה האמיתית היא לא COM עצמו, אלא ההנחה של רישום גלובלי.
5. איך המנגנון של Reg-Free COM עובד
5.1 ה-application manifest כותב את התלות
קודם כל, צד האפליקציה כותב ב-application manifest באיזה side-by-side assembly היא תלויה.
את ה-manifest הזה אפשר:
- לשים ליד ה-EXE כמו
MyApp.exe.manifest - לעשות embed בתוך ה-EXE כ-resource
בפועל, אם רוצים להקל על הפצה והחלפה: קובץ חיצוני; אם עדיפות לחוסן ולפשטות הפצה: embed.
שימו לב: אם יש גם קובץ חיצוני וגם embed, ה-manifest שב-file system מקבל עדיפות.
5.2 ה-component manifest כותב את מידע ה-COM
בשלב הבא, צד ה-COM מחזיק ב-component manifest מידע שבמקור היה ב-registry.
מה שנכנס לכאן, למשל:
comClassclsidprogidthreadingModeltypelib- אם צריך, גם proxy / stub או window class
כלומר, במקום registry מתארים את ה-COM ב-XML.
את ה-manifest הזה אפשר להרכיב:
- כקובץ נפרד מה-DLL
- כ-embed בתוך ה-DLL כ-resource
בפועל, embed בתוך ה-DLL כ-private assembly נוטה פחות להישבר. תפעול עם קובץ נפרד קל להבנה, אבל קל להיתקע על התאמה בין שם הקובץ ל-assemblyIdentity, על מיקום ההצבה, או על קובץ שנשכח בהעתקה.
flowchart TB
accTitle: איך מחזיקים את ה-component manifest
accDescr: תרשים שמראה שמידע כמו comClass, clsid, typelib שבמקור היה ב-registry, ה-component manifest מחזיק אותו כ-XML, ואפשר לבחור בין קובץ נפרד מה-DLL לבין embed בתוך ה-DLL כ-resource, כאשר בפועל ה-embed נוטה פחות להישבר.
cm1["מידע מה-registry, ב-XML"] --> cm2["מוצב כקובץ נפרד"]
cm1 --> cm3["embed בתוך ה-DLL"]
cm2 -.-> cm4["קל לשכוח להעתיק"]
cm3 -.-> cm5["בפועל פחות נשבר"]
איור 6: תוכן המידע זהה, אבל אופן ההצבה קובע את שיעור הכשלים.
5.3 ב-runtime נבדק קודם ה-activation context
כאן נמצא הלב של Reg-Free COM.
כשהאפליקציה קוראת ל-CLSIDFromProgID או ל-CoCreateInstance, ה-COM runtime בודק את ה-activation context הפעיל.
אם יש שם את המידע הנדרש להמרה מ-ProgID ל-CLSID ומ-CLSID ל-DLL, ה-resolution מתבצע בלי registry.
לעומת זאת, אם המידע הנדרש חסר ב-manifest, זה נופל ל-resolution הרגיל לפי registry. בגלל ההתנהגות הזו נוצרת מלכודת: על מחשב הפיתוח זה עובד במקרה. חשבתם שהצלחתם לעבור ל-Reg-Free, אבל בפועל הרישום המקומי עוזר מאחורי הקלעים.
זו המלכודת הכי מעצבנת של Reg-Free COM.
flowchart TB
accTitle: המלכודת שנפלה ל-resolution לפי registry
accDescr: תרשים שמראה שב-resolution של CoCreateInstance נבדק קודם ה-activation context, אבל אם חסר מידע ב-manifest זה נופל ל-resolution הרגיל לפי registry, ולכן נוצרת מלכודת שבה זה עובד במקרה במחשב הפיתוח בזכות registration מקומי.
ac1["ה-resolution בודק קודם activation context"] --> ac2{"יש מספיק מידע ב-manifest?"}
ac2 -->|"יש"| ac3["resolve בלי registry"]
ac2 -->|"אין"| ac4["נופל ל-resolution לפי registry"]
ac4 -.-> ac5["מלכודת: עובד במקרה אצל המפתח"]
איור 7: ה-fallback השקט הוא ה-pitfall הכי גדול במנגנון הזה.
6. מה מרוויחים מזה
היתרונות של Reg-Free COM ברורים למדי בפועל.
6.1 קל להפיץ ב-XCOPY
אפשר לשים את כל הקבצים הנדרשים יחד בתיקיית האפליקציה, כך שהתקנה ורישום נעשים קלים יותר.
כמובן, כתיבה תחת Program Files היא עדיין עניין של הרשאות, אבל לפחות עבודת Administrator לצורך רישום COM קל יותר לצמצם.
6.2 קל יותר לצמצם התנגשות גרסאות
גם אם יש כמה גרסאות של קומפוננטת COM על אותה מכונה, קל יותר להפריד איזו גרסה כל אפליקציה משתמשת בה. קל יותר להימנע מתקלה של “ההתנהגות השתנתה פתאום בגלל התקנה של מוצר אחר”.
6.3 לרוב אין צורך לשנות משמעותית קוד קיים
Reg-Free COM לא משנה מהיסוד את אופן הקריאה של הקוד הקיים. הוא מנגנון שמשנה את אופן ה-resolution.
לכן, אם זה מסתדר, אפשר להטמיע כמעט בלי לגעת בקוד של CoCreateInstance.
6.4 מחיקה ו-rollback נהיים קלים יותר
כי זה סגור לאפליקציה, עדכון ו-rollback נהיים ישירים יותר. בקיצוניות, קל יותר לחשוב במונחי החלפת תיקייה שלמה.
flowchart TB
accTitle: היתרון בסגירה לרמת אפליקציה
accDescr: תרשים שמראה שסגירת קומפוננטות COM לרמת אפליקציה מולידה יתרונות כמו קלות הפצה ב-XCOPY, צמצום התנגשות גרסאות, הטמעה כמעט בלי לגעת בקוד הקיים, ופשטות במחיקה וב-rollback.
cl1["סוגרים לרמת האפליקציה"] --> cl2["קל להפיץ ב-XCOPY"]
cl1 --> cl3["פחות התנגשות גרסאות"]
cl2 -.-> cl5["אפשר להחליף תיקייה שלמה"]
cl3 -.-> cl4["כמעט בלי לגעת בקוד הקריאה"]
איור 8: כל היתרונות יוצאים מאותה תכונה: סוגרים את זה לאפליקציה.
7. מתי זה מתאים ומתי לא
7.1 מקרים שמתאימים
במקרים הבאים, Reg-Free COM חזק למדי.
| מצב | התאמה |
|---|---|
| רוצים לצרף COM DLL / OCX ייעודי לאפליקציה | טוב מאוד |
| רוצים להחזיק כמה גרסאות באותו PC | טוב מאוד |
| רוצים להימנע מתקלת רישום של קומפוננטת vendor | טוב |
| רוצים להשתמש ב-ActiveX / OCX כ-private באפליקציית desktop קיימת | טוב |
| רוצים להקל על ההפצה בלי לשנות משמעותית את הקריאה הקיימת | טוב |
בדרך כלל זה מתאים ל-אפליקציות עסקיות ב-desktop, כלי אינטגרציה מול ציוד, ו-assets קיימים של VB6 / MFC / WinForms.
7.2 מקרים שלא מתאימים, או שכדאי לבדוק בזהירות
מצד שני, יש גם מקרים שכדאי לבדוק בזהירות.
| מצב | הערה |
|---|---|
| רוצים לשתף COM ברמת המכונה | אין כאן הרבה יתרון ל-Reg-Free |
| ה-bitness לא תואם | Reg-Free לא פותר את זה |
| תלות חזקה במידע רישום לא סטנדרטי או בהתקנה ייחודית | קשה לתרגם ל-manifest |
| הפצת dependent DLL או VC++ runtime לא מסודרת | זה יישבר במקום אחר בכל מקרה |
| כלי design-time או הגדרת references ב-IDE מניחים registry | צריך לתכנן תהליך עבודה נפרד |
בפרט, הנקודה האחרונה חשובה. Reg-Free COM עוזר ל-activation ב-runtime, אבל לא משנה בבת אחת על מה כלי ההגדרה ב-design-time מניח.
flowchart TB
accTitle: runtime נעזר, design-time נשאר
accDescr: תרשים שמראה ש-Reg-Free COM עוזר ל-activation ב-runtime, אבל אם כלי או IDE להגדרת references ב-design-time מניחים registry, זה לא משתנה כך סתם, וצריך לתכנן תהליך עבודה נפרד.
rt1["activation ב-runtime"] --> rt2["Reg-Free עוזר"]
dt1["UI של design-time references"] --> dt2["לפעמים מניח registry"]
dt2 -.-> dt3["צריך תהליך עבודה נפרד"]
איור 9: גם כשמנגנון ההפעלה מסודר, מנגנון הפיתוח צריך טיפול נפרד.
8. אי-הבנות נפוצות
8.1 עם Reg-Free COM, בעיית ה-bitness נעלמת
לא נעלמת. process של 32bit יכול לטעון רק in-proc COM DLL של 32bit, ו-process של 64bit רק DLL של 64bit. זה נשאר כמו קודם גם עם Reg-Free.
8.2 עם Reg-Free COM, ה-registry כבר לא נבדק בכלל
גם זה לא נכון. אם חסר מידע נדרש ב-manifest, זה נופל ל-resolution הרגיל לפי registry. בגלל זה, הצלחה במחשב הפיתוח לא אומרת בהכרח שקונפיגורציית ה-Reg-Free נכונה.
8.3 עם Reg-Free COM, גם נושא ה-type library נפתר אוטומטית
זה נכון רק בחצי.
אפשר לכתוב גם מידע typelib ב-manifest, אבל הגדרת references ב-VBA, #import ב-C++, יצירת references ב-design-time בצד .NET - הטיפול ב-type information לרוב עדיין דורש תכנון נפרד.
Reg-Free COM הוא בעיקר עניין של לגרום לזה לעלות. איך מפתחים עם types הוא הנושא הבא.
flowchart TB
accTitle: הסדר בין נושא ה-activation לנושא ה-types
accDescr: תרשים שמראה שגם אם אפשר לכתוב מידע typelib ב-manifest, הטיפול ב-type information, הגדרת references ב-VBA, import ב-C++, יצירת interop ב-.NET, עדיין דורש תכנון נפרד, ו-Reg-Free COM עוסק קודם כל ב-activation, ופיתוח עם types הוא הנושא הבא.
st1["מגדירים Reg-Free COM"] --> st2["קודם כל, שזה יעלה"]
st2 --> st3["אחר כך, פיתוח עם types"]
st3 -.-> st4["VBA references / import / interop"]
איור 10: היכולת לכתוב typelib והשלמת ניהול ה-type information הן שני דברים שונים.
8.4 עם Reg-Free COM, כל ActiveX / OCX עובד ישירות
גם זה מסוכן. אם הקומפוננטה מבוססת על מידע רישום COM סטנדרטי, קל להתקדם, אבל אם יש הגדרת registry ייחודית, התקנה נוספת, עיבוד רישוי, או תלות חזקה בקבוצת מודולים אחרת, המעבר ל-Reg-Free נהיה פתאום קשה.
8.5 Reg-Free COM דומה לחלוטין בין .NET Framework ל-.NET 8
יש דמיון, אבל ה-toolchain שונה למדי.
ההקשר של .NET Framework + RegAsm וההקשר של .NET 5+ / .NET 8 + comhost הם אותו COM, אבל ה-tooling שונה.
9. ההבדל בין native / .NET Framework / .NET 5+ / .NET 8
כאן קל להתבלבל, ולכן נפריד את זה פעם אחת.
| משפחה | בקצרה |
|---|---|
| native COM DLL / OCX | בסיסי לחשוב במונחי application manifest + component manifest |
| COM Interop מבוסס .NET Framework | בנוסף ל-application manifest בסגנון Win32, נדרש גם manifest מצד הקומפוננטה ה-managed |
| חשיפת COM ב-.NET 5+ / .NET 8 | יוצרים COM host עם EnableComHosting, ומייצרים manifest ל-Reg-Free עם EnableRegFreeCom |
9.1 COM מבוסס .NET Framework
ב-COM מבוסס .NET Framework יש שתי שכבות: application manifest בסגנון Win32 מצד אפליקציית ה-COM, ו-component manifest מצד הקומפוננטה ה-managed.
כלומר, בהשוואה ל-COM native, נוסף manifest אחד. מגבלת השם ו-resource ID מסעיף 10.4 חלה באותה מידה גם על ה-component manifest כאן.
9.2 חשיפת COM ב-.NET 5+ / .NET 8
ב-.NET 5+ / .NET 8, ה-entry point לחשיפת COM הופך ל-*.comhost.dll.
בנוסף, אם מוסיפים EnableRegFreeCom=true, מיוצא side-by-side manifest ל-Reg-Free COM.
הטיפול ב-type information כנושא נפרד כפי שתואר בסעיף 8.3, אבל ב-.NET Core / .NET 5+ זה משתנה עוד יותר. מכיוון שזה לא עולם שבו TLB נוצר באופן טבעי מה-assembly כמו בתקופת .NET Framework, אם נדרש שימוש typed, יש לבנות בנפרד את יצירת ה-TLB, ה-embed וה-registration. השלבים המדויקים מסוכמים ב-“שימוש ב-DLL של .NET 8 מ-VBA עם טיפוסים - חשיפת COM ו-TLB ב-dscom”.
flowchart TB
accTitle: Reg-Free COM ב-.NET 5 ואילך
accDescr: תרשים שמראה שב-.NET 5 ואילך, EnableComHosting יוצר את comhost.dll כ-entry point לחשיפת COM, EnableRegFreeCom מייצא side-by-side manifest ל-Reg-Free COM, אבל יצירה ו-registration של TLB דורשים בנייה נפרדת.
dn1["build עם EnableComHosting"] --> dn2["comhost.dll הופך ל-entry point"]
dn2 --> dn3["מפעילים EnableRegFreeCom"]
dn3 --> dn4["יוצא manifest ל-Reg-Free"]
dn4 -.-> dn5["טיפול ב-TLB דורש עבודה נפרדת"]
איור 11: ב-.NET 8 שני properties מכינים את התשתית, אבל type information דורש עבודה נפרדת.
10. תמונה של קונפיגורציה מינימלית
כאן נציג תמונה מינימלית שבה MyApp.exe משתמש ב-Vendor.CameraControl.dll דרך Reg-Free COM.
10.1 מבנה הקבצים
MyApp.exe
MyApp.exe.manifest
Vendor.CameraControl.Asm.manifest
Vendor.CameraControl.dll
Vendor.Helper.dll
בדוגמה למעלה, ההנחה היא שה-component manifest מוצב כקובץ נפרד. אפשר גם לעשות embed בתוך ה-DLL, אבל אז חלות מגבלות קבועות על שם ה-assembly ועל resource ID. ההליך המלא מובא בסעיף 10.4.
10.2 תמונת ה-application manifest
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
type="win32"
name="KomuraSoft.MyApp"
version="1.0.0.0"
processorArchitecture="amd64" />
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Vendor.CameraControl.Asm"
version="1.0.0.0"
processorArchitecture="amd64" />
</dependentAssembly>
</dependency>
</assembly>
10.3 תמונת ה-component manifest
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
type="win32"
name="Vendor.CameraControl.Asm"
version="1.0.0.0"
processorArchitecture="amd64" />
<file name="Vendor.CameraControl.dll">
<comClass
clsid="{01234567-89AB-CDEF-0123-456789ABCDEF}"
progid="Vendor.CameraControl.1"
threadingModel="Apartment"
tlbid="{89ABCDEF-0123-4567-89AB-CDEF01234567}" />
<typelib
tlbid="{89ABCDEF-0123-4567-89AB-CDEF01234567}"
version="1.0"
helpdir="" />
</file>
</assembly>
מה שבאמת חשוב בדוגמה הזו, יותר מפרטי ה-XML, הוא ש-dependentAssembly בצד האפליקציה ו-assemblyIdentity בצד הקומפוננטה תואמים.
אם יש כאן פער, זה הופך לכשל בהפעלה שאי אפשר להבין מהשגיאה עצמה.
שימו לב: ה-GUID והשמות למעלה הם דוגמה להמחשה. בפועל צריך לכתוב אותם נכון לפי ה-CLSID / TLBID / ProgID / threading model שהקומפוננטה חושפת בפועל.
flowchart TB
accTitle: דרישת ההתאמה בין שני ה-manifests
accDescr: תרשים שמראה שה-dependentAssembly בצד האפליקציה וה-assemblyIdentity בצד הקומפוננטה חייבים להתאים בשם ובגרסה, ואם יש פער זה הופך לכשל בהפעלה שהסיבה לו לא נראית ממראה השגיאה.
mm1["dependentAssembly בצד האפליקציה"] --> mm3{"השם והגרסה תואמים?"}
mm2["assemblyIdentity בצד הקומפוננטה"] --> mm3
mm3 -->|"תואם"| mm4["ה-resolution מצליח"]
mm3 -->|"לא תואם"| mm5["כשל בהפעלה בלי סיבה ברורה"]
איור 12: יותר מפרטי ה-XML, מה שקובע הוא שהשני התגים תואמים.
10.4 איפה מציבים ואיך עושים embed ל-manifest
זו הנקודה שהכי קל להיתקע בה בפועל בהליך. אם מציבים כקובץ נפרד או עושים embed בבינארי, השם המותר משתנה.
side-by-side מחפש private assembly בתיקיית האפליקציה בסדר הזה.
- תיקיית WinSxS
<appdir>\<assemblyname>.DLL<appdir>\<assemblyname>.manifest<appdir>\<assemblyname>\<assemblyname>.DLL<appdir>\<assemblyname>\<assemblyname>.manifest
אם נמצא DLL עם שם זהה לשם ה-assembly, החיפוש נעצר שם. מכאן יש רק שתי אפשרויות ישימות.
| הצבה | שם ה-assembly | קובץ | resource ID ל-embed |
|---|---|---|---|
| קובץ נפרד | שם שונה משם ה-DLL. לדוגמה: Vendor.CameraControl.Asm |
Vendor.CameraControl.Asm.manifest ליד ה-DLL |
לא עושים embed |
| embed ב-DLL | יכול להיות זהה לשם ה-DLL. לדוגמה: Vendor.CameraControl |
רק Vendor.CameraControl.dll |
1 |
כלומר, אם משתמשים בשם Vendor.CameraControl.Asm כמו בדוגמאות 10.2 ו-10.3, זה תפעול לקובץ נפרד. אם עוברים ל-embed, משנים את ה-name של assemblyIdentity ל-Vendor.CameraControl, ומיישרים גם את dependentAssembly לאותו שם.
flowchart TB
accTitle: המנגנון שבו החיפוש נעצר
accDescr: תרשים שמראה ש-side-by-side מחפש private assembly בסדר מ-WinSxS ואילך בתיקיית האפליקציה, ואם נמצא DLL עם שם זהה לשם ה-assembly החיפוש נעצר שם, ולכן בתפעול של קובץ נפרד צריך שהשם יהיה שונה משם ה-DLL.
se1["מתחילים חיפוש private assembly"] --> se2["מחפשים DLL בשם ה-assembly"]
se2 -->|"נמצא"| se3["החיפוש נעצר שם"]
se2 -->|"לא נמצא"| se4["מחפשים קובץ manifest באותו שם"]
se3 -.-> se5["בקובץ נפרד, מבדילים את השם"]
איור 13: מגבלת השמות מגיעה מזה שהחיפוש נעצר באמצע.
יש עוד מגבלה שקל לטעות בה. אי אפשר לשים component manifest כ-resource ב-EXE. מה שאפשר לשים ב-EXE הוא ה-application manifest.
ל-embed משתמשים ב-mt.exe (Manifest Tool) מ-Windows SDK. הריצו אותו מ-Developer Command Prompt של Visual Studio.
rem 1. לפני ה-embed, קודם עוברים syntax check
mt.exe -manifest MyApp.exe.manifest -validate_manifest
mt.exe -manifest Vendor.CameraControl.manifest -validate_manifest
rem 2. embed של ה-application manifest לתוך ה-EXE
mt.exe -manifest MyApp.exe.manifest -outputresource:MyApp.exe;#1
rem 3. embed של ה-component manifest לתוך ה-DLL (resource ID 1)
mt.exe -manifest Vendor.CameraControl.manifest -outputresource:Vendor.CameraControl.dll;#1
rem 4. שולפים ובודקים אם ה-embed הצליח
mt.exe -inputresource:Vendor.CameraControl.dll;#1 -out:extracted.manifest
אם משווים את extracted.manifest שהוצא בפקודה הרביעית ל-XML המקורי, אפשר לתפוס מצב של “חשבתי שעשיתי embed, אבל זה לא נכנס”.
flowchart TB
accTitle: הליך ה-embed עם mt.exe
accDescr: תרשים שמראה שב-mt.exe, קודם עוברים syntax check עם validate_manifest, עושים embed של ה-application manifest ל-EXE, של ה-component manifest ל-DLL עם resource ID 1, ולבסוף שולפים ומשווים ל-XML המקורי.
mt1["עוברים syntax check"] --> mt2["embed של צד האפליקציה ל-EXE"]
mt2 --> mt3["embed של הקומפוננטה ל-DLL (ID 1)"]
mt3 --> mt4["שולפים ומשווים ל-XML המקורי"]
איור 14: את ה-embed מריצים כהליך אחד שכולל גם את הבדיקה.
יש שתי הערות ל-mt.exe.
- הקבצים שה-manifest מפנה אליהם חייבים להיות באותה תיקייה כמו ה-manifest. אם כתוב
<file name="Vendor.CameraControl.dll">, יש להציב את ה-DLL הזה ליד ה-manifest לפני ההרצה. אם פלט הבנייה ומיקום ניהול ה-manifest מופרדים, זה ייעצר כאן - אם משמיטים את ה-resource ID ב-
-outputresource, נעשה שימוש ב-CREATEPROCESS_MANIFEST_RESOURCE(= 1). כדי לא להסתמך על 1 בלי כוונה, עדיף לציין;#1במפורש. זה גם קריא יותר
אם רוצים רק להחליף embed קיים, אפשר להשתמש ב--updateresource:<file>;#1. זה שקול לזה שמעבירים את אותם arguments ל--inputresource ול--outputresource.
10.5 הליך בדיקה כולל סביבה נקייה
ל-Reg-Free COM כמעט אין משמעות ל”עבד במחשב הפיתוח”. תמיד נשארת האפשרות שזה רק בזכות registration מקומי ב-registry. בודקים בסדר הזה.
- בונים, ומרכזים את כל חבילת ההפצה בתיקייה אחת. EXE, manifest, COM DLL, dependent DLL, VC++ runtime, ואפילו DLL של proxy / stub
- מכינים סביבת בדיקה. אידיאלי שזו סביבה שה-COM הזה מעולם לא נרשם בה. Windows Sandbox מאפשר לנסות בכל פעם ממצב נקי לגמרי
- בסביבה הזו, קודם מוודאים שה-COM הרלוונטי לא רשום. אם הפקודה למטה מחזירה שגיאה שהמפתח לא נמצא, זה סימן שלא נרשם
- מעתיקים את התיקייה ומפעילים ישירות. בלי להריץ installer ובלי
regsvr32 - מוודאים שגם יצירת אובייקט COM עוברת. רק ההפעלה לא בודקת קומפוננטות שנוצרות באיחור. מפעילים בפועל את המסך או הפונקציה שבה
CoCreateInstanceרץ - אם נכשל, אוספים לוג לפי ההליך בסעיף 11.2
הפקודה בשלב 3 היא כך.
reg query "HKCR\CLSID\{01234567-89AB-CDEF-0123-456789ABCDEF}" /reg:64
reg query "HKCR\CLSID\{01234567-89AB-CDEF-0123-456789ABCDEF}" /reg:32
reg query "HKCR\Vendor.CameraControl.1"
תצוגת ה-registry של 32bit ו-64bit נפרדות, ולכן חובה לבדוק את הצד שתואם ל-bitness של האפליקציה. אם בודקים גם /reg:64 וגם /reg:32, זה בטוח יותר.
כשרוצים לנסות במחשב הפיתוח, מבטלים זמנית את הרישום עם regsvr32 /u Vendor.CameraControl.dll ואז בודקים. עם זאת, בסביבה שבה מוצר אחר משתמש באותו COM, זה משפיע גם עליו, ולכן בטוח יותר להכין סביבה נקייה.
flowchart TB
accTitle: תהליך הבדיקה בסביבה נקייה
accDescr: תרשים שמראה שמרכזים את חבילת ההפצה בתיקייה אחת, מכינים סביבת בדיקה נקייה שה-COM הרלוונטי לא נרשם בה, מוודאים קודם שהוא לא רשום, מעתיקים ומפעילים ישירות, ומריצים עד לפונקציה שבה רץ CoCreateInstance.
cv1["מרכזים את חבילת ההפצה"] --> cv2["מכינים סביבת בדיקה נקייה"]
cv2 --> cv3["מוודאים קודם שאין registration"]
cv3 --> cv4["מעתיקים ומפעילים ישירות"]
cv4 --> cv5["מריצים עד פונקציה שיוצרת COM"]
cv5 -.->|"אם נכשל"| cv6["אוספים לוג וחוקרים"]
איור 15: שילוב בדיקת אי-הרישום מוציא מהבדיקה את מקרה “עובד במקרה”.
11. מלכודות נפוצות
11.1 עובד במחשב הפיתוח, לא עובד ביעד ההפצה
מה שכדאי לחשוד בו ראשית הוא הדפוס שבו בעצם הרישום ב-registry עזר מאחורי הקלעים. בטוח יותר לבצע את בדיקת Reg-Free COM בסביבה נקייה אם אפשר.
11.2 ההפעלה נכשלת עם “side-by-side configuration is incorrect”
הבעיה הזו קורה בגלל אי-התאמה ב-manifest, dependent DLL חסר, VC++ runtime חסר, או פער בארכיטקטורה.
כי הודעת השגיאה עצמה לא ידידותית, מקובל לחקור עם Event Log ו-sxstrace.
את ה-Event Log פותחים דרך Event Viewer > Windows Logs > Application, ומחפשים שגיאה שהמקור שלה SideBySide. שם רואים איזה assembly נכשל ב-resolution.
את sxstrace אוספים תוך שחזור הכשל. עדיף לפתוח Command Prompt כ-Administrator.
rem 1. מתחילים trace. החלון הזה נשאר פתוח
sxstrace trace -logfile:sxstrace.etl
rem 2. בחלון אחר, מפעילים את האפליקציה ומשחזרים את הכשל
rem 3. עוצרים את ה-trace. לוחצים Enter בחלון 1, או מריצים מחלון אחר
sxstrace stoptrace
rem 4. ממירים את ה-.etl הגולמי לפורמט קריא לבני אדם
sxstrace parse -logfile:sxstrace.etl -outfile:sxstrace.txt
אם לא רוצים שתופיע הודעת עצירה, מוסיפים -nostop לשלב 1. אם הפלט ארוך מדי, אפשר להוסיף -filter:MyApp.exe לשלב 4 כדי לצמצם רק לאפליקציה הרלוונטית.
ב-sxstrace.txt שהתקבל אחרי ההמרה, מפורטים לפי הסדר אילו manifest חיפש ואיפה לא היה תואם. גם כשטועים בכללי השם מסעיף 10.4, אפשר לזהות זאת דרך “שם הקובץ שחיפש”.
flowchart TB
accTitle: איך חוקרים שגיאת side-by-side
accDescr: תרשים שמראה שכשההפעלה נכשלת עם side-by-side configuration is incorrect, בודקים ב-Event Viewer שגיאה עם מקור SideBySide, מתחילים trace עם sxstrace, משחזרים את הכשל, ואחרי העצירה ממירים עם parse וקוראים היכן הייתה אי-התאמה.
sx1["בודקים SideBySide ב-Event Viewer"] --> sx2["מתחילים trace עם sxstrace"]
sx2 --> sx3["מפעילים את האפליקציה ומשחזרים את הכשל"]
sx3 --> sx4["עוצרים וממירים עם parse"]
sx4 --> sx5["קוראים איפה היה חיפוש ואי-התאמה"]
איור 16: לא נתקעים על הודעת השגיאה החיצונית. עוקבים אחרי מסלול ה-resolution בלוג וב-trace.
11.3 ההתאמה בין component manifest ל-application manifest נשברת
- ה-name שונה
- ה-version שונה
- ה-processorArchitecture שונה
- ה-manifest שחשבו שהעתיקו הוא בעצם ישן
הפערים האלה נראים קטנים לעין, אבל בזמן ההפעלה משפיעים משמעותית.
11.4 שוכחים למקם dependent DLL
אם מסתפקים בבדיקת Vendor.CameraControl.dll בלבד ומרגישים בטוחים, חסרים Vendor.Helper.dll שנקרא מתוכו, VC++ runtime, ו-DLL של proxy / stub.
Reg-Free COM מקטין את בעיית רישום ה-COM, אבל לא מוחק את בעיית ה-resolution של תלויות native.
11.5 דוחים את הטיפול ב-type library וב-references
גם אם ה-runtime activation עובר, אם יש דרישות כמו:
- רוצים early binding מ-VBA
- רוצים
#importב-C++ - רוצים יצירת interop ב-design-time בצד .NET
צריך דרך להפיץ type information. Reg-Free COM לא מסדר את זה אוטומטית לגמרי, ולכן חשוב לחשוב בנפרד על runtime ו-design-time.
12. סיכום
אם אומרים את Reg-Free COM במשפט אחד, זה מנגנון שמעביר את מידע רישום ה-COM מרמת המכונה לרמת אפליקציה בודדת.
בזכות זה יש יתרונות כמו:
- קל לסגור COM DLL / OCX לרמה מקומית לאפליקציה
- קל יותר לצמצם התנגשות גרסאות
- קל יותר לפשט הפצה ו-rollback
מצד שני, אלה עדיין חשובים:
- 32bit / 64bit
- dependent DLL
- TLB / הגדרת references
- תלות ברישום לא סטנדרטי
- בדיקה בסביבה נקייה
לכן, הגישה הבסיסית להטמעת Reg-Free COM היא כך.
- מתייחסים לזה כעניין של activation
- מפרידים בין נקודות המבט של runtime ו-design-time
- בודקים בסביבה נקייה
- מיישרים מראש bitness ו-dependent DLL-ים
אם עוקבים אחר הסדר הזה, קל בהרבה להימנע מתקלות.
flowchart TB
accTitle: הגישה הבסיסית בזמן ההטמעה
accDescr: תרשים שמראה שבהטמעת Reg-Free COM, מתייחסים לזה כעניין של activation, מפרידים בין נקודות המבט של runtime ו-design-time, בודקים בסביבה נקייה, ומיישרים מראש bitness ו-dependent DLL-ים. אם עוקבים בסדר הזה, קל יותר להימנע מתקלות.
bs1["מתייחסים לזה כ-activation"] --> bs2["מפרידים runtime ו-design-time"]
bs2 --> bs3["בודקים בסביבה נקייה"]
bs3 --> bs4["מיישרים מראש bitness ו-dependent DLL"]
איור 17: אם שומרים על ארבע הגישות בסדר הזה, אפשר לצמצם מאוד תקלות בהטמעה.
13. מאמרים קשורים
- מה זה COM / ActiveX / OCX - הסבר על ההבדלים והקשרים
- איך לטפל היום ב-ActiveX / OCX - טבלת החלטה בין לשמר, לעטוף ולהחליף
- שימוש ב-DLL של .NET 8 מ-VBA עם טיפוסים - חשיפת COM ו-TLB ב-dscom
14. מקורות
- Microsoft Learn - Registration-Free COM オブジェクトの作成
- Microsoft Learn - アプリケーション マニフェスト
- Microsoft Learn - Assembly Manifests (מגבלת resource ID והשם בזמן embed)
- Microsoft Learn - Assembly Searching Sequence (סדר החיפוש של private assembly)
- Microsoft Learn - Manifest File Schema
- Microsoft Learn - Mt.exe
- Microsoft Learn - 登録を必要としない COM 相互運用機能 (.NET Framework)
- Microsoft Learn - COM への .NET Core コンポーネントの公開
- Microsoft Learn - sxstrace
מאמרים קשורים
מאמרים עדכניים עם אותן תגיות, להעמקה בנושאים קרובים.
COM, ActiveX ו-OCX — ההבדלים והקשר ביניהם
מהו COM, מהו ActiveX ומהו OCX — סקירה מעשית של ההבדלים והקשר ביניהם, הזיקה ל-OLE, איפה זה בשימוש, ואיך כדאי להתייחס לזה היום.
WinRT הוא COM — IInspectable, .winmd, language projections, ולמה WinUI עדיין יושב על חוזה בינארי
WinRT אינו managed runtime אלא ABI שנבנה על COM ועליו metadata של .winmd ו-language projections. מכסה IUnknown מול IInspectable, הגדרת HW...
מה זה OLE object? — איך embedding ו-linking עובדים, והמלכודות במסמכים עסקיים
OLE object הוא מה שמטמיע טבלת Excel ב-Word. לומדים embedding מול linking, compound files, In-Place Activation, קישורים שבורים, ניפוח ואבטחה.
איך clipboard ו-drag-and-drop עובדים — טיפול נכון ב-OLE data transfer באפליקציות עסקיות
למה הדבקת Excel מתפרקת ולמה paste נכשל אחרי סגירת המקור: clipboard formats, delayed rendering, OLE drag-and-drop, ומדיניות clipboard hist...
Context menu ו-file association ב-Windows 11 — IExplorerCommand, MSIX ו-sparse package
ב-Windows 11 פריטי context menu של האפליקציה נדחקים מאחורי "הצג אפשרויות נוספות". המאמר מסביר את שרשרת extension → ProgID → verb, מגבלות ...
נושאים קשורים
העמודים האלה ממקמים את הנושא בהקשר רחב יותר של שירותים והחלטות.
נושאים טכניים ב-Windows
שער לנושאי פיתוח Windows, חקירת תקלות וניצול נכסים קיימים.
העברת ActiveX
החלטות אם לשמר, לעטוף או להחליף רכיבי COM / ActiveX / OCX.
שירותים הקשורים לנושא הזה
המאמר קשור ישירות לשירותים הבאים.
פיתוח יישומי Windows
נושא שיושב ישירות על מימוש אפליקציית desktop ל-Windows: הפצה של COM DLL / OCX, הרכבת manifest, bitness, ו-dependent DLL-ים.
ייעוץ טכני וסקירת תכנון
מתאים גם כשצריך להחליט אם לאמץ Reg-Free COM או להשאיר תפעול מבוסס registry, ואיך מפרידים type library מ-design-time references.
שאלות נפוצות
שאלות נפוצות בפניות בנושא המאמר.
- מה זה Reg-Free COM?
- מנגנון שבו מידע הרישום של COM יושב ב-manifest במקום ב-registry. זה קיצור של Registration-Free COM. בזמן ריצה, ב-resolution של CoCreateInstance או CLSIDFromProgID נבדק קודם ה-activation context, וממנו מזוהה ה-DLL לפי מה שכתוב ב-manifest. ככה אפשר להחזיק COM DLL/OCX כ-private לכל אפליקציה, עם יתרונות כמו הפצה ב-XCOPY, פחות התנגשות גרסאות, ו-uninstall שפחות נשבר.
- אם עוברים ל-Reg-Free COM, גם בעיית 32bit/64bit נפתרת?
- לא. process של 32bit יכול לטעון רק in-proc COM DLL של 32bit, ו-process של 64bit רק DLL של 64bit. גם עם Reg-Free זה נשאר כמו קודם. בנוסף צריך לטפל בנפרד בהפצה של dependent DLL-ים ו-VC++ runtime, ב-type library, ב-design-time references, ובתלות במידע רישום לא סטנדרטי. מה ש-Reg-Free COM מעלים הוא בעיקר את הכאב שנגרם מרישום גלובלי.
- למה קונפיגורציית Reg-Free COM עובדת במחשב הפיתוח אבל לא ביעד ההפצה?
- קודם כל כדאי לחשוד שהרישום ב-registry עזר מאחורי הקלעים. אם חסר מידע ב-manifest, ה-COM runtime נופל ל-resolution הרגיל לפי registry, ולכן על מחשב הפיתוח זה עלול לעבוד במקרה בזכות registration מקומי. לכן בודקים Reg-Free COM בסביבה נקייה. אם ההפעלה נכשלת עם 'side-by-side configuration is incorrect', הסיבה בדרך כלל היא manifest לא תואם או dependent DLL חסר, ו-Event Log יחד עם sxstrace הם הדרך הסטנדרטית לחקור.
- אפשר להפוך גם קומפוננטת COM שנבנתה ב-.NET 8 ל-Reg-Free COM?
- כן. ב-.NET 5+ / .NET 8, EnableComHosting יוצר את *.comhost.dll שנכנסת כ-entry point לחשיפת COM, ואם מוסיפים גם EnableRegFreeCom=true, מתקבל side-by-side manifest ל-Reg-Free COM. עם זאת, Reg-Free COM ואסטרטגיית TLB הן שתי שאלות נפרדות. ב-.NET Core / .NET 5+ ה-TLB לא נוצר מה-assembly באופן טבעי כמו ב-.NET Framework, ולכן אם צריך שימוש typed כמו early binding ב-VBA, עדיף לטפל בנפרד ביצירת ה-TLB, ב-embed שלו וב-registration.