نحتاج في بعض الأحيان للقيام بأعمال تتطلب صلاحيات خاصة في ويندوز فيستا وهنا سنواجه منعا من قبل 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" Try Dim p As Process = Process.Start(startInfo) Catch ex As System.ComponentModel.Win32Exception Return 'If cancelled, do nothing End Try Application.Exit() End Sub
بقي لدينا إضافة أيقونة الدرع الخاصة بالأزرار التي تستخدم صلاحيات مدير إلى زر الأوامر المطلوب حيث يتم ذلك باستخدام الدالة SendMessage الموجودة في المكتبة user32.dll التي تقوم بإرسال الرسالة BCM_SETSHIELD إلى الزر المطلوب كما في الإجراء
Friend Shared Sub AddShieldToButton(ByVal b As Button) b.FlatStyle = FlatStyle.System SendMessage(b.Handle, BCM_SETSHIELD, 0, &HFFFFFFF) End Sub
وسيصبح الكود الكامل للفئة التي سنستخدمها لإجراء عملية تمكين الزر من تنفيذ أعمال تتطلب صلاحيات مدير كما يلي
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) End Sub '/ <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" Try Dim p As Process = Process.Start(startInfo) Catch ex As System.ComponentModel.Win32Exception Return 'If cancelled, do nothing End Try Application.Exit() End Sub End Class
دعنا نجرب معا الفئة التي قمنا بإنشائها للتو
سأقوم بالتجربة على كود مقتطف من برنامج قديم لي وهو يراقب خدمة النظام الخاصة بـ SQl Server ويتحكم بها وبما أن كود بدء أو إيقاف هذه الخدمة يعتبر من الأمور التي تحتاج إلى صلاحيات مدير لذا سأضع فقط قطعة الكود التي تفيدنا هنا حيث سنحتاج في البداية إلى إضافة مرجع إلى System.ServiceProcess وإلى الاستيرادات التالية في بداية الملف أيضا
Imports System.ServiceProcess Imports Microsoft.Win32
وهذا هو الكود
Private SqlServiceCon As New _
System.ServiceProcess.ServiceController("MSSQL$SQLEXPRESS")
Private Sub StopSQL()
Try
SqlServiceCon.Refresh()
If SqlServiceCon.CanStop = True Then SqlServiceCon.Stop()
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
Private Sub StartSql()
Try
SqlServiceCon.Refresh()
If SqlServiceCon.Status <> ServiceControllerStatus.Running And _
SqlServiceCon.Status <> ServiceControllerStatus.StartPending Then
SqlServiceCon.Start()
End If
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Subالآن سنستخدم فئتنا السابقة VistaSecurity للتحقق أولا من أن برنامجنا يمتلك الصلاحيات المطلوبة باستخدام الدالة IsAdmin فإن لم يمتلك تلك الصلاحيات نعيد بدء العملية Process الحالية رافعين الصلاحيات للمستوى المطلوب باستخدام الدالة RestartElevated كما في الكود
If VistaSecurity.IsAdmin = True Then StartSql() Else VistaSecurity.RestartElevated() End If
وعملية إضافة أيقونة الدرع إلى زر الأوامر تتم باستخدام الكود
VistaSecurity.AddShieldToButton(Button1)
وفيما يلي سرد كامل لكود النافذة Form1 التي استخدمناها هنا وهي تمتلك زري أوامر Button1 و Button2
Imports System.ServiceProcess
Imports Microsoft.Win32
Public Class Form1
Private SqlServiceCon As New System.ServiceProcess.ServiceController("MSSQL$SQLEXPRESS")
Private Sub StopSQL()
Try
SqlServiceCon.Refresh()
If SqlServiceCon.CanStop = True Then SqlServiceCon.Stop()
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
Private Sub StartSql()
Try
SqlServiceCon.Refresh()
If SqlServiceCon.Status <> ServiceControllerStatus.Running And _
SqlServiceCon.Status <> ServiceControllerStatus.StartPending Then
SqlServiceCon.Start()
End If
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
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()
End If
End Sub
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()
End If
End Sub
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
VistaSecurity.AddShieldToButton(Button1)
VistaSecurity.AddShieldToButton(Button2)
End Sub
End Class