如何编写我也可以作为winforms程序运行的c#服务?
我有一个用C#编写的Windows服务,它充当一堆网络设备到后端数据库的代理。为了测试并添加一个仿真层来测试后端,我希望有一个GUI让测试操作员能够运行仿真。还可以作为演示发送出去。 GUI和服务不必同时运行。实现这一决斗的最佳方式是什么?如何编写我也可以作为winforms程序运行的c#服务?
编辑: 这里是Marc Gravell
我的解决方案从this question,Am I Running as a Service和Install a .NET windows service without InstallUtil.exe使用this excellent code梳理的东西它使用以下行测试,如果运行GUI或运行的服务。
if (arg_gui || Environment.UserInteractive || Debugger.IsAttached)
这是代码。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.ComponentModel;
using System.ServiceProcess;
using System.Configuration.Install;
using System.Diagnostics;
namespace Form_Service
{
static class Program
{
///
/// The main entry point for the application.
///
[STAThread]
static int Main(string[] args)
{
bool arg_install = false;
bool arg_uninstall = false;
bool arg_gui = false;
bool rethrow = false;
try
{
foreach (string arg in args)
{
switch (arg)
{
case "-i":
case "-install":
arg_install = true; break;
case "-u":
case "-uninstall":
arg_uninstall = true; break;
case "-g":
case "-gui":
arg_gui = true; break;
default:
Console.Error.WriteLine("Argument not expected: " + arg);
break;
}
}
if (arg_uninstall)
{
Install(true, args);
}
if (arg_install)
{
Install(false, args);
}
if (!(arg_install || arg_uninstall))
{
if (arg_gui || Environment.UserInteractive || Debugger.IsAttached)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
else
{
rethrow = true; // so that windows sees error...
ServiceBase[] services = { new Service1() };
ServiceBase.Run(services);
rethrow = false;
}
}
return 0;
}
catch (Exception ex)
{
if (rethrow) throw;
Console.Error.WriteLine(ex.Message);
return -1;
}
}
static void Install(bool undo, string[] args)
{
try
{
Console.WriteLine(undo ? "uninstalling" : "installing");
using (AssemblyInstaller inst = new AssemblyInstaller(typeof(Program).Assembly, args))
{
IDictionary state = new Hashtable();
inst.UseNewContext = true;
try
{
if (undo)
{
inst.Uninstall(state);
}
else
{
inst.Install(state);
inst.Commit(state);
}
}
catch
{
try
{
inst.Rollback(state);
}
catch { }
throw;
}
}
}
catch (Exception ex)
{
Console.Error.WriteLine(ex.Message);
}
}
}
[RunInstaller(true)]
public sealed class MyServiceInstallerProcess : ServiceProcessInstaller
{
public MyServiceInstallerProcess()
{
this.Account = ServiceAccount.NetworkService;
}
}
[RunInstaller(true)]
public sealed class MyServiceInstaller : ServiceInstaller
{
public MyServiceInstaller()
{
this.Description = "My Service";
this.DisplayName = "My Service";
this.ServiceName = "My Service";
this.StartType = System.ServiceProcess.ServiceStartMode.Manual;
}
}
}
你基本上有两种选择。可以在服务上公开一个API,然后您可以从UI应用程序调用此服务,也可以将服务作为Winforms应用程序或服务运行。
第一个选项是很容易的 - 使用远程或WCF暴露的API。
第二个选项可以通过移动你的应用程序的“胆量”到一个单独的类,然后创建一个服务包装和双赢的形式包装来实现这两个呼叫到您的“胆量”级。
static void Main(string[] args)
{
Guts guts = new Guts();
if (runWinForms)
{
System.Windows.Forms.Application.EnableVisualStyles();
System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false);
FormWrapper fw = new FormWrapper(guts);
System.Windows.Forms.Application.Run(fw);
}
else
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[] { new ServiceWrapper(guts) };
ServiceBase.Run(ServicesToRun);
}
}
创建一个新的winforms应用程序引用您的服务的程序集。
你必须实现一个单独的进程,可以使用您的服务进行通信。尽管在XP和更早版本的系统上有可能提供显示用户界面的服务,但在Vista和更高版本上不再可能。
另一种可能是不使用服务,而是使用驻留在任务栏中的应用程序(认为Roxio Drag-to-Disc,&最有可能是您的防病毒软件居住在那里)时钟,它在右键单击时启动菜单,双击时则启动UI。
必须是不是我选择的服务。 – 2009-01-07 18:51:45
如果你的服务是否正常调节,你可以在任何一个可执行的托管服务作为一项服务,或者与GUI测试用的可执行文件。 我们也将这种方法与我们的服务一起使用,独立的服务可执行文件托管在生产环境中的服务,但我们也有托管服务的控制台应用程序。
单独的代码分成不同的部分组成:一个组件来管理服务方面,一个用于执行实际的业务逻辑。创建服务组件的业务逻辑并与其进行交互。为了测试(您的业务逻辑),您可以创建一个使用业务逻辑组件的WinForm或控制台应用程序,而不使用服务组件。更好的是,对于大部分测试使用单元测试框架。服务组件中的许多方法无疑也是单元可测试的。
如果使用下面的代码:
[DllImport("advapi32.dll", CharSet=CharSet.Unicode)]
static extern bool StartServiceCtrlDispatcher(IntPtr services);
[DllImport("ntdll.dll", EntryPoint="RtlZeroMemory")]
static extern void ZeroMemory(IntPtr destination, int length);
static bool StartService(){
MySvc svc = new MySvc(); // replace "MySvc" with your service name, of course
typeof(ServiceBase).InvokeMember("Initialize", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod,
null, svc, new object[]{false});
object entry = typeof(ServiceBase).InvokeMember("GetEntry",
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod, null, svc, null);
int len = Marshal.SizeOf(entry) * 2;
IntPtr memory = Marshal.AllocHGlobal(len);
ZeroMemory(memory, len);
Marshal.StructureToPtr(entry, memory, false);
return StartServiceCtrlDispatcher(memory);
}
[STAThread]
static void Main(){
if(StartService())
return;
Application.Run(new MainWnd()); // replace "MainWnd" with whatever your main window is called
}
然后你的EXE会无论是作为服务运行(如果由SCM启动)或作为GUI(如果任何其他进程启动)。
本质上,我在这里完成的所有操作都使用Reflector来确定ServiceBase.Run
的功能,并在此复制(需要反射,因为它调用私有方法)。不直接调用ServiceBase.Run
的原因是它弹出一个消息框告诉用户该服务无法启动(如果没有被SCM启动)并且不返回任何东西以告知代码该服务无法启动。
由于这种使用反射来调用私有框架方法,它可能无法正确地在框架的未来版本的功能。 警告代码。
如果将业务逻辑封装在服务类中,然后使用工厂模式创建这些服务,则可以为桌面应用程序(桌面工厂)和Web服务(WCF中的主机)使用同一组服务, 。
服务定义:
[ServiceContract]
public interface IYourBusinessService
{
[OperationContract]
void DoWork();
}
public class YourBusinessService : IYourBusinessService
{
public void DoWork()
{
//do some business logic here
}
}
厂桌面的WinForms得到的服务进行业务
public class ServiceFactory
{
public static IYourBusinessService GetService()
{
//you can set any addition info here
//like connection string for db, etc.
return new YourBusinessService();
}
}
您在此要么与WCF的ServiceHost类,或IIS。两者都允许你指定如何实例化服务的每个实例,这样就可以做到像初始化连接字符串的能力,等等。
您可以创建服务,所以它是没有形式运行调用另一个可执行文件的命令行参数。当这个exe被称为没有命令行参数时,它显示窗体并作为正常。
我该如何运行它作为服务?我从cmd运行我的程序,我编写了c:\ myservice.exe - 安装没有任何问题。如果我只写c:\ myservice.exe打开我的应用程序。我做错了什么。或者我该如何使用它? – 2012-06-18 16:13:28
你有没有理解下面的解决方案?至于创建包装类等? – 2016-02-26 20:31:27