C# 在同一进程中使用Windows窗体的Windows服务

C# 在同一进程中使用Windows窗体的Windows服务,c#,windows,windows-services,C#,Windows,Windows Services,我有一个c#应用程序,它作为windows服务运行,控制套接字连接和其他东西。 此外,还有另一个windows窗体应用程序来控制和配置此服务(带启动、停止、带配置参数的显示窗体的systray) 我正在使用.net远程处理来完成IPC,这很好,但现在我想显示一些真实的流量和其他报告,远程处理将无法满足我的性能要求。因此,我想将这两个应用程序合并到一个应用程序中 问题是: 当我从windows服务启动表单时,什么都没有发生。通过谷歌搜索,我发现我必须右键单击该服务,登录并选中“允许服务与桌面交互”

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

我正在使用.net远程处理来完成IPC,这很好,但现在我想显示一些真实的流量和其他报告,远程处理将无法满足我的性能要求。因此,我想将这两个应用程序合并到一个应用程序中

问题是:

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

那么,如何解决这个问题呢?如何让任何登录用户都能在同一进程中使用带有systray控件的windows服务,这是最好的方法?


更新:谢谢你们的评论,伙计们。我同意使用IPC更好,我知道混合使用windows服务和用户界面是不好的。尽管如此,我还是想知道如何做到这一点。

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


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

实际上,您不应该将您的服务与管理UI结合起来。

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

非常简单-您需要为执行应用程序事件创建一个线程。 像这样(C++源代码CLR,但是你可以用C语言做这个):

并在main中创建线程

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;
}
int main(数组^args)
{
bool bService=RunaService(L“SimpleService with IConTrayandWindow”);
如果(b服务)
{
System::Threading::Thread^Thread=gcnew System::Threading::Thread(gcnew ParameterizedThreadStart(RunWindow::MakeWindow));
线程->开始();
ServiceBase::Run(gcnewsimplewinservice());
Application::Exit();
}
其他的
{
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
//创建主窗口并运行它
应用程序::运行(gcnew tmaninform());
}
返回0;
}

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

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

  • 不完整性-交互式服务从未看到外壳消息,因此无法与通知区域图标交互


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

这里有一种将服务和表单混合的方法


我从(单击“方法”表中的“更改”链接)了解了如何执行此操作


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

  • 我们将不使用windows窗体创建服务项目,而是创建包含windows服务项目、windows窗体项目和安装项目的visual studio解决方案

  • 这个想法是要有一个数据库、一个文件或任何你喜欢存储数据的东西,你可以在其中存储你的windows服务运行时经常使用的参数。因此,您的windows服务和windows窗体应用程序应该能够从中修改和检索数据

  • 要访问Windows应用程序的主窗体,请在窗体上拖放Notify图标,在“属性”选项卡中,浏览并选择一个.ico图像(您可以在visual studio中创建一个图像,但这是您可以在google上获得的另一个主题或与我联系)当您运行应用程序且主窗体处于活动状态或显示状态时,它将显示在系统托盘中,请尝试,然后运行应用程序

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

  • 添加windows服务比这更进一步,但这也是google it的另一个主题

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

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


  • 我完全同意这一点。如果可能的话,我会考虑一些沟通
    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;
    }
    
    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);
    }