Windows 服务与 Windows 窗体在同一进程中

Posted

技术标签:

【中文标题】Windows 服务与 Windows 窗体在同一进程中【英文标题】:Windows services with windows forms in the same process 【发布时间】:2010-09-14 14:48:15 【问题描述】:

我有一个 c# 应用程序,它作为 Windows 服务运行,控制套接字连接和其他东西。 此外,还有另一个 Windows 窗体应用程序来控制和配置此服务(系统托盘带有启动、停止、显示带有配置参数的表单)。

我正在使用 .net 远程处理进行 IPC,这很好,但现在我想显示一些真实的流量和其他报告,远程处理无法满足我的性能要求。所以我想将这两个应用程序合二为一。

问题来了:

当我从 Windows 服务启动表单时,什么也没发生。谷歌搜索我发现我必须右键单击该服务,转到登录并选中“允许服务与桌面交互”选项。因为我不想让我的用户这样做,所以我在安装期间再次进行了一些代码谷歌搜索以在用户的​​ regedit 中设置此选项。问题是即使设置了这个选项,它也不起作用。我必须打开服务的登录选项(已选中),取消选中并再次检查。

那么,如何解决呢?在同一进程中拥有一个带有系统托盘控件的 Windows 服务的最佳方式是什么,任何登录的用户都可以使用?

更新:感谢 cmets 到目前为止,伙计们。我同意使用 IPC 更好,而且我知道混合 Windows 服务和用户界面是不好的。不过,我想知道该怎么做。

【问题讨论】:

“我知道将 UI 与我的服务相结合会破坏我的软件,可能还会破坏我用户的计算机,但我还是想这样做。”退后 3 或 4 步,重新评估你是否应该做你想做的事情。它甚至有意义吗? @Greg D:我不知道你从哪里得到这个报价,但我从来没有说过带有 UI 的服务会破坏用户的计算机。实际上,任何软件都可能对用户的计算机造成很多麻烦,因此您根本没有意义。无论如何,我从来没有问过我是否应该这样做,我问的是如何去做。 【参考方案1】:

使用您选择的技术进行通信的两个独立进程。带有 UI 的服务是一个坏主意。不要走这条路——你会后悔的。

通过简单的套接字连接进行服务通信,我取得了非常好的结果 - 好好记录您的服务协议,使其尽可能简单,这将比您想象的要容易。

【讨论】:

我完全同意这一点。如果可能的话,我会考虑一些不像远程处理那样臃肿的通信方法。套接字、管道等 @andrecarlucci:当我们谈论带有 UI 的服务时,这不是“个人偏好”,而是架构上的事实。使用 TCP/IP 套接字和一个简单的、有据可查的协议将是您从这里到达那里的最佳方式,而不会在同一过程中出现与 Service+UI 相关的问题。 大家好。我同意你们的看法。 DotNet 远程处理就是这样。但这不是问题,问题是如何在同一过程中做到这一点的最佳方式,而不是最好的方式。我会改写我的更新,以免听起来很粗鲁,对此感到抱歉。 你不能在同一个过程中安全地做到这一点。问题是您希望程序的一部分独立于会话,而程序的一部分依赖于会话。在现代环境中这是不可能的。【参考方案2】:

实际上,您不应将服务与管理 UI 耦合。

【讨论】:

【参考方案3】:

我同意格雷格的观点。也许您可以检查不同的 IPC 机制。也许使用套接字和您自己的协议。或者,如果您的服务控制应用只能控制本地机器上的服务,您可以使用命名管道(甚至更快)。

【讨论】:

【参考方案4】:

这是一种混合服务和表单的方法

http://www.codeproject.com/KB/system/SystemTrayIconInSvc.aspx

【讨论】:

【参考方案5】:

我从this article 知道了如何做到这一点(点击方法表中的“更改”链接)。

string wmiPath = "Win32_Service.Name='" + SERVICE_NAME + "'";
using (ManagementObject service = new ManagementObject(wmiPath))

    object[] parameters = new object[11];
    parameters[5] = true;  // Enable desktop interaction
    service.InvokeMethod("Change", parameters);

【讨论】:

是的,我在这里使用了你的代码,它可以工作。但是我制作了在服务安装完成后运行的新控制台应用程序......所以,如果有人试图把它放在 OnStart() 的某个地方,它就不会那样工作。 您可能想要编辑它并添加:parameters[6] = "LocalSystem"; parameters[7] = "";,以便选择登录为本地系统帐户。 ;) 假设您需要 LocalSystem 帐户。大多数时候,我认为人们会希望保持帐户不变,而不是确保帐户是 LocalSystem。【参考方案6】:

我有几个步骤的解决方案,这是计划

    我们不会使用 windows 窗体创建服务项目,而是要创建一个包含 windows 服务项目、windows 窗体项目和设置项目的 Visual Studio 解决方案。

    我们的想法是拥有一个数据库或文件或任何您喜欢存储数据的东西,您将在其中存储 Windows 服务将始终用于运行的参数。因此,您的 Windows 服务和您的 Windows 窗体应用程序应该能够从中修改和检索数据。

    到您的 Windows 应用程序的主窗体,在窗体上拖放一个 NotifyIcon,在属性选项卡中,浏览并选择一个 .ico 图像(您可以在 Visual Studio 中创建一个,但这是您可以在 google 上获得的另一个主题或联系我)当您运行应用程序并且主窗体处于活动状态或显示时,它将显示在系统托盘中,试试吧,运行应用程序。

    将它们都添加为解决方案的设置项目中的输出。要将项目添加到设置项目,它们必须位于同一解决方案中。右键单击解决方案资源管理器中的设置项目,突出显示添加,然后选择项目输出,添加 windows 服务和 windows 窗体输出,您将在设置项目下的解决方案资源管理器中看到它们。

    添加 Windows 服务比这更进一步,但这也是另一个话题 google it

    为windows应用程序创建快捷方式并将其添加到启动文件夹也是另一个主题google或联系我。

    注意 对表单进行编程,使关闭按钮不显示并且表单变为 Me.visible = false 并双击系统托盘中的图标是设置 me.visible=true.that 的唯一方法每当计算机启动时,您的 Windows 窗体应用程序也会启动,并且 visible 会立即设置为 false,但由于它有一个带有图标图像的 notifyicon,它将显示在系统托盘中并双击它使窗体可见以编辑为服务存储的设置,该服务也会自动启动,因为您会在设置项目中设置服务时设置它。 我的邮件是 iamjavademon@gmail.com 以便使用屏幕截图更好地说明并完整解释

【讨论】:

【参考方案7】:

这很简单——您需要创建一个线程来执行应用程序事件。 像这样(带有 CLR 的 C++ 源代码,但你可以在 C# 中制作):

ref class RunWindow
public:
    static void MakeWindow(Object^ data)
    
        Application::EnableVisualStyles();
        Application::SetCompatibleTextRenderingDefault(false); 

        Application::Run(gcnew TMainForm());
    ;
;

并在主线程中创建线程

int main(array<System::String ^> ^args)

    bool bService = RunAsService(L"SimpleServiceWithIconInTrayAndWindow");

    if (bService)
    

        System::Threading::Thread ^thread = gcnew System::Threading::Thread(gcnew ParameterizedThreadStart(RunWindow::MakeWindow));
        thread->Start();

        ServiceBase::Run(gcnew simpleWinService());
        Application::Exit();
    
    else
    
        Application::EnableVisualStyles();
        Application::SetCompatibleTextRenderingDefault(false); 

        // Create the main window and run it
        Application::Run(gcnew TMainForm());
    

    return 0;

【讨论】:

【参考方案8】:

交互式服务的主要问题是:

安全性 - 其他进程可以通过其消息泵向它发送消息,从而获得对 SYSTEM/LOCAL 进程的访问权限。

不完整 - 交互式服务永远不会看到 shell 消息,因此它无法与通知区域图标交互。

我们经常使用 TCP 和 UDP 连接将信息从服务传递给其他 exe,在某些情况下,还包括 MSMQ。

【讨论】:

以上是关于Windows 服务与 Windows 窗体在同一进程中的主要内容,如果未能解决你的问题,请参考以下文章

在同一 Windows 窗体应用程序的实例之间拖放

如何使用 WCF 在 Windows 服务和 Windows 窗体之间进行通信?

SignalR - 在Windows窗体服务器上验证Windows窗体用户

从同一解决方案中引用 Wcf Tcp 服务

C# - 从 Windows 服务启动 Windows 窗体 [重复]

ASP.NET 网站 + Windows 窗体应用程序 + WCF 服务:客户端凭据