Service Controller
לעיתים אנו רוצים לשלוט ב WinService על המחשב דרך הקוד. בתוכנה שלנו למשל, מופעלים מספר שירותים על המחשב, ועוד אחד שמפקח עליהם, ועוד יש התקנה של המוצר שצריכה גם להתקין את הסרביסים האלה, ולכן שליטה מוחלטת דרך הקוד הכרחית עבורנו. לשם כך ניעזר במחלקה ServiceController.
קודם כל צריכים להוסיף הפניה לאסמבלי System.ServiceProcess שכן הפניה לשם לא קיימת בברירת המחדל
כעת אפשר להתחיל להשתמש בו
System.ServiceProcess.ServiceController myController = new System.ServiceProcess.ServiceController(svcName);
כפי שניתן לראות הבנאי שלו מקבל את שם הסרביס שאיתו אני רוצה לעבוד - השם הוא כמו שמופיע כשלוחצים alt+ctrl+del ובוחרים את טאב הסרביסים
בינתיים גם אם הסרביס לא קיים הכל עובד כרגיל. ישנה בעיה קטנה - אם פונקציה שעונה לי פשוט האם סרביס קיים או לא, ולכן נתחכם קצת :
if (myController != null)
{
try
{
string s=myController.DisplayName;
myController.Dispose();
return true;
}
catch { return false; }
}
return false;
ניסינו בעצם למשוך מידע ממנו - ואם הסרביס לא קיים אז נקבל טעות ונדע שהוא לא קיים
טוב אז יש לנו סרביס שקיים, כעת אנו רוצים לדעת מה המצב שלו - הוא מותקן בוודאות, אבל אולי הוא התרסק ועצר?
ServiceControllerStatus scs= myController.Status;
אם נרצה לעצור אותו, נשתמש בפונקציה מובנית
switch (myController.Status)
{
case ServiceControllerStatus.Stopped:
//already stopped...nothing to do
break;
case ServiceControllerStatus.StopPending:
myController.WaitForStatus(ServiceControllerStatus.Stopped);
break;
default:
myController.Stop();
myController.WaitForStatus(ServiceControllerStatus.Stopped);
break;
}
myController.Close();
רק נוודא שאנו ממתינים שהוא אכן יעצור...
ולהריץ אותו נשתמש בשיטה זהה:
switch (myController.Status)
{
case ServiceControllerStatus.Stopped:
myController.Start();
myController.WaitForStatus(ServiceControllerStatus.Running);
break;
case ServiceControllerStatus.StopPending:
//wait for it to stop
myController.WaitForStatus(ServiceControllerStatus.Stopped);
//... and then start
myController.Start();
myController.WaitForStatus(ServiceControllerStatus.Running);
break;
case ServiceControllerStatus.StartPending:
//nothing to do...just wait
myController.WaitForStatus(ServiceControllerStatus.Running);
break;
case ServiceControllerStatus.Running:
//nothing to do.already running...
break;
default:
myController.Start();
myController.WaitForStatus(ServiceControllerStatus.Running);
break;
}
כפי שניתן לראות השימוש די אינטואיטיבי, אנו בודקים את מצבו כרגע, לפי זה מחליטים מה לעשות, ובסוף ממתינים שאכן השינוי יתבצע
עכשיו, כדי להתקין ולהסיר סרביסים דרך הקוד, כבר נצטרך שימוש בקוד c++, אבל לא הרבה... להתקנת שירות נגדיר קצת קבועים:
int SC_MANAGER_CREATE_SERVICE = 0x0002;
int SERVICE_WIN32_OWN_PROCESS = 0x00000010;
//int SERVICE_DEMAND_START = 0x00000003;
int SERVICE_ERROR_NORMAL = 0x00000001;
int STANDARD_RIGHTS_REQUIRED = 0xF0000;
int SERVICE_QUERY_CONFIG = 0x0001;
int SERVICE_CHANGE_CONFIG = 0x0002;
int SERVICE_QUERY_STATUS = 0x0004;
int SERVICE_ENUMERATE_DEPENDENTS = 0x0008;
int SERVICE_START = 0x0010;
int SERVICE_STOP = 0x0020;
int SERVICE_PAUSE_CONTINUE = 0x0040;
int SERVICE_INTERROGATE = 0x0080;
int SERVICE_USER_DEFINED_CONTROL = 0x0100;
int SERVICE_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED |
SERVICE_QUERY_CONFIG |
SERVICE_CHANGE_CONFIG |
SERVICE_QUERY_STATUS |
SERVICE_ENUMERATE_DEPENDENTS |
SERVICE_START |
SERVICE_STOP |
SERVICE_PAUSE_CONTINUE |
SERVICE_INTERROGATE |
SERVICE_USER_DEFINED_CONTROL);
int SERVICE_AUTO_START = 0x00000002;
הפונקציה שלנו תקבל 3 משתנים
string svcPath, string svcName, string svcDispName
את הנתיב הפיסי לסרביס שאנו רוצים להתקין, את שם הסרביס ואת השם שבו הוא יוצג
וכעת להתקנה עצמה:
IntPtr sc_handle =NativeMethods.OpenSCManager(null, null, SC_MANAGER_CREATE_SERVICE);
if (sc_handle.ToInt32() != 0)
{
IntPtr sv_handle = CreateService(sc_handle,-->c++ native method
svcName, svcDispName, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, svcPath, null,
0, null, null, null);
if (sv_handle.ToInt32() == 0)
{
CloseServiceHandle(sc_handle);-->c++ native method
throw new ExternalException("Could not create service");
}
}
else
{
throw new ExternalException("Could not open handle to service manager");
}
כשאנו משתמשים בעצם ב 2 פונקציות מספריית windows
כמובן שצריך להגדיר פונקציות חיצוניות לפני השימוש...:
[DllImport("advapi32.dll")]
public static extern IntPtr OpenSCManager(string lpMachineName, string lpSCDB, int scParameter);
[DllImport("Advapi32.dll")]
public static extern IntPtr CreateService(IntPtr SC_HANDLE, string lpSvcName, string lpDisplayName,
int dwDesiredAccess, int dwServiceType, int dwStartType, int dwErrorControl, string lpPathName,
string lpLoadOrderGroup, int lpdwTagId, string lpDependencies, string lpServiceStartName, string lpPassword);
ההגדרה DllImport היא ממש לפי המשמעות - אנו מגדירים הפניה ל DLL מקומי, מגדירים איך נראית הפונקציה ואז ניתן לפנות אליה
זה אמור להספיק כדי להתקין את הסרביס...
אם נרצה להסיר אותו השיטה דומה- קודם נעצור אותו בעזרת השיטה שראינו לעיל, אחר כך נקרא לפונקציות חיצוניות כדי להסיר ולמחוק אותו
StopService(svcName);-->out function
int GENERIC_WRITE = 0x40000000;
IntPtr sc_hndl = OpenSCManager(null, pan class="code_keywords">nunull, GENERIC_WRITE);-->c++ method
if (sc_hndl.ToInt32() != 0)
{
int DELETE = 0x10000;
IntPtr svc_hndl = OpenService(sc_hndl, svcName, DELETE);-->c++ method
if (svc_hndl.ToInt32() != 0)
{
int i = DeleteService(svc_hndl);-->c++ method
if (i != 0)
{
CloseServiceHandle(sc_hndl);-->c++ method
}
else
{
CloseServiceHandle(sc_hndl);-->c++ method
throw new ExternalException("Could not delete service"); }
}
else
throw new ExternalException("Could not open service");
}
else
throw new ExternalException("Could not open handle to service manager");
לא מסובך כל כך... אומנם משעמם להשתמש בפונקציות והגדרות מובנות במערכת ההפעלה, אבל סביר...
להורדת הפרוייקט
חלק מהקוד נלקח מ http://www.thescripts.com/forum/post932461-9.html