Running a ClickOnce app as Administrator under Windows 8





5.00/5 (4 votes)
How to run a ClickOnce app as Administrator on Windows 8 when the app includes a SQL Compact database
Introduction
This article explains how to run a ClickOnce application as an Administrator under Windows 8, when the app also includes a SQL Compact Database.
My particular application must run as Administrator because it needs to pass data to a MYOB accounting data file, and the MYOB ODBC Driver requires elevated permission under Windows 8. Because the app is to be distributed to many different customers, I like the deployment simplicity that ClickOnce offers. However, ClickOnce deployments do not support changing the requestedExecutionLevel
in the manifest to "requireAdministrator".
Background
I saw a few articles such as this which put me on the right track. The code checks if the app is running as Administrator, and if not, restarts the app as an Administrator. However, I found a couple of problems with this solution. First, "Application.Current.Shutdown()" does not actually shutdown the application immediately, but continues to execute code, as I found described in several articles. I found this caused my app to keep restarting ad inifinitum under Windows 7. Secondly, when the app is restarted as Administrator, it loses its "ClickOnce" status. Therefore, any references to System.Deployment.Application result in error - which has implications for the path to my app's SQL Compact database. The reference to ApplicationDeployment.CurrentDeployment.DataDirectory
becomes invalid and the app chooses to look in the executable's working directory for the database, which of course it cannot find, and returns the error "The underlying provider failed on Open. Database was not found".
After several hours (actually, several days), I finally found the workarounds to all these issues, and my ClickOnce WPF app now runs beautifully under Windows 8, happily connecting to MYOB and sharing data.
Using the code
Here is the full code listing.
Class Application
' Application-level events, such as Startup, Exit, and DispatcherUnhandledException
' can be handled in this file.
Protected Overrides Sub OnStartup(e As StartupEventArgs)
Dim blnCloseInstance As Boolean = False
' Check if OS is Windows 8 or higher
Dim osVer As System.OperatingSystem = System.Environment.OSVersion
If osVer.Version.Major > 6 Or (osVer.Version.Major = 6 And osVer.Version.Minor >= 2) Then
' Check if user is NOT admin
If Not IsRunningAsAdministrator() Then
' Setting up start info of the new process of the same application
Dim processStartInfo As New ProcessStartInfo(Assembly.GetEntryAssembly().CodeBase)
' Set the DataDirectory as an argument to the new process
processStartInfo.Arguments = "" & Chr(34) & ApplicationDeployment.CurrentDeployment.DataDirectory & Chr(34) & ""
' Using operating shell and setting the ProcessStartInfo.Verb to “runas” will let it run as admin
processStartInfo.UseShellExecute = True
processStartInfo.Verb = "runas"
' Start the application as new process
Process.Start(processStartInfo)
blnCloseInstance = True
Application.Current.Shutdown()
End If
End If
If blnCloseInstance = False Then
'set DataDirectory
If IsRunningAsAdministrator() = True Then
Dim arguments As String() = Environment.GetCommandLineArgs()
Try
Dim datadir As String = arguments(1)
AppDomain.CurrentDomain.SetData("DataDirectory", datadir)
Catch ex As Exception
'already running as administrator - app was not restarted
End Try
End If
' Do your startup tasks
MyBase.OnStartup(e)
End If
End Sub
Public Function IsRunningAsAdministrator() As Boolean
' Get current Windows user
Dim windowsIdentity__1 As WindowsIdentity = WindowsIdentity.GetCurrent()
' Get current Windows user principal
Dim windowsPrincipal As New WindowsPrincipal(windowsIdentity__1)
' Return TRUE if user is in role "Administrator"
Return windowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator)
End Function
End Class
This is a WPF application, so I have overridden Application_OnStartup
.
First I check the current operating system. I found the ClickOnce app runs fine under Windows 7, therefore there is no need to check whether the app is running as Administrator under Windows 7.
Dim osVer As System.OperatingSystem = System.Environment.OSVersion
If osVer.Version.Major > 6 Or (osVer.Version.Major = 6 And osVer.Version.Minor >= 2) Then
If the OS is Windows 8 or higher, the app then checks if it is running as Administrator. On the first execution, this will be False - so the app gets restarted by starting a new process using the same assembly name. The important thing to note here is the argument passed to the process - which is the path to the SQL Compact datbase. This is necessary for when the app restarts in non-ClickOnce mode.
' Check if user is NOT admin
If Not IsRunningAsAdministrator() Then
' Setting up start info of the new process of the same application
Dim processStartInfo As New ProcessStartInfo(Assembly.GetEntryAssembly().CodeBase)
' Set the DataDirectory as an argument to the new process
processStartInfo.Arguments = "" & Chr(34) & ApplicationDeployment.CurrentDeployment.DataDirectory & Chr(34) & ""
' Using operating shell and setting the ProcessStartInfo.Verb to “runas” will let it run as admin
processStartInfo.UseShellExecute = True
processStartInfo.Verb = "runas"
' Start the application as new process
Process.Start(processStartInfo)
blnCloseInstance = True
Application.Current.Shutdown()
End If
The boolean variable blnCloseInstance
determines whether the current instance will be closed. It only gets set to True when the current OS is Windows 8 and the app is not running as Administrator.
The current instance of the app is then shutdown. As noted above, Application.Current.Shutdown()
does not immediately exit the application - the thread still runs and code continues to execute. Therefore, I have ensured the remaining code in the sub is only executed when blnCloseInstance = False. So on the first execution under Windows 8, this code will not be executed.
When the app restarts in non-ClickOnce mode, it will be running as Administrator, and blnCloseInstance will be False. Therefore only this code will execute:
If blnCloseInstance = False Then
'set DataDirectory
If IsRunningAsAdministrator() = True Then
Dim arguments As String() = Environment.GetCommandLineArgs()
Try
Dim datadir As String = arguments(1)
AppDomain.CurrentDomain.SetData("DataDirectory", datadir)
Catch ex As Exception
'already running as administrator - app was not restarted
End Try
End If
' Do your startup tasks
MyBase.OnStartup(e)
End If
Being in non-ClickOnce mode, the app does not support references to System.Deployment.Application, and it will report an error trying to locate the database. The code above sets the SQL Compact database location manually, using the argument passed in when the process was started.
History
Version 1 (19 July 2014): Original version posted to CodeProject.
Version 2 (21 July 2014): Minor change to text.