从Windows服务启动浏览器

3
我对微软的世界还不是很熟悉。我阅读了这个回答和其他许多链接,我知道在Vista之后,Windows服务无法与桌面交互。
然而,我有一个紧急情况,需要快速找到解决方法。我需要我的Windows服务以某种方式打开一个带URL的浏览器(现在任何一种不好看的黑客都可以)。 答案说明:
你需要编写一个单独的代理应用程序,在用户登录时自动启动,并与您的服务通信。然后代理可以启动浏览器或执行您所需的任何其他操作。
请问有人能简单地解释一下我该如何做吗?该服务如何与该'代理'通信?这个“代理”究竟是什么?
非常感谢任何链接或建议。
编辑:目前,我的服务尝试运行以下代码:System.Diagnostics.Process.Start("www.google.com"); 我后来发现这并不起作用
4个回答

6

是的,在服务中,您可以在桌面上运行代码...问题在于哪个桌面?因为可能会有多个用户同时登录(例如,在Windows Server中,可能会有多个远程登录的用户)。

如果像您的示例中那样从服务中使用Process.Start

System.Diagnostics.Process.Start("www.google.com");

该程序不会出现在任何用户的桌面上,因为该服务与用户的任何桌面都没有关联。
解决方法是检测本地登录的用户,然后以该用户的身份(并在该用户的特权下)执行程序。为此,我想链接到我的另一个答案,其中演示了如何从服务中调用程序。那个答案中,我提供了所需的代码技术
注意:那个答案是关于屏幕捕获的,但我描述的技术是在用户会话中调用程序,而该方法可以从服务中工作。因此,它适用于您的问题。
您可以使用此方法直接执行所需的浏览器。作为替代方案,您可以使用它来调用您的服务可执行文件,在其中您可以使用System.Environment.UserInteractive检测可执行文件是否正在运行为服务或不是。如果将URL作为执行参数传递给服务可执行文件,则可以像上面的示例一样使用Process.Start打开默认浏览器。
注意:该代码已在Windows 7上进行了测试。

4
很抱歉,没有“快速的方法”可以做到这一点。 (由于服务无法与用户的桌面交互,即使它可以启动浏览器,用户也无法看到它。)
您提到的“代理应用程序”是完全独立的应用程序,在用户登录时在其桌面下运行。 它必须使用某种进程间通信方法(命名管道,TCP / IP等)与服务进行通信。
服务不与桌面交互的原因是出于安全考虑。 没有“快速解决方案”可用,也不应该有。 也没有“简单的方法”来规避这种变化(再次强调,如果您可以以简单的方式绕过安全性,那么这就不是太安全了)。

我曾经遇到过同样的问题,但是这个链接帮助了我:http://asprosys.blogspot.ro/2009/03/allow-service-to-interact-with-desktop.html - Cornel Urian

2

使用 PInvoke 调用 ShellExecute http://www.pinvoke.net/default.aspx/shell32.shellexecute

C# Signature:
public enum ShowCommands : int
{
    SW_HIDE         = 0,
    SW_SHOWNORMAL       = 1,
    SW_NORMAL       = 1,
    SW_SHOWMINIMIZED    = 2,
    SW_SHOWMAXIMIZED    = 3,
    SW_MAXIMIZE     = 3,
    SW_SHOWNOACTIVATE   = 4,
    SW_SHOW         = 5,
    SW_MINIMIZE     = 6,
    SW_SHOWMINNOACTIVE  = 7,
    SW_SHOWNA       = 8,
    SW_RESTORE      = 9,
    SW_SHOWDEFAULT      = 10,
    SW_FORCEMINIMIZE    = 11,
    SW_MAX          = 11
}

[DllImport("shell32.dll")]
static extern IntPtr ShellExecute(
IntPtr hwnd,
string lpOperation,
string lpFile,
string lpParameters,
string lpDirectory,
ShowCommands nShowCmd);





 // Asks default mail client to send an email to the specified address.
    ShellExecute( IntPtr.Zero, "open", "mailto:support@microsoft.com", "", "", ShowCommands.SW_SHOWNOACTIVATE    );

    // Asks default browser to visit the specified site.
    ShellExecute( IntPtr.Zero, "open", "http://channel9.msdn.com", "", "", ShowCommands.SW_SHOWNOACTIVATE );

    // Opens default HTML editing app to allow for edit of specified file
    ShellExecute( IntPtr.Zero, "edit", @"c:\file.html", "", "", ShowCommands.SW_SHOWNOACTIVATE );
   //Modified by Aljaz: Replaced the last zero in these calls with 4  otherwise it wouldn't show anything
   // 0 stands for SW_HIDE contant, which means execute but don't show the window which is probably not 
   // what we want.

这看起来很简单。我惊讶地发现在我的快速研究中没有遇到过这个。有什么缺点或者我漏掉了什么吗?我会尝试一下。 - Ayrton Senna
2
@AyrtonSenna "有什么缺点"?是的:由于您正在从服务中运行,因此使用shellexecute调用的程序将不会出现在用户的桌面上,因此它将不可见。顺便说一句,Proccess.Start是.NET中shellexecute的等效项,这就是为什么在C#中很少使用shellexecute的原因。 - Theraot

1
如果您需要紧急处理某些事情-我会尝试从服务中将消息放入MSMQ,并编写一些简单的客户端来等待队列消息并处理它们。
更新:
实际上,如果您需要非常快速的操作-您甚至可以写入某个文件,并编写简单的桌面(winforms?)应用程序以在池中读取此文件(每5秒钟),并在需要时打开浏览器。

如果你的应用程序中还没有实现MSMQ,那么它并不是“快速”的。 - Ken White
我确实喜欢将数据写入文件的想法。事实上,这个Windows服务很简单,只是不断轮询一个URI以获取新的更新(每5秒钟一次)。我能否替换服务本身,并使应用程序在后台执行此操作?这应该在一个线程中进行吗?就像我说的,我对Microsoft技术还很陌生。 - Ayrton Senna
1
这取决于你想要实现什么。如果你希望无论用户是否登录,都能够发生某些事情 - 你需要像服务一样的东西。如果你只需要在用户登录时才需要这个功能 - 是的,你可以从应用程序中实现它。你可以使用Threading.Timer来安排操作。此外,你可能会发现“隐藏”应用程序窗体(可能留到托盘)很有用。 - evgenyl
是的,我认为当用户登录时我会想要这个功能,而且可能需要隐藏。非常感谢您的帮助。 - Ayrton Senna
有一些非常好的回复,我很可能会在明天选择一些对我有用的作为答案。 - Ayrton Senna

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