如何使一个使用Click-once部署的应用程序在启动时运行?

27

我该如何让Click-once部署的应用在启动时自动运行?

通过搜索,我找到的最佳选项是将应用程序的发布者设置为“启动”,这样开始菜单快捷方式就会放置在启动文件夹中,但这似乎是一个巨大的破解,我希望有一个可以让人们找到的开始菜单图标。

我有哪些选择?


Ivan Leonenko在其ClickOnce应用程序自动启动和清洁卸载方面提供了一个很好的解决方案,其中包括自动启动。 - Martin Braun
11个回答

36

我认为将应用程序添加到启动文件夹是不专业的。我强烈建议使用启动注册表键来启动您的应用程序。

与许多有关此主题的资料相反,设置一个键以启动单击一次应用程序非常简单,并且不需要设置其他快捷方式。您只需使用安装时创建的快捷方式即可:

// The path to the key where Windows looks for startup applications
RegistryKey rkApp = Registry.CurrentUser.OpenSubKey(
                    @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run", true);

//Path to launch shortcut
string startPath = Environment.GetFolderPath(Environment.SpecialFolder.Programs) 
                   + @"\YourPublisher\YourSuite\YourProduct.appref-ms";

rkApp.SetValue("YourProduct", startPath);

虽然我同意你的观点,但在我的情况下,应用程序的要求指定其出现在启动文件夹中。需求过度?可能是的。超出我的控制范围?绝对是的 =) - DiscDev
@DiscDev 有时候,告诉你的老板/客户他们是错的是适当的行动。这将是其中之一;你的解决方案在任何公司环境下都不会起作用,并且它正在使一个简单的任务变得复杂化。 - Don Scott
当然,有时候你应该对抗要求独裁者——但这不是那种情况。医生们想要某些东西,如果他们没有得到他们要求的东西,他们会去别处寻找业务。我们的应用程序已经在数十个国家的医院部署了几年,这方面没有任何问题,所以我不知道“在任何公司环境中都无法工作”的评论是关于什么的。我的经验恰恰相反,我们甚至支持XP到Win8(包括server 2003/2008)。 - DiscDev
1
优秀的解决方案。我不得不在结尾处添加rkApp.Close()才能使其正常工作。 - Jeremy Sigrist
2
一些反病毒应用程序会因为这样的操作而默默地排斥你,不幸的是。我浪费了近一个小时来找出我的应用程序为什么无法运行。系统管理员说:“哦,有这些来自反病毒软件的消息,但我正在通话中。希望你没有花费太多时间进行调试。” - Heki
显示剩余3条评论

12

阅读了本主题中的所有评论和上述提到的johnnycoder博客文章后,我想出了以下解决方案:

  1. 将您的ClickOnce应用程序添加到启动文件夹
  2. 在ClickOnce应用程序被卸载后自动删除启动项(重新启动或注销/登录后)
  3. 经过测试,在Windows XP、Windows 7、Windows Server 2000/2003和Windows 8上都能正常工作

我的解决方案

基本上,您的应用程序将会向启动文件夹写入一个.bat文件,以便为您启动ClickOnce应用程序。这个.bat文件足够聪明,可以检测应用程序是否已被卸载,并且如果找不到ClickOnce应用程序,它会自行删除。

第一步

让批处理文件正常工作。用正确的值替换PUBLISHER_NAME和APPLICATION_NAME。您可以通过安装ClickOnce应用程序,然后按照其在文件系统上的路径找到它们:

@echo off

IF EXIST "%appdata%\Microsoft\Windows\Start Menu\Programs\PUBLISHER_NAME\APPLICATION_NAME.appref-ms" (
"%appdata%\Microsoft\Windows\Start Menu\Programs\PUBLISHER_NAME\APPLICATION_NAME.appref-ms"
) ELSE (start /b "" cmd /c del "%~f0"&exit /b)

批处理文件将检查您的ClickOnce应用程序是否已安装(通过查看是否存在appref-ms文件),如果已安装,则启动它。否则,批处理文件将通过这里概述的方法删除自身。

现在您有了批处理文件,请测试一下。将其放入启动文件夹中,以确保在登录时启动您的应用程序。

第二步

现在,在您的应用程序代码中,您需要将此批处理文件编写到启动文件夹中。以下是在C#中使用上面的批处理文件的示例(请注意,其中涉及一些转义和环境变量):

string[] mystrings = new string[] { @"@echo off

IF EXIST ""%appdata%\Microsoft\Windows\Start Menu\Programs\PUBLISHER_NAME\APPLICATION_NAME.appref-ms"" (
""%appdata%\Microsoft\Windows\Start Menu\Programs\PUBLISHER_NAME\APPLICATION_NAME.appref-ms""
) ELSE (start /b """" cmd /c del ""%~f0""&exit /b)"};

string fullPath = "%appdata%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\StartMyClickOnceApp.bat";

//Expands the %appdata% path and writes the file to the Startup folder
System.IO.File.WriteAllLines(Environment.ExpandEnvironmentVariables(fullPath), mystrings);

以上就是全部内容。欢迎评论或提出改善意见。

编辑:修正了第2步中的引号。


当我测试时,批处理程序无法运行,如何解决,请给予建议。 - meda
@meda,没有足够的信息来帮助你。正如我所说的,首先手动运行批处理文件以确保它执行了预期的操作,然后将批处理文件简单地保存到启动文件夹中,它将在登录时自动运行。 - DiscDev
@Dave 你可能是对的 - 我可能需要稍微调整引号,但如果有什么问题,它应该是相当明显的。不幸的是,我现在无法访问原始代码,但这个想法在一个非常广泛分发的生产ClickOnce应用程序中正在工作。 - DiscDev
1
是的,我进行了一些微调,现在它可以正常工作了。这是一个很好的解决方案。 - Dave
1
谢谢@Dave!我非常沮丧,因为ClickOnce没有提供这个功能,而我们必须使用它...所以这是我能想到的最好的方法。它已经广泛部署了大约6个月,在Windows XP-Windows 8上运行良好,没有问题。 - DiscDev
显示剩余4条评论

5

很不幸,所有这些技巧都不能在Vista上使用。由于某种原因,Vista会在启动时阻止这些程序。

如@thijs所建议的那样,您可以轻松绕过vista的“安全”设置。请参阅有关如何在Windows启动时运行clickonce应用程序的博客文章。


为了阻止恶意恶意软件编写者执行诸如在用户未请求的情况下在启动时运行ClickOnce应用程序之类的操作。 - Spence
2
你可以轻松地绕过Vista的“安全”限制,查看我的博客文章了解如何在Windows启动时运行ClickOnce应用程序:http://www.brokenwire.net/bw/Programming/116/run-clickonce-app-on-startup - thijs
@Spence 不明白这有什么意义。为什么只禁止一种特定类型的应用程序?不是任何其他恶意软件在登录时启动同样有害吗?... - Roman Starkov
是的,但 ClickOnce 应用程序在某些情况下可以在没有管理员权限的情况下“安装”。因此,它们运行的沙箱受到限制,以防止恶意软件在 Windows 启动时运行。 - Spence
2
@thijs,您提供的链接已经失效了! - Peter
@Petoj 它已经修复了。 - thijs

2

首先感谢Discdev的回答。为了让这个工作与Å Ä Ö和其他特殊字符一起工作,我进行了修改,使用UTF-8不同的代码页和没有BOM。

string[] mystrings = new string[] { "chcp 65001", @"IF EXIST ""%appdata%\Microsoft\Windows\Start Menu\Programs\<Publisher>\<App_Name>.appref-ms"" (""%appdata%\Microsoft\Windows\Start Menu\Programs\<Publisher>\<App_Name>.appref-ms"") ELSE (start /b """" cmd /c del ""%~f0""&exit /b)" };
string fullPath = "%appdata%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\StartErrandDynamicMenu.bat";
System.Text.Encoding utf8WithoutBOM = new System.Text.UTF8Encoding(false);
System.IO.File.WriteAllLines(Environment.ExpandEnvironmentVariables(fullPath), mystrings, utf8WithoutBOM);

2

感谢DiscDev和dbenham。这个解决方案非常好用。我想分享最新的、基于dbenham的代码。

 const string fullPathEnvVar =
            "%appdata%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\StartMyClickOnceApp.bat";
 //Expands the %appdata% path
 string fullPath = Environment.ExpandEnvironmentVariables(fullPathEnvVar);

if (!File.Exists(fullPath))
        {
            string[] mystrings =
            {
                @"@echo off 
if exist ""%appdata%\Microsoft\Windows\Start Menu\Programs\<PublisherName>\<ApplicationName>.appref-ms"" (
""%appdata%\Microsoft\Windows\Start Menu\Programs\<PublisherName>\<ApplicationName>.appref-ms""
) else (
(goto) 2>nul & del ""%~f0""
)"
            };
            //write the file to the Startup folder
            File.WriteAllLines(fullPath, mystrings);
        }

2

这是另一种在卸载后清理启动文件夹的解决方案:https://dev59.com/uHRC5IYBdhLWcg3wJNYN#23500425 - DiscDev

1
如果有人仍在寻找解决方案,可能会对底部的评论http://johnnycoder.com/blog/2009/02/24/clickonce-run-at-startup/有所帮助。建议设置ClickOnce发布选项(在VS.Net 2010中,这是在项目属性、发布屏幕和选项中),将发布者名称设置为“Startup”,套件名称留空。这确实使程序快捷方式出现在Windows 7的启动文件夹中。我不知道其他版本的Windows会怎样。

未找到http://johnnycoder.com/blog/2009/02/24/clickonce-run-at-startup/相关内容。 - Kiquenet

1

就实际启动应用程序而言,在启动文件夹中放置链接是最好的选择。如果不是在启动文件夹中,则可以使用启动注册表键。

解决图标未处于正常位置的方法是,让应用程序在启动时将自己的链接放置到启动文件夹中。ClickOnce 应用程序将在首次安装时运行。应用程序可以利用此启动来将链接放置在启动文件夹中。现在链接将出现在两个位置,您应该会很满意。

但问题在于,现在删除 ClickOnce 应用程序将不再真正删除它。ClickOnce 不会跟踪手动添加的链接,因此每次有人卸载您的应用程序并重新启动时,它都会重新安装。我会开始考虑该程序的行为是否良好 :(。


我本以为ClickOnce沙盒会防止您未经特定管理员请求就触碰这两个目录之一? - Spence
我找到了一种方法,可以将ClickOnce应用程序添加到启动文件夹中,并在卸载后(经过1次重新启动或注销/登录周期)将其删除。请参见我的答案:https://dev59.com/uHRC5IYBdhLWcg3wJNYN#23500425 - DiscDev

1

你可以将你的应用程序添加到适当的“运行”启动注册表键中。这样,即使在删除应用程序时无法删除它,也不会对任何东西造成伤害,也不会有人看到损坏的引用。


0

我做了这个,对我有用

 Sub AddToStartup()
    If My.Application.IsNetworkDeployed Then


        Dim str As String = My.Application.Deployment.UpdatedApplicationFullName.ToString

        Dim saida As String = ""

        For i As Integer = 0 To str.Split(",").Length

            If Not saida.Contains("msil") Then
                saida += str.Split(",")(i) & ", "
            End If
        Next

        If saida.Contains("msil") Then
            saida = saida.Substring(0, saida.Length - 2)
        End If

        Dim name As String = My.Application.Info.Title
        Dim file As String = Path.Combine(My.Application.Info.DirectoryPath, name & ".appref-ms")
        My.Computer.FileSystem.WriteAllText(file, saida, False)

        Dim reg As RegistryKey = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", True)
        reg.SetValue(name, file)

    End If


End Sub

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接