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

本文关键字:Windows 进程 服务 窗体 | 更新日期: 2023-09-27 17:47:23

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

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

问题是这样的:

当我从Windows服务启动表单时,没有任何反应。谷歌搜索我发现我必须右键单击该服务,转到登录并选中"允许服务与桌面交互"选项。由于我不想要求我的用户这样做,所以我再次在谷歌上搜索了一些代码,以便在安装期间在用户的注册表编辑器中设置此选项。问题是即使设置此选项也不起作用。我必须打开服务的登录选项(已选中),取消选中并再次检查。

那么,如何解决这个问题呢?如何让在同一进程中具有系统托盘控制的 Windows 服务可供任何登录用户使用?

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

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

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

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

实际上,

不应将服务与管理 UI 耦合。

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

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

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

我从这篇文章中弄清楚了如何做到这一点(单击方法表中的"更改"链接)。

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);
}

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

  1. 我们不打算创建一个带有Windows窗体的服务项目,而是创建一个Visual Studio解决方案,其中包含一个Windows Service项目,一个Windows窗体项目和一个安装项目。

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

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

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

  5. 添加Windows服务比这更进一步,但这也是另一个主题谷歌它

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

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

这很简单 - 您需要创建一个线程来执行应用程序事件。像这样(使用 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;
}

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

  • 安全性 - 其他进程可以通过其消息泵向其发送消息,从而获得对系统/本地进程的访问权限。

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

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