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