הפצת אפליקציית Windows כקובץ אחד: מה single binary כן מכסה, ומה נשאר תלוי ב-OS

· עודכן בתאריך: · · Windows, הפצה, single binary, .NET, C++, WebView2, WinUI

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

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

Go Komura (2026). הפצת אפליקציית Windows כקובץ אחד: מה single binary כן מכסה, ומה נשאר תלוי ב-OS. KomuraSoft LLC. https://doi.org/10.5281/zenodo.22173668 https://comcomponent.com/he/blog/windows-single-binary-and-os-dependencies/

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

המאמר הזה התחיל משיחת מסדרון: עד כמה מותר לקרוא לזה single binary ב-Windows? הפוסט שהתחיל את זה למטה. אפשר לדלג עליו; מה שאחריו עומד לבד.

ב-Windows, “אם אפשר, נפיץ כקובץ אחד” היא בקשה די רגילה. בכלים פנימיים, כלי חיבור לציוד, תחנות ניטור, סביבות offline, ובשטח שרוצה להימנע מ-installer ככל האפשר, single binary מפתה מאוד.

אבל אם לא מפרידים את הנושא מראש, השיחה בדרך כלל מתחילה להתפזר באמצע. כי כשאומרים ב-Windows “רוצים single binary”, מתערבבים בקלות ארבעה נושאים שונים.

  • רוצים שהחבילה תהיה קובץ אחד
  • רוצים בלי התקנת runtime מראש של .NET או Visual C++
  • רוצים שזה ירוץ אחרי ששמים תיקייה, בלי installer ובלי הרשאות admin
  • לא רוצים תלות בהבדלים בין גרסאות Windows של היעד

ארבעת אלה אינם אותו דבר. בפועל, החשיבה הכי פחות סוטה היא זו:

אפשר לארוז הרבה דברים ל-EXE אחד. אבל אי אפשר לאפס את התלות ב-Windows של היעד.

המאמר הזה מסמן את הגבול הזה מנקודת מבט מעשית של אפליקציות Windows.

אפשר EXE אחד, התלות ב-OS לא נעלמתבבקשה ל-single binary מתערבבים שיח על הפצה ושיח על תלות; אפשר לארוז לחבילת EXE אחת, אבל אי אפשר לאפס תלות ב-Windows של היעד.רוצים single binaryשיח הפצה: קובץ אחד, לשים ולרוץשיח תלות: בלי runtime, בלי תלות ב-OSאריזה ל-EXE אחד אפשרית בהרבה מקריםאי אפשר לאפס תלות ב-OS

איור 1: ב”להפיץ כקובץ אחד” מעורבבים מה שאפשר ומה שאי אפשר.

1. קודם המסקנה

אם מסכמים קודם:

  • ל-EXE שולחן עבודה רגיל אפשר להגיע רחוק עם single binary
  • אבל היכולת לארוז ל-EXE אחד והיעדר תלות ב-Windows של היעד הם שני דברים שונים
  • ב-Shell extension, שירות Windows, מנהל התקן, WebView2, וחלק מ-WinUI 3, הנושא הוא פחות מספר קבצים ויותר מה רושמים ב-OS ומה מניחים מראש
  • בפועל מה שחשוב ביותר הוא להפריד: רוצים single binary, או בלי installer, או פחות תלות ב-OS

במילים אחרות, קו הגבול ב-Windows הוא כזה:

  • לארוז את החבילה לקובץ אחד: אפשרי בהרבה מקרים
  • לשאת runtime נוסף בחבילה: אפשרי בהרבה מקרים
  • להתקרב להפצת xcopy: תלוי בסוג האפליקציה
  • לבטל תלות בצד Windows של היעד: בלתי אפשרי
קווי הגבול ב-Windowsלארוז לקובץ אחד ולשאת runtime נוסף אפשרי בהרבה מקרים; להתקרב להפצת xcopy תלוי בסוג האפליקציה; לבטל תלות בצד Windows אי אפשר.לארוז לקובץ אחדאפשרי בהרבה מקריםלצרף runtime נוסףלהתקרב להפצת xcopyתלוי בסוג האפליקציהלבטל תלות בצד OSבלתי אפשרי

איור 2: מה אפשר, מה תלוי בסוג, ומה אי אפשר.

1.1 מונחים במאמר

קודם כמה מילים שיחזרו הרבה.

מונח איך קוראים / מה זה מה זה אומר כאן
UCRT Universal C Runtime חלק ספריית C הסטנדרטית אחרי שפיצלו את ה-C runtime ב-Visual Studio 2015. מ-Windows 10 הוא מגיע כחלק מה-OS
VC++ Redistributable Visual C++ Redistributable installer שמכניס למכונת היעד את DLL של Visual C++ runtime שאינם UCRT
framework-dependent הפצה שתלויה ב-.NET של היעד צורת הפצה שמניחה ש-runtime של .NET כבר מותקן במכונת היעד
self-contained .NET runtime מצורף האפליקציה נושאת איתה את כל ה-runtime של .NET
single-file פרסום כקובץ יחיד אפשרות פרסום של .NET שמאגדת את החבילה ל-EXE אחד
Native AOT Native Ahead-Of-Time צורת הפצה שמקמפלת IL מראש לקוד native בזמן הפרסום. אין JIT בזמן ריצה
app-local הפצה ליד ה-EXE שמים DLL באותה תיקייה כמו ה-EXE, לשימוש האפליקציה הזו בלבד
xcopy הפצה בהעתקה בלי installer ובלי הרשאות admin: שמים תיקייה והיא רצה
SCM Service Control Manager מנגנון ה-OS שמנהל רישום, הפעלה ועצירה של שירותי Windows
UAC User Account Control מנגנון האבטחה של Windows ששולט בהעלאה להרשאות admin
Shell extension הרחבת Explorer רכיב COM שנטען לתוך תהליך כמו Explorer ורץ שם
Evergreen מצב עדכון שוטף מצב הפצה של WebView2 Runtime: עותק משותף אחד שמתעדכן אוטומטית
Fixed Version מצב גרסה קבועה מצרפים גרסה מסוימת של WebView2 Runtime לאפליקציה
arch architecture, ארכיטקטורת CPU x86 / x64 / Arm64

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

2. חושבים על single binary בארבע רמות

אם מציירים את הרמות קודם, זה נראה כך. הקושי עולה מלמטה למעלה, ואת הרמה העליונה אי אפשר להשיג ב-Windows.

רמה A: החבילה היא קובץ אחדאפשרי בהרבה מקריםרמה B: בלי התקנת runtime של השפה מראשאפשרי בהרבה מקריםרמה C: בלי התקנה או רישוםתלוי בסוג האפליקציהרמה D: בלי תלות ב-Windows של היעדב-Windows אי אפשר להגיע לכאן

איור 3: ארבע הרמות של single binary. הקושי עולה מלמטה למעלה, ולרמה D אי אפשר להגיע.

כשאומרים “רוצים single binary”, העבודה משתנה לגמרי לפי איזו מארבע הרמות מתכוונים.

2.1 רמה A: החבילה היא קובץ אחד

השכבה הכי חיצונית:

  • אפשר לשלוח קובץ אחד במייל
  • מספיק לשים קובץ אחד על USB
  • ביעד שמים רק app.exe

זה שיח על יחידת ההפצה הנראית. גם אם בזמן הפעלה יש extract זמני, וגם אם יש תלות ב-DLL של ה-OS, התנאי הזה לבדו יכול להתקיים.

2.2 רמה B: בלי התקנת runtime של השפה מראש

השלב הבא: המכונה רצה בלי .NET runtime או VC++ Redistributable מראש.

  • static linking ב-C/C++
  • self-contained ב-.NET
  • single-file ב-.NET
  • Native AOT ב-.NET

ברמה הזו תחושת “אפשר לקחת את זה כמו שהוא” מתחזקת מאוד.

2.3 רמה C: בלי התקנה או רישום

מכאן זה נהיה קשה בבת אחת.

EXE רגיל לפעמים באמת רץ אחרי ששמים אותו. אלה לא:

  • Shell extension
  • שירות Windows
  • custom URL scheme או file association
  • מנהל התקן
  • רכיב שנטען לתהליך אחר, כמו Explorer או Office

באזור הזה לא מספיק לשים קבצים. צריך רישום בצד ה-OS, וחיבור למארח.

אזורים שבהם לשים קובץ לא מספיקShell extension, שירות Windows, file association, מנהל התקן, ורכיב שנטען לתהליך אחר לא מסתפקים בהנחת קבצים; צריך רישום ב-OS וחיבור למארח.Shell extension ושירותלשים קובץ לא מספיקassociation ומנהל התקןרכיב שנטען לתהליך אחרצריך רישום ב-OS וחיבור למארח

איור 4: רמה C נהיית קשה כי נכנס רישום ב-OS.

2.4 רמה D: בלי תלות ב-Windows של היעד

ב-Windows זה בלתי אפשרי.

אפליקציית Windows רצה בסוף מעל API של Windows, ה-loader, מודל האבטחה ומחסנית ההתקנים. single binary מכסה את תחום האחריות של האפליקציה. הוא לא לוקח איתו את ה-OS.

3. איפה קל יחסית להגיע ל-EXE אחד

גם ב-Windows יש אפליקציות שקל יחסית לארוז ל-EXE אחד.

  • כלי שולחן עבודה שרץ לבד
  • אפליקציה עסקית שבה ה-EXE עצמו מחזיק UI ועיבוד
  • כלים לתקשורת, עיבוד קבצים, איסוף לוגים, ניטור, שליטה בציוד
  • בלי אינטגרציה למארח כמו Explorer או Office
  • UI שלא מניח web runtime

בסוג כזה אפשר לכלול באפליקציה עצמה הרבה דברים.

  • קוד שלכם
  • resources
  • manifest
  • הגדרות ברירת מחדל
  • נתוני תבנית
  • חלק מספריות צד שלישי
  • runtime של השפה עצמו

ועוד: גם בלי לדחוף DLL לגמרי לתוך ה-EXE, הפצת app-local עם DLL ליד ה-EXE היא אפשרות חזקה ורגילה ב-Windows. בפועל,

  • app.exe אחד
  • או app.exe ועוד כמה DLL לידו
  • אבל בלי installer, בלי הרשאות admin, ואפשר להפיץ ב-xcopy

לא נדיר שזה קל יותר לתחזוקה מדחיסה בכוח ל-EXE אחד.

app-local כנקודת נחיתהגם בלי לדחוף DLL לגמרי לתוך ה-EXE, אם שמים DLL ליד ה-EXE ואפשר להפיץ ב-xcopy בלי installer ובלי הרשאות admin, לא נדיר שזה קל יותר לתחזוקה מדחיסה בכוח ל-EXE אחד.app.exe ועוד כמה DLL לידובלי installer ובלי הרשאות adminאפשר להפיץ ב-xcopyלפעמים קל יותר לתחזוקה מדחיסת EXE אחת

איור 5: גם בלי להתעקש על EXE אחד, app-local לעיתים קרובות ממלא את המטרה.

4. תלות ב-Windows שנשארת גם עם EXE אחד

אם חושבים “EXE אחד אומר שאין תלות ב-Windows של היעד”, כאן נופלים. גם אחרי אריזה ל-EXE אחד נשארות תלויות.

4.1 תלות בגרסת OS

לכל Windows API יש גרסת OS מינימלית נתמכת. יש גם הבדל בין x64 ל-Arm64. כלומר גם עם EXE יחיד צריך לקבע מראש:

  • האם זה רץ עד Windows 10
  • האם זה מניח Windows 11
  • האם מריצים גם על Windows Server
  • איזה arch: x86 / x64 / Arm64

4.2 תלות ב-DLL של המערכת

גם אם חשבתם שזה EXE אחד, בזמן ריצה אתם כמובן משתמשים ברכיבים שה-OS מספק.

  • kernel32.dll
  • user32.dll
  • advapi32.dll
  • תשתית COM
  • תשתית בקרת שירותים

זה תחום האחריות של Windows.

4.3 תלות במודל האבטחה

  • UAC
  • ACL של קבצים
  • Service Control Manager
  • Registry
  • מדיניות חתימת מנהלי התקן

את אלה האפליקציה לא יכולה לקחת על עצמה לבד.

תלויות שנשארות גם עם EXE אחדגם אחרי אריזה ל-EXE אחד נשארות תלות בגרסת OS מינימלית וב-arch, ב-DLL מערכת כמו kernel32.dll ובתשתית COM, ובמודל האבטחה כמו UAC, ACL ומדיניות חתימת מנהלי התקן.אפליקציה כ-EXE אחדגרסת OS ו-archDLL מערכת ותשתית COMמודל האבטחהאף אחד מאלה האפליקציה לא יכולה לקחת על עצמה

איור 6: גם כשהחבילה היא קובץ אחד, התלות בתחום האחריות של Windows נשארת.

4.4 תלות במארח או ב-runtime

אם זה לא EXE שרץ לבד אלא עיצוב שיושב על מארח, התלות קופצת בבת אחת.

  • WebView2: צריך WebView2 Runtime
  • WinUI 3 / Windows App SDK: צריך לסדר את מצב ההפצה
  • Shell extension: צריך רישום בצד Explorer

כלומר בחירת UI או אינטגרציה הופכת ישר לקושי בהפצה.

בחירת UI ואינטגרציה הופכת לקושי בהפצהWebView2 דורש Runtime, WinUI 3 דורש סידור מצב הפצה, ו-Shell extension דורש רישום ב-Explorer; בחירת מארח או runtime הופכת ישר לקושי בהפצה.משתמשים ב-WebView2צריך Runtimeמשתמשים ב-WinUI 3צריך לסדר מצב הפצהבונים Shell extensionצריך רישום ב-Explorer

איור 7: ככל שמתרחקים מ-EXE שרץ לבד, התלות קופצת.

5. נקודת נחיתה לפי טכנולוגיה

5.1 native C/C++

native C/C++ נמצא בצד עם יותר חופש ל-single binary. יש מקום לבחור static linking, ול-EXE שרץ לבד קל יחסית להתקרב לשם.

אבל יותר חשוב מלדחוף הכול לקובץ אחד:

  • מה עושים עם UCRT ו-VC++ runtime
  • האם שמים DLL של צד שלישי כ-app-local
  • עד כמה מצמצמים CPU / OS יעד

נקודת הכניסה: ההבדל בין /MT ל-/MD

ב-MSVC, מה שמכריע בפועל אם נושאים runtime הוא אפשרות הקומפיילר הזו.

אפשרות מה מקושר מה צריך במכונת היעד
/MD import libraries ל-DLL: ucrt.lib ו-vcruntime.lib. בזמן ריצה משתמשים ב-ucrtbase.dll וב-vcruntime<גרסה>.dll UCRT ו-VC++ runtime
/MT static link של libucrt.lib, libvcruntime.lib, libcmt.lib בלי runtime נוסף

ניסוי מינימלי ב-Developer Command Prompt:

cl /std:c++17 /EHsc /O2 /MT app.cpp /Fe:app.exe

כאן יש שלוש נקודות שכדאי לזכור.

  • UCRT הפך לרכיב של Windows כשפיצלו את ה-C runtime ב-Visual Studio 2015, ומ-Windows 10 הוא מגיע כחלק מה-OS. אם היעד ישן יותר, צריך redistribute עם vcredist.
  • לא מומלץ לבנות DLL ב-/MT. CRT ב-static link סוגר את המצב בתוך אותו DLL, ולכן הקצאת זיכרון, locale, וההשפעה של _set_se_translator יכולים לסטות בין EXE ל-DLL. אם מיישרים בגסות “גם ה-EXE וגם ה-DLL ב-/MT”, מקבלים תקלות בהקצאה ושחרור שחוצים גבול.
  • אי אפשר לשלב /clr עם /MT. אם יש C++/CLI, הולכים ל-/MD.
שלוש נקודות על static linkingUCRT מגיע עם Windows 10 ומעלה אבל ב-Windows ישן יותר צריך vcredist; /MT בצד DLL לא מומלץ כי מצב ה-CRT נסגר בתוך ה-DLL והקצאה/שחרור שחוצים גבול נשברים; /clr ו-/MT לא משתלבים.static link עם /MTWindows ישן יותר צריך vcredist/MT בצד DLL לא מומלץלא משתלב עם /clrתקלות בהקצאה/שחרור שחוצים גבול

איור 8: /MT חזק, אבל צריך לשים לב להנחות על UCRT, לגבול DLL, ול-C++/CLI.

5.2 .NET

ב-.NET יש single-file, self-contained ו-Native AOT, כך שיחידת ההפצה הנראית יכולה להיות קטנה מאוד.

אבל צריך להפריד:

  • framework-dependent: תלוי ב-.NET של סביבת היעד
  • self-contained: נושא את ה-runtime של .NET
  • single-file: אוסף את החבילה לקובץ אחד
  • Native AOT: מקטין עוד תלות בזמן הפעלה, אבל עם מגבלות יכולת

“single-file ולכן פחות תלות ב-OS” זה לא נכון. מה שקטן הוא בעיקר ריכוז חבילת האפליקציה.

מה ש-single-file מקטין הוא ריכוז החבילהframework-dependent תלוי ב-.NET של היעד, self-contained נושא runtime, single-file רק אוסף את החבילה לקובץ אחד; זה לא מקטין תלות ב-OS.framework-dependentתלוי ב-.NET של היעדself-containedנושא runtime של .NETsingle-fileאוסף את החבילה לקובץ אחדזה לא מקטין תלות ב-OS

איור 9: בכל צורת פרסום של .NET, מה שקטן ומה שנשאר שונים.

נקודת הכניסה: שלושה דפוסים של dotnet publish

כדי לראות את ההבדל בריצה, שלושת אלה מספיקים.

rem 1. framework-dependent + single-file: צריך .NET runtime במכונת היעד
dotnet publish -c Release -r win-x64 --self-contained false -p:PublishSingleFile=true

rem 2. self-contained + single-file: נושא את ה-runtime של .NET
dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true

rem 3. Native AOT: מפרסמים אחרי שכתבתם PublishAot ב-csproj
dotnet publish -c Release -r win-x64

את PublishSingleFile ו-PublishAot עדיף לכתוב ב-csproj, לא בשורת הפקודה. זו ההמלצה הרשמית, כי ניתוח תאימות בזמן build נדלק, ופחות נופלים על זה אחרי הפרסום.

<PropertyGroup>
  <PublishSingleFile>true</PublishSingleFile>
  <PublishAot>true</PublishAot>
</PropertyGroup>

שימו לב: כשמציינים RuntimeIdentifier, SelfContained הופך כברירת מחדל ל-true. אם רוצים framework-dependent, ציינו --self-contained false. כדי לפרסם Native AOT ב-Windows צריך את עומס העבודה Desktop development with C++ ב-Visual Studio.

המחיר: “קובץ אחד” הוא לא בחינם

אם כותבים רק “אפשר”, נופלים. הנה גם המחיר.

extract בזמן הפעלה של single-file

  • כברירת מחדל נארזים רק managed DLL. בזמן הפעלה הם נטענים לזיכרון ולא נפרסים לתיקייה. הבינארי native של ה-runtime עצמו נשאר כקבצים נפרדים.
  • אם רוצים לכלול גם native binaries בקובץ אחד משתמשים ב-IncludeNativeLibrariesForSelfExtract; אם רוצים לפרוס הכול לפני ההפעלה, IncludeAllContentForSelfExtract. שניהם עובדים כ”פורסים ואז מפעילים”, וב-Windows זה נפרס תחת %TEMP%\.net. אפשר לשנות מיקום עם DOTNET_BUNDLE_EXTRACT_BASE_DIR, אבל אל תשימו מקום שמשתמשים עם הרשאות שונות או שירותים יכולים לכתוב אליו.
  • EnableCompressionInSingleFile מקטין מאוד את ה-EXE, אבל בזמן הפעלה יש decompress בזיכרון וההפעלה איטית יותר. גם התיעוד הרשמי אומר למדוד גם גודל וגם עלות הפעלה לפני שמשתמשים. ההשפעה משתנה מאוד בין אפליקציות.

אי-תאימות API ב-single-file

כשהחבילה היא קובץ אחד, קוד שמניח נתיב קובץ נשבר בשקט.

API התנהגות ב-single-file
Assembly.Location מחזיר מחרוזת ריקה
Assembly.CodeBase PlatformNotSupportedException
Assembly.GetFile IOException
Module.Name מחזיר את המחרוזת <Unknown>

לקבצים ליד ה-EXE לכו ל-AppContext.BaseDirectory; לנתיב קובץ ההרצה עצמו לכו ל-Environment.ProcessPath. גם ספריות צד שלישי משתמשות בזה לפעמים מבפנים, אז אחרי מעבר ל-single-file צריך פעם אחת להפעיל על מכונה אמיתית.

קוד שמניח נתיב נשבר בשקטכשהחבילה היא קובץ אחד, APIs שמניחים נתיב קובץ משנים התנהגות, למשל Assembly.Location מחזיר מחרוזת ריקה; לקבצים ליד ה-EXE משתמשים ב-AppContext.BaseDirectory, לנתיב ההרצה ב-Environment.ProcessPath, ובודקים הפעלה על מכונה אמיתית.עוברים ל-single-fileהתנהגות API של נתיבים משתנהקבצים ליד: AppContext.BaseDirectoryקובץ הרצה: Environment.ProcessPathבודקים הפעלה פעם אחת על מכונה אמיתית

איור 10: אחרי single-file מחליפים את לקיחת הנתיב ובודקים על מכונה אמיתית.

מגבלות יכולת של Native AOT

בתמורה ל”הפעלה מהירה ובלי runtime” הדברים הבאים לא זמינים:

  • טעינה דינמית כמו Assembly.LoadFile
  • יצירת קוד בזמן ריצה כמו System.Reflection.Emit
  • C++/CLI
  • ב-Windows, built-in COM
  • trimming חובה, אז גם מגבלות ה-trimming מגיעות איתו
  • בפועל זה single-file, אז גם אי-התאימות של ה-API למעלה מגיעה איתו
  • System.Linq.Expressions תמיד רץ כ-interpreter, ואיטי יותר מקוד שנוצר בזמן ריצה

גם פלטפורמת היעד קבועה מראש. ב-.NET 8 Windows הוא x64 ו-Arm64; מ-.NET 9 נוסף x86. הרעיון “AnyCPU כקובץ אחד” לא עומד מההתחלה.

המחיר של Native AOTNative AOT מקטין תלות בזמן הפעלה אבל חוסם טעינה דינמית, יצירת קוד בזמן ריצה, C++/CLI ו-built-in COM ב-Windows; גם מגבלות trimming ו-single-file מגיעות, ופלטפורמת היעד קבועה מראש.Native AOTמקטין תלות בזמן הפעלהבלי טעינה דינמית ובלי codegen בזמן ריצהבלי C++/CLI ובלי built-in COMגם מגבלות trimming ו-single-fileOS ו-arch של היעד קבועים מראש

איור 11: את היתרון של Native AOT רואים יחד עם מה שנעלם.

5.3 WebView2

ברגע שמאמצים WebView2, קושי ה-single binary משתנה לגמרי. הנושא כאן הוא לא מספר ה-EXE, אלא איך מטפלים ב-WebView2 Runtime.

לפני “אפשר EXE אחד?” יש שאלות אחרות.

  • האם מניחים Runtime קיים בסביבה
  • האם משתמשים ב-Evergreen
  • האם מצרפים Fixed Version
  • עד כמה לוקחים אחריות בהפצה offline

ההבדל בין Evergreen ל-Fixed Version בגדול:

  Evergreen Fixed Version
מי מעדכן Microsoft. עדכון אוטומטי אתם. מחליפים יחד עם עדכון האפליקציה
מה יושב במכונה עותק אחד משותף לכל האפליקציות מצורף לכל אפליקציה
גודל ההפצה כמעט לא גדל יותר מ-250 MB
איך מכניסים Bootstrapper של כ-2 MB מוריד מה שצריך. ב-offline מצרפים Standalone Installer מפיצים את כל הבינארי שנפרס יחד עם האפליקציה
מגבלה קיימת צריך לבדוק אם זה כבר במכונה, לפני ההפעלה אי אפשר להריץ מנתיב רשת או UNC

Evergreen Runtime מותקן מראש כחלק מ-Windows 11. ב-Windows 10 עדיין יש מכונות בלי זה, וגם Microsoft כותבת שמגם אם בחרתם Evergreen עדיף להפיץ Runtime. כלומר בכל מקרה האפליקציה צריכה לבדוק אם זה קיים ולהתקין אם חסר. Fixed Version נותן לכם שליטה על תזמון העדכון, בתמורה לתוספת של מאות MB לחבילה. את זה מחליטים לפני השיח על EXE אחד.

ב-WebView2 מחליטים קודם על שיטת ההפצהEvergreen הוא עותק משותף ש-Microsoft מעדכנת אוטומטית אבל לפעמים הוא לא במכונה; Fixed Version נותן שליטה על עדכון אבל מוסיף מאות MB לחבילה; בשני המקרים האפליקציה צריכה לבדוק אם Runtime קיים ולהתקין אם חסר.מאמצים WebView2Evergreen: עותק משותף עם עדכון אוטומטיFixed Version: מצרפים ומעדכנים בעצמכםבודקים אם קיים, ומתקינים אם חסרהחבילה גדלה במאות MB

איור 12: הנושא ב-WebView2 הוא לא מספר ה-EXE, אלא הטיפול ב-Runtime.

5.4 WinUI 3 / Windows App SDK

גם WinUI 3 משנה דרישות הפצה ברגע שמאמצים אותו. בחירת טכנולוגיית UI הופכת ישר לבחירת שיטת הפצה.

יש שני צירים להחליט:

  • packaging: packaged (MSIX), packaged שמצביע למיקום חיצוני, או unpackaged
  • runtime: framework-dependent או self-contained

ומבחינת single binary, המגבלה שמשפיעה היא השילוב הזה:

  • PublishSingleFile זמין רק לאפליקציית WinUI 3 שהיא unpackaged וגם self-contained, ודורש Windows App SDK 1.5 ומעלה.
  • באפליקציה packaged, וגם ב-packaged שמצביע למיקום חיצוני, PublishSingleFile לא זמין.
  • unpackaged מאבד package identity, ולכן יכולות Windows שמניחות package identity (התראות, background tasks, file association, הרחבת תפריט הקשר) לא זמינות.

כלומר ב-WinUI 3, “EXE אחד” ו”להשתמש ביכולות הרחבה של Windows” מתנגשים חזיתית. אם single binary בראש הסולם, לעיתים קרובות מהיר יותר לבדוק קודם את הנחות טכנולוגיית ה-UI.

ההתנגשות בין EXE אחד ל-package identityב-WinUI 3, PublishSingleFile זמין רק ב-unpackaged+self-contained; unpackaged מאבד package identity, ואז התראות ו-file association לא זמינות, כך ש-EXE אחד ויכולות הרחבה של Windows מתנגשים.רוצים PublishSingleFileרק unpackaged+self-containedמאבדים package identityהתראות, association וכו' לא זמיניםEXE אחד ויכולות הרחבה מתנגשים

איור 13: ב-WinUI 3, בחירה ב-EXE אחד היא בחירה לוותר על package identity.

6. אזורים שבהם רישום ותלות הם הנושא עצמו

6.1 Shell extension

Shell extension שנטען ל-Explorer הוא לא “EXE ששמים ומריצים”. כאן הנושא הוא לא מספר קבצים, אלא איך רושמים ב-Explorer.

6.2 שירות Windows

אפשר לארוז את ה-exe של השירות עצמו לקובץ אחד, אבל ההפצה היא סיפור אחר.

  • רישום ב-SCM
  • הרשאות
  • חשבון הפעלה
  • הגדרות שחזור

כלומר שירות הוא אזור שבו מחדדים איך מתקינים, לא איך מגיעים ל-EXE אחד.

6.3 מנהל התקן

כאן זה עוד יותר ברור. זה נשלם רק עם INF, חתימה ותהליך התקנה, ולכן מלכתחילה קשה להיכנס לזירת single binary.

אזורים שבהם רישום וחתימה הם הנושאShell extension הוא רישום ב-Explorer, שירות Windows הוא רישום ב-SCM עם הרשאות וחשבון הפעלה, ומנהל התקן נשלם עם INF וחתימה; לכן הנושא הוא תכנון רישום ותלות, לא מספר קבצים.Shell extensionרישום ב-Explorerשירותרישום ב-SCM, הרשאות, חשבוןמנהל התקןINF וחתימההנושא הוא תכנון רישום, לא מספר קבצים

איור 14: בשלושת האזורים האלה מחדדים איך רושמים, לא איך מגיעים ל-EXE אחד.

7. טבלת החלטה מעשית

להחלטה גסה, הטבלה הזו שימושית.

מה בונים עד כמה EXE אחד ריאלי מה לחשוב קודם
כלי Win32 / C++ שרץ לבד גבוה static linking, OS / arch יעד
כלי WinForms / WPF שרץ לבד גבוה התאמה ל-self-contained, single-file, Native AOT
אפליקציית WinUI 3 / Windows App SDK בינוני מצב הפצה, תלויות נוספות
UI שולחן עבודה מבוסס WebView2 נמוך עד בינוני שיטת הפצת Runtime
הרחבת קליק ימני או preview ב-Explorer נמוך רישום COM / Registry
שירות Windows בינוני רישום SCM, הרשאות, תהליך עדכון
אפליקציה עם מנהל התקן מצורף נמוך INF, חתימה, התקנה

מה שחשוב בטבלה הוא להבין שמספר הבינאריים ותחום האחריות של ההפצה הם שני דברים שונים.

8. מה לקבע לפני תכנון ההפצה

אם רוצים ש-single binary יצליח, יש דברים להחליט לפני המימוש.

8.1 להחליט מה רוצים שיהיה “אחד”

  • שהחבילה תהיה קובץ אחד
  • לבטל התקנת runtime מראש
  • לבטל צורך ב-installer
  • להקל על עדכון offline

התשובה משנה את הטכנולוגיה שבוחרים.

8.2 לקבע בהתחלה Windows מינימלי ו-arch

גם single-file וגם Native AOT הם ביסודם ספציפיים ל-OS / architecture. אם ממשיכים עם “סתם קובץ אחד” בלי לקבע את זה, בסוף נתקעים על API חסר או runtime לא תואם.

8.3 לכתוב במפורש מה מצורף ומה נשאר ל-Windows

בפועל, הטבלה הזו לבדה מורידה הרבה תקלות.

  • מה מצורף לאפליקציה
    • exe ראשי
    • DLL שלכם
    • תבניות הגדרות
    • self-contained runtime
  • מה נשאר ל-Windows
    • DLL מערכת
    • OS API
    • SCM / Registry / Explorer
    • תשתית מנהלי התקן
  • מה מניחים כתנאי נפרד
    • WebView2 Runtime
    • VC++ Redistributable
    • Office / Excel
    • מנהל התקן ייעודי
לכתוב במפורש שלוש קטגוריות אחריותאם כותבים מראש מה מצורף לאפליקציה, מה נשאר ל-Windows, ומה מניחים כתנאי נפרד, תקלות הפצה יורדות מאוד.תחום האחריות של החבילהמה מצורף לאפליקציהמה נשאר ל-Windowsמה מניחים כתנאי נפרדעצם הכתיבה מורידה תקלות

איור 15: מקבעים מראש שלוש קטגוריות: מצורף, באחריות OS, ותנאי נפרד.

8.4 אם single binary בעדיפות, מורידים אינטגרציה למארח

זה עובד חזק.

  • במקום Shell extension, EXE רגיל
  • במקום שירות, Task Scheduler או הפעלה מפורשת
  • במקום WebView2, UI native
  • COM נשאר בתוך התהליך שלכם

בקיצור, ככל שמורידים עיצוב שבו ה-OS טוען אתכם או שאתם נרשמים אליו, מתקרבים ל-single binary.

ככל שמורידים אינטגרציה למארח, מתקרביםבמקום Shell extension עוברים ל-EXE רגיל, במקום שירות להפעלה מפורשת או Task Scheduler, במקום WebView2 ל-UI native; ככל שמורידים רישום וטעינה מצד ה-OS, מתקרבים ל-single binary.במקום Shell extension, EXE רגילמורידים עיצוב שרושם ב-OSבמקום שירות, הפעלה מפורשתבמקום WebView2, UI nativeמתקרבים ל-single binary

איור 16: אם single binary בעדיפות, מורידים את האינטגרציה למארח עצמה.

9. סיכום

single binary ב-Windows אפשרי עד רחוק. אבל הנקודה שמגיעים אליה היא המשפט הזה.

אפשר לארוז אפליקציה ל-EXE אחד. אי אפשר לארוז ל-EXE אחד גם את Windows שהאפליקציה תלויה בו.

חמש נקודות שכדאי לזכור:

  • ל-EXE רגיל שרץ לבד אפשר להתקרב רחוק להפצה כקובץ אחד
  • static linking ב-C/C++, single-file ב-.NET, ו-Native AOT הם כלים חזקים
  • אבל תלות בגרסת OS, arch, DLL מערכת ומודל אבטחה לא נעלמת
  • ב-Shell extension, שירות, מנהל התקן, WebView2 וחלק מ-WinUI 3, הסיפור הוא רישום ב-OS או runtime נוסף
  • הצלחת single binary נקבעת לפי זה שמפרידים בהתחלה מה רוצים שיהיה “אחד”

אם single binary בעדיפות גבוהה, הרבה יותר קל להצליח אם כבר בבחירת הטכנולוגיה מורידים את רמת הצימוד ל-OS.

מפתח ההצלחה הוא רמת הצימוד ל-OSאפשר לארוז אפליקציה ל-EXE אחד, אבל אי אפשר לארוז גם את Windows שהיא תלויה בו; אם single binary בעדיפות גבוהה, מצליחים יותר כשמורידים צימוד ל-OS כבר בבחירת הטכנולוגיה.לארוז אפליקציה ל-EXE אחדאפשרלארוז גם את Windows שהיא תלויה בואי אפשרלכן בבחירת טכנולוגיה מורידים צימוד ל-OS

איור 17: הצלחת EXE אחד נקבעת בתכנון הצימוד ל-OS בהתחלה.

10. מקורות

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

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

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

שאלות נפוצות

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

אפשר להפיץ אפליקציית Windows כקובץ EXE אחד בלבד?
לכלי שולחן עבודה שרץ לבד, אפשר להגיע רחוק. static linking ב-C/C++, self-contained או single-file ב-.NET, ו-Native AOT מאפשרים לארוז את חבילת ההפצה ל-EXE אחד. אבל היכולת לארוז ל-EXE אחד והיעדר תלות ב-Windows של היעד הם שני דברים שונים. תלות בגרסת OS, בארכיטקטורה, ב-DLL של המערכת ובמודל האבטחה לא נעלמת.
single-file ב-.NET מבטל את התלות ב-OS?
לא. single-file מקטין בעיקר את מספר הקבצים בחבילה, לא את התלות ב-OS. framework-dependent תלוי ב-.NET שמותקן במכונת היעד, self-contained נושא את ה-runtime של .NET, ו-Native AOT יכול להקטין עוד תלות בזמן ההפעלה אבל עם מגבלות יכולת. גם single-file וגם Native AOT הם ביסודם ספציפיים ל-OS ולארכיטקטורה, ולכן מקבעים מראש את גרסת Windows המינימלית ואת ה-arch.
אילו אפליקציות קשה לארוז ל-EXE אחד?
Shell extensions, שירותי Windows, מנהלי התקן, UI מבוסס WebView2, וחלק מ-WinUI 3. אצל אלה הנושא הוא לא מספר הקבצים, אלא רישום ב-OS וטיפול ב-runtime נוסף. Shell extension דורש רישום ב-Explorer; שירות דורש תכנון של רישום ב-SCM, הרשאות וחשבון הפעלה; מנהל התקן נשלם רק עם INF וחתימה. לכן הפצה של 'רק לשים קובץ' לא עובדת. ב-WebView2 צריך להחליט קודם איך מפיצים את WebView2 Runtime.
יש שיטת הפצה טובה יותר מלדחוף הכול לתוך EXE אחד?
הפצת app-local, שבה שמים DLL ליד ה-EXE, היא אפשרות חזקה. גם בצורה של app.exe ועוד כמה DLL לידו, אם אפשר להפיץ ב-xcopy בלי installer ובלי הרשאות admin, לא נדיר שזה קל יותר לתחזוקה מדחיסה בכוח ל-EXE אחד. מה שחשוב הוא להפריד מראש: רוצים קובץ אחד, או לבטל התקנת runtime מראש, או לבטל צורך ב-installer.

פרופיל הכותב

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

Go Komura

מנהל KomuraSoft LLC

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

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

חזרה לבלוג