65.9K
CodeProject 正在变化。 阅读更多。
Home

在 Windows 8 下以管理员身份运行 ClickOnce 应用

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2014年7月21日

CPOL

3分钟阅读

viewsIcon

14711

如何在 Windows 8 上以管理员身份运行 ClickOnce 应用,当该应用包含 SQL Compact 数据库时

引言

本文介绍了如何在 Windows 8 下以管理员身份运行 ClickOnce 应用程序,特别是当该应用程序还包含 SQL Compact 数据库时。

我的特定应用程序必须以管理员身份运行,因为它需要将数据传递给 MYOB 会计数据文件,并且 MYOB ODBC 驱动程序在 Windows 8 下需要提升的权限。由于该应用程序将分发给许多不同的客户,因此我喜欢 ClickOnce 提供的部署简单性。但是,ClickOnce 部署不支持更改清单中的 requestedExecutionLevel 为 "requireAdministrator"。

背景

我看到了一些文章,例如这篇,它让我找到了正确的方向。代码检查应用程序是否以管理员身份运行,如果不是,则以管理员身份重新启动应用程序。但是,我发现此解决方案存在一些问题。首先,"Application.Current.Shutdown()" 实际上不会立即关闭应用程序,而是继续执行代码,正如我在几篇文章中描述的那样。我发现这导致我的应用程序在 Windows 7 下不断重启。其次,当应用程序以管理员身份重新启动时,它会失去其 "ClickOnce" 状态。因此,任何对 System.Deployment.Application 的引用都会导致错误 - 这对我的应用程序的 SQL Compact 数据库的路径有影响。对 ApplicationDeployment.CurrentDeployment.DataDirectory 的引用变为无效,应用程序选择在可执行文件的工作目录中查找数据库,当然它无法找到,并返回错误“基础提供程序在 Open 上失败。未找到数据库”。

经过几个小时(实际上是几天),我终于找到了解决所有这些问题的方法,我的 ClickOnce WPF 应用程序现在在 Windows 8 下运行良好,愉快地连接到 MYOB 并共享数据。

使用代码

这是完整的代码清单。

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

这是一个 WPF 应用程序,因此我覆盖了 Application_OnStartup

首先,我检查当前的操作系统。我发现 ClickOnce 应用程序在 Windows 7 下运行良好,因此无需检查应用程序是否在 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

如果操作系统是 Windows 8 或更高版本,则应用程序会检查它是否以管理员身份运行。在第一次执行时,这将为 False - 因此应用程序通过使用相同的程序集名称启动一个新进程来重新启动。这里需要注意的重要一点是传递给进程的参数 - 这是 SQL Compact 数据库的路径。这对于应用程序在非 ClickOnce 模式下重新启动时是必要的。

' 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

布尔变量 blnCloseInstance 确定是否关闭当前实例。只有当当前操作系统是 Windows 8 且应用程序未以管理员身份运行时,它才会被设置为 True。

然后关闭应用程序的当前实例。如上所述,Application.Current.Shutdown() 不会立即退出应用程序 - 线程仍在运行,代码继续执行。因此,我已确保子程序中的其余代码仅在 blnCloseInstance = False 时执行。因此,在 Windows 8 下的第一次执行中,此代码将不会执行。

当应用程序在非 ClickOnce 模式下重新启动时,它将以管理员身份运行,并且 blnCloseInstance 将为 False。因此,只有此代码将执行

        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

处于非 ClickOnce 模式时,应用程序不支持对 System.Deployment.Application 的引用,并且它将报告尝试定位数据库时出错。上面的代码使用进程启动时传入的参数手动设置 SQL Compact 数据库位置。

 

历史

版本 1(2014 年 7 月 19 日):原始版本发布到 CodeProject。

版本 2(2014 年 7 月 21 日):对文本进行了细微更改。

 

© . All rights reserved.