Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / SQL

Running a ClickOnce app as Administrator under Windows 8

5.00/5 (4 votes)
21 Jul 2014CPOL3 min read 14.4K  
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.

VB.NET
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.

VB.NET
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. 

VB.NET
' 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:

VB.NET
 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.

 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)