如何编写也可以作为 winforms 程序运行的 c# 服务?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/421516/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to write c# service that I can also run as a winforms program?
提问by Rex Logan
I have a windows service written in C# that acts as a proxy for a bunch of network devices to the back end database. For testing and also to add a simulation layer to test the back end I would like to have a GUI for the test operator to be able run the simulation. Also for a striped down version to send out as a demo. The GUI and service do not have to run at the same time. What is the best way to achieve this duel operation?
我有一个用 C# 编写的 Windows 服务,它充当一堆网络设备到后端数据库的代理。为了测试并添加一个模拟层来测试后端,我希望有一个 GUI 供测试操作员能够运行模拟。也用于作为演示发送的精简版。GUI 和服务不必同时运行。实现这种决斗操作的最佳方法是什么?
Edit: Here is my solution combing stuff from this question, Am I Running as a Serviceand Install a .NET windows service without InstallUtil.exeusing this excellent codeby Marc Gravell
编辑:这是我的解决方案,结合了这个问题的内容,我是否作为服务运行并使用Marc Gravell编写的这段出色代码在没有 InstallUtil.exe 的情况下安装 .NET Windows 服务
It uses the following line to test if to run the gui or run as service.
它使用以下行来测试是运行 gui 还是作为服务运行。
if (arg_gui || Environment.UserInteractive || Debugger.IsAttached)
Here is the code.
这是代码。
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;
}
}
}
采纳答案by bozag
You basically have two choices. Either expose an API on the service which you can then call from the UI app OR enable the service to run either as a winforms app or a service.
你基本上有两个选择。在服务上公开一个 API,然后您可以从 UI 应用程序调用该 API,或者使该服务能够作为 winforms 应用程序或服务运行。
The first option is pretty easy - use remoting or WCF to expose the API.
第一个选项非常简单 - 使用远程处理或 WCF 来公开 API。
The second option can be achieved by moving the "guts" of your app into a separate class then create a service wrapper and a win-forms wrapper that both call into your "guts" class.
第二个选项可以通过将您的应用程序的“胆量”移动到一个单独的类中,然后创建一个服务包装器和一个 win-forms 包装器来实现,它们都调用您的“胆量”类。
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);
}
}
回答by Abtin Forouzandeh
Create a new winforms app the references the assembly of your service.
创建一个新的 winforms 应用程序,引用您的服务程序集。
回答by Ray Booysen
There is also FireDaemon. This allows you to run any windows application as a service.
还有FireDaemon。这允许您将任何 Windows 应用程序作为服务运行。
回答by Stefan
you have to implement a separate process that can communicate with your service. While it is possible on XP and earlier systems to have a service showing an UI, that's no longer possible on Vista and later.
您必须实现一个可以与您的服务进行通信的单独进程。虽然在 XP 和更早的系统上可以提供显示 UI 的服务,但在 Vista 和更高版本上不再可能。
回答by Stephen Wrighton
Another possibility is to NOT use a service, but to use an application which resides in the Taskbar (think Roxio Drag-to-Disc, & most likely your Anti-virus software lives down there) which has an icon down by the clock, which launches a menu, when it is right-clicked, and a UI when double-clicked.
另一种可能性是不使用服务,而是使用驻留在任务栏中的应用程序(想想 Roxio Drag-to-Disc,很可能你的防病毒软件就在那里),它有一个按时钟向下的图标,右键单击时启动菜单,双击时启动 UI。
回答by Oliver Friedrich
If your service is modulated properly, you could host the service either in a executable as a service, or with an executable with gui for the test. We use this method with our service too, the standalone service-executable hosts the service in productive environment, but we have a console-app for hosting the service, too.
如果您的服务被正确调制,您可以将服务托管在作为服务的可执行文件中,或者使用带有 gui 的可执行文件进行测试。我们也将这种方法用于我们的服务,独立的服务可执行文件在生产环境中托管服务,但我们也有一个控制台应用程序来托管服务。
回答by tvanfosson
Separate your code into different components: one component to manage the service aspects and one to perform the actual business logic. Create and interact with the business logic from the service component. For testing (of your business logic) you can create a WinForm or console application that uses the business logic component without the service component. Better yet, use a unit testing framework for the bulk of your testing. Many of the methods in the service component will undoubtedly be unit testable as well.
将您的代码分成不同的组件:一个组件用于管理服务方面,另一个用于执行实际的业务逻辑。从服务组件创建业务逻辑并与之交互。为了测试(您的业务逻辑),您可以创建一个 WinForm 或控制台应用程序,该应用程序使用业务逻辑组件而不使用服务组件。更好的是,使用单元测试框架进行大部分测试。服务组件中的许多方法无疑也可以进行单元测试。
回答by P Daddy
If you use the below code:
如果您使用以下代码:
[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
}
Then your EXE will run as either a service (if launched by the SCM) or as a GUI (if launched by any other process).
然后,您的 EXE 将作为服务(如果由 SCM 启动)或作为 GUI(如果由任何其他进程启动)运行。
Essentially, all I've done here is used Reflectorto figure out what the meat of ServiceBase.Run
does, and duplicate it here (reflection is required, because it calls private methods). The reason for not calling ServiceBase.Run
directly is that it pops up a message box to tell the userthat the service cannot be started (if not launched by the SCM) and doesn't return anything to tell the codethat the service cannot be started.
本质上,我在这里所做的一切都是使用Reflector来弄清楚ServiceBase.Run
它的作用,并在这里复制它(需要反射,因为它调用私有方法)。不ServiceBase.Run
直接调用的原因是它弹出一个消息框告诉用户该服务无法启动(如果不是由SCM启动)并且不返回任何内容告诉代码该服务无法启动。
Because this uses reflection to call private framework methods, it may not function correctly in future revisions of the framework. Caveat codor.
因为这使用反射来调用私有框架方法,所以它在框架的未来修订版中可能无法正常运行。 警告气味。
回答by Jason Hymanson
If you encapsulate your business logic in service classes and then use a factory pattern to create those services, you can use the same set of services for a desktop application (desktop factory) and as web services (host in WCF).
如果将业务逻辑封装在服务类中,然后使用工厂模式来创建这些服务,则可以将同一组服务用于桌面应用程序(桌面工厂)和 Web 服务(WCF 中的宿主)。
Service definition:
服务定义:
[ServiceContract]
public interface IYourBusinessService
{
[OperationContract]
void DoWork();
}
public class YourBusinessService : IYourBusinessService
{
public void DoWork()
{
//do some business logic here
}
}
Factory for desktop WinForms to get at services to do business:
桌面 WinForms 工厂以获得服务来开展业务:
public class ServiceFactory
{
public static IYourBusinessService GetService()
{
//you can set any addition info here
//like connection string for db, etc.
return new YourBusinessService();
}
}
You host this either with the WCF ServiceHost class, or in IIS. Both allow you the ability to specify how to instantiate each instance of the service so that you can do initialization like connection strings, etc.
您可以使用 WCF ServiceHost 类或在 IIS 中托管它。两者都允许您指定如何实例化服务的每个实例,以便您可以进行连接字符串等初始化。
回答by Ash
See Am I running as a servicefor some further useful information.
有关更多有用的信息,请参阅我是否作为服务运行。
The most important thing covered is how to reliably determine whether we are running interactively or via a service.
涵盖的最重要的事情是如何可靠地确定我们是以交互方式运行还是通过服务运行。