ActiveX / OCX: להשאיר, לעטוף או להחליף
· עודכן בתאריך: · Go Komura · COM, ActiveX, OCX, .NET, פיתוח Windows, modernization
היסטוריית עדכונים (גרסה ראשונה, פורסמה בתאריך 12 Mar 2026)
- פרסום ראשון
לצטט את המאמר הזה(DOI: 10.5281/zenodo.22173380)
מאמר זה מאוחסן בארכיון Zenodo. להלן גם ה-DOI שתמיד מפנה לגרסה האחרונה וגם ה-DOI המקובע לגרסה שאתם קוראים.
Go Komura (2026). ActiveX / OCX: להשאיר, לעטוף או להחליף. KomuraSoft LLC. https://doi.org/10.5281/zenodo.22173380 https://comcomponent.com/he/blog/activex-ocx-keep-wrap-replace-decision-table/
- DOI (הגרסה האחרונה)
- 10.5281/zenodo.22173380
- DOI (הגרסה הזו)
- 10.5281/zenodo.22173381
פרויקטים שבהם עולות המילים ActiveX / OCX, בדרך כלל האווירה קצת כבדה.
- אפליקציית VB6 או C++ / MFC ישנה עדיין בשימוש
- ה-SDK של ציוד תעשייתי או מכשיר מדידה מציע רק OCX
- אתר פנים-ארגוני שמניח ActiveX, ואי אפשר לצאת מ-IE mode
- רוצים לעבור מ-32bit ל-64bit, אבל OCX בודד מסרב
אבל גם “זה ישן אז זורקים הכול” וגם “זה עובד אז שומרים לנצח” הן גישות רשלניות. החשוב הוא להבחין אם ה-ActiveX / OCX הוא סתם רכיב UI, או שהוא ממשק גבול שנושא בתוכו מפרט עסקי או מפרט של ציוד.
המאמר הזה עובר בסדר קל לקבלת החלטה, מה לבחור כשמוצאים ActiveX / OCX — להשאיר, לעטוף או להחליף.
המקרים הרלוונטיים הם, לדוגמה:
- אפליקציות desktop קיימות מסוג VB6 / MFC / WinForms
- מעבר הדרגתי ל-C# / .NET
- מסכי legacy שכוללים WebBrowser / IE mode
- אפליקציות Windows שכוללות פקדי ActiveX של vendors
תוכן עניינים
- קודם כל, המסקנות
- ActiveX / OCX במאמר הזה
- טבלת ההחלטה
- 3.1. סקירה
- 3.2. החלטה להשאיר
- 3.3. החלטה לעטוף
- 3.4. החלטה להחליף
- 3.5. תלות בדפדפן נשקלת בנפרד
- נקודות שקל לטעות בהן
- 4.1. רכיב UI, או רכיב שנושא spec
- 4.2. 32bit / 64bit וגבול ה-process
- 4.3. registration, הפצה, הרשאות, רישיונות
- 4.4. STA / message loop / callback
- 4.5. יש בדיקות? אפשר לצפות?
- המלצות לפי דפוסים נפוצים
- 5.1. אפליקציית desktop פנים-ארגונית שיציבה כרגע
- 5.2. רוצים להעביר OCX 32bit לצד 64bit
- 5.3. מסך שמניח IE / WebBrowser
- 5.4. ActiveX עם בקרת התקן או מפרט ייחודי
- anti-patterns נפוצים
- checklist להתחלת מעבר
- איך בוחרים בפועל
- סיכום
- סוג ההתייעצות שמתאים לזה
- מקורות
ב-diagram, solid line מציינת relation שתמיד מתקיים ו-dashed line מציינת relation מותנה (התנאים מופיעים בהסבר של כל relation ב-detail page). הרשימה המלאה של ה-relations (סה”כ 24, כולל evidence ו-certainty) וההגדרות של ה-concepts המרכזיים נמצאות ב-detail page של ה-knowledge map (ביפנית). Data: JSON-LD / Turtle
1. קודם כל, המסקנות
- כשרואים ActiveX / OCX, השאלה הראשונה אינה “האם זה ישן”, אלא מה הרכיב הזה באמת אחראי עליו
- אם זה סתם רכיב UI, ההחלפה יחסית קלה
- אם הוא נושא בקרת התקן, דוחות, פורמט קובץ ייחודי או הרגלי הפעלה ישנים — בטוח יותר קודם לעטוף מאשר לממש מחדש ישר
- אם הוא יציב ב-desktop ותחום השינוי קטן, גם ההחלטה להשאיר סבירה לגמרי
- תלות ב-ActiveX בדפדפן אפשר להאריך חיים, אבל העתיד שלה דל — עדיף להסתכל מתוך עדיפות להחלפה
- אי אפשר לטעון OCX 32bit לתוך process 64bit ישירות. זו מגבלה שלא עוברים בכוח רצון
- חיכוך שאינו טכני — registration, DLL תלויים, הרשאות admin, רישיונות, STA / MTA — נוטה להיות המכשול הגדול
- גם “סתם לכתוב הכול מחדש” וגם “להקפיא לנצח כי זה מפחיד” הן בחירות עם שיעור תקלות גבוה
בקיצור, סדר ההחלטה הוא:
- מה יש בתוך ה-OCX הזה
- האם צריך להשתמש בו באותו process
- האם זה נתקע ב-32bit / 64bit, registration או תלות בדפדפן
- האם צריך קודם ליצור גבול שניתן לבדיקה, ורק אז להחליף
בסדר הזה, קל בהרבה לקבל החלטה.
flowchart TB
accTitle: סדר ההחלטה
accDescr: תרשים המראה שהסדר לבדיקה הוא מה יש בתוך ה-OCX, האם צריך אותו process, האם 32bit או 64bit או registration או תלות בדפדפן חוסמים, והאם צריך ליצור גבול שניתן לבדיקה לפני שמחליפים.
s1["מה יש בתוך ה-OCX"] --> s2["האם צריך אותו process"]
s2 --> s3["האם bitness, registration או דפדפן חוסמים"]
s3 --> s4["ליצור גבול לפני שמחליפים"]
איור 1: לא “האם זה ישן” — בסדר הזה קל יותר להחליט בין להשאיר, לעטוף או להחליף.
2. ActiveX / OCX במאמר הזה
קודם כול, נבהיר איך נעשה שימוש במילים במאמר הזה.
| מילה | המשמעות במאמר הזה |
|---|---|
| COM | מודל הרכיבים הבינארי-תואם של Windows. ממשקים ציבוריים, registration, מודל ה-Apartment וכדומה הם הבסיס שלו |
| ActiveX / OCX | בעבודה בפועל, המונחים משמשים לרוב יחד לציון פקדים מבוססי COM ונכסים סביבם. כולל בעיקר פקדי UI מסוג .ocx ורכיבים שמוטמעים ב-IE / container |
| תלות ב-WebBrowser / IE | גם אם זה לא ActiveX ממש, זה כולל דפדפן מוטמע או אינטגרציה שמניחים “עולם IE”. מבחינת ההחלטה, זו בעיה קרובה מאוד |
בהגדרה מדויקת, ActiveX ו-COM אינם אותו הדבר. אבל הנקודות שמקשות בעבודה בפועל דומות מאוד.
- האם ה-32bit / 64bit מתאימים
- איך מפיצים את ה-registration וה-DLL התלויים
- באיזה host / container זה רץ
- האם STA, message loop או callback לא חוסמים
- האם לא נשארה תלות בדפדפן
המאמר הזה מרכז את נקודות ההחלטה המעשיות האלה.
flowchart TB
accTitle: נקודות שמקשות בעבודה בפועל
accDescr: תרשים המראה שגם אם ActiveX ו-COM אינם אותו הדבר במדויק, הנקודות שמקשות בעבודה בפועל דומות מאוד: התאמת bitness, הפצת registration ו-DLL, זהות ה-host, והנחות STA ו-callback שמובילות לפעמים לתלות בדפדפן.
ax["מקרה ActiveX / OCX"] --> p1["האם bitness מתאים"]
ax --> p2["הפצת registration ו-DLL תלויים"]
ax --> p3["באיזה host זה רץ"]
ax --> p4["הנחות STA ו-callback"]
p4 -.-> p5["האם נשארה תלות בדפדפן"]
איור 2: גם אם ActiveX ו-COM שונים בהגדרה מדויקת, הנקודות שמקשות בעבודה בפועל מתרכזות באזור הזה.
בנוסף, נסכם מראש את הקיצורים שיופיעו בהמשך כמובנים מאליהם. גם אם בפרק 7, ב-checklist, כתוב “למפות ProgID ו-CLSID”, בלי להבין את זה קשה להתקדם.
| מונח | הגייה / שם מלא | משמעות |
|---|---|---|
| CLSID | Class ID | GUID שמזהה באופן ייחודי את המימוש (המחלקה) של רכיב COM. גם registration ב-Registry נשען על הערך הזה |
| ProgID | Programmatic Identifier | שם קריא לבני אדם שמוצמד ל-CLSID. מחרוזת כמו Excel.Application |
| IID | Interface ID | GUID שמזהה ממשק COM באופן ייחודי. שונה מ-CLSID |
| TLB | Type Library | קובץ שמחזיק בפורמט בינארי מידע טיפוסים כמו ממשקים, שיטות וטיפוסי ארגומנטים. בזכות זה אפשר לקרוא “עם טיפוסים” מ-VB6 או מ-.NET |
| RegAsm | Assembly Registration Tool | כלי שמגיע עם .NET Framework. רושם assembly של .NET ב-Registry כדי שיהיה אפשר להשתמש בו מ-COM |
| AxHost | — | מחלקת בסיס לאירוח פקד ActiveX בתוך Windows Forms |
| AxImp | ActiveX Control Importer | כלי שמייצר מ-OCX assembly עוטף עבור Windows Forms |
| in-proc / out-of-proc | בתוך ה-process / מחוץ ל-process | האם זה רץ באותו process כמו הקורא (DLL או OCX), או ב-process נפרד (שרת EXE) |
| LocalServer | — | צורה שבה שרת COM רץ כ-EXE ב-process נפרד. מאפשרת לעבור את חומת ה-bitness ולבודד קריסות |
| Reg-Free COM / side-by-side | COM ללא registration | מנגנון שפותר COM לפי מידע שכתוב במניפסט של האפליקציה, בלי registration ב-Registry |
| רישיון design-time / runtime | בזמן פיתוח / בזמן ריצה | בפקדים של vendors, לפעמים הטיפול ברישיון שונה בין הדבקה על המסך במחשב הפיתוח לבין הרצה אצל הלקוח |
| adapter / facade | — | טיפוס תכנוני שמחליף API ישן ומפורט ב-API גס ונוח יותר |
| STA / MTA | Single / Multi Threaded Apartment | מודל ה-threading של COM. קובע מאיזה thread מותר לקרוא |
3. טבלת ההחלטה
3.1. סקירה
אם מסתכלים על הטבלה הזו קודם, רוב הכיוון כבר נקבע.
| מצב | הבחירה הראשונה | הסיבה |
|---|---|---|
| תלות ב-ActiveX בדפדפן | נטייה להחליף | Edge עצמו לא תומך ב-ActiveX, ו-IE mode ממוקם כאמצעי הארכת חיים |
| ב-desktop, ה-OCX יציב ותחום השינוי קטן | נטייה להשאיר | עלות הפירוק עכשיו לרוב גדולה יותר |
| רוצים רק לעבור ל-.NET בסביבה, אבל התנהגות הפקד לא ברורה | נטייה לעטוף | קודם לקבע את הגבול בטוח יותר |
| רוצים לטעון OCX 32bit לתוך process 64bit ישירות | לעטוף / שינוי מבנה | זה גבול שאי אפשר לעבור in-proc |
| נעשה בו שימוש רק כרכיב UI, ויש תחליף | נטייה להחליף | לרוב מספיקה החלפה של השטח |
| vendor סיים תמיכה, חתימה, registration או DLL תלויים גורמים תקלה כל פעם | נטייה להחליף | עלות התפעול כבר מתגלה כחוב טכני |
| כולל בקרת התקן, דוחות או פרוטוקול ייחודי | נטייה לעטוף | קודם צריך לקבע את ההתנהגות, אחרת עלות ההחלפה לא ניתנת לחיזוי |
flowchart TD
accTitle: תרשים ההחלטה המלא - להשאיר, לעטוף או להחליף
accDescr: תרשים המראה שההסתעפות הראשונה היא תלות בדפדפן, ואם כן עדיפות להחלפה כי IE mode הוא רק הארכת חיים; אחרת בודקים אם זה בעיקר רכיב UI עם תחליף שקול ואז שוקלים החלפה או קודם עטיפה; אם לא, בודקים אם יש בקרת התקן או מפרט ייחודי ואז קודם עוטפים ומכינים בדיקות; ואם לא, בודקים אם registration, bitness או הפצה כואבים ואז בוחנים מחדש את המבנה, אחרת ההחלטה להשאיר סבירה.
start["יש ActiveX / OCX"] --> q1{"תלות בדפדפן?"}
q1 -- "כן" --> p1["עדיפות להחלפה — IE mode הוא רק הארכת חיים"]
q1 -- "לא" --> q2{"בעיקר רכיב UI?"}
q2 -- "כן" --> q3{"יש תחליף שקול?"}
q3 -- "כן" --> p2["לשקול החלפה"]
q3 -- "לא" --> p3["קודם לעטוף ולקבע את הגבול"]
q2 -- "לא" --> q4{"יש בקרת התקן, מפרט ייחודי או לוגיקת דוחות?"}
q4 -- "כן" --> p4["קודם לעטוף — להכין בדיקות ואז להחליף בהדרגה"]
q4 -- "לא" --> q5{"registration, bitness או הפצה כואבים?"}
q5 -- "כן" --> p5["בחינה מחדש של המבנה — לשקול out-of-proc, חיבור ל-process נפרד או Reg-Free COM"]
q5 -- "לא" --> p6["גם ההחלטה להשאיר ריאלית"]
איור 3: ההסתעפות הראשונה היא תלות בדפדפן, ואחר כך הכיוון נקבע לפי רכיב UI, קיום תחליף, או מפרט שהרכיב נושא.
מכאן נסקור כל דפוס בתורו.
3.2. החלטה להשאיר
רק בגלל שזה ActiveX / OCX, זה לא אומר שזה מיד מועמד להחלפה. אם מתקיימים תנאים כאלה, לרוב זול יותר פשוט להשאיר:
- טווח השימוש סגור, וסביבת התפעול (הפצה פנים-ארגונית, צירוף לציוד וכדומה) קבועה
- הפקד יציב גם כרגע, ודרישות השינוי לא גדולות
- ה-vendor עדיין פעיל, או שיש יכולת תחזוקה מינימלית בבית
- זה לא תלוי בדפדפן, ונשלם בתוך ה-host הקיים ב-desktop
- אין צורך לשנות בינתיים את הנחת ה-32bit / 64bit
החשוב כאן הוא שלהשאיר לא שווה להזניח. אם משאירים, כדאי לעשות לפחות את זה:
- לתעד בכתב את מערכת ההפעלה הנתמכת, ה-bitness, ה-DLL התלויים הנדרשים ושלבים ל-registration
- להעביר התקנה, registration ו-unregister לסקריפט או למתקין, ולא להסתמך על פתק ידני
- להכין smoke test בסביבה נקייה
- לרכז את הקריאות לפקד במקום אחד ככל האפשר, במקום לפזר אותן בכל האפליקציה
הכי גרוע הוא להמשיך 10 שנים עם “זה עובד אז לא נוגעים”, עד ששום אחד כבר לא יכול להסביר את ההנחות. ככל שבוחרים להשאיר, הפיכת ההנחות לגלויות הופכת חשובה יותר.
flowchart TB
accTitle: הפיכת ההנחות לגלויות כחלק מההחלטה להשאיר
accDescr: תרשים המראה שההחלטה להשאיר אינה הזנחה אלא כוללת תיעוד ההנחות, הפיכת שלבי ה-registration לסקריפט, הכנת smoke test בסביבה נקייה, וריכוז הקריאות במקום אחד.
keep["ההחלטה להשאיר"] --> d1["תיעוד ההנחות בכתב"]
keep --> d2["הפיכת שלבי ה-registration לסקריפט"]
keep --> d3["הכנת smoke test"]
keep --> d4["ריכוז הקריאות במקום אחד"]
איור 4: להשאיר זה לא להזניח — ככל שבוחרים להשאיר, כדאי יותר לחשוף את ההנחות.
3.3. החלטה לעטוף
בעבודה בפועל, זו הבחירה שדורשת הכי הרבה עבודה.
“לעטוף” כאן משמעו לכלוא את ה-ActiveX / OCX בתוך גבול צר, ולהראות אותו לסביבה כ-API חדש או כרכיב מסך חדש.
זה יעיל מאוד. מכיוון שאם נכנסים למימוש מחדש מלא לפני שהתנהגות הרכיב הישן ברורה עד הסוף, בקלות נופלים לצרה כפולה — חשיפת spec ושחזור תקלות. בטוח יותר לבודד קודם את הרכיב הישן, ולקבע רק את הגבול.
flowchart TB
accTitle: המבנה של הבחירה לעטוף
accDescr: תרשים המראה שכליאת ה-ActiveX / OCX בתוך גבול צר והצגתו לסביבה כ-API או כרכיב מסך חדש מונעים את הצרה הכפולה של חשיפת spec ושחזור תקלות שנובעת ממימוש מחדש מלא לפני שההתנהגות ברורה.
old["ActiveX / OCX"] --> wall["כליאה בתוך גבול צר"]
wall --> api["הצגה כ-API חדש"]
api --> app["הסביבה רואה רק את הצוהר החדש"]
wall -.-> safe["הימנעות מחשיפת spec ומשחזור כפול"]
איור 5: “לעטוף” הוא בידוד הרכיב הישן ובניית צוהר חדש, כשלב מקדים לפני מימוש מחדש מלא.
יש כמה תבניות לעטיפה.
| דרך עטיפה | מתאים ל | נקודות לבדוק |
|---|---|---|
| host ב-WinForms + AxHost / AxImp | הטמעה במסך desktop קיים, השארת מעט מסכים בלבד | STA, אירועים, תלות בזמן עיצוב, רישיון |
| helper EXE של 32bit / COM LocalServer / גישור ל-process נפרד | רוצים לעבור ל-64bit, לבודד קריסות | תקשורת בין-process, סדר עלייה, ניטור, פריסה |
| צוהר תאימות COM בצד ה-.NET | רוצים לשמר את קוראי ה-COM הקיימים אבל לעדכן את הפנים | IID / CLSID / TLB / שיטת registration / bitness |
אחרי שהכיוון נקבע, נכתוב גם מה הצעד הראשון.
| דרך עטיפה | מה עושים ראשון | הסבר מפורט |
|---|---|---|
| host ב-WinForms + AxHost | ב-Visual Studio, לחיצה ימנית על ה-Toolbox ← “Choose Toolbox Items” ← בוחרים את היעד בלשונית “COM Components”. בשורת פקודה: aximp |
המלכודות שב-registration וב-bitness בפיתוח COM/OCX/ActiveX |
| helper EXE של 32bit / LocalServer | רושמים את ה-EXE של 32bit כשרת COM, וקוראים לו מצד ה-64bit ב-out-of-proc | COM שימושי — מקרה של קריאה ל-DLL 64bit מאפליקציית 32bit |
| Reg-Free COM | כותבים file ו-comClass במניפסט של האפליקציה, ופותרים בלי registration ב-Registry |
מהו Reg-Free COM — מנגנון להשתמש ב-COM ללא registration |
| צוהר תאימות COM בצד ה-.NET | חושפים את צד ה-.NET כ-COM, ובמידת הצורך מייצרים TLB עם dscom | איך משתמשים ב-DLL .NET 8 מ-VBA עם טיפוסים — חשיפת COM ו-dscom TLB |
מריצים את aximp מתוך Developer Command Prompt של Visual Studio.
aximp C:\path\to\MyControl.ocx
בעזרת זה נוצרים שני קבצים: runtime callable wrapper מסוג COM, ועוטף עבור Windows Forms שיורש מ-AxHost. שימו לב ששם הקובץ נקבע לפי ה-ProgID, לא לפי שם הקובץ המקורי. בדוגמה מהתיעוד של Microsoft, מ-msdxm.ocx יוצאים MediaPlayer.dll ו-AxMediaPlayer.dll. מוסיפים את השני כהפניה, ומדביקים את AxMediaPlayer על הטופס.
flowchart TB
accTitle: מה aximp מייצר
accDescr: תרשים המראה שהעברת OCX ל-aximp מייצרת שני קבצים — DLL עוטף מסוג COM ו-DLL עוטף שיורש מ-AxHost — כשהאחרון מתווסף כהפניה ומודבק על הטופס, ושם הקובץ נקבע לפי ProgID.
ocx["ה-OCX היעד"] --> tool["הרצת aximp"]
tool --> rcw["DLL עוטף מסוג COM"]
tool --> ax["DLL עוטף שיורש מ-AxHost"]
ax --> form["הוספה כהפניה והדבקה על הטופס"]
tool -.-> name["שם הקובץ נקבע לפי ProgID"]
איור 6: הפלט של aximp הוא שני DLL, ומה שמדביקים על הטופס הוא העוטף שיורש מ-AxHost.
עבור Reg-Free COM, המניפסט הצדדי של האפליקציה במינימום נראה כך:
<?xml version="1.0" encoding="utf-8"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32" name="MyApp" version="1.0.0.0" />
<file name="MyControl.ocx">
<comClass
clsid="{01234567-89AB-CDEF-0123-456789ABCDEF}"
threadingModel="Apartment"
progid="MyCompany.MyControl.1" />
</file>
</assembly>
בשיטת ה-LocalServer, ב-Registry נכנס נתיב ה-EXE תחת HKEY_CLASSES_ROOT\CLSID\{CLSID}\LocalServer32. שרת EXE שנבנה עם ATL או MFC לרוב תומך, כמוסכמה, ב-self-registration וב-unregister עם MyServer.exe /regserver ו-/unregserver. אבל בגלל שהתצוגה ב-Registry נחלקת לפי bitness, חשוב תמיד לזכור ש-EXE של 32bit נרשם בצד ה-32bit של ה-Registry.
flowchart TB
accTitle: registration של LocalServer ותצוגת ה-Registry
accDescr: תרשים המראה שבשיטת LocalServer נרשם נתיב ה-EXE תחת LocalServer32 מתחת ל-CLSID, שאפשר לרשום עצמית עם המוסכמה של regserver, אבל תצוגת ה-Registry נחלקת לפי bitness ו-EXE של 32bit נרשם בצד ה-32bit.
exe["שרת EXE"] --> reg["רישום נתיב תחת LocalServer32"]
reg --> view{"מה ה-bitness של ה-EXE?"}
view -->|"32bit"| v32["רישום בתצוגת ה-32bit"]
view -->|"64bit"| v64["רישום בתצוגת ה-64bit"]
איור 7: יעד ה-registration של LocalServer נקבע לפי תצוגת ה-Registry שמחולקת לפי bitness.
מה שחשוב במיוחד הוא לא להעתיק בעטיפה 200 שיטות מה-API הישן כמו שהן. אם עושים את זה, פשוט מייבאים את המגבלות הישנות לקוד החדש.
כשעוטפים, שימת לב לדברים האלה עוזרת מאוד:
- להפוך לשיטות בגרגר גס
- לא לתת לקוד המסך לגעת ישירות ב-OCX
- לתעד את הלוג הנדרש בכשל בגבול עצמו
- לקבוע בגבול את האחריות ל-timeout, ניסיון חוזר והמרת exceptions
- לבנות כך שגם התחליף העתידי יוכל להתחלף עם אותו interface
יש גם מקרים שבהם רוצים לשמר רק את כניסת ה-COM בצד ה-.NET החדש. במקרה כזה, מבנה של “לעדכן את הפנים תוך שמירה על חוזה ה-COM בלבד” הוא ריאלי. עם זאת, לא בטוח שמספיק “סתם RegAsm” בתחושה של תקופת ה-.NET Framework. הטיפול הנוכחי ב-COM host, ב-TLB, ב-bitness וב-Registry-Free COM של .NET כדאי לתכנן מראש — זה נוח יותר בהמשך. פרטים על זה נמצאים ב-איך משתמשים ב-DLL .NET 8 מ-VBA עם טיפוסים — חשיפת COM ו-dscom TLB וב-מהו Reg-Free COM — מנגנון להשתמש ב-COM ללא registration, כולל שלבים מפורטים.
flowchart TB
accTitle: אחריות שנקבעת בגבול
accDescr: תרשים המראה שכשעוטפים הופכים לשיטות בגרגר גס, לא נותנים לקוד המסך לגעת ישירות ב-OCX, קובעים בגבול את אחריות הלוג, ה-timeout וההמרה, ובונים כך שגם התחליף העתידי יוכל להתחלף עם אותו interface.
b["הגבול בעטיפה"] --> r1["שיטות בגרגר גס"]
b --> r2["תיעוד לוג בגבול"]
b --> r3["timeout והמרת exceptions"]
b --> r4["צוהר אחיד להחלפה עתידית"]
r1 -.-> ng["לא להעתיק 200 שיטות ישנות"]
איור 8: הערך של העטיפה הוא ריכוז האחריות בגבול; העתקה נאמנה של ה-API הישן לא מייצרת את הערך הזה.
3.4. החלטה להחליף
ההחלפה מתאימה בעיקר במקרים שבהם הבעיה היא ישנוּת השטח בלבד.
כדאי להסתכל מתוך עדיפות להחלפה במקרים כאלה:
- ה-ActiveX משמש רק כרכיב UI
- ה-vendor הוציא ממשיך ל-.NET / WPF / WebView2
- תלות בדפדפן או הנחת IE מעכבות
- נתקעים כל פעם ב-registration, חתימה, הרשאות admin או הגדרות אבטחה
- יש בדיקות או תרחישים עסקיים שאפשר לאמת בהם מימוש חלופי
מנגד, אם זורקים בבת אחת רכיב שנושא לוגיקת בקרת התקן או דוחות רק בגלל שהמראה שלו ישן, זה כמעט תמיד מתדרדר.
אם מחליפים, מתחילים מה-UI.
- grid
- לוח שנה
- עץ (tree)
- חלק תצוגת דפדפן
- סיוע קלט פשוט
אלה יחסית קלים להחלפה. מצד שני, יש דברים שנראים כמו UI אבל התוכן שלהם עשיר:
- ActiveX של vendor לבקרת התקן
- פקד שמשולב עם הדפסה או יצירת דוחות
- פקד שכולל קריאה וכתיבה של פורמט קובץ ייחודי
- פקד עם הנחות של callback ב-COM וסביב threads
אם מפספסים את ההבדל הזה, הערכת המאמץ מתפרקת בבת אחת.
flowchart TB
accTitle: ההבחנה בהחלטה להחליף
accDescr: תרשים המראה שאם השימוש הוא רק רכיב UI עם תחליף קל להחליף, אך אם הוא נושא spec כמו בקרת התקן, דוחות, פורמט ייחודי או הנחות thread, זריקה מיידית מתדרדרת ועדיף לעבור להחלטה לעטוף.
q{"מה יש מאחורי המראה?"}
q -->|"רק ישנוּת השטח"| easy["קל להחליף"]
q -->|"נושא גוש spec"| heavy["זריקה מיידית מתדרדרת"]
heavy --> wrap["עברו להחלטה לעטוף"]
איור 9: האם זה מתאים להחלפה נקבע לפי ההבחנה בין “ישנוּת השטח” לבין “עושר התוכן”.
3.5. תלות בדפדפן נשקלת בנפרד
זה בעצם קטגוריה נפרדת לגמרי.
בשונה מ-OCX ב-desktop, ל-ActiveX בדפדפן יש סיבה חלשה למדי להמשיך ולפתח אותו.
הסיבה פשוטה: תשתיות הדפדפנים המודרניים לא הופכות את זה לזירה מרכזית. Microsoft Edge עצמו לא תומך ב-ActiveX. מצד שני, IE mode משמש כשכבת תאימות שמפעילה מנוע מסדרת IE עבור אתרים שהוגדרו, ומריצה חלק מתכונות IE כולל ActiveX.
כלומר:
- אפשר להאריך חיים כדי שזה יעבוד עכשיו
- אבל כתכנון ארוך טווח, העתיד לא רחב
flowchart TB
accTitle: המעמד של ActiveX בדפדפן
accDescr: תרשים המראה ש-Microsoft Edge עצמו לא מריץ ActiveX, ו-IE mode מאפשר הארכת חיים כשכבת תאימות עבור אתרים שהוגדרו, אבל העתיד ארוך הטווח דל, ולכן עדיף להסתכל מתוך עדיפות להחלפה.
bax["ActiveX בדפדפן"] --> edge["לא רץ ב-Edge עצמו"]
bax --> iem["אפשר להאריך חיים ב-IE mode"]
iem --> future["העתיד ארוך הטווח דל"]
future --> rep["להסתכל מתוך עדיפות להחלפה"]
איור 10: תלות ב-ActiveX בדפדפן — מפרידים בין הארכת חיים לבין תכנון קבוע, ומסתכלים מתוך עדיפות להחלפה.
אותו הדבר קורה גם לפקד WebBrowser שמוטמע באפליקציית Windows.
WebBrowser גורר אחריו את “עולם” ה-IE, ולכן אם רק רוצים להציג HTML, טבעי יותר להפוך את WebView2 למועמד הראשון לעבודה חדשה מהיום.
עם זאת, חשוב לזכור ש-WebView2 אינו רכיב חלופי מלא ל-WebBrowser.
- סקריפטים שמניחים DOM של IE
- תלות ב-ActiveX
- הנחות סביב
window.external - התנהגות שמניחה אזורי אבטחה או אינטראנט
אלה לא עוברים כמו שהם. אם מחליפים, צריך לתכנן מחדש לא רק את מנוע הרינדור, אלא גם את שטח החיבור בין הדפדפן ל-native.
flowchart TB
accTitle: מה לא עובר ל-WebView2
accDescr: תרשים המראה ש-WebView2 מתאים כמועמד ראשון אם רק רוצים להציג HTML, אבל הוא לא רכיב חלופי מלא, וסקריפטים שמניחים DOM של IE, תלות ב-ActiveX, הנחות window.external והתנהגות אזורי אבטחה לא עוברים כמו שהם.
wb["מ-WebBrowser ל-WebView2"] --> ok["מועמד ראשון אם רק תצוגת HTML"]
wb --> ng["מה שלא עובר כמו שהוא"]
ng --> n1["סקריפטים עם DOM של IE"]
ng --> n2["תלות ב-ActiveX"]
ng --> n3["הנחות window.external"]
ng --> n4["התנהגות אזורי אבטחה"]
איור 11: WebView2 הוא רכיב חלופי למנוע הרינדור, אבל לא יורש את שטח החיבור של עולם ה-IE.
4. נקודות שקל לטעות בהן
4.1. רכיב UI, או רכיב שנושא spec
זו הנקודה החשובה ביותר.
grid או לוח שנה ישן — מתקדמים די רחוק רק בבדיקת תאימות המראה והאירועים. מצד שני, ל-ActiveX שנושא בקרת התקן, דוחות או פורמט ייחודי יש גוש spec מאחורי המראה.
גם אם זה נראה כמו אותו “פקד על המסך”, בפועל יש טווח כזה:
- רכיב תצוגת רשימה בלבד
- רכיב ששולח פקודות להתקן בפרוטוקול ייחודי
- רכיב שמטפל פנימית גם ב-timeout, חיבור מחדש, שידור חוזר וספיגת exceptions
- רכיב שנושא תאימות הדפסה או פורמט ייצוא
מימוש מחדש מיידי של האחרון הופך כמעט תמיד לפרויקט חשיפת spec. כאן בטוח יותר לעטוף קודם.
flowchart TB
accTitle: רכיב UI מול רכיב שנושא spec
accDescr: תרשים המראה שגם אם זה נראה כמו אותו פקד על המסך, רכיב תצוגה בלבד מתקדם רק בבדיקת תאימות, בעוד רכיב שנושא גוש spec כמו פקודות להתקן או תאימות דוחות הופך במימוש מחדש מיידי לפרויקט חשיפת spec, ולכן עדיף לעטוף קודם.
look["פקד על המסך"] --> ui["רכיב תצוגה בלבד"]
look --> spec["רכיב שנושא גוש spec"]
ui --> go["בדיקת תאימות מספיקה"]
spec --> dig["מימוש מחדש מיידי הופך לחשיפת spec"]
dig --> wrap["עדיף לעטוף קודם"]
איור 12: ההבחנה החשובה ביותר. גם אם המראה זהה, האם מאחוריו יש גוש spec קובע את דרך ההתקדמות.
4.2. 32bit / 64bit וגבול ה-process
לרוב מפספסים את הנקודה הזו, אבל היא ממש מהותית.
OCX in-proc חייב להתאים ב-bitness ל-process שטוען אותו. כלומר, אי אפשר לטעון OCX 32bit לתוך אפליקציית 64bit ישירות.
האפשרויות הריאליות במקרה הזה הן בעצם השלוש האלה:
- להשאיר בינתיים גם את אפליקציית ה-host כ-32bit
- לכלוא ב-process 32bit נפרד, ולהתחבר לצד ה-64bit דרך IPC או COM מסוג out-of-proc
- להתחיל להחליף מהמקומות שאפשר להסיר בהם את התלות ב-OCX
כאן, ה-“מכיוון שזה Any CPU זה בטח יסתדר” בדרך כלל לא עובד. גם כשבונים חלון תאימות COM בצד ה-.NET החדש, המראה של קוד managed וה-bitness האמיתי של ה-COM host הם בעיות נפרדות. אם מתחילים בזה ברישול, יוצא הדבר המעצבן: ה-build עובר, אבל זה לא עובד אצל הלקוח.
flowchart TB
accTitle: שלוש האפשרויות ל-OCX 32bit ולמעבר ל-64bit
accDescr: תרשים המראה שמכיוון ש-OCX 32bit לא נטען in-proc לתוך process 64bit, שלוש האפשרויות הריאליות הן להשאיר את ה-host כ-32bit, לכלוא ב-process 32bit נפרד ולהתחבר עם IPC או COM out-of-proc, או להחליף מהמקומות שאפשר להסיר בהם את התלות.
wall["OCX 32bit לא נטען in-proc"] --> o1["השארת ה-host כ-32bit"]
wall --> o2["כליאה ב-process 32bit נפרד"]
wall --> o3["החלפה מהמקומות שאפשר"]
o2 -.-> ipc["חיבור עם IPC או COM out-of-proc"]
איור 13: את חומת ה-bitness אי אפשר לעבור בכוח רצון; האפשרויות הריאליות מצטמצמות לשלוש אלה.
4.3. registration, הפצה, הרשאות, רישיונות
טכנית אפשר לקרוא לזה, אבל זה מת בהפצה. זה קורה הרבה מאוד ב-ActiveX / OCX.
המכשולים האופייניים הם בערך אלה:
- ההנחה סביב
regsvr32תלויה באדם - מיקום ה-DLL התלויים לא מתועד במפורש
- נדרשות הרשאות admin, אבל זה לא הוטמע בנוהל התפעול
- רישיון design-time / runtime של פקד ה-vendor מחולק
- עובד במחשב הפיתוח, אבל לא בסביבה נקייה
אלה עוצרים את הפרויקט גם בלי לגעת בשורת קוד אחת.
יש מקרים שבהם מבנה ללא registration או פריסת side-by-side מקלים, אבל זה לא אבקת קסמים. צריך לוודא התאמה עם ה-container ועם שיטת ההפצה.
בקיצור, מעבר ActiveX / OCX הוא לא רק מימוש, אלא גם תכנון הפצה. אם דוחים את זה, נופלים בגדול בסוף.
flowchart TB
accTitle: מכשולים שעוצרים בהפצה
accDescr: תרשים המראה שתלות אנושית ב-regsvr32, מיקום DLL לא מתועד, הרשאות admin שלא הוטמעו בנוהל, ורישיון מחולק בין design-time ל-runtime עוצרים את הפרויקט גם בלי לגעת בקוד.
dist["תכנון ההפצה"] --> d1["regsvr32 תלוי באדם"]
dist --> d2["DLL תלויים לא מתועדים"]
dist --> d3["הרשאות admin מחוץ לנוהל"]
dist --> d4["רישיון מחולק לשניים"]
d1 -.-> stopx["עוצר גם בלי לגעת בקוד"]
איור 14: זה נקרא טכנית, אבל מת בהפצה — מכשול שמתכננים בנפרד מהמימוש עצמו.
4.4. STA / message loop / callback
ActiveX / OCX הוא לא סתם קריאת DLL. לפעמים יש לו הנחות על מודל ה-threading של COM ועל ה-message loop.
כדאי במיוחד לשים לב למקרים כאלה:
- יציב רק בהנחת UI thread
- הנחת STA, אבל קוראים לו ברשלנות מצד MTA
- callback חוזר באמצע קריאה סינכרונית
- ההנחה על thread קבלת האירועים לא ברורה
בהתחלה זה מופיע כתקלה לא עקבית: “לפעמים נתקע”, “לפעמים אירוע לא מגיע”. אבל בפועל, ברוב המקרים זו הפרת הנחה.
לכן, גם כשעוטפים וגם כשמחליפים, כדאי לקבע מראש באיזה thread יוצרים, באיזה thread קוראים, ואיפה מקבלים אירועים.
flowchart TB
accTitle: קיבוע הנחות ה-thread מראש
accDescr: תרשים המראה שבלי לקבע מראש באיזה thread יוצרים, באיזה thread קוראים, ואיפה מקבלים אירועים, זה הופך לתקלות לא עקביות של תקיעה או אירועים חסרים שהם בפועל הפרת הנחה.
fix["שלוש הנקודות שמקבעים מראש"] --> t1["באיזה thread יוצרים"]
fix --> t2["באיזה thread קוראים"]
fix --> t3["איפה מקבלים אירועים"]
t1 -.-> ghost["אי-בהירות הופכת לתקלה לא עקבית של הפרת הנחה"]
איור 15: “לפעמים זה נתקע” הוא לרוב הפרת הנחה — קיבוע מראש של הבטחות ה-thread מונע את זה.
4.5. יש בדיקות? אפשר לצפות?
ההחלפה קשה לא רק בגלל שהקוד ישן. אלא בגלל שאין קריטריון לכך ש“זה פעל אותו הדבר”.
עצם קיומם של אלה כבר עושה הבדל גדול:
- smoke test לכל תרחיש הפעלה
- דוגמאות קלט/פלט
- צילומי מסך או דוגמאות דוחות
- תבניות שגיאה והתנהגות צפויה
- לוג בזמן timeout או כשההתקן לא מחובר
כשמעורבים התקן או דוחות, קורה דבר מוזר: ההתנהגות בפועל אמינה יותר מהמפרט הכתוב. בלי אמצעי תצפית כאן, ההחלפה הופכת לחפירה ארכיאולוגית.
flowchart TB
accTitle: אמצעי תצפית תומכים בהחלפה
accDescr: תרשים המראה שבלי אמצעי תצפית כמו smoke test, דוגמאות קלט ופלט, דוגמאות דוחות ותבניות שגיאה, אין קריטריון לכך שזה פעל אותו הדבר, וההחלפה הופכת לחפירה ארכיאולוגית.
q{"יש אמצעי תצפית?"}
q -->|"יש"| ok["אפשר לומר שזה פעל אותו הדבר"]
q -->|"אין"| dig["ההחלפה הופכת לחפירה ארכיאולוגית"]
ok -.-> ex["smoke test ודוגמאות מכל סוג"]
איור 16: קושי ההחלפה נקבע פחות לפי ישנות הקוד, ויותר לפי קיום אמצעי תצפית שיודע לומר “פעל אותו הדבר”.
5. המלצות לפי דפוסים נפוצים
5.1. אפליקציית desktop פנים-ארגונית שיציבה כרגע
ההמלצה: נטייה להשאיר.
בתנאים כאלה, לרוב עדיף לא לקלף בכוח:
- נעשה בו שימוש פנים-ארגוני בלבד
- המחשבים ומערכת ההפעלה קבועים במידה מסוימת
- ה-OCX הזה משמש רק בכמה מסכים
- דרישות התיקון קטנות, ואורך החיים צפוי
אבל לא משאירים את זה “עירום” — ריכוז מוקדי הקריאה בלבד ישתלם בהמשך.
כלומר, המדיניות היא כזו:
- כרגע משאירים
- אבל מקבעים רק את הגבול
- כך שכשתידרש החלפה, אפשר יהיה להתחיל משם
המבנה בן שלוש השכבות הזה הכי פשוט.
5.2. רוצים להעביר OCX 32bit לצד 64bit
ההמלצה: לעטוף / לשנות מבנה.
אם ניגשים לזה ישירות, נתקעים. כי אי אפשר לטעון OCX 32bit לתוך process 64bit in-proc.
בפועל, נוח יותר מבנה שכולא ב-process helper של 32bit או ב-LocalServer, ומתקשר עם אפליקציית ה-64bit דרך API בגרגר גס.
sequenceDiagram
accTitle: מבנה שבו helper של 32bit מגשר בין אפליקציית 64bit ל-OCX
accDescr: תרשים רצף המראה שאפליקציית .NET של 64bit פונה ל-helper או LocalServer של 32bit דרך API בגרגר גס, וזה קורא in-proc ל-OCX של 32bit ומחזיר תוצאה מומרת בחזרה.
participant App as אפליקציית .NET 64bit
participant Bridge as helper / LocalServer של 32bit
participant Ocx as OCX של 32bit
App->>Bridge: פנייה עם API בגרגר גס
Bridge->>Ocx: קריאה in-proc
Ocx-->>Bridge: תוצאה / אירוע
Bridge-->>App: תוצאה מומרת
איור 17: OCX שכלוא ב-helper של 32bit, ואפליקציית ה-64bit משתמשת בו דרך API בגרגר גס.
הנקודה כאן היא לא לתווך את כל השיטות הקטנות כמו שהן. גבול בין-process מתעייף מהר אם שולחים דרכו הרבה קריאות קטנות.
- להתקרב לגרגר של פעולה אחת = בקשה אחת
- לעצב ערכי החזרה ושגיאות ליחידות בעלות משמעות
- לתעד לוג בגבול
בצורה הזו, גם קל יותר בהמשך כשבאמת מחליפים את הפנים.
כאן, לפני שרצים לכתוב helper EXE משלנו, יש שלב אחד שכדאי לבדוק קודם.
אם ה-OCX / DLL הזה הוא שרת COM מסוג in-proc, כלומר כזה שאפשר לרשום ב-InprocServer32, אפשר להעלות אותו על process ה-surrogate שמגיע עם Windows ולהוציא אותו כ-Local Server מסוג out-of-proc.
מצמידים ל-CLSID ערך AppID, כותבים במפתח ה-AppID הזה DllSurrogate עם מחרוזת ריקה — וזה הכול, בלי שורת קוד אחת מצידנו.
שלבי ה-registration מרוכזים ב-3.5 של המלכודות שב-registration וב-bitness בפיתוח COM/OCX/ActiveX.
אבל surrogate לא מוחק את הפער ב-bitness. מה שנעלם הוא רק המגבלה שחייבים לרוץ באותו process; הקריאות הופכות ל-out-of-proc, והעלות של marshaling ושל תקשורת בין-process נשארת במלואה.
מה לבחור מבין השניים נקבע בדרך כלל לפי הטבלה הזו.
| מצב | מה בוחרים | הסיבה |
|---|---|---|
| automation object שמסתפק בקריאות לשיטות ובאירועים | קודם surrogate | ה-registration לבדו כבר הופך אותו ל-out-of-proc, ואין קוד לכתוב |
הטיפוסים שעוברים ניתנים ל-marshaling דרך IDispatch או דרך proxy / stub רשומים |
קודם surrogate | הטיפול במעבר הגבול כבר קיים |
ל-CLSID הזה כבר יש registration של EXE, למשל LocalServer32 |
אין ל-surrogate תפקיד כאן | הפעלת שרת ה-EXE או השירות תמיד מקבלת עדיפות |
| רוצים להשתמש בו כפקד ויזואלי שמדביקים על טופס והוא מצייר | לחשוב על מבנה אחר | ההנחה היא שהחלון נמצא בתוך ה-process של ה-host |
| קריאות קטנות בתדירות גבוהה, ותיווך ישיר שלהן יוצא כבד | helper EXE משלנו | צריך שכבה משלנו שתאגד אותן ל-API בגרגר גס |
| ל-interface ייחודי אין marshaler | helper EXE משלנו | מהר יותר להכין proxy / stub או לקבוע בעצמנו את הטיפוסים בגבול |
| רוצים להחזיק בעצמנו את סדר האתחול, החיבור מחדש, ה-timeout והלוג | helper EXE משלנו | אורך החיים של process ה-surrogate נתון בידי COM |
כלומר, surrogate הוא המהלך המינימלי ששווה לנסות ראשון, וה-EXE שלנו הוא המהלך למי שרוצה לתכנן את הגבול בעצמו. השורה בטבלה של 3.1 — “רוצים לטעון OCX 32bit לתוך process 64bit ישירות ← לעטוף / שינוי מבנה” — לא משתנה. פשוט תקראו את ה”לעטוף” הזה כמשהו שיש בתוכו שני שלבים.
5.3. מסך שמניח IE / WebBrowser
ההמלצה: עדיפות להחלפה.
זה תחום שבו “עובד עכשיו” ו”קל לתחזק גם בהמשך” לא בקלות מתלכדים. IE mode עוזר מאוד לתאימות, אבל ההנחה עדיין נשארת מסדרת IE.
לכן, ברור יותר לחלק את החשיבה כך:
- להאריך חיים ב-IE mode כדי לא לעצור פעילות פנים-ארגונית
- אבל לא לבלבל בין הארכת חיים לתכנון קבוע
- לבחור יעד החלפה מבין WebView2, Web טהור, או היברידי של UI native + Web
נכתוב גם את הטיפול הקונקרטי בצד הארכת החיים. IE mode הוא לא משהו ש”פועל מעצמו כשמתקינים Edge” — הוא נפתח עם מנוע מסדרת IE רק אחרי שמציינים את האתר היעד ב-policy. שלושה ערוצי הגדרה:
| שיטה | הגדרה | הערה |
|---|---|---|
| רשימת אתרים | ב-Group Policy של Microsoft Edge מגרסה 78 ואילך, ב-“Configure the Enterprise Mode Site List” מציינים את מיקום ה-XML של רשימת האתרים ב-Enterprise Mode | הצורה הבסיסית ביותר |
| שימוש ברשימת ה-IE הישן | מדיניות “Use the Enterprise Mode IE website list” של Internet Explorer | אם קיימת מדיניות בצד Edge, היא תקבל עדיפות |
| הפניית כל האינטראנט | הפעלת Group Policy של Microsoft Edge מגרסה 77 ואילך: “Send all intranet sites to Internet Explorer” | הטווח רחב, ולכן זה לא תחליף למיפוי מסודר |
התנאים המקדימים: עדכונים אחרונים ל-Windows ול-Edge, תבנית הניהול (template) של Microsoft Edge מותקנת, ותכונת Windows של Internet Explorer 11 מופעלת. אם אחד מאלה חסר, IE mode נכשל.
בנוסף, ב-IE mode פועלים גם פקדי ActiveX וגם Browser Helper Object. כלומר, הארכת החיים באמת עובדת. בדיוק בגלל זה, אם ממשיכים להשתמש בזה בלי לקבוע תנאי סיום, אי אפשר להיחלץ. איך לקלף מוסבר ב-מדריך היציאה ממערכת שתלויה ב-IE mode.
flowchart TB
accTitle: אופן החשיבה על הארכת חיים ב-IE mode
accDescr: תרשים המראה שרק אחרי שמציינים את האתר היעד ב-policy, המצב נפתח כ-IE mode, ואפשר להאריך חיים כולל ActiveX, אבל אם לא קובעים תנאי סיום ומשתמשים בזה כך, אי אפשר להיחלץ.
policy["ציון אתר יעד ב-policy"] --> iem["נפתח ב-IE mode"]
iem --> alive["הארכת חיים כולל ActiveX"]
alive --> cond["שימוש עם תנאי סיום"]
cond -.-> exitw["בלי תנאי סיום אי אפשר להיחלץ"]
איור 18: הארכת החיים של IE mode “באמת עובדת” — בדיוק בגלל זה כדאי להשתמש בה יחד עם תנאי סיום.
בפרט אם משתמשים בפקד WebBrowser רק כמציג HTML פשוט,
עדיפות ההחלפה גבוהה.
מצד שני, אם ה-ActiveX בתוך הדפדפן נושא גם תפקידים כמו קובץ מקומי, התקן, חתימה או תוסף ייחודי, זה כבר לא החלפת מנוע רינדור, אלא תכנון מחדש של החיבור ל-native. כאן הסיפור מתחיל להיות כבד יותר.
5.4. ActiveX עם בקרת התקן או מפרט ייחודי
ההמלצה: קודם לעטוף.
הסוג הזה, התוכן שלו עשיר יותר מהמראה. גם אם תיעוד ה-SDK דל, כתוצאה משנים של הפעלה בשטח, עשויים להצטבר בו התנהגויות שקטות כאלה:
- אופן ההמתנה בכשל חיבור
- ניסיון חוזר אחרי timeout
- סדר האירועים
- טיפול עוקף שסופג הרגלים של המכשיר בפועל
- פרשנות של exceptions וקודי שגיאה
אם בונים מחדש רכיב מהסוג הזה רק בגלל “זה ממילא ישן”, יש סיכוי גבוה שבדיקות השטח יישרפו.
לכן, בטוח יותר להתחיל מכאן:
- לכלוא את הרכיב הקיים בתוך הגבול
- להוסיף לוג כדי לראות מה קורה
- לאסוף תרחישי בדיקה ותבניות ממכשיר אמיתי
- ורק אז לחתוך את הטווח הניתן להחלפה
זה לא מרשים, אבל בעבודה בפועל זה הכי יעיל.
flowchart TB
accTitle: הסדר להתקדמות עם ActiveX שנושא spec
accDescr: תרשים המראה שהסדר הוא לכלוא את הרכיב הקיים בתוך הגבול, להוסיף לוג לחשיפה, לאסוף תרחישי בדיקה ותבניות ממכשיר אמיתי, ורק אז לחתוך את הטווח הניתן להחלפה.
s1["כליאה בתוך הגבול"] --> s2["הוספת לוג לחשיפה"]
s2 --> s3["איסוף תרחישים ותבניות אמיתיות"]
s3 --> s4["חיתוך הטווח הניתן להחלפה"]
איור 19: רכיב שנושא בקרת התקן או מפרט ייחודי — בסדר הזה בדיקות השטח פחות נשרפות.
6. anti-patterns נפוצים
| anti-pattern | מה קשה בו | תיקון ראשוני |
|---|---|---|
| כתיבה מחדש מלאה בגלל שיש ActiveX | פספוס spec וניפוח מאמץ | קודם מיפוי וחיתוך גבול |
| טעינת OCX 32bit ישירות לתוך אפליקציית 64bit | בלתי אפשרי עקרונית | בידוד בצד ה-32bit או שינוי מבנה |
| קריאה ישירה ל-API של הפקד מתוך קוד המסך | נוטה להפוך לבלתי-ניתן-להחלפה | לעבור ל-adapter / facade |
תפעול נוהל regsvr32 באופן ידני |
תקלה כל פעם עקב הבדל בין סביבות | לשקול מתקין, סקריפט או מניפסט |
| להירגע כי יש IE mode | קל לבלבל בין הארכת חיים לתגובה קבועה | לקבוע תוכנית החלפה ותנאי סיום |
| לא לתעד את ההתנהגות לפני ההחלפה | אי אפשר לקבוע מתי זה הושלם | להכין smoke test, דוגמאות נתונים ולוג |
מבין אלה, שלושה נראים במיוחד בעבודה בפועל:
- ממהרים לכתיבה מחדש מלאה
- מזלזלים בחומת ה-bitness
- מפזרים API על פני כל האפליקציה
רק הימנעות משלושת אלה כבר מורידה משמעותית את שיעור התקלות.
flowchart TB
accTitle: שלושת ה-anti-patterns הנפוצים ביותר
accDescr: תרשים המראה שהימנעות משלושה דברים — מיהור לכתיבה מחדש מלאה, זלזול בחומת ה-bitness, ופיזור API על פני כל האפליקציה — מורידה משמעותית את שיעור התקלות.
a1["מיהור לכתיבה מחדש מלאה"] --> avoid["הימנעות משלושת אלה"]
a2["זלזול בחומת ה-bitness"] --> avoid
a3["פיזור API על כל האפליקציה"] --> avoid
avoid --> down["שיעור תקלות נמוך משמעותית"]
איור 20: מבין ה-anti-patterns, שלושת אלה הכי נפוצים, וההימנעות מהם משפיעה הכי הרבה.
7. checklist להתחלת מעבר
בפרויקט ActiveX / OCX, מוצלח יותר לעשות קודם מיפוי מלא ורק אז להיכנס למימוש. הסדר הוא בערך כך:
- למפות את ה-OCX / DLL שנמצאים בשימוש
- שם קובץ, גרסה, ProgID, CLSID, vendor, קיום רישיון
- למפות איפה נעשה בהם שימוש
- מסך, תכונה, דוח, התקן, batch, אינטגרציית Office וכדומה
- לבדוק את תנאי ה-bitness וה-host
- 32bit / 64bit, in-proc / out-of-proc, הנחת STA, תלות בדפדפן
- לבדוק את תנאי ההפצה
- שיטת registration, DLL תלויים, הרשאות admin, התקנה שקטה, שחזור בסביבה נקייה
- להכין smoke test
- לא רק מסלול רגיל, גם כשל, חוסר חיבור ו-timeout
- ליצור גבול
- adapter, service, facade, גישור ל-process נפרד וכדומה
- לנסות ביחידה קטנה — מסך אחד, תכונה אחת, התקן אחד
- מהגבול שהצליח, להרחיב בהדרגה את להשאיר / לעטוף / להחליף
אם מדלגים על השלבים האלה, בהמשך קשה אפילו להסביר “מה בכלל היה קשה”.
8. איך בוחרים בפועל
| מצב | הבחירה הראשונה |
|---|---|
| שימוש פנים-ארגוני בלבד, יציב, שינוי קטן | להשאיר |
| רוצים רק ל-.NET-פיקציה סביבתית | לעטוף |
| 32bit / 64bit מתנגשים | לעטוף / לשנות מבנה |
| תלות ב-IE / WebBrowser / ActiveX בדפדפן | להחליף |
| רכיב UI פשוט עם תחליף | להחליף |
| כולל בקרת התקן, דוחות או מפרט ייחודי | לעטוף |
| נתקעים כל פעם ב-registration או בהפצה | לעטוף או להחליף |
כשמתלבטים, אם קודם מבחינים אם זה רכיב UI או ממשק גבול שנושא spec, קשה בהרבה לטעות.
9. סיכום
איך מתייחסים ל-ActiveX / OCX זה לא נושא שמחליטים לפי “זה legacy אז שונאים את זה”.
יש ארבע נקודות שצריך להסתכל עליהן קודם:
- האם הרכיב סתם UI, או ממשק גבול שנושא spec
- האם צריך להשתמש בו באותו process
- האם 32bit / 64bit, registration, תלות בדפדפן או רישיון חוסמים
- האם אפשר לצפות בהתנהגות לפני ההחלפה
אם ארבעת אלה ברורים, בערך אפשר לסדר כך:
- יציב וצפוי — להשאיר
- רוצים לחדש רק את הסביבה — לעטוף
- רכיב UI או תלות בדפדפן — להחליף
- רכיב שנושא גוש spec — קודם לעטוף ואז להחליף בהדרגה
טכנולוגיית legacy אינה נושא ללעג, אלא נכס ממשי שמכיל היסטוריה וחוזים. עם זאת, כדי להתמודד עם הנכס הזה נדרש תכנון גבול.
כשלומדים לחשוב על “להשאיר, לעטוף, להחליף” יחד, פרויקט ActiveX / OCX הופך פתאום לבעיה שאפשר להתמודד איתה.
flowchart TB
accTitle: הסידור המסכם
accDescr: תרשים המראה שאם יציב וצפוי משאירים, אם רוצים לחדש רק את הסביבה עוטפים, אם רכיב UI או תלות בדפדפן מחליפים, ואם זה גוש spec קודם עוטפים ואז מחליפים בהדרגה.
q["איך מתייחסים ל-ActiveX / OCX"] --> k["יציב וצפוי — להשאיר"]
q --> w["חידוש הסביבה — לעטוף"]
q --> r["UI או תלות בדפדפן — להחליף"]
q --> p["גוש spec — לעטוף ואז להחליף בהדרגה"]
איור 21: אם ארבע נקודות ההסתכלות ברורות, להשאיר / לעטוף / להחליף מסתדרים בצורה הזו.
10. סוג ההתייעצות שמתאים לזה
בנושא הזה, יש ערך גם רק בקיבוע המדיניות לפני שנכנסים ישר לפיתוח.
לדוגמה, ההתייעצויות האלה מתאימות מאוד:
- רוצים למפות אילו OCX-ים באמת צריך להחליף
- רוצים לקבע קודם רק את נקודות התקיעה של 32bit / 64bit
- רוצים לעבור ל-.NET, אבל לשמר רק את כניסת ה-COM
- רוצים להשוות בין הארכת חיים לנסיגה עבור ActiveX שה-vendor שלו סיים תמיכה
- רוצים לראות מאיפה אפשר להתחיל לקלף את התלות ב-IE / WebBrowser
- רוצים להפריד קודם בבטחה מסך אחד, תכונה אחת
בפרויקט ActiveX / OCX, לרוב איך חותכים את הגבול קובע יותר מהמימוש עצמו. כשלב מקדים לשיפוץ מלא, יש ערך רב כבר רק בסידור מצב קיים, השוואת מבנים ותכנון סדר המעבר.
11. מקורות
- Microsoft Learn: AxHost Class (System.Windows.Forms)
- https://learn.microsoft.com/ja-jp/dotnet/api/system.windows.forms.axhost
- Microsoft Learn: Aximp.exe (Windows フォーム ActiveX コントロール インポーター)
- https://learn.microsoft.com/ja-jp/dotnet/framework/tools/aximp-exe-windows-forms-activex-control-importer
- Microsoft Learn: How to: Add ActiveX Controls to Windows Forms
- https://learn.microsoft.com/en-us/dotnet/desktop/winforms/controls/how-to-add-activex-controls-to-windows-forms
- Microsoft Learn: Expose .NET Core components to COM
- https://learn.microsoft.com/en-us/dotnet/core/native-interop/expose-components-to-com
- Microsoft Learn: Registration-Free COM Interop
- https://learn.microsoft.com/en-us/dotnet/framework/interop/registration-free-com-interop
- Microsoft Learn: Microsoft Edge に関してよく寄せられる質問
- https://learn.microsoft.com/ja-jp/deployedge/microsoft-edge-frequently-asked-questions
- Microsoft Learn: What is Internet Explorer (IE) mode?
- https://learn.microsoft.com/en-us/deployedge/edge-ie-mode
- Microsoft Learn: WebBrowser Class (System.Windows.Forms)
- https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.webbrowser
- Microsoft Learn: Introduction to Microsoft Edge WebView2
- https://learn.microsoft.com/en-us/microsoft-edge/webview2/
- Microsoft Learn: DllSurrogate
- https://learn.microsoft.com/en-us/windows/win32/com/dllsurrogate
- Microsoft Learn: Registering the DLL Server for Surrogate Activation
- https://learn.microsoft.com/en-us/windows/win32/com/registering-the-dll-server-for-surrogate-activation
- KomuraSoft Blog: הידע הבסיסי למניעת תקיעה עם STA/MTA של COM
- /he/blog/sta-mta-com-relationship/
- KomuraSoft Blog: כשמשתמשים ב-DLL native של C++ מתוך C#, למה עדיף לבנות עוטף עם C++/CLI
- /he/blog/cpp-cli-wrapper-for-native-dlls/
- KomuraSoft Blog: מקרה שבו COM שימושי — כשרוצים לקרוא ל-DLL 64bit מאפליקציית 32bit
- /he/blog/com-case-study-32bit-to-64bit/
- KomuraSoft Blog: מה זה COM / ActiveX / OCX — הסבר מרוכז על ההבדלים והקשרים
- /he/blog/what-is-com-activex-ocx/
- KomuraSoft Blog: איך משתמשים ב-DLL .NET 8 מ-VBA עם טיפוסים — חשיפת COM ו-dscom TLB
- /he/blog/dotnet8-dll-typed-vba-com-dscom-tlb/
- KomuraSoft Blog: מהו Reg-Free COM — מנגנון להשתמש ב-COM ללא registration
- /he/blog/what-is-reg-free-com/
- KomuraSoft Blog: המלכודות שב-registration וב-bitness בפיתוח COM/OCX/ActiveX
- /en/blog/2026/04/15/001-com-ocx-activex-pitfalls-visual-studio-bitness-admin-rights/
- KomuraSoft Blog: מדריך היציאה ממערכת שתלויה ב-IE mode
- /en/blog/2026/04/25/003-ie-mode-internal-web-system-life-extension-and-exit/
- KomuraSoft Blog: אחרי IE mode, האם WebView2 מספיק? — המגבלה שבה ActiveX לא פועל, ותכנון מעבר ריאלי
- /en/blog/webview2-embed-web-ui-in-windows-apps/
מאמרים קשורים
מאמרים עדכניים עם אותן תגיות, להעמקה בנושאים קרובים.
COM, ActiveX ו-OCX — ההבדלים והקשר ביניהם
מהו COM, מהו ActiveX ומהו OCX — סקירה מעשית של ההבדלים והקשר ביניהם, הזיקה ל-OLE, איפה זה בשימוש, ואיך כדאי להתייחס לזה היום.
צ'קליסט לפני migration מ-.NET Framework ל-.NET
צ'קליסט מעשי לפני migration מ-.NET Framework ל-.NET: סוג פרויקט, טכנולוגיות לא נתמכות, תלויות NuGet, SDK-style, WPF/WinForms, CI/CD ותפעול.
מה זה COM — למה התכנון של Windows COM עדיין מחזיק
COM הוא binary contract בין components ב-Windows. המאמר מסביר את התכנון של interfaces, IUnknown, GUID ו-binary compatibility, ולמה העקרונ...
Time Travel Debugging — להקליט ולהריץ אחורה באגים שלא משתחררים באפליקציות ארוכות-ריצה
באג פעם בחודש משאיר ב-crash dump רק את התוצאה. מקליטים ומריצים אחורה את הביצוע עם WinDbg Time Travel Debugging (TTD): TTD.exe, ring buffe...
למה arguments נשברים — כללי command-line arguments ב-Windows
Windows מעביר ל-CreateProcess מחרוזת אחת שהמקבל מפצל. מכסה את כללי CommandLineToArgvW, CRT ו-.NET, ArgumentList, ובניה ב-C++.
נושאים קשורים
העמודים האלה ממקמים את הנושא בהקשר רחב יותר של שירותים והחלטות.
נושאים טכניים ב-Windows
שער לנושאי פיתוח Windows, חקירת תקלות וניצול נכסים קיימים.
העברת ActiveX
החלטות אם לשמר, לעטוף או להחליף רכיבי COM / ActiveX / OCX.
שירותים הקשורים לנושא הזה
המאמר קשור ישירות לשירותים הבאים.
שימוש חוזר והעברה של נכסים קיימים
ההחלטה איך לטפל ב-COM / ActiveX / OCX — להשאיר, לעטוף או להחליף — היא בדיוק הנושא המרכזי של שימוש בנכסים קיימים וליווי מעבר.
ייעוץ טכני וסקירת תכנון
בשלב שבו רוצים לקבע גבולות וסדר החלפה לפני המימוש, מתאים להתקדם כייעוץ טכני וסקירת תכנון.
שאלות נפוצות
שאלות נפוצות בפניות בנושא המאמר.
- כדאי להחליף ActiveX / OCX?
- ההחלטה לא נשענת על "האם זה ישן", אלא על מה שהרכיב הזה באמת אחראי עליו. אם זה רק רכיב UI ויש תחליף — מחליפים; אם הוא כולל בקרת התקן, דוחות או פורמט קובץ ייחודי — קודם עוטפים ומקבעים גבול; ואם הוא יציב ותחום השינויים קטן — גם החלטה להשאיר אותו ריאלית. גם "סתם לכתוב הכול מחדש" וגם "להקפיא לנצח כי זה מפחיד" הן בחירות עם שיעור תקלות גבוה.
- אפשר להשתמש ב-OCX 32bit מתוך אפליקציית 64bit?
- לא in-proc. ה-OCX חייב להתאים ל-bitness של ה-process שטוען אותו — זו מגבלה עקרונית. שלוש האפשרויות הריאליות הן: להשאיר בינתיים גם את אפליקציית ה-host כ-32bit; לכלוא אותו ב-process 32bit נפרד (helper EXE או COM LocalServer) ולהתחבר אל צד ה-64bit דרך IPC או COM מסוג out-of-proc; או להתחיל להחליף מהמקומות שבהם אפשר להסיר את התלות ב-OCX.
- מה עושים עם תלות ב-ActiveX בדפדפן?
- עדיף להסתכל על זה מתוך עדיפות להחלפה. Microsoft Edge עצמו לא תומך ב-ActiveX, ו-IE mode ממוקם כאמצעי הארכת חיים בלבד. גם פקד WebBrowser גורר אותו "עולם" של IE, ולכן אם רק רוצים להציג HTML, WebView2 הוא המועמד הראשון. עם זאת, WebView2 אינו רכיב חלופי מלא: סקריפטים שמניחים DOM של IE וההנחות סביב window.external לא עוברים כמו שהם.
- מה זה אומר בפועל "לעטוף" ActiveX / OCX?
- לכלוא את ה-ActiveX / OCX בתוך גבול צר, ולהראות אותו לסביבה כ-API חדש או כרכיב מסך. יש כמה תבניות: host ב-WinForms עם AxHost, גישור ל-process נפרד עם helper EXE של 32bit או LocalServer, וחלון תאימות COM בצד ה-.NET. כשעוטפים, לא מעתיקים את ה-API הישן בהמוניו — הופכים אותו לשיטות בגרגר גס, קובעים בגבול את האחריות ללוג, timeout והמרת exceptions, ובונים כך שאפשר יהיה להחליף בעתיד גם את התחליף עם אותו interface.