איך לוודא שסגירת משאבים תתבצע גם בעת עצירת ניפוי באגים ב-Visual Studio?
-
יש לי קוד שמופעל כאשר התוכנה נסגרת כדי לסגור משאבים שלא נסגרים אוטומטית (כמו חיבורי COM שגיליתי שנשארים פתוחים באמצעות בדיקה ב-Task Manager).
הבעיה היא שאם אני לוחץ על לחצן ה-Stop ב-Visual Studio במהלך ניפוי הבאגים (debugging), הקוד לסגירת המשאבים לא רץ, מה שגורם לשגיאות לא נעימות בתוכנה בכל פעם מחדש.
האם יש דרך לפתור את הבעיה הזו? -
אם תמצא פיתרון תודיע לי
-
אחרי בדיקת stackoverflow אני משתכנע שבצורה הלא עדינה שהדיבאגר עוצר את התהליך אין דרך לטפל בניקוי באמצעות קוד בתוך התהליך כי שום קוד לא ירוץ
נשאר רק פתרון גאוני אחד.
אם לחיצה על לחצן ה-Stop עושה משהו לא רצוי, פשוט אל תלחץ עליו...
איך אם כן תסגור את התוכנה? מכיון שלא גילית הרבה על התוכנה אני לא יודע בודאות איך עוצרים אותה, אבל בד"כ אם זה תוכנה גרפית יש איקס אדום למעלה בפינה ואם זה תוכנת שורת פקודה אפשר עם ctrl+c, ואם זה service אפשר דרך הפקודה sc, בכל הצורות הנ"ל ניתן להוסיף קוד בתוכנה שירוץ לפני הסגירה הסופית -
@pcinfogmach עכשיו אני מבין ששאלת שאלה חזקה.
כמה מחשבות שעולות לי:- סתם מחשבה: העניין הזה שהתוכנה נסגרת בלי לנקות אחריו עלול לקרות גם ב-"production". אי אפשר לסמוך על זה שהתוכנה לא תקרוס או מאיזה סיבה אחרת תיסגר בצורה "לא נקיה". אם כן כדאי לתת את הדעת על איך התוכנה תוכל להתמודד עם מצב זה בצורה אוטומטית עד כמה שאפשר.
לידיעה (להבין את תהליך המחשבה שלי בנושא): מה קרה לתוכנה כאשר ה-UI "תקוע"?
הלב הפועם של כל תוכנה גרפית בווינדוס הוא ה-message loop. זה "לולאה קיומית" שכל תוכנה גרפית נמצא בו כל הזמן, הלולאה מקבלת "אירועים" ממערכת ההפעלה (לדוגמה לחיצות מקשים ותזוזות עכבר וכו') ומטפלת בהם. בד"כ הלולאה רצה על הת'רד הראשי. תוכנה שתקועה, הכוונה שהלולאה הראשית הפסיקה לטפל באירועים, בד"כ כי הוא אוחז באמצע פעולה ארוכה או תקועה.
כל אינטראקציה עם תוכנה גרפית שולחת אירוע ללולאה, אם הלולאה תקועה, התוכנה לא תגיב. לכן אי אפשר לסגור אותה עם האיקס האדום כי זה פועל על ידי שליחת אירוע ל-message loop- המחשבה הבאה שלי היתה שאפשר לסגור תוכנה שלא מגיבה על ידי קריאת פונקציה בחלון ה-Immediate Window
לשם בדיקת הצעה זו יצרתי פרוייקט WPF (לפי ההיסטוריה שלך אני מניח שגם התוכנה שלך WPF) והוספתי Sleep ארוך בת'רד הראשי, ואז ניסיתי לסגור את התוכנה באמצעות ה-Immediate Window
הקוד שניסיתי:
Application.Current.Shutdown()
התוצאה בתמונה:
בשלב הראשון קיבלתי את ההודעה הראשונה, אחרי חיפושים פתרתי אותה על ידי ההגדרה Tools > Options... > Debugging > General > Suppress JIT optimization on module load
ואז קיבלתי את ההודעה השניה
בשלב הזה התייאשתי מכיוון זה
ייתכן שיש מקרים של תקיעה שבהם זה כן יכול לעבוד- כיוון נוסף שחשבתי:
הצורה שווינדוס מטפלת באירוע ctrl+c בקונסול, שונה מהטיפול באירועיים "גרפיים". בקונסול כאשר לוחצים ctrl+c, ווינדוס יוצרת ת'רד חדש בתהליך ובתוך הת'רד החדש קורא למטפל באירוע.
(מתועד פה: https://learn.microsoft.com/en-us/windows/console/handlerroutine)
אם ככה, גם תהליך שלא מגיב לאירועים גרפיים אמור להגיב ל-ctrl+c בקונסול!
יצרתי פרוייקט נסיון
החלקים הרלוונטיים:
בקובץ ה-csproj הוספתי ככה (כתבתי את זה ידני, לא מצאתי דרך לעשות את זה ב-UI)
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <OutputType>Exe</OutputType> </PropertyGroup>
כלומר: במצב דיבוג - תיצור תוכנת קונסול (ההשלכה היחידה שאני מכיר הוא שזה פותח חלון שחור של קונסול במקביל לחלון הרגיל - ועוד הבדל טכני עדין שלא נוגע פה, בקיצור זה לא אמור להזיק לכלום)
בקובץ app.xaml.cs:
public void App_Startup(object sender, StartupEventArgs e) { #if DEBUG Console.CancelKeyPress += (s, e) => { // Do cleanup }; #endif } }
ניסיתי את הקוד ונראה שזה פועל כרצוי
אבל שים לב: מכיון שהקוד ב-CancelKeyPress רץ בת'רד נפרד, ייתכנו השלכות לא צפויות! ראה הוזהרת!
נ.ב בהתחלה חשבתי שאוכל לכתוב בתוך ה-CancelKeyPress רק את השורה
Application.Current.Shutdown
ואז להסתמך על אירוע Exit לעשות את הנקיון, אבל מתברר שאירוע Exit מטופל על ידי הת'רד הראשי, ואם הוא תקוע זה לא יתבצענהניתי מחקירת הנושא
אשמח להערות רעיונות ושיפורים