DCSIMG
January 2008 - Posts - נועם - תעופה זה לא רק באג

נועם - תעופה זה לא רק באג

Everything on Everything, Technology, Programming, Gadgets And Day to Day staff

January 2008 - Posts

בשבוע שעבר כתבתי דוגמת קוד לשימוש ב ServiceBase על מנת לממש Windows Service תחת .Net.

ServiceBase ממש ועוטף את כל מה שנדרש מנת לממש Service תחת .Net אבל לא כל הפונקציונליות של ה Service Control Manager או בקיצור SCM נחשפת ואנחנו צריכים לקרוא ל SCM API באופן ישיר דרך PINVOKE עבור חלק משירותים. אחד מהשירותים הוא היכולת של Service לעדכן את הSCM לגבי מצבו ב Startup/Shutdown כגון כמה זמן על ה SCM להמתין עד שהService יסיים את הפעולה.

בMSDN קיימת דוגמת קוד אשר מראה כיצד לקרוא לפונקציה SetServiceStatus על מנת לעדכן את ה SCM במצב ה Service

הדוגמה מגדירה Struct בשם public struct SERVICE_STATUS אשר מכיל את כל השדות הנדרשים ו public enum State אשר מכיל את הערכים אשר ה SCM מצפה לקבל.

CODE

    1 [StructLayout(LayoutKind.Sequential)]

    2 public struct SERVICE_STATUS

    3 {

    4     public int serviceType;

    5     public int currentState;

    6     public int controlsAccepted;

    7     public int win32ExitCode;

    8     public int serviceSpecificExitCode;

    9     public int checkPoint;

   10     public int waitHint;

   11 }

   12 

   13 public enum State

   14 {

   15     SERVICE_STOPPED = 0x00000001,

   16     SERVICE_START_PENDING = 0x00000002,

   17     SERVICE_STOP_PENDING = 0x00000003,

   18     SERVICE_RUNNING = 0x00000004,

   19     SERVICE_CONTINUE_PENDING = 0x00000005,

   20     SERVICE_PAUSE_PENDING = 0x00000006,

   21     SERVICE_PAUSED = 0x00000007,

   22 }

ואת הגדרת הפונקציה

 

[DllImport("advapi32.dll", SetLastError = true)]

public static extern int SetServiceStatus(IntPtr hServiceStatus, SERVICE_STATUS lpServiceStatus);

 

נפעיל את הפונקציה

protected override void OnStart(string[] args)

{

    IntPtr handle = this.ServiceHandle;

 

    try

    {

        myServiceStatus.currentState = (int) State.SERVICE_START_PENDING;

        SetServiceStatus(handle, ref myServiceStatus);

 

        // Do our Service Init staff here

        ...

        ...

    }

    catch (Exception ex)

    {

        ServiceEventLog.WriteEntry(String.Format("Failed to start Time Logger Service, Exception is {0} on {1} at {2}",

            ex.ToString(),

            ex.TargetSite,

            ex.StackTrace), EventLogEntryType.Error);

 

        myServiceStatus.currentState = (int) State.SERVICE_STOPPED;

        SetServiceStatus(handle, ref myServiceStatus);

        throw;

    }

}

כאשר ננסה להפעיל את ה Service לאחר ההתקנה נקבל את ההודעה הבאה ב EventLog

Service cannot be started. System.AccessViolationException:
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
at ServiceSample.SimpleService.SetServiceStatus(IntPtr hServiceStatus, SERVICE_STATUS lpServiceStatus)
at ServiceSample.SimpleService.OnStart(String[] args)
at System.ServiceProcess.ServiceBase.ServiceQueuedMainCallback(Object state)"

OK אז מה עכשיו ????

דבר ראשון נסתכל על ההגדרה של SetServiceStatus בתיעוד

 

BOOL WINAPI SetServiceStatus(
  __in  SERVICE_STATUS_HANDLE hServiceStatus,
  __in  LPSERVICE_STATUS lpServiceStatus
);

עד כאן טוב ויפה, זה נראה זהה להגדרה שלי ב DLLImport ועדיין אני מקבל את התעופה.

אז לאחר כמה דקות על Google נמצאה הבעיה. ההגדרה של ה DLLImport מוטעת ההגדרה הנכונה היא

[DllImport("advapi32.dll", SetLastError = true)]

public static extern int SetServiceStatus(IntPtr hServiceStatus, ref SERVICE_STATUS lpServiceStatus);

כאשר אנחנו קוראים לSetServiceStatus מקוד Native אין שום בעיה מאחר ועובר מצביע לעותק Struct והפונקציה הנקראת לא יכולה לשנות את ה Struct המקורי אבל אף אחד לא באמת בודק מה היא עושה איתו, מתברר שהיא כן כותבת לשם וכאשר אנחנו רצים בסביבת Managed של .Net הבעיה מתחילה ואנחנו מקבלים את ה Exception.

לאחר זה ישבתי עם WinDBG על מנת לנסות להבין את הבעייה וראיתי את ה Machine Code של הפונקציה ומצאתי את הנקודה שכותבת ל Struct. זאת כנראה הייתה הדרך בה הייתי נוקט אם לא הייתי מוצא שום דבר חיפוש אחר הבעייה.

לסיכומו של עניין, מה שכנראה כבר ידענו מזמן, גם בתיעוד של MSDN יש טעויות.

Posted by Noam | with no comments

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

  1. אני ענת (אישתי) ודרור (בן השמונה חודשים) בילינו שבוע חופשה מדהימה ברומא ... Rome @Night 067
  2. שבוע אחרי זה אני נסעתי לנסיעת עבודה ללא גישה לרשת (יש עדיין מקומות כאלה בעולם) וכמובן ללא זמן חופשי לכתוב כלום.
  3. חזרתי מאז אני מאוד עסוק בעבודה מול לקוח מאוד תובעני ...

אבל מה על הפרק בקרוב :

  1. טעויות בקוד דמו של Microsoft (לא יכול להיות)
  2. המשך הסדרה על החיים ללא משרד פיזי