加载dll并将其作为windows服务托管
本文关键字:windows 服务 dll 加载 | 更新日期: 2023-09-27 18:11:09
我有3个Restful服务,我想作为windows服务托管。我已经创建了一个安装程序,它将同时托管这三个服务。我想让我的安装程序有点可配置。我想向同一个安装程序添加新的服务,而不需要它来编辑代码。
这是我的安装程序的当前代码。在这里我给参考的dll和托管他们。这是任何windows主机项目的正常代码。
应用程序。配置文件
<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>
安装程序中的代码。
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中删除了服务端点配置,并在代码中进行了操作。我把所有的dll放在一个文件夹中,然后用代码加载这些dll。为了获得服务和接口,我在app.config中添加参数,如下所示,这将提供服务名称和接口名称,以便从加载的程序集检索。一切都很好。但是我有一个小问题,我在下面解释了。
新建App.config文件
<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主机代码
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服务托管。
ServiceHost = new ServiceHost(instance, baseAddress);
如果你的服务行为有InstanceContextMode设置为single,这工作得很好。否则它会给出一个错误。
错误:"服务无法启动。系统。InvalidOperationException:为了使用接受服务实例的ServiceHost构造函数之一,服务的InstanceContextMode必须设置为InstanceContextMode. single。这可以通过ServiceBehaviorAttribute来配置。否则,请考虑使用带有Type参数的ServiceHost构造函数。"
我试着像这样修改代码
ServiceHost = new ServiceHost(typeof(serviceToHost), baseAddress);
但是它不起作用。请帮助。还有其他方法可以实现这个吗?
谢谢
下面是完整的工作代码。我已经根据需要添加了端点和服务行为。我想不是每个人都需要相同的配置。我将dll名称附加到基址的末尾。因此,它根据dll名称为每个服务创建新地址。在app.config中定义的参数应该与dll的名称完全匹配。例如,我使用三个dll如下,那么我的app.config将如下面的代码所示。
- Service1.dll
- Service2.dll
- Service3.dll
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 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
}
}
}
欢迎任何更新代码的建议。谢谢。