2009-01-07 22 views
7

我有一個用C#編寫的Windows服務,它充當一堆網絡設備到後端數據庫的代理。爲了測試並添加一個仿真層來測試後端,我希望有一個GUI讓測試操作員能夠運行仿真。還可以作爲演示發送出去。 GUI和服務不必同時運行。實現這一決鬥的最佳方式是什麼?如何編寫我也可以作爲winforms程序運行的c#服務?

編輯: 這裏是Marc Gravell

我的解決方案從this questionAm I Running as a ServiceInstall 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; 
     } 
    } 

} 
+0

我該如何運行它作爲服務?我從cmd運行我的程序,我編寫了c:\ myservice.exe - 安裝沒有任何問題。如果我只寫c:\ myservice.exe打開我的應用程序。我做錯了什麼。或者我該如何使用它? – 2012-06-18 16:13:28

+0

你有沒有理解下面的解決方案?至於創建包裝類等? – 2016-02-26 20:31:27

回答

17

你基本上有兩種選擇。可以在服務上公開一個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); 
    } 
} 
2

創建一個新的winforms應用程序引用您的服務的程序集。

1

還有FireDaemon。這使您可以將任何Windows應用程序作爲服務運行。

0

你必須實現一個單獨的進程,可以使用您的服務進行通信。儘管在XP和更早版本的系統上有可能提供顯示用戶界面的服務,但在Vista和更高版本上不再可能。

0

另一種可能是不使用服務,而是使用駐留在任務欄中的應用程序(認爲Roxio Drag-to-Disc,&最有可能是您的防病毒軟件居住在那裏)時鐘,它在右鍵單擊時啓動菜單,雙擊時則啓動UI。

+0

必須是不是我選擇的服務。 – 2009-01-07 18:51:45

0

如果你的服務是否正常調節,你可以在任何一個可執行的託管服務作爲一項服務,或者與GUI測試用的可執行文件。 我們也將這種方法與我們的服務一起使用,獨立的服務可執行文件託管在生產環境中的服務,但我們也有託管服務的控制檯應用程序。

0

單獨的代碼分成不同的部分組成:一個組件來管理服務方面,一個用於執行實際的業務邏輯。創建服務組件的業務邏輯並與其進行交互。爲了測試(您的業務邏輯),您可以創建一個使用業務邏輯組件的WinForm或控制檯應用程序,而不使用服務組件。更好的是,對於大部分測試使用單元測試框架。服務組件中的許多方法無疑也是單元可測試的。

2

如果使用下面的代碼:

[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啓動)並且不返回任何東西以告知代碼該服務無法啓動。

由於這種使用反射來調用私有框架方法,它可能無法正確地在框架的未來版本的功能。 警告代碼。

0

如果將業務邏輯封裝在服務類中,然後使用工廠模式創建這些服務,則可以爲桌面應用程序(桌面工廠)和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。兩者都允許你指定如何實例化服務的每個實例,這樣就可以做到像初始化連接字符串的能力,等等​​。

1

一些進一步的有用信息,請參見Am I running as a service

覆蓋最重要的是如何可靠地確定我們是否以交互方式或通過服務運行。

0

您可以創建服務,所以它是沒有形式運行調用另一個可執行文件的命令行參數。當這個exe被稱爲沒有命令行參數時,它顯示窗體並作爲正常。

相關問題