What is COM Elevation?
The simplest way to explain it would be executing code that runs in the context of an administrator. If you need to perform a few tasks that require administrative rights then use COM elevation. It's not neccesary to manifest your applications if your application doesn't need administrative rights "all the time".
The tutorial uses Visual Studio 2008 Professional and the .NET 3.5 framework.
You can find the complete project solution template at the end of this article. You may use it as a building block for adding COM elevations to your applications.
- 1
- Creating the project
On the Visual Studio 2008 IDE [menu] choose File > New > Project...
Choose from the [Add New Project] dialog Visual Basic > Windows > Windows Forms Application
Name the project: MyApplication
Locate the solution explorer window and select MyApplication project.
Right click and choose from the [menu] Add > New Item...
Choose from the [Add New Item] dialog Common items > Code > Module
Name the module: Elevation
Locate the solution explorer window and select the Solution 'My Application'
Right click and choose from the [menu] Add > New > Project...
Choose from the [Add New Project] dialog Visual Basic > Windows > Class Library
Name the project: COMAdmin
Under the COMAdmin project class1.vb is created by default. Right click and choose Delete.
Locate the solution explorer window and select COMAdmin project.
Right click and choose from the [menu] Add > New Item...
Choose from the [Add New Item] dialog Common items > Code > COM Class
Name the COM Class: UACAdmin
Locate the solution explorer window and select COMAdmin project.
Right click and choose from the [menu] Add > New Item...
Choose from the [Add New Item] dialog Common items > Code > Module
Name the module: UACRegister
Locate the solution explorer window and select COMAdmin project.
Right click and choose from the [menu] Add > New Item...
Choose from the [Add New Item] dialog Common items > General > Installer Class
Name the class: UACAdminInstaller
- From the COMAdmin project do the following:
Right click UACAdminInstaller and choose View Code
Add the following source code to UACAdminInstaller.vb
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: | Imports System.ComponentModel
Imports System.Configuration.Install
Imports System.Runtime.InteropServices
Public Class uacAdminInstaller
Inherits Installer
Public Sub New()
MyBase.New()
InitializeComponent()
End Sub
|
Right click UACRegister and choose View Code
Add the following source code to UACRegister.vb
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: | Imports System.Runtime.InteropServices
Imports System.Reflection
Imports Microsoft.Win32
Namespace UserAccountControlRegister
Module uacRegister
Public Sub UacRegisterElevations(ByVal ComClassId As String, ByVal ResourceStringId As Integer)
Dim ComAppId As String = GetExecAssemblyGuid()
Dim ComLocation As String = GetExecAssemblyLocation()
Dim ComPermission() As Byte = GetAccessPermissions()
Dim RegElevation1 As RegistryKey = Registry.ClassesRoot.OpenSubKey("CLSID\{" & ComClassId & "}", True)
RegElevation1.SetValue(ElevationStrings.AppId, "{" & ComAppId & "}", RegistryValueKind.String)
RegElevation1.SetValue(ElevationStrings.LocalizedString, "@" & ComLocation & ",-" & ResourceStringId.ToString, RegistryValueKind.String)
RegElevation1.CreateSubKey(ElevationStrings.Elevation)
RegElevation1.Close()
Dim RegElevation2 As RegistryKey = Registry.ClassesRoot.OpenSubKey("CLSID\{" & ComClassId & "}\Elevation\", True)
RegElevation2.SetValue(ElevationStrings.Enabled, 1, RegistryValueKind.DWord)
RegElevation2.Close()
Dim RegElevation3 As RegistryKey = Registry.ClassesRoot.OpenSubKey("AppID\", True)
RegElevation3.CreateSubKey("{" & ComAppId & "}")
RegElevation3.Close()
Dim RegElevation4 As RegistryKey = Registry.ClassesRoot.OpenSubKey("AppID\{" & ComAppId & "}", True)
RegElevation4.SetValue(ElevationStrings.DllSurrogate, "", RegistryValueKind.String)
RegElevation4.SetValue(ElevationStrings.AccessPermission, ComPermission, RegistryValueKind.Binary)
RegElevation4.Close()
End Sub
Friend Function GetAccessPermissions() As Byte()
Dim pSd As IntPtr
Dim pSdLength As IntPtr
Dim pSdBytes() As Byte = Nothing
Dim lpszSDDL As String = "O:BAG:BAD:(A;;0x3;;;IU)(A;;0x3;;;SY)"
If UnSafeNativeMethods.ConvertStringSecurityDescriptorToSecurityDescriptor(lpszSDDL, UnSafeNativeMethods.SDDL_REVISION_1, pSd, pSdLength) Then
ReDim pSdBytes(pSdLength)
Marshal.Copy(pSd, pSdBytes, 0, pSdLength)
UnSafeNativeMethods.LocalFree(pSd)
End If
Return pSdBytes
End Function
Friend Function GetExecAssemblyGuid() As String
Dim id As [Assembly]
Dim Attributes As Object
id = [Assembly].GetExecutingAssembly
Attributes = id.GetCustomAttributes(GetType(GuidAttribute), False)
Return (DirectCast(Attributes(0), GuidAttribute).Value).ToString
End Function
Friend Function GetExecAssemblyLocation() As String
Return [Assembly].GetExecutingAssembly().Location.ToString
End Function
End Module
End Namespace
Friend Module ElevationStrings
Friend LocalizedString As String = "LocalizedString"
Friend DllSurrogate As String = "DllSurrogate"
Friend AccessPermission As String = "AccessPermission"
Friend Elevation As String = "Elevation"
Friend Enabled As String = "Enabled"
Friend AppId As String = "AppId"
End Module
Friend Module UnSafeNativeMethods
Friend Const SDDL_REVISION_1 = 1
|
Right click UACAdmin and choose View Code
Add the following source code to UACAdmin.vb
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: | Imports System.Runtime.InteropServices Imports COMAdmin.UserAccountControlRegister '// see [UACRegister.vb] |
Right click COMAdmin project and choose from the [menu] > Build
- From the MyApplication project do the following:
Right click MyApplication project and choose Properties
Locate the References tab then click on the Add.. button.
Locate COMAdmin and add it as a reference to the MyApplication project.
Right click Elevation and choose View Code
Add the following source code to Elevation.vb
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: | Imports System
Imports System.Threading
Imports System.Security.Principal
Imports System.Security.Permissions
Imports System.Runtime.InteropServices
Namespace UserAccountControl
Module Elevation
Public Function CreateElevatedComObject(ByVal comclsid As Guid, ByVal comiid As Guid) As Object
' Executes a COM method with administrative rights.
Dim comClsidDirective As String = comclsid.ToString("B") ' use format directive {GUID}
Dim bo3 As New UnsafeNativeMethods.BIND_OPTS3
Dim comObj As Object = Nothing
Dim szmoniker As String = "Elevation:Administrator!new:" & comClsidDirective
bo3.cbStruct = Marshal.SizeOf(bo3)
bo3.hwnd = IntPtr.Zero
bo3.dwClassContext = UnsafeNativeMethods.CLSCTX_LOCAL_SERVER
Try
comObj = CoGetObject(szmoniker, bo3, comiid)
Catch
comObj = Nothing
End Try
Return comObj
End Function
Public Function CreateElevatedProcess(ByVal FileName As String, ByVal param As String) As Process
' Launches a processes using RunAs verb.
Dim psi As New ProcessStartInfo()
psi.UseShellExecute = True
psi.WorkingDirectory = Environment.CurrentDirectory
psi.FileName = FileName
psi.Verb = "runas"
psi.Arguments = param
psi.ErrorDialog = True
Return Process.Start(psi)
End Function
Public Sub Button_SetElevationRequiredState(ByVal this As IntPtr, ByVal bShow As Boolean)
' Adds shield icons to buttons/links or command links
UnsafeNativeMethods.SendMessage(this, BCM_SETSHIELD, IntPtr.Zero, New IntPtr(If(bShow, 1, 0)))
End Sub
Public Function IsAdminRole() As Boolean
Dim myPrincipal As WindowsPrincipal = CType(Thread.CurrentPrincipal, WindowsPrincipal)
Dim sid As New SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, Nothing)
Return myPrincipal.IsInRole(sid)
End Function
Public Function IsElevatedOs() As Boolean
Dim v As OperatingSystem
v = Environment.OSVersion
Return If(v.Version.Major >= 6, 1, 0)
End Function
End Module
End Namespace
Friend Module UnsafeNativeMethods
Friend Const CLSCTX_LOCAL_SERVER As UInteger = &H4
Friend Const BCM_SETSHIELD As UInteger = &H160C
' Windows Vista and above structure.
|
Double click Form1, from the toolbox add a new Button to the form.(Button1) by default.
Add the following source code to Form1
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: | Imports MyApplication.UserAccountControl
Imports COMAdmin
' Visual Studio 2008 .NET 3.5
' TODO: You can start building your application from here
' with user account control elevations ready to be
' used with your new application.
' NOTE: Don't forget to add a reference to [COMAdmin.dll] in
' this application project.
' NOTE: You must have full edition to take advanatage of the
' setup installer project. The Express editions don't
' have visual studio installer templates.
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' TODO: Example that adds a shield icon to the button
' informing the user the action requires elevated
' user rights.
Button1.FlatStyle = FlatStyle.System
Elevation.Button_SetElevationRequiredState(Button1.Handle, True)
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim clsid As Guid = New Guid(COMAdmin.UACAdmin.ClassId)
Dim iid As Guid = New Guid(COMAdmin.UACAdmin.InterfaceId)
If Elevation.IsElevatedOs Then
Dim comRef As UACAdmin._UACAdmin = Nothing ' Interface
comRef = Elevation.CreateElevatedComObject(clsid, iid)
If IsNothing(comRef) <> True Then
' admin::task
comRef.Admin_CreateRootFile("c:\uac.txt", "working")
End If
Else
If Elevation.IsAdminRole Then
' admin::task
Dim comRef As New UACAdmin
comRef.Admin_CreateRootFile("c:\uac.txt", "working")
End If
End If
End Sub
End Class |
When you have finished choose from the Visual Stuido IDE [menu] Build > Build Solution.
- 2
- Creating and Embending Win32 resource into VB.NET
There is a few things you need to do before elevation can take place. The first is that the DLL needs to have a Win32 Resource string embended inside the DLL. This resource string is what will be displayed on the UAC prompt if it is enabled.
Locate your projects directory under windows.
example: C:\Documents and Settings\User\My Documents\Visual Studio 2008\Projects\MyApplicatio
Create a new folder named: Win32Resource
Create a new text file named: resource.h
Add the following:
1: 2: | #define IDS_ELEVATION 100 |
Create a new text file named:ElevationPrompt.rc
Add the following:
1: 2: 3: 4: 5: 6: 7: | #include "resource.h"
STRINGTABLE
BEGIN
IDS_ELEVATION "My Application"
END |
Create a new file named: ElevationPrompt.bat
Add the following:
1: 2: | rc.exe ElevationPrompt.rc |
Copy RC.EXE to the same location. You may also need to copy its dependancy RCDLL.DLL
Run ElevationPrompt.bat a new file should be created named ElevationPrompt.res
Copy the file ElevationPrompt.res file into the COMAdmin project folder located in the root of your project solution.
Under the same location right click COMAdmin.vbproj and choose Open With > Notepad
Add the following line to the
1: 2: |
|
Save the changes to ComAdmin.vbproj . The VB.NET IDE should detect the change when presented with the dialog just click 'Reload'
When you have finished choose from the Visual Studio IDE [menu] Build > Rebuild Solution.
The steps described for adding the line to the project file are used for VB.NET projects. The VB.NET IDE doesn't have a direct option for adding true Win32 resources to projects. You must follow the steps described to embend the string resource into the DLL for VB.NET.
Note: C# projects can directly add resource files under the Properties > Application tab section.
- 3
- Creating a Setup Installation package that automatically installs and registers the COM class
Locate the solution explorer window and select Solution 'My Application'.
Right click and choose from the [menu] Add > New > Project...
Choose from the [Add New Project] dialog Other Project Types > Setup and Deployment > Setup Project
Name the setup project: MyApplicationDeployment
- From the MyApplicationDeployment project do the following:
Locate the solution explorer window and select MyApplicationDeployment project.
Right click and choose from the [menu] Add > File...
Add the COMAdmin.dll file from the dialog.
Example: C:\Documents and Settings\User\My Documents\Visual Studio 2008\Projects\MyApplicatio
Right click and choose from the [menu] Add > File...
Add the MyApplication.exe file from the dialog.
Example: C:\Documents and Settings\User\My Documents\Visual Studio 2008\Projects\MyApplicatio
Right click and choose from the [menu] View > Custom Actions...
Right click the Install folder and choose Add Custom Action...
When the dialog appears double click the Applications Folder option.
Select the COMAdmin.dll and choose OK.
Repeat the following steps above for the Uninstall folder.
Right click the UnInstall folder and choose Add Custom Action...
When the dialog appears double click the Applications Folder option.
Select the COMAdmin.dll and choose OK.
Right click the MyApplicationDeployment project.
Choose from the [menu] Build.
Visual Studio should have created a setup (MSI) package.
You can download the solution project template here. Everything that has been discussed in the tutorial can be located in the project.
















0 comments:
Post a Comment