加载DLL和主机作为窗口服务加载、窗口、主机、DLL

2023-09-07 16:09:02 作者:老板来碗杂碎面

我有,我想举办Windows服务3 RESTful服务。我已创建了一个安装程序将举办三个服务结合在一起。我想我安装了一下配置。我想以同样的安装程序添加新的服务,而不需要它来编辑code。

I have 3 Restful services which I want to host as windows service. I have created one installer which will host all three services together. I want to make my installer a bit configurable. I want to add new services to the same installer without needing it to edit the code.

下面是我目前的code的安装程序。在这里,我给引用的DLL和托管他们。这只是正常的$ C $下任何Windows主机的项目。

Here is my current code for the installer. Here I am giving reference to the dll and hosting them. This is just normal code for any windows host project.

App.config文件

App.config file

 <services>
  <service name="Service1">
    <endpoint binding="webHttpBinding" contract="IService1" ehaviorConfiguration="REST"/>
  </service>
  <service name="Service2">
    <endpoint binding="webHttpBinding" contract="IService2" behaviorConfiguration="REST"/>
  </service>
  <service name="Service3">
    <endpoint binding="webHttpBinding" contract="IService3" behaviorConfiguration="REST"/>
  </service>
</services>
<behaviors>
  <serviceBehaviors>
    <behavior>
      <serviceMetadata httpGetEnabled="True"/>
      <serviceDebug includeExceptionDetailInFaults="true"/>
    </behavior>
  </serviceBehaviors>

  <endpointBehaviors>
    <behavior name="REST">
      <webHttp automaticFormatSelectionEnabled="true" defaultOutgoingResponseFormat="Json" helpEnabled="true"/>
    </behavior>
  </endpointBehaviors>
</behaviors>

code。在安装程序。

Code in the installer.

public partial class Service : ServiceBase
{
    public ServiceHost Service1Host = null;
    public ServiceHost Service2Host = null;
    public ServiceHost Service3Host = null;

    public Service()
    {
        InitializeComponent();
    }

    protected override void OnStart(string[] args)
    {


        Uri Service1_baseAddress = new Uri("http://localhost:9999/Service1");
        Uri Service2_baseAddress = new Uri("http://localhost:9999/Service2");
        Uri Service3_baseAddress = new Uri("http://localhost:9999/Service3");

        if (Service1Host != null)
        {
            Service1Host.Close();
        }
        Service1Host = new ServiceHost(typeof(Service1), Service1_baseAddress);                        
        Service1Host.Open();

        if (Service2Host != null)
        {
            Service2Host.Close();
        }
        Service2Host = new ServiceHost(typeof(Service2), Service2_baseAddress); 
        Service2Host.Open();

        if (Service3Host != null)
        {
            Service3Host.Close();
        }
        Service3Host = new ServiceHost(typeof(Service3), Service3_baseAddress);
        Service3Host.Open();
    }

    protected override void OnStop()
    {
        if (Service1Host != null)
        {
            Service1Host.Close();
            Service1Host= null;
        }
        if (Service2Host != null)
        {
            Service2Host.Close();
            Service2Host = null;
        }
        if (Service3Host != null)
        {
            Service3Host.Close();
            Service3Host = null;
        }
    }
}

我已经试过就在这里。我删除了服务端点配置的的app.config和code做到了。我把一个文件夹中,并加载在code这些DLL所有的DLL。要获取服务和接口我加入的参数在App.config如下,这将给服务名和接口名称从加载的程序集检索。这一切工作正常。但我有一个小问题,我已经解释如下。

What I have tried is here. I removed the service endpoint configuration from app.config and did it in code. I put all the dlls in one folder and loading those dlls in code. To get the service and interface I am adding parameters in the app.config as follows which will give the service name and interface name to retrieve from loaded assembly. It all works fine. But I have a little problem, I have explained below.

新App.config文件

New App.config file

<appSettings>
<add key="Service1.dll" value="Service1"/>
<add key="IService1.dll" value="IService1"/>
<add key="Service2.dll" value="Service2"/>
<add key="IService2.dll" value="IService2"/>
<add key="Service3.dll" value="Service3"/>
<add key="IService3.dll" value="IService3"/>
</appSettings>

新的Windows主机code

New Windows host code

public partial class Service : ServiceBase
{
    public ServiceHost ServiceHost = null;

    public WinService()
    {
        InitializeComponent();
    }

    protected override void OnStart(string[] args)
    {
    string[] files = Directory.GetFiles(@"C:\Users\Desktop\WindowsHost\dlls", "*.dll");
        for (int i = 0; i < files.Length; i++)
            files[i] = Path.GetFileName(files[i]);

        foreach (var dllName in files)
        {
            string filePath = @"C:\Users\Desktop\WindowsHost\dlls\" + dllName;
            Assembly assembly = Assembly.LoadFrom(filePath);

            string serviceName = ConfigurationManager.AppSettings[dllName];
            string interfaceName = ConfigurationManager.AppSettings["I" + dllName];

            Type serviceToHost = assembly.GetType(serviceName);
            var instance = Activator.CreateInstance(serviceToHost);

            Type contract = service.GetInterface(interfaceName, true);

            string address = dllName.Remove(dllName.LastIndexOf("."));

            Uri baseAddress = new Uri("http://localhost:9999/" + address);

            if (ServiceHost != null)
            {
                ServiceHost.Close();
            }
            ServiceHost = new ServiceHost(instance, baseAddress);
            ServiceEndpoint sEP = ServiceHost.AddServiceEndpoint(contract, new WebHttpBinding(), "");
            WebHttpBehavior webHttpBeh = sEP.Behaviors.Find<WebHttpBehavior>();

            if (webHttpBeh != null)
            {
                webHttpBeh.AutomaticFormatSelectionEnabled = true;
                webHttpBeh.DefaultOutgoingResponseFormat = WebMessageFormat.Json;
                webHttpBeh.HelpEnabled = true;
            }
            else
            {
                WebHttpBehavior newWebHttpBeh = new WebHttpBehavior();
                newWebHttpBeh.AutomaticFormatSelectionEnabled = true;
                newWebHttpBeh.DefaultOutgoingResponseFormat = WebMessageFormat.Json;
                newWebHttpBeh.HelpEnabled = true;
                sEP.Behaviors.Add(newWebHttpBeh);
            }

            ServiceHost.Open();
        }
    }

    protected override void OnStop()
    {
        if (ServiceHost != null)
        {
            ServiceHost.Close();
            ServiceHost = null;
        }
    }
}

我在做什么这里创建加载的DLL的实例,并收留了它作为Windows服务。

What I am doing here is creating an instance of the loaded dll and hosting it as a windows service.

ServiceHost = new ServiceHost(instance, baseAddress);

这工作正常,如果你的服务行为已经InstanceContextMode设置为单。否则它给出了一个错误。

This works fine if your service behaviour have InstanceContextMode set to single. Else it gives an error.

错误: 服务无法启动​​System.InvalidOperationException:为了使用ServiceHost的构造方法,它需要一个服务实例中的一个,该服务的InstanceContextMode必须设置为InstanceContextMode.Single这可以通过ServiceBehaviorAttribute配置否则。请考虑使用ServiceHost的构造函数,它接受一个类型参数。

我试图修改code这样

I tried modifying the code like this

ServiceHost = new ServiceHost(typeof(serviceToHost), baseAddress);

不过,这是行不通的。请帮忙。有没有实现这一目标的任何其他方式。

But it does not work. Please help. Is there any other way to achieve this.

感谢

推荐答案

下面是完整的工作code。我已经加入了终端和服务的行为,因为我需要。不是每个人都需要相同的配置,我猜。我把DLL的名称,并把它添加到基地址的末端。因此,它的每一个根据DLL名称的服务创造新的地址。在App.config中定义的参数应该完全匹配的DLL的名称。对于如我用三个dll文件,如下所示,然后我的app.config将在以下code中所示的。

Here is the complete working code. I have added the endpoint and service behavior as I need. Not everyone will need the same configuration I guess. I take the dll name and append it to the end of the base address. Hence it create new address for each of the service depending on the dll name. The parameters defined in the app.config should match exactly to the name of the dll. For e.g. I am using three dlls as follows then my app.config will be as shown in the below code.

Service1.dll Service2.dll Service3.dll

的App.config

App.config

<appSettings>
 <add key="baseAddress" value="http://localhost:9999/"/>
 <add key="Service1.dll" value="namespace.Service1"/>
 <add key="Service1.dll" value="namespace.IService1"/>
 <add key="Service2.dll" value="namespace.Service2"/>
 <add key="Service2.dll" value="namespace.IService2"/>
 <add key="Service3.dll" value="namespace.Service3"/>
 <add key="Service3.dll" value="namespace.IService3"/>
</appSettings>

Windows安装程序code

Windows Installer Code

public ServiceHost[] serviceHost = null;

    public MyService()
    {
        InitializeComponent();
    }

    protected override void OnStart(string[] args)
    {            
        try
        {
            //Get path for the executing assemblly
            string exePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            //Path to the dlls to be hosted
            string filePath = exePath + "\\DLLsToHost\\";
            //Retrieve only dll files from the folder
            string[] files = Directory.GetFiles(@filePath, "*.dll");

            //get the dll file names
            for (int i = 0; i < files.Length; i++)
                files[i] = Path.GetFileName(files[i]);

            //create an array of ServiceHost type
            serviceHost = new ServiceHost[files.Length];
            //get the base address for the services from config file
            string address = ConfigurationManager.AppSettings["baseAddress"];

            int j = 0;

            foreach (var dllName in files)
            {
                string dllPath = filePath + dllName;
                //Load the dll
                Assembly assembly = Assembly.LoadFrom(@dllPath);
                //Get the class name implementing the service
                string serviceName = ConfigurationManager.AppSettings[dllName];
                //get the interface name implemented by the class
                string interfaceName = ConfigurationManager.AppSettings["I" + dllName];

                if (serviceName == null || interfaceName == null)
                {
                    //Log the error
                }
                else
                {
                    //Get the class implementing the service
                    Type service = assembly.GetType(serviceName);

                    if (service != null)
                    {
                        //Get the interface implemented by the class
                        Type contract = service.GetInterface(interfaceName, true);

                        if (contract != null)
                        {
                            //Create a base address for the service
                            Uri baseAddress = new Uri(address + dllName.Remove(dllName.LastIndexOf(".")));

                            if (serviceHost[j] != null)
                            {
                                serviceHost[j].Close();
                            }

                            serviceHost[j] = new CustomServiceHost(service, baseAddress);
                            //add the service endpoint and contract
                            ServiceEndpoint sEP = serviceHost[j].AddServiceEndpoint(contract, new WebHttpBinding(), "");
                            WebHttpBehavior webHttpBeh = sEP.Behaviors.Find<WebHttpBehavior>();

                            //Set the service and endpoint behaviours
                            if (webHttpBeh != null)
                            {
                                webHttpBeh.AutomaticFormatSelectionEnabled = true;
                                webHttpBeh.DefaultOutgoingResponseFormat = WebMessageFormat.Json;
                                webHttpBeh.HelpEnabled = true;
                                sEP.Behaviors.Add(new BehaviorAttribute());     //Add CORS support
                            }
                            else
                            {
                                WebHttpBehavior newWebHttpBeh = new WebHttpBehavior();
                                newWebHttpBeh.AutomaticFormatSelectionEnabled = true;
                                newWebHttpBeh.DefaultOutgoingResponseFormat = WebMessageFormat.Json;
                                newWebHttpBeh.HelpEnabled = true;
                                sEP.Behaviors.Add(newWebHttpBeh);
                                sEP.Behaviors.Add(new BehaviorAttribute());     //Add CORS support
                            }

                            serviceHost[j].Open();
                        }
                        else
                        {
                            //Log the error
                        }
                    }
                    else
                    {
                        //Log the error
                    }
                }
                j++;
            }
        }
        catch(Exception ex)
        {
            //Throw the exception OR Log it
        }
    }

    protected override void OnStop()
    {
        try
        {
            for (int k = 0; k <= serviceHost.Length - 1; k++)
            {
                if (serviceHost[k] != null)
                {
                    serviceHost[k].Close();
                    serviceHost[k] = null;
                }
            }
        }
        catch (Exception ex)
        {
            //Log
        }
    }
}

任何建议,以更新code是值得欢迎的。谢谢你。

Any suggestions to update the code are welcome. Thanks.