2010-06-27 99 views
14

我將從一個例子開始:Apache web服務器(在Windows下)有一個很好的功能:它既可以作爲獨立的應用程序運行(具有當前用戶權限),也可以作爲Windows服務來安裝和運行直接(作爲本地系統帳戶),使用相同的可執行文件。如何製作Windows服務應用程序,以便它可以作爲獨立程序運行?

爲了使應用程序作爲獨立的應用程序運行,它需要做的只是在某些公共類中使用靜態公共Main()。

爲了使應用程序可以作爲服務來安裝和運行,它必須以某種方式實現ServiceBase和Installer類。但是,如果像這樣的應用程序作爲獨立的應用程序運行,它將顯示消息框。

這種類似Apache的操作模式如何實現?我相信解決方案很簡單,但我真的不知道從哪裏開始。

下面的一段代碼用於調用服務。可以修改它以允許獨立使用嗎?

static class Program 
{ 
    /// <summary> 
    /// The main entry point for the application. 
    /// </summary> 
    static void Main() 
    { 
     ServiceBase[] ServicesToRun; 
     ServicesToRun = new ServiceBase[] 
     { 
      new Service() // defined elsewhere as Service : ServiceBase 
     }; 
     ServiceBase.Run(ServicesToRun); 
    } 
} 

我選擇的語言是C#。目前,我已經將公共代碼抽象爲單獨的程序集(我們稱之爲Library.dll),並且我有兩個可執行文件:Console.exe和Service.exe,分別是獨立的和Windows服務應用程序,而這兩者都只是調用Library.dll的手段。

我的目標是將這兩個可執行文件合併爲一個,它仍然會調用Library.dll。

回答

7

經過一番挖掘,我終於看了下。NET罩(System.ServiceProcess.ServiceBase.Run方法),只發現它檢查Environment.UserInteractive bool以確保可執行文件不交互運行。

爲我的作品過於簡單的解決方案:

class Program 
{ 
    static void Main(string[] args) 
    { 
     if (!Environment.UserInteractive) 
     { 
      ServiceBase[] ServicesToRun; 
      ServicesToRun = new ServiceBase[] 
      { 
       // Service.OnStart() creates instance of MainLib() 
       // and then calls its MainLib.Start() method 
       new Service() 
      }; 
      ServiceBase.Run(ServicesToRun); 
      return; 
     } 

     // Run in a console window 
     MainLib lib = new MainLib(); 
     lib.Start(); 
     // ... 
    } 
} 
+3

請注意:Environment.UserInteractive僅用於檢查是否存在圖形桌面會話,並且結果僅用於確定是顯示對話框還是控制檯消息。當作爲允許與桌面交互的服務運行時,UserInteractive也會返回true。真正的檢查是在本地方法中完成的。 – Ishmaeel 2010-10-13 12:17:41

+0

@Ishmaeel:謝謝,好點。不知道這兩種模式有關係。你有一個本地方法的例子可以檢查嗎? – 2010-10-13 22:57:40

+1

顯然,ServiceBase只是調用StartServiceCtrlDispatcher函數(http://msdn.microsoft.com/en-us/library/ms686324(VS.85).aspx)和一個非零返回值(可能)意味着您不作爲服務啓動。當然,作爲一個檢查是沒有用的,因爲它是你想要決定是否執行的操作。我還沒有找到合適的檢查功能。 – Ishmaeel 2010-10-14 10:22:06

0

在您的網站示例中,我相當確信Apache應用程序是用C或C++編寫的。爲此,您需要一個ServiceMain函數。如果你像普通程序那樣執行它,main會被調用。如果您將服務控制管理器指向它,ServiceMain會被調用。

關於C#,不能說我知道這件事。如果我不得不在C#中編寫服務,我想我會從這裏開始 - http://msdn.microsoft.com/en-us/library/bb483064.aspx

3

您應該真正將所有功能抽象到庫中。它恰好從Windows服務運行的事實應該不重要。事實上,如果你有一個叫做ServiceFrontEnd的面向類有一個Start()和Stop() - Windows服務應用程序可以調用它,命令行應用程序,Windows應用程序或其他。

你在這裏描述的只是需要更多的抽象。 「服務」的功能不需要與Windows服務的運行方式緊密結合。希望有幫助

+0

我現在所擁有的是一個包含所有的邏輯庫。我從獨立控制檯應用程序和服務應用程序調用同一個庫。我正在尋找的是將兩個可執行文件合併爲一個的方法。因此,Console.exe和Service.exe都調用Library.dll,並且我想要單個ConsoleAndService.exe,它調用Library.dll :) – 2010-06-27 23:50:24

+0

控制檯應用程序的入口點是一個靜態的Main()方法。服務的入口點是一個從ServiceBase繼承的類 - 並調用OnStart()和OnStop()。不應該有衝突。但如果你發現有經驗的話,我不認爲有單獨的可執行文件是不合理的。畢竟,它們將以完全不同的方式運行。希望有幫助 – 2010-06-27 23:57:56

+0

當然,擁有單獨的可執行文件是完全可以接受的。如果可能的話,我只是在尋找合併它們的方法。如果沒有,那麼它將繼續像以前一樣工作。 – 2010-06-28 00:18:58

10

在C#中,一個簡單的方法是使用命令行參數將其作爲服務運行。如果參數不存在,則運行您的表單/控制檯應用程序。然後,只需有您的安裝包括可執行文件路徑的參數時,安裝服務,所以它看起來像這樣:

C:\MyApp\MyApp.exe -service 

這將是這個樣子:

static void Main(string[] args) 
{ 
    foreach (string arg in args) 
    { 
     //Run as a service if our argument is there 
     if (arg.ToLower() == "-service") 
     { 
      ServiceBase[] servicesToRun = new ServiceBase[] { new Service1() }; 
      ServiceBase.Run(servicesToRun); 
      return; 
     } 
    } 

    //Run the main form if the argument isn't present, like when a user opens the app from Explorer. 
    Application.Run(new Form1()); 
} 

這只是一個給例子你是一個想法,有可能更清潔的方式來編寫這段代碼。

+0

這就是我以前正在研究的內容,但是我找不到一種方法將參數傳遞給installutil,以便服務將以一些參數開始。另一種方法是要求參數以交互方式運行,但這並不方便。看我自己的答案,看看我到底是怎麼做到的。很簡單。 – 2010-06-28 01:00:04

+0

無論如何,你能告訴我如何將參數傳遞給installutil,以便將來我知道嗎? – 2010-06-28 01:00:47

相關問題