و ضم المواض ع التال ة: UAC Security من برنامجك تمك ن استخدام صالح ات ف ستا على مد ر ف األزرار أحد بجعل نقوم ك ف برنامجنا نفذ أوامر و ندوز ف ستا تتطلب صالح ات ف مد ر
UAC Security استعزاض UAC بشكل عام ال مكن للبرنامج تأد ة أعمال تتطلب صالح ات ال متلكها المستخدم فإن لم متلك ذلك المستخدم الصالح ات الكاف ة لحذف ملفات ف مجلد الو ندوز فال مكن للبرنامج المشغل من قبله أن حذف تلك الملفات أ ضا ومع ذلك مكن للمستخدم تنف ذ أعمال من المفترض أنه ممنوع منها. والمطورون علمون منذ مدة طو لة أن التطب ق جب أن متلك بعض الصالح ات لك تمكن من إتمام العمل فإن كان التطب ق تطلب العد د من الصالح ات فوحدهم المستخدمون الذ ن متلكون تلك الصالح ات مكنهم تشغ ل ذلك البرنامج. ولسوء الحظ فإن العد د من التطب قات الت تقوم بأعمال قو ة تحتاج إلى إنشاء أو حذف ملفات ف مجلد الو ندوز أو الوصول إلى مناطق متعلقة بالنظام أو التعد ل على متغ رات الب ئة أو مسجل النظام فإن كان التطب ق حتاج تلك الصالح ات فعندها جب أن متلك تلك الصالح ات عند تشغ له مما عن أنه على العد د من المستخدم ن امتالك صالح ات مد ر نظام حتى ستط عوا تشغ ل تلك البرامج. والتعامل مع صالح ات بهذا المستوى أت مع أخطار إضاف ة فإن أساء التطب ق التصرف فقد تسبب بانه ار النظام حتى لو كان التطب ق ذات نفسه عمل بصورة طب ع ة فقد قوم المستخدم بعمل شئ كارث عن طر ق الخطأ عندما كون قد دخل بصالح ات مد ر فقد قوم بحذف ملفات هامة صبح معها من المستح ل استعادة النظام و كون الحل األمثل ف هذه الحالة هو السماح للتطب ق برفع مستوى الصالح ات الت ستخدمها بشكل مؤقت أثناء تأد ته لتلك الوظائف القو ة فإن اخطأ التطب ق عند تشغ له لجزئ ة مع نة من الكود فلن كون لد ه الصالح ات الكاف ة لعمل ضرر كب ر ولن كون للمستخدم صالح ات مد ر بشكل دائم وبهذا نكون قد قللنا من احتمال الحوادث المدمرة ف النظام. ف نسخ الو ندوز السابقة لف ستا عندما تقوم بالدخول كمستخدم متلك صالح ات مد ر عندها ستتمكن من الق ام بعمل أي شئ تقر با ولكن ف و ندوز ف ستا فأن ال UAC تصرف بطر قة مختلفة قل ال فعندما تدخل كمد ر كون دخولك عبارة عن شق ن األول عبارة عن مستخدم عادي ذو صالح ات محدودة والثان مد ر نظام متلك كافة الصالح ات فف البدا ة كون عملك كمستخدم عادي ح ث تم استخدام الشق الثان عند الحاجة فقط فعندما تر د الق ام بعمل ة تحتاج إلى صالح ات إضاف ة فال UAC ظهر لك صندوق حوار سألك الموافقة فإن وافقت على تنف ذ ذلك العمل تم رفع صالح اتك بشكل مؤقت إلى مستوى مد ر حتى نته تنف ذ ذلك العمل وعندها تعود صالح اتك إلى مستخدم عادي ثان ة وإن كنت قد دخلت باسم مستخدم عادي ال متلك صالح ات مد ر فمازال بإمكانك تنف ذ أمر تطلب تلك الصالح ات المرتفعة ح ث ظهر لك ال UAC صندوق حوار تحذ ري مكنك من الدخول كمد ر فإن قمت بالدخول كمد ر بنجاح عندها تم منحك صالح ات مد ر بشكل مؤقت حتى نته تنف ذ ذلك العمل. و كون الفرق ب ن الحالت ن بس طا فعندما تدخل كمد ر فإن ال UAC سألك موافقتك على العمل بالصالح ات المرتفعة وإن دخلت كمستخدم آخر فإن ال UAC طلب منك إدخال كلمة السر الخاصة بالمد ر انتص يى ين اجم UAC لن قوم ال UAC برفع صالح ات التطب ق بعد أن تم تشغ له فهو قوم بإسناد الصالح ات لذلك التطب ق عندما بدأ ولن قوم بعدها بتغ ر تلك الصالح ات فإن احتاج التطب ق للعمل بصالح ات مرتفعة فعل ه أن حصل على تلك الصالح ات عندما بدأ ولتجنب إعطاء التطب ق صالح ات أكثر من الالزم جب عل ك تقس م كودك إلى أجزاء بحسب احت اجه لتلك الصالح ات فالبرنامج الرئ س جب أن عمل بصالح ات عاد ة والحقا جب عل ه تنف ذ تطب قات أخرى تعمل بصالح ات مرتفعة عند الحاجة. فمثال إن كان لد نا تطب ق قوم بحفظ الب انات ف قاعدة ب انات Sql Server فهو ال حتاج لصالح ات مد ر ومع ذلك إن أراد إنشاء ملف بملخص العمل ات ف مجلد الو ندوز مجلد محم فس حتاج عندها لصالح ات مد ر ف مكنك عندها تقس م التطب ق إلى عدة أجزاء فالتطب ق الرئ س قوم بمعظم العمل وتطب ق آخر قوم بكتابة معلومات الخالصة إلى ذلك الملف عندها مكن للتطب ق األول تشغ ل الثان من أجل كتابة المعلومات ف ذلك الملف. وعندما كون باإلمكان فضل أن تع د كتابة التطب ق لتجنب استخدام صالح ات مرتفعة فالعد د من البرامج على سب ل المثال تكون منصبة ف المجلد Program Files وهذا من المجلدات المحم ة وبهذا إن احتاج التطب ق إلى تخز ن معلومات ف ملف متواجد بنفس المجلد الذي حتوي على الملف التنف ذي للتطب ق فسوف حتاج إلى صالح ات إضاف ة للق ام بتلك العمل ة و مكنك تجاوز هذه المشكلة بجعل التطب ق كتب ذلك الملف ف المجلد الخاص بالمستخدم الحال. والعمل ات األخرى الت تحتاج لصالح ات مرتفعة تتضمن الكتابة إلى المجلدات المحم ة والتعامل بشكل مباشر مع العتاد وتعد ل األقسام المحم ة ف سجل النظام مثل.HKEY_LOCAL_MACHINE وتقس م التطب ق إلى أقسام تتطلب صالح ات مرتفعة وأخرى ال تتطلب تلك الصالح ات ال مكن التطب ق من استخدام أقل الصالح ات الممكنة فحسب ولكنه بسط القسم األخطر ف كودك و جعله أسهل للتنق ح فمثال مكننا استخدام كود شب ه بالتال لتنف ذ تطب ق تطلب صالح ات مرتفعة
Private Sub btnrun_click() Handles btnrun.click Start the process. Dim pro As System.Diagnostics.Process pro = System.Diagnostics.Process.Start( _ txtprogram.text, txtarguments.text) Wait for the process to exit. pro.waitforexit() Display the process s exit code. MessageBox.Show( Exit code: & pro.exitcode) Catch ex As System.ComponentModel.Win32Exception This happens if the user fails to elevate to Administrator. MessageBox.Show( Operation canceled, _ Canceled, MessageBoxButtons.OK, _ MessageBoxIcon.Information) End الكود السابق ستخدم الوظ فة System.Diagnostics.Process.Start لتشغ ل التطب ق ممررا مسار التطب ق الذي نر د تنف ذه ومحددات سطر األوامر الخاصة بها وهو ستخدم الدالة WaitForExit من الغرض المعاد الت تنتظر حتى االنتهاء من تنف ذ البرنامج ثم تم التأكد عبر الخاص ة ExitCode من الق مة المعادة من التطب ق المنفذ. و مثل الكود التال اإلجراء main ف البرنامج المستدعى Function Main(ByVal cmdargs() As String) As Integer Dim frm As New frmchoices Display the arguments. For Each str As String In cmdargs frm.lstarguments.items.add(str) Next str Select the first item. If frm.lstarguments.items.count > 0 Then frm.lstarguments.selectedindex = 0 Return the index of the selected item. If frm.showdialog() = DialogResult.Cancel Then Return -1 Else Return frm.lstarguments.selectedindex End Function ح ث بدأ التطب ق بإنشاء نموذج frmchoices وإضافة محددات سطر األوامر إلى صندوق القائمة lstarguments ونختار العنصر األول منه ثم نظهر النموذج فإن قام المستخدم بالضغط على الزر Cancel فالتطب ق ع د الق مة -1 وإن ضغط على الزر OK فهو ع د ق مة الخاص ة Index من صندوق القائمة والموافقة للعنصر الذي تم اخت اره منها والكود المستدع للتطب ق ستقبل تلك الق مة عبر الخاص ة.ExitCode وكجزء من خصائص المستخدم ل UAC فأي عمل تطلب صالح ات مرتفعة جب أن تم تعل مه بواسطة الدرع الق اس ل UAC ح ث جب إظهاره لتحذ ر المستخدم بأنه نفذ تطب ق تطلب صالح ات مرتفعة. وف الوقت الحال ال توجد طر قة بس طة إلظهار ذلك الدرع ف ف جول با ز ك لذلك سنستخدم داالت API لجعل الزر ظهر ذلك الدرع كما هو ظاهر ف قطعة الكود التال ة Imports System.Runtime.InteropServices Module UacStuff Declare Auto Function SendMessage Lib user32.dll _
(ByVal hwnd As HandleRef, ByVal msg As Int32, _ ByVal wparam As IntPtr, ByVal lparam As IntPtr) As Int32 Make the button display the UAC shield. Public Sub AddShieldToButton(ByVal btn As Button) Const BCM_SETSHIELD As Int32 = & H160C btn.flatstyle = Windows.Forms.FlatStyle.System SendMessage(New HandleRef(btn, btn.handle), _ BCM_SETSHIELD, IntPtr.Zero, CType(1, IntPtr)) End Module فف البدا ة نقوم بتعر ف الدالة SendMessage المتواجدة ف المكتبة User32.dll ح ث قوم اإلجراء AddShieldToButton بضبط الخاص ة FlatStyle الخاصة بالزر إلى System ثم ستخدم الدالة SendMessage إلرسال الرسالة BCM_SETSHIELD إلى الزر وال توفر لنا ما كروسوفت حال ا طر قة إلضافة الدرع لتحكمات أخرى غ ر زر األوامر فإن أردت إضافته إلى تحكم أخر فستقوم بذلك لوحدك كما مكنك عمل صورة للدرع ووضعها ببساطة على تحكماتك ولكن هذه الصورة لن تتغ ر إن تم تغ ر صورة الدرع الخاصة بالنظام رفع صالحيات انثزايج مكن للمستخدم رفع المستوى الذي تم تنف ذ التطب ق ضمنه بواسطة اخت ار األمر Run As Administrator من القائمة الت تظهر لك عند الضغط بزر الفأرة ال م ن على الملف التنف ذي للتطب ق ف قوم النظام بإظهار صندوق حوار UAC الخاص وبعد أن قوم المستخدم بإدخال كلمة سر المد ر تم تنف ذ البرنامج باستخدام الصالح ات المرتفعة وهذه الطر قة بس طة وال تتطلب تدخال منك كمبرمج ولكنها تتطلب من المستخدم الق ام بخطوة إضاف ة ولهذا قد ال تكون هذه الفكرة ه الحل األفضل دوما. و مكننا جعل تطب قنا بدأ تطب ق مع ن باستخدام صالح ات مرتفعة بطر قة تشابه تلك الطر قة الت ستخدمها المستخدم فهو بدأ تشغ ل التطب ق طالبا من النظام تشغ له بصالح ات مد ر ح ث مكن استخدام كود شب ه بالتال لتشغ ل تطب ق آخر بصالح ات مرتفعة مع أن ذلك التطب ق بذاته ال طلب تلك الصالح ات عند بدء تشغ له Use the runas verb to start the process. Dim psi As New ProcessStartInfo psi.verb = runas psi.useshellexecute = True psi.filename = txtprogram.text psi.arguments = txtarguments.text Dim pro As System.Diagnostics.Process pro = System.Diagnostics.Process.Start(psi) Wait for the process to exit. pro.waitforexit() Display the process s exit code. MessageBox.Show( Exit code: & pro.exitcode) Catch ex As System.ComponentModel.Win32Exception This happens if the user fails to elevate to Administrator. MessageBox.Show( Operation canceled, _ Canceled, MessageBoxButtons.OK, _ MessageBoxIcon.Information) End ح ث بن الكود السابق الغرض ProcessStartInfo واصفا التطب ق الذي س شغله الكود ح ث قوم بضبط الخاص ة Verb إلى الق مة runas لك ب ن للنظام أن التطب ق جب أن تم تشغ له كمد ر كما ضبط اسم البرنامج ومحددات بدء التشغ ل الخاصة به. وإن كنت تعلم أن التطب ق جب أن تم تشغ له دوما باستخدام صالح ات مرتفعة مكنك جعل ذلك التطب ق طلب رفع صالح اته بنفسه وذلك باستخدام manifest مضمنة داخل الملف التنف ذي للتطب ق وإلنشائها انقر نقرا مزدوجا على My Project ف Solution
Explorer وف صفحة Application انقر على الزر التال المحتو ات االبتدائ ة لذلك الملف View UAC Settings الذي قوم بفتح الملف app.manifest ح ث ظهر الكود <?xml version="1.0" encoding="utf-8"?> <asmv1:assembly manifestversion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoftcom:asm.v2" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance"> <assemblyidentity version="1.0.0.0" name="myapplication.app"/> <trustinfo xmlns="urn:schemas-microsoft-com:asm.v2"> <security> <requestedprivileges xmlns="urn:schemas-microsoft-com:asm.v3"> <!-- UAC Manifest Options If you want to change the Windows User Account Control level replace the requestedexecutionlevel node with one of the following. <requestedexecutionlevel level="asinvoker" uiaccess="false" /> <requestedexecutionlevel level="requireadministrator" uiaccess="false" /> <requestedexecutionlevel level="highestavailable" uiaccess="false" /> If you want to utilize File and Registry Virtualization for backward compatibility then delete the requestedexecutionlevel node. --> <requestedexecutionlevel level="asinvoker" uiaccess="false" /> </requestedprivileges> </security> </trustinfo> </asmv1:assembly> ولجعل التطب ق طلب من UAC رفع صالح اته غ ر ق مة السطر requestexecutionlevel إلى requireadministrator واآلن عندما تقوم بعمل Compile للتطب ق قوم ف جول ستود و بتعل م التطب ق بأنه بحاجة إلى صالح ات مد ر فعندما قوم المستخدم أو أي برنامج آخر بتشغ له س حاول النظام بصورة آل ة رفع صالح اته مظهرا صندوق الحوار الخاص ب UAC للمستخدم طالبا منه الموافقة على رفع تلك الصالح ات انخالصة القواعد األساس ة لبرمجة UAC تتطلب استخدام الحد األدنى من الصالح ات لتنف ذ العمل المراد و جب على التطب ق استخدام صالح ات مستخدم عادي عندما كون ذلك ممكنا وإن كان عل ه تنف ذ مهمة تتطلب صالح ات أكبر ف جب عل ه تنف ذ تطب ق آخر منفصل متلك تلك الصالح ات المرتفعة. وقد ورد ف هذه المقالة ثالثة طرق لبدء البرنامج بصالح ات مرتفعة: األولى ه الطلب من المستخدم فعل ذلك وذلك من خالل النقر بزر الفأرة ال م ن على التطب ق واخت ار األمر Run As Administrator وهذه ل ست بالطر قة المالئمة بشكل عام ولكنها تبقى مقبولة إن كان المستخدم س شغل ذلك التطب ق مرات نادرة والثان ة ه جعل التطب ق بدأ التطب ق اآلخر بصالح ات مرتفعة وهذه طر قة أفضل من األولى ولكنه مازال باإلمكان تشغ ل التطب ق بدون الصالح ات الت حتاجها والثالثة ه تضم ن manifest ضمن التطب ق المستدعى لجعله طلب صالح ات مرتفعة ف كل مرة بدأ ف ها تشغ له
ت كين تزنايجك ين استخداو صالحيات يديز عهى فيستا لتمك ن برنامجك من العمل بصالح ات مد ر شغل ب ئة التطو ر دوما بصالح ات مد ر - انقر بزر الفأرة ال م ن على اختصار ب ئة التطو ر واختر األمرAdministrator - Run As افتح خصائص My Project ثم انقر زر View UAC Settings من صفحة Application ثم ف نافذة خصائص UAC الت تظهر لك استبدل السطر <requestedexecutionlevel level="asinvoker" uiaccess="false" /> بالسطر <requestedexecutionlevel level="requireadministrator" uiaccess="false" /> نفذ األمر Build Solution من قائمة Build وب ئة التطو ر مازالت تعمل ضمن مستوى Administrator كما تأكد بأنك تستخدم ب ئة التطو ر بصالح ات مد ر عندما تقوم بعمل برنامج ال Setup أ ضا
كيف نقوو تجعم أحد األسرار في تزنايجنا ينفذ أوايز تتطهة صالحيات يديز في ويندوس فيستا UAC الخاص نحتاج ف بعض األح ان للق ام بأعمال تتطلب صالح ات خاصة ف و ندوز ف ستا وهنا سنواجه منعا من قبل بو ندوز ف ستا ولك تمكن برنامجنا من تنف ذ هذه المهمة حب عل نا تنف ذ ذلك الكود بمستوى صالح ات مد ر Administrator ح ث سنقوم ف البدا ة بتعر ف فئة تتعامل مع نظام األمان ف و ندوز ف ستا مستخدم ن الفئة WindowsIdentity للتعرف على مستخدم و ندوز الذي نعمل عل ه والفئة WindowsPrincipal للتعرف على المجموعات الت نتسب إل ها ذلك المستخدم ثم نتحقق من أنه عمل بصالح ات مد ر كما ف اإلجراء Friend Shared Function IsAdmin() As Boolean Dim id As WindowsIdentity = WindowsIdentity.GetCurrent() Dim p As WindowsPrincipal = New WindowsPrincipal(id) Return p.isinrole(windowsbuiltinrole.administrator) End Function فإن لم كن المستخدم عمل بصالح ات مد ر هنا نع د بدء العمل ة الحال ة إلى مستوى مد ر كما ف اإلجراء Restart Current Process رافع ن مستوى صالح ات المستخدم Friend Shared Sub RestartElevated() Dim startinfo As ProcessStartInfo = New ProcessStartInfo() startinfo.useshellexecute = True startinfo.workingdirectory = Environment.CurrentDirectory startinfo.filename = Application.ExecutablePath startinfo.verb = "runas" Dim p As Process = Process.Start(startInfo) Catch ex As System.ComponentModel.Win32Exception Return 'If cancelled, do nothing End Application.Exit() بق لد نا إضافة أ قونة الدرع الخاصة باألزرار الت تستخدم صالح ات مد ر إلى زر األوامر المطلوب ح ث تم ذلك باستخدام الدالة SendMessage الموجودة ف المكتبة user32.dll الت تقوم بإرسال الرسالة BCM_SETSHIELD إلى الزر المطلوب كما ف اإلجراء Friend Shared Sub AddShieldToButton(ByVal b As Button) b.flatstyle = FlatStyle.System SendMessage(b.Handle, BCM_SETSHIELD, 0, &HFFFFFFF) وس صبح الكود الكامل للفئة الت سنستخدمها إلجراء عمل ة تمك ن الزر من تنف ذ أعمال تتطلب صالح ات مد ر كما ل Imports System Imports System.Collections.Generic Imports System.Text Imports System.Runtime.InteropServices Imports System.Diagnostics Imports System.Windows.Forms Imports System.Security.Principal Public Class VistaSecurity Private Declare Auto Function SendMessage Lib "user32.dll" _ (ByVal HWND As IntPtr, ByVal MSG As UInteger, ByVal WParam As UInt32, _ ByVal LParam As UInt32) As UInt32 Private Const BCM_FIRST = &H1600 Private Const BCM_SETSHIELD = (BCM_FIRST + &HC) Friend Shared Function IsVistaOrHigher() As Boolean
Return Environment.OSVersion.Version.Major < 6 End Function '/ <summary> '/ Checks if the process is elevated '/ </summary> '/ <returns>if is elevated</returns> Friend Shared Function IsAdmin() As Boolean Dim id As WindowsIdentity = WindowsIdentity.GetCurrent() Dim p As WindowsPrincipal = New WindowsPrincipal(id) Return p.isinrole(windowsbuiltinrole.administrator) End Function '/ <summary> '/ Add a shield icon to a button '/ </summary> '/ <param name="b">the button</param> Friend Shared Sub AddShieldToButton(ByVal b As Button) b.flatstyle = FlatStyle.System SendMessage(b.Handle, BCM_SETSHIELD, 0, &HFFFFFFF) '/ <summary> '/ Restart the current process with administrator credentials '/ </summary> Friend Shared Sub RestartElevated() Dim startinfo As ProcessStartInfo = New ProcessStartInfo() startinfo.useshellexecute = True startinfo.workingdirectory = Environment.CurrentDirectory startinfo.filename = Application.ExecutablePath startinfo.verb = "runas" Dim p As Process = Process.Start(startInfo) Catch ex As System.ComponentModel.Win32Exception Return 'If cancelled, do nothing End Application.Exit() End Class دعنا نجرب معا الفئة الت قمنا بإنشائها للتو سأقوم بالتجربة على كود مقتطف من برنامج قد م ل وهو راقب خدمة النظام الخاصة ب SQl Server و تحكم بها وبما أن كود بدء أو إ قاف هذه الخدمة عتبر من األمور الت تحتاج إلى صالح ات مد ر لذا سأضع فقط قطعة الكود الت تف دنا هنا ح ث سنحتاج ف البدا ة إلى إضافة مرجع إلى System.ServiceProcess وإلى االست رادات التال ة ف بدا ة الملف أ ضا Imports System.ServiceProcess Imports Microsoft.Win32 وهذا هو الكود Private SqlServiceCon As New _ System.ServiceProcess.ServiceController("MSSQL$SQLEXPRESS") Private Sub StopSQL() SqlServiceCon.Refresh() If SqlServiceCon.CanStop = True Then SqlServiceCon.Stop() Catch ex As Exception MsgBox(ex.Message) End
Private Sub StartSql() SqlServiceCon.Refresh() If SqlServiceCon.Status <> ServiceControllerStatus.Running And _ SqlServiceCon.Status <> ServiceControllerStatus.StartPending Then SqlServiceCon.Start() Catch ex As Exception MsgBox(ex.Message) End اآلن سنستخدم فئتنا السابقة VistaSecurity للتحقق أوال من أن برنامجنا متلك الصالح ات المطلوبة باستخدام الدالة IsAdmin فإن لم متلك تلك الصالح ات نع د بدء العمل ة Process الحال ة رافع ن الصالح ات للمستوى المطلوب باستخدام الدالة RestartElevated كما ف الكود If VistaSecurity.IsAdmin = True Then StartSql() Else VistaSecurity.RestartElevated() VistaSecurity.AddShieldToButton(Button1) Imports System.ServiceProcess Imports Microsoft.Win32 Public Class Form1 وعمل ة إضافة أ قونة الدرع إلى زر األوامر تتم باستخدام الكود وف ما ل سرد كامل لكود النافذة Form1 الت استخدمناها هنا وه تمتلك زري أوامر Button1 و Button2 Private SqlServiceCon As New System.ServiceProcess.ServiceController("MSSQL$SQLEXPRESS") Private Sub StopSQL() SqlServiceCon.Refresh() If SqlServiceCon.CanStop = True Then SqlServiceCon.Stop() Catch ex As Exception MsgBox(ex.Message) End Private Sub StartSql() SqlServiceCon.Refresh() If SqlServiceCon.Status <> ServiceControllerStatus.Running And _ SqlServiceCon.Status <> ServiceControllerStatus.StartPending Then SqlServiceCon.Start() Catch ex As Exception MsgBox(ex.Message) End Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
If VistaSecurity.IsAdmin = True Then StartSql() Else VistaSecurity.RestartElevated() Private Sub Button2_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button2.Click If VistaSecurity.IsAdmin = True Then StopSQL() Else VistaSecurity.RestartElevated() Private Sub Form1_Load(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Me.Load VistaSecurity.AddShieldToButton(Button1) VistaSecurity.AddShieldToButton(Button2) End Class سؤال هل من الممكن شرح استخدام هذه الطر قة مع Windows XP انجواب هذه الطر قة خاصة حصرا ل Windows Vista وال مكن استعمالها مع Windows XP