问题描述
我创建了一个 WPF 桌面应用程序和一个工作服务(所有 .NET 6.0 预览版 3),使用 Microsoft Visual Studio 安装程序项目扩展将它们打包在 .MSI 安装文件中,该扩展将在机器上安装 WPF 应用程序。
虽然应用程序安装并正常运行,但我必须以某种方式实现应在安装 WPF 应用程序后运行的服务安装。我为此创建了一个函数,它以管理员身份运行 sc.exe 并使用 Process.Start()
安装服务,如下所示:
private static void InstallService()
{
const string ServiceName = "SomeService";
var path = Path.GetFullPath(@".\SomeService.exe");
var psi = new processstartinfo
{
FileName = @"C:\Windows\system32\sc.exe",Arguments = $"create { ServiceName } binPath= { path } start= auto",Verb = "runas",UseShellExecute = true,};
try
{
Process.Start(psi);
}
catch (Exception ex)
{
MessageBox.Show($"Installation has Failed: { ex.Message + ex.StackTrace }");
}
}
此函数的问题在于,当应用程序在 Visual Studio 中运行时以及从 Visual Studio 创建的 'bin\Release' 文件夹中运行时,它都能正常执行。然后该服务安装并且可以启动。但是,当使用 .MSI 包安装程序时,服务不会安装并且不显示MessageBox
,这表明没有抛出异常 .
我尝试过的:
private static void RunService()
{
const string ServiceName = "SomeService";
var psi = new processstartinfo
{
FileName = @"C:\Windows\system32\sc.exe",Arguments = $"start { ServiceName }",};
try
{
Process.Start(psi);
}
catch (Exception ex)
{
MessageBox.Show($"Running was not approved or Failed: { ex.Message }");
}
}
然而,此功能在两种情况下都能正常运行,尽管很明显只有在先前安装了该服务时才能使用,而这在 .MSI 安装的应用程序中无法完成。至于使用Process.Start()
代替ServiceController
类,应用默认不应该以管理员身份运行,而使用ServiceController
是不可能的,所以我将 Process.Start()
与 Verb = "runas"
一起使用,它以管理员身份运行进程,仅在需要时显示 UAC 提示(仅在服务尚未运行时才启动)。
有没有办法解决这个问题并在 .MSI 安装的 WPF 应用程序中安装一个工作服务?
解决方法
当我进一步分析所有可能的因素时,我终于注意到了导致这个问题的原因。
通常,Visual Studio 生成的路径没有任何空格,因此它们可以作为命令参数编写,无需双引号。在我的例子中,包含项目文件的路径也没有任何空格,这导致没有双引号的命令可以正常执行。然而,安装路径确实包含空格,因为它被设计为更加用户友好,这导致这段代码无法按预期执行:
// the path parameter in the command will end when there will be a space somewhere in the path
Arguments = $"create { ServiceName } binPath= { path } start= auto"
path
变量仅包含完整路径,不包含在双引号中。
为防止出现此问题,必须使用双引号,并且包含 \
符号会通知编译器这些双引号是字符串的一部分:
// the path parameter is wrapped in double quotes and will be fully read
Arguments = $"create { ServiceName } binPath= \"{ path }\" start= auto"
当路径完全写入代码时,丢失的双引号很容易被注意到。然而,当使用字符串插值时,它可能会导致问题,就像我的情况一样。