Làm thế nào một dịch vụ windows có thể tự khởi động lại theo chương trình?


135

Tôi cần phải viết mã mạnh mẽ trong .NET để cho phép dịch vụ windows (máy chủ 2003) tự khởi động lại. Cách tốt nhất để làm điều này là gì? Có một số API .NET để làm điều đó?


7
"Mạnh mẽ" là một từ chồn ( stackoverflow.fogormsz.com/default.asp?W13086 ) - bạn cần những đặc điểm cụ thể nào?
Aidan Ryan

Câu trả lời:


187

Đặt dịch vụ để khởi động lại sau khi thất bại (nhấp đúp vào dịch vụ trong bảng điều khiển và xem qua các tab đó - tôi quên tên của nó). Sau đó, bất cứ khi nào bạn muốn dịch vụ khởi động lại, chỉ cần gọi Environment.Exit(1)(hoặc bất kỳ trả lại khác không) và HĐH sẽ khởi động lại cho bạn.


7
Fyi vị trí nằm trong bảng điều khiển dịch vụ, nhấp chuột phải vào dịch vụ được đề cập và chọn thuộc tính, sau đó chọn tab khôi phục.
James Michael Hare

20
Tôi thấy cách này đạt được hành vi mong muốn nhưng mã trả về 1 có nghĩa là báo cho hệ thống biết rằng có lỗi. Đây có phải là thực tế tồi nếu thực tế không có lỗi và bạn chỉ muốn khởi động lại dịch vụ của mình?
mgttlinger

4
Điều này có thể được thực hiện theo chương trình bởi trình cài đặt dịch vụ trong sự kiện sau khi cài đặt, không cần phải bấm xung quanh ... Điều gì sẽ xảy ra nếu dịch vụ của bạn ở trên máy chủ từ xa và bạn cần cài đặt nhiều lần trong ngày, ví dụ như trong quá trình thử nghiệm?
Dean Kuga

5
@mgttlinger: Tôi nghĩ rằng có, là một thực hành tồi khi dịch vụ của bạn khỏe mạnh, vì vậy nó sẽ không bao giờ cần phải được khởi động lại. Nhưng một số kiến ​​trúc dịch vụ nằm ngoài tầm với của chúng tôi và nếu chúng cần được khởi động lại là một triệu chứng không ổn, do đó, không có vấn đề gì để đóng và giải phóng một số thay đổi tối thiểu (nếu có thể) và 'cắt chân', kể từ khi rời khỏi dịch vụ chạy không đúng cách có thể tồi tệ hơn (vô dụng).
Luciano

5
@JohnLeidegren nếu bạn muốn tắt máy đúng cách, bạn có thể đặt Service.ExitCode = 1và sau đó thực thi Service.Stop()cùng với bật hộp kiểm 'Bật hành động cho các điểm dừng có lỗi' trên màn hình cài đặt khôi phục dịch vụ. Làm theo cách này cho phép tắt máy đúng cách và vẫn kích hoạt khởi động lại.
Chris Rice

22
Dim proc As New Process()
Dim psi As New ProcessStartInfo()

psi.CreateNoWindow = True
psi.FileName = "cmd.exe"
psi.Arguments = "/C net stop YOURSERVICENAMEHERE && net start YOURSERVICENAMEHERE"
psi.LoadUserProfile = False
psi.UseShellExecute = False
psi.WindowStyle = ProcessWindowStyle.Hidden
proc.StartInfo = psi
proc.Start()

3
Nhớ thêm "" arround tên dịch vụ của bạn nếu nó chứa dấu cách.
ngày

4
Điều này chỉ dành cho các dịch vụ chạy trong tài khoản đặc quyền, dù sao đó cũng là ý tưởng tồi.
Dmitry Gusarov

17

Bạn không thể chắc chắn rằng tài khoản người dùng mà dịch vụ của bạn đang chạy thậm chí có quyền dừng và khởi động lại dịch vụ.


Mặc dù + 1-ed khi tôi gặp phải vấn đề tương tự, nhưng khi nhìn vào sc.exe, hóa ra nó sử dụng SERVICE_QUERY_CONFIG là dwDesiredAccess khi gọi OpenSCManager () và sau đó gọi OpenService () với SERVICE_START | SERVICE_STOP để mở một dịch vụ cụ thể và nó hoạt động tốt (không có vấn đề với quyền truy cập). Không chắc chắn về cách làm điều này có thể được thực hiện trong .NET.
n0p

Hầu hết các Dịch vụ Windows chạy dưới dạng Hệ thống, vì vậy đây không phải là vấn đề.
Chuyển

@Switch có một vòng tròn lớn các dịch vụ được chạy trong DỊCH VỤ MẠNG, theo mặc định, thậm chí không có quyền mở tệp thực thi của chính nó.
Đặc vụ

@AgentFire, đúng, nhưng mặc định là Hệ thống cục bộ, không phải DỊCH VỤ MẠNG. Hệ thống cục bộ vốn đã có mức đặc quyền cao nhất đối với hệ điều hành - vượt quá các đặc quyền được gán cho các thành viên của nhóm Quản trị viên cục bộ.
Chuyển

@Switch nhưng sử dụng các đặc quyền có sẵn nhất là vi phạm Nguyên tắc PoL .
AgentFire

12

Bạn có thể tạo một quy trình là một dấu nhắc lệnh DOS tự khởi động lại:

 Process process = new Process();
 process.StartInfo.FileName = "cmd";
 process.StartInfo.Arguments = "/c net stop \"servicename\" & net start \"servicename\"";
 process.Start();

Điều này, mà không làm gì khác, sẽ kế thừa bối cảnh bảo mật của chuỗi cuộc gọi ... và miễn là đó là bối cảnh có đủ mã lực để khởi động lại, bạn vẫn ổn.
Clay

12
const string strCmdText = "/C net stop \"SERVICENAME\"&net start \"SERVICENAME\"";
Process.Start("CMD.exe", strCmdText);

Ở đâu SERVICENAME là tên của dịch vụ của bạn (bao gồm dấu ngoặc kép vào tài khoản cho các không gian trong tên dịch vụ, có thể bỏ qua bằng cách khác).

Sạch sẽ, không cần cấu hình tự động khởi động lại.


9
đã học được một điều mới về & vs &&: [lệnh1] & [lệnh2] sẽ luôn thực thi cả hai lệnh một cách tuần tự, trong khi [lệnh1] && [lệnh2] chỉ chạy lệnh2 nếu lệnh1 chạy thành công ( autoitscript.com/forum/topic/ tựa )
Jeffrey Hiệp sĩ

5

Nó sẽ phụ thuộc vào lý do tại sao bạn muốn nó tự khởi động lại.

Nếu bạn chỉ tìm cách để dịch vụ tự dọn dẹp định kỳ thì bạn có thể có một bộ đếm thời gian chạy trong dịch vụ định kỳ gây ra thói quen thanh lọc.

Nếu bạn đang tìm cách khởi động lại khi thất bại - chính máy chủ dịch vụ có thể cung cấp khả năng đó khi thiết lập.

Vậy tại sao bạn cần khởi động lại máy chủ? Bạn đang cố gắng để đạt được điều gì?


1
Điều này sẽ không hoạt động nếu bạn quan tâm đến Heap đối tượng lớn và phân mảnh bộ nhớ. Các quy trình được cho là .NET 4 / 4.5 và 64 bit đã giúp ích rất nhiều cho việc này.
David Kassa

3

Tôi không nghĩ bạn có thể trong một dịch vụ độc lập (khi bạn gọi Khởi động lại, nó sẽ dừng dịch vụ, điều này sẽ làm gián đoạn lệnh Khởi động lại và nó sẽ không bao giờ bắt đầu lại). Nếu bạn có thể thêm .exe thứ hai (ứng dụng Console sử dụng lớp ServiceManager), thì bạn có thể khởi động .exe độc ​​lập và khởi động lại dịch vụ sau đó thoát.

Về ý nghĩ thứ hai, có lẽ bạn có thể yêu cầu dịch vụ đăng ký Tác vụ theo lịch trình (ví dụ sử dụng lệnh 'at') để khởi động dịch vụ và sau đó tự dừng dịch vụ; điều đó có thể sẽ làm việc


3

Vấn đề với việc tách ra một tệp bó hoặc EXE là một dịch vụ có thể có hoặc không có quyền cần thiết để chạy ứng dụng bên ngoài.

Cách sạch nhất để làm điều này mà tôi đã tìm thấy là sử dụng phương thức OnStop (), đây là điểm vào của Trình quản lý điều khiển dịch vụ. Sau đó, tất cả mã dọn dẹp của bạn sẽ chạy và bạn sẽ không có bất kỳ ổ cắm treo hoặc các quy trình khác, giả sử mã dừng của bạn đang thực hiện công việc của mình.

Để làm điều này, bạn cần đặt cờ trước khi chấm dứt phương thức OnStop để thoát với mã lỗi; sau đó SCM biết rằng dịch vụ cần phải được khởi động lại. Nếu không có cờ này, bạn sẽ không thể dừng dịch vụ theo cách thủ công từ SCM. Điều này cũng cho rằng bạn đã thiết lập dịch vụ để khởi động lại khi có lỗi.

Đây là mã dừng của tôi:

...

bool ABORT;

protected override void OnStop()
{
    Logger.log("Stopping service");
    WorkThreadRun = false;
    WorkThread.Join();
    Logger.stop();
    // if there was a problem, set an exit error code
    // so the service manager will restart this
    if(ABORT)Environment.Exit(1);
}

Nếu dịch vụ gặp sự cố và cần khởi động lại, tôi khởi chạy một luồng dừng dịch vụ từ SCM. Điều này cho phép dịch vụ tự dọn dẹp:

...

if(NeedToRestart)
{
    ABORT = true;
    new Thread(RestartThread).Start();
}

void RestartThread()
{
    ServiceController sc = new ServiceController(ServiceName);
    try
    {
        sc.Stop();
    }
    catch (Exception) { }
}

2

Tôi sẽ sử dụng Bộ lập lịch Windows để lên lịch khởi động lại dịch vụ của bạn. Vấn đề là bạn không thể tự khởi động lại, nhưng bạn có thể tự dừng lại. (Về cơ bản, bạn đã cưa nhánh cây mà bạn đang ngồi ... nếu bạn có sự tương tự của tôi) Bạn cần một quy trình riêng để làm điều đó cho bạn. Bộ lập lịch Windows là một cái thích hợp. Lên lịch tác vụ một lần để khởi động lại dịch vụ của bạn (thậm chí từ bên trong chính dịch vụ) để thực hiện ngay lập tức.

Nếu không, bạn sẽ phải tạo một quy trình "chăn cừu" phù hợp với bạn.


2

Câu trả lời đầu tiên cho câu hỏi là giải pháp đơn giản nhất: "Môi trường.Exit (1)" Tôi đang sử dụng điều này trên Windows Server 2008 R2 và nó hoạt động hoàn hảo. Dịch vụ tự dừng, O / S đợi 1 phút, sau đó khởi động lại.


Thời gian chờ đợi bao lâu tùy thuộc vào thời gian bạn thiết lập để chờ đợi. Mặc định là 1 phút, nhưng nếu ai đó không muốn rằng câu trả lời của bạn sẽ gây ấn tượng sai.
Espen

1

Tôi không nghĩ rằng nó có thể. Khi một dịch vụ bị "dừng", nó sẽ được tải hoàn toàn.

Chà, OK, luôn có một cách tôi cho là. Chẳng hạn, bạn có thể tạo một quy trình tách rời để dừng dịch vụ, sau đó khởi động lại, sau đó thoát.


0

Cách tiếp cận tốt hơn có thể là sử dụng Dịch vụ NT làm trình bao bọc cho ứng dụng của bạn. Khi Dịch vụ NT được khởi động, ứng dụng của bạn có thể bắt đầu ở chế độ "nhàn rỗi" chờ lệnh bắt đầu (hoặc được định cấu hình để bắt đầu tự động).

Hãy nghĩ về một chiếc xe, khi nó khởi động, nó bắt đầu ở trạng thái không hoạt động, chờ lệnh của bạn tiến lên hoặc lùi lại. Điều này cũng cho phép các lợi ích khác, chẳng hạn như quản trị từ xa tốt hơn khi bạn có thể chọn cách hiển thị ứng dụng của mình.


0

Chỉ cần vượt qua: và nghĩ rằng tôi sẽ thêm một số thông tin thêm ...

bạn cũng có thể đưa ra một ngoại lệ, điều này sẽ tự động đóng dịch vụ windows và các tùy chọn khởi động lại tự động chỉ cần khởi động. Vấn đề duy nhất là nếu bạn có môi trường dev trên máy tính thì JIT sẽ cố gắng khởi động, và bạn sẽ nhận được lời nhắc gỡ lỗi Y / N. nói không và sau đó nó sẽ đóng lại, và sau đó bắt đầu lại đúng cách. (trên PC không có JIT, nó chỉ hoạt động). Lý do tôi bị troll, là JIT này mới đối với Win 7 (nó được sử dụng để hoạt động tốt với XP, v.v.) và tôi đang cố gắng tìm cách vô hiệu hóa JIT .... tôi có thể thử phương pháp Môi trường. nó cũng hoạt động

Kristian: Bristol, Vương quốc Anh


3
Tất cả các giải pháp ném ngoại lệ, thoát (1) đều tránh việc dọn dẹp ứng dụng đúng cách.
Thoát

0

Tạo một tập tin restart.bat như thế này

@echo on
set once="C:\Program Files\MyService\once.bat"
set taskname=Restart_MyService
set service=MyService
echo rem %time% >%once%
echo net stop %service% >>%once%
echo net start %service% >>%once%
echo del %once% >>%once%

schtasks /create /ru "System" /tn %taskname% /tr '%once%' /sc onstart /F /V1 /Z
schtasks /run /tn %taskname%

Sau đó xóa tác vụ% taskname% khi% dịch vụ% của bạn bắt đầu


0

Tạo một tên miền riêng để lưu trữ mã ứng dụng. Khi yêu cầu khởi động lại, chúng tôi có thể tải và tải lại tên miền appd thay vì quá trình (dịch vụ windows). Đây là cách nhóm ứng dụng IIS hoạt động, họ không chạy ứng dụng asp.net trực tiếp, họ sử dụng appdmain riêng biệt.


-2

Cách dễ nhất là có một tệp bó với:

net dừng net bắt đầu

và thêm tệp vào bộ lập lịch với khoảng thời gian bạn muốn


Nó không hoạt động vì lô bị dừng giữa cả hai lệnh, vì đó là một quá trình con của chính dịch vụ.
Orabîg

-3
private static void  RestartService(string serviceName)
    {
        using (var controller = new ServiceController(serviceName))
        {
            controller.Stop();
            int counter = 0;
            while (controller.Status != ServiceControllerStatus.Stopped)
            {
                Thread.Sleep(100);
                controller.Refresh();
                counter++;
                if (counter > 1000)
                {
                    throw new System.TimeoutException(string.Format("Could not stop service: {0}", Constants.Series6Service.WindowsServiceName));
                }
            }

            controller.Start();
        }
    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.