Cách dễ dàng hơn để gỡ lỗi một dịch vụ Windows


325

Có cách nào dễ dàng hơn để chuyển mã hơn là khởi động dịch vụ thông qua Trình quản lý kiểm soát dịch vụ Windows và sau đó đính kèm trình gỡ lỗi vào luồng? Đó là loại cồng kềnh và tôi tự hỏi nếu có một cách tiếp cận đơn giản hơn.


Tôi đã tạo vé Người dùng bằng giọng nói này. Hãy xem xét bỏ phiếu cho nó: visualstudio.uservoice.com/forums/121579-visual-studio-ide/ mẹo
David

Câu trả lời:


271

Nếu tôi muốn nhanh chóng gỡ lỗi dịch vụ, tôi chỉ cần đăng nhập Debugger.Break()vào đó. Khi đạt được dòng đó, nó sẽ đưa tôi trở lại VS. Đừng quên xóa dòng đó khi bạn đã hoàn tất.

CẬP NHẬT: Là một thay thế cho #if DEBUGpragma, bạn cũng có thể sử dụng Conditional("DEBUG_SERVICE")thuộc tính.

[Conditional("DEBUG_SERVICE")]
private static void DebugMode()
{
    Debugger.Break();
}

Trên của bạn OnStart, chỉ cần gọi phương thức này:

public override void OnStart()
{
     DebugMode();
     /* ... do the rest */
}

Ở đó, mã sẽ chỉ được kích hoạt trong quá trình xây dựng Debug. Trong khi bạn làm việc đó, có thể hữu ích khi tạo Cấu hình xây dựng riêng để gỡ lỗi dịch vụ.


45
Hoặc bạn có thể sử dụng Debugger.Launch () bạn sẽ phải bao gồm một câu lệnh sử dụng cho không gian tên The Systems.Diagnostics.
Omar Kooheji

1
Bài đăng trên blog của bạn hoạt động tốt và tiết kiệm trong ngày của tôi :) tuy nhiên Debugger.Break () không hoạt động với tôi. có vẻ như .Net bỏ qua chức năng DebugMode vì một số lý do liên quan đến tối ưu hóa.
Bizhan

3
Debugger.Launch () hoạt động với tôi khi Debugger.Break () không hoạt động. (Quá trình thoát với mã 255.)
Oliver Bock

Làm thế nào các bạn có được điều này để làm việc? Chẳng có gì xảy ra. Tôi đã thử Break () và Launch ().
Không gian thứ

13
@ 4thSpace: 1. tạo trình cài đặt cho dịch vụ của bạn, để bạn có thể cài đặt dịch vụ của mình. 2. Thêm dòng Debugger.Launch (); ở đầu Main () của bạn. 3. Xây dựng mã của bạn ở chế độ Gỡ lỗi. 4. Ghi đè các dll đã cài đặt với debug-dll's. 5. Bắt đầu dịch vụ từ bảng Windows Services. Bây giờ một cửa sổ bật lên xuất hiện để yêu cầu bạn đính kèm với trình gỡ lỗi. Cách này làm việc cho tôi. Hy vọng cho bạn là tốt.
ffonz

210

Tôi cũng nghĩ rằng có một "phiên bản" riêng để thực thi thông thường và vì dịch vụ là cách để đi, nhưng nó có thực sự cần thiết để dành một công tắc dòng lệnh riêng cho mục đích đó không?

Bạn không thể làm:

public static int Main(string[] args)
{
  if (!Environment.UserInteractive)
  {
    // Startup as service.
  }
  else
  {
    // Startup as application
  }
}

Điều đó sẽ có "lợi ích", rằng bạn chỉ có thể khởi động ứng dụng của mình thông qua doubleclick (OK, nếu bạn thực sự cần điều đó) và bạn chỉ có thể nhấn F5trong Visual Studio (mà không cần phải sửa đổi cài đặt dự án để bao gồm /consoleTùy chọn đó ).

Về mặt kỹ thuật, Environment.UserInteractivekiểm tra xem WSF_VISIBLECờ có được đặt cho trạm cửa sổ hiện tại không, nhưng có lý do nào khác để trả lại không false, ngoài việc được chạy như một dịch vụ (không tương tác)?


Tuyệt quá! Trước đây tôi đã sử dụng phương thức "if #debug" để bắt đầu như một ứng dụng nếu gỡ lỗi, nếu không thì là một dịch vụ. Điều này dẫn đến ứng dụng không thể chạy được như một dịch vụ nếu bạn muốn gỡ lỗi, nhưng giải pháp của bạn giải quyết vấn đề này và để nó có thể chạy được trong cả bốn kết hợp dịch vụ / ứng dụng và phát hành / gỡ lỗi.
Jonas

29
Nếu bạn không muốn chương trình chạy khi nhấp đúp chuột (người dùng có thể bị nhầm lẫn và chạy một số trường hợp, v.v.), thì bạn có thể sử dụng System.Diagnostics.Debugger.IsAttachedthay vì Environment.UserInteractive.
Blorgbeard ra khỏi

5
nhưng có bất kỳ lý do nào khác mà nó sẽ trả về false, ngoài việc được chạy như một dịch vụ (không tương tác) không? Tôi có thể nghĩ về một: Một tác vụ theo lịch trình không gắn với bàn điều khiển.
Hogan

7
tôi sử dụng các tham số dòng lệnh cho trường hợp này. --install để cài đặt dịch vụ, --uninstall để gỡ cài đặt dịch vụ và - tương tác để chạy dịch vụ dưới dạng ứng dụng. i thêm - tương tác với các tùy chọn dự án (Gỡ lỗi> Đối số lệnh). Vì vậy, tôi có thể dễ dàng gỡ lỗi từ VS. nhấp đúp sẽ không tạo ra một trường hợp chạy không mong muốn vì - cần phải hoạt động. chỉ 2 xu của tôi.
Tiểu vương quốc Akaydın

@ EmirAkaydın Vâng, thực sự tôi cũng có các tham số dòng lệnh là "sao lưu". Tuy nhiên, tôi thực sự muốn có một ví dụ "tương tác" khi nhấp đúp và không và thông báo lỗi về thực tế là một dịch vụ không thể được bắt đầu theo cách đó. Tôi đoán các mục tiêu khác nhau ;-)
Christian.K

122

Khi tôi thiết lập một dự án dịch vụ mới vài tuần trước tôi đã tìm thấy bài đăng này. Mặc dù có nhiều gợi ý tuyệt vời, tôi vẫn không tìm thấy giải pháp mình muốn: Khả năng gọi các lớp dịch vụ OnStartOnStopphương thức mà không có bất kỳ sửa đổi nào đối với các lớp dịch vụ.

Giải pháp tôi đưa ra là sử dụng Environment.Interactivechế độ chạy chọn, như được đề xuất bởi các câu trả lời khác cho bài đăng này.

static void Main()
{
    ServiceBase[] servicesToRun;
    servicesToRun = new ServiceBase[] 
    {
        new MyService()
    };
    if (Environment.UserInteractive)
    {
        RunInteractive(servicesToRun);
    }
    else
    {
        ServiceBase.Run(servicesToRun);
    }
}

Người RunInteractivetrợ giúp sử dụng sự phản chiếu để gọi các phương thức được bảo vệ OnStartOnStop:

static void RunInteractive(ServiceBase[] servicesToRun)
{
    Console.WriteLine("Services running in interactive mode.");
    Console.WriteLine();

    MethodInfo onStartMethod = typeof(ServiceBase).GetMethod("OnStart", 
        BindingFlags.Instance | BindingFlags.NonPublic);
    foreach (ServiceBase service in servicesToRun)
    {
        Console.Write("Starting {0}...", service.ServiceName);
        onStartMethod.Invoke(service, new object[] { new string[] { } });
        Console.Write("Started");
    }

    Console.WriteLine();
    Console.WriteLine();
    Console.WriteLine(
        "Press any key to stop the services and end the process...");
    Console.ReadKey();
    Console.WriteLine();

    MethodInfo onStopMethod = typeof(ServiceBase).GetMethod("OnStop", 
        BindingFlags.Instance | BindingFlags.NonPublic);
    foreach (ServiceBase service in servicesToRun)
    {
        Console.Write("Stopping {0}...", service.ServiceName);
        onStopMethod.Invoke(service, null);
        Console.WriteLine("Stopped");
    }

    Console.WriteLine("All services stopped.");
    // Keep the console alive for a second to allow the user to see the message.
    Thread.Sleep(1000);
}

Đây là tất cả các mã cần thiết, nhưng tôi cũng đã viết hướng dẫn với các giải thích.


Điều này tạo ra một phương thức mở rộng tốt cho ServiceBase []. Tôi có nhiều dịch vụ trong giải pháp của mình thay vì có một lớp cơ sở chung cho Program.cs, tôi chỉ gọi dịch vụToRun.RunInteractive (args). Giải pháp tuyệt vời @Anders!
David Keaveny

3
Giải pháp tuyệt vời thực sự. Tôi đã tạo một tiện ích mở rộng đơn giản cho ServiceBase [] như David đề xuất cho phép chạy các dịch vụ chỉ trong một dòng mã: pastebin.com/F0fhhG2R
Funbit

4
+1 Một đồng nghiệp cũ của tôi đã tạo ra một lớp cơ sở "EasyRunService" (kế thừa ServiceProcess), điều này khá giống với điều đó, nhưng không cần phải phản ánh (vì OnStart hiện đang ở lớp cơ sở). Nó thực sự làm cho việc gỡ lỗi một dịch vụ windows trở nên dễ dàng.
sondergard

3
@ Chazt3n Đảm bảo rằng loại đầu ra dự án của bạn được đặt thành "Ứng dụng bảng điều khiển". Đối với cài đặt dịch vụ, không quan trọng loại đầu ra nào được chọn, hành vi là như nhau.
Funbit

2
Vẫn là một giải pháp tuyệt vời! Điều duy nhất tôi sẽ thêm (như thể hiện trong walk through) là đảm bảo bạn đi vào các thuộc tính của dự án và thay đổi loại đầu ra thành Console Applicationtrước khi bạn cố gắng biên dịch và chạy. Tìm nó tại Project Properties -> Application -> Output type -> Console Application. Ngoài ra, để điều này hoạt động đúng với tôi, cuối cùng tôi phải chạy ứng dụng bằng startlệnh. Vd: C:\"my app name.exe" -servicesẽ không làm việc cho tôi. Thay vào đó tôi đã sử dụngC:\start /wait "" "my app name.exe" -service
Arvo Bowen

47

Đôi khi, điều quan trọng là phân tích những gì đang diễn ra trong quá trình khởi động dịch vụ. Đính kèm vào quy trình không giúp ích gì ở đây, vì bạn không đủ nhanh để đính kèm trình gỡ lỗi trong khi dịch vụ đang khởi động.

Câu trả lời ngắn gọn là, tôi đang sử dụng 4 dòng mã sau đây để làm điều này:

#if DEBUG
    base.RequestAdditionalTime(600000); // 600*1000ms = 10 minutes timeout
    Debugger.Launch(); // launch and attach debugger
#endif

Chúng được chèn vào OnStartphương thức của dịch vụ như sau:

protected override void OnStart(string[] args)
{
    #if DEBUG
       base.RequestAdditionalTime(600000); // 10 minutes timeout for startup
       Debugger.Launch(); // launch and attach debugger
    #endif
    MyInitOnstart(); // my individual initialization code for the service
    // allow the base class to perform any work it needs to do
    base.OnStart(args);
}

Đối với những người chưa làm điều đó trước đây, tôi đã bao gồm các gợi ý chi tiết bên dưới , bởi vì bạn có thể dễ dàng bị mắc kẹt. Các gợi ý sau đây đề cập đến Windows 7x64Visual Studio 2010 Team Edition , nhưng cũng hợp lệ cho các môi trường khác.


Quan trọng: Triển khai dịch vụ ở chế độ "thủ công" (sử dụng InstallUtiltiện ích từ dấu nhắc lệnh VS hoặc chạy dự án trình cài đặt dịch vụ bạn đã chuẩn bị). Mở Visual Studio trước khi bạn khởi động dịch vụ và tải giải pháp chứa mã nguồn của dịch vụ - thiết lập các điểm dừng bổ sung khi bạn yêu cầu chúng trong Visual Studio - sau đó khởi động dịch vụ qua Bảng điều khiển dịch vụ.

Do Debugger.Launchmã, điều này sẽ gây ra hộp thoại "Một ngoại lệ Microsoft .NET Framework chưa được xử lý xảy ra trong Servicename.exe ." xuất hiện. Nhấp vào như hiển thị trong ảnh chụp màn hình:Elevate Yes, debug Servicename.exe
Khung ngoại lệ

Sau đó, đặc biệt trong Windows 7 UAC có thể nhắc bạn nhập thông tin đăng nhập của quản trị viên. Nhập chúng và tiến hành Yes:

UACPrompt

Sau đó, cửa sổ gỡ lỗi Visual Studio Just-In-Time Debugger nổi tiếng xuất hiện. Nó hỏi bạn nếu bạn muốn gỡ lỗi bằng trình gỡ lỗi được chọn. Trước khi bạn nhấp Yes, chọn rằng bạn không muốn mở một phiên bản mới (tùy chọn thứ 2) - một phiên bản mới sẽ không hữu ích ở đây, vì mã nguồn sẽ không được hiển thị. Vì vậy, bạn chọn đối tượng Visual Studio bạn đã mở trước đó: VSDebuggerPrompt

Sau khi bạn đã nhấp Yes, sau một lúc Visual Studio sẽ hiển thị mũi tên màu vàng ngay trong dòng có Debugger.Launchcâu lệnh và bạn có thể gỡ lỗi mã của mình (phương thức MyInitOnStartchứa phần khởi tạo của bạn). VSDebuggerBreakpoint

Nhấn F5tiếp tục thực hiện ngay lập tức, cho đến khi điểm dừng tiếp theo bạn đã chuẩn bị đạt được.

Gợi ý: Để duy trì dịch vụ chạy, chọn Gỡ lỗi -> Tháo tất cả . Điều này cho phép bạn chạy một ứng dụng khách liên lạc với dịch vụ sau khi nó khởi động chính xác và bạn đã hoàn tất việc gỡ lỗi mã khởi động. Nếu bạn nhấn Shift+F5 (dừng gỡ lỗi), điều này sẽ chấm dứt dịch vụ. Thay vì làm điều này, bạn nên sử dụng Bảng điều khiển dịch vụ để dừng nó.

Lưu ý rằng

  • Nếu bạn xây dựng Bản phát hành, thì mã gỡ lỗi sẽ tự động bị xóa và dịch vụ sẽ chạy bình thường.

  • Tôi đang sử dụng Debugger.Launch(), khởi động và đính kèm một trình gỡ lỗi . Tôi cũng đã thử nghiệm Debugger.Break(), nhưng nó không hoạt động , vì chưa có trình gỡ lỗi nào được cài đặt khi khởi động dịch vụ (gây ra "Lỗi 1067: Quá trình bị chấm dứt bất ngờ." ).

  • RequestAdditionalTimeđặt thời gian chờ lâu hơn cho việc khởi động dịch vụ (nó không trì hoãn mã, nhưng sẽ ngay lập tức tiếp tục với Debugger.Launchcâu lệnh). Mặt khác, thời gian chờ mặc định để bắt đầu dịch vụ quá ngắn và bắt đầu dịch vụ không thành công nếu bạn không gọi base.Onstart(args)đủ nhanh từ trình gỡ lỗi. Thực tế, thời gian chờ là 10 phút sẽ tránh cho bạn thấy thông báo " dịch vụ không phản hồi ..." ngay sau khi trình gỡ lỗi được khởi động.

  • Khi bạn đã quen với nó, phương pháp này rất dễ dàng vì nó chỉ yêu cầu bạn thêm 4 dòng vào mã dịch vụ hiện có, cho phép bạn nhanh chóng giành quyền kiểm soát và gỡ lỗi.


1
Vì tò mò, bạn có biết liệu có thời gian chờ tương tác của người dùng với lời nhắc của người dùng Debugger.Launch () không?
Shiv

1
Theo mô tả, base.RequestAdditionalTime(600000)sẽ ngăn điều khiển dịch vụ chấm dứt dịch vụ trong 10 phút nếu nó không gọi base.OnStart(args)trong khoảng thời gian đó). Ngoài ra, tôi nhớ rằng UAC cũng sẽ hủy bỏ nếu bạn không nhập thông tin đăng nhập của quản trị viên sau một thời gian (tôi không biết chính xác bao nhiêu giây, nhưng tôi nghĩ bạn phải nhập nó trong vòng một phút, nếu không thì hủy bỏ UAC) , sẽ chấm dứt phiên gỡ lỗi.
Matt

2
Tôi thấy đây là phương pháp tốt nhất để gỡ lỗi các tin nhắn CustomCommand. +1.
Justin

40

Những gì tôi thường làm là gói gọn logic của dịch vụ trong một lớp riêng biệt và bắt đầu từ logic của một lớp 'người chạy'. Lớp người chạy này có thể là dịch vụ thực tế hoặc chỉ là một ứng dụng giao diện điều khiển. Vì vậy, giải pháp của bạn có (ít nhất) 3 dự án:

/ConsoleRunner
   /....
/ServiceRunner
   /....
/ApplicationLogic
   /....

1
Tôi cũng đã từng sử dụng phương pháp này, nhưng tôi nghĩ rằng sự kết hợp giữa điều này và câu trả lời ở trên có tác dụng.
RobS

27

Đây video trên YouTube bởi Fabio Scopel giải thích làm thế nào để gỡ lỗi một dịch vụ Windows khá độc đáo ... phương pháp thực tế để làm việc đó bắt đầu lúc 4:45 trong video ...

Đây là mã được giải thích trong video ... trong tệp Program.cs của bạn, thêm nội dung cho phần Gỡ lỗi ...

namespace YourNamespace
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        static void Main()
        {
#if DEBUG
            Service1 myService = new Service1();
            myService.OnDebug();
            System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
#else
            ServiceBase[] ServicesToRun;
            ServicesToRun = new ServiceBase[]
            {
                new Service1()
            };
            ServiceBase.Run(ServicesToRun);
#endif

        }
    }
}

Trong tệp Service1.cs của bạn, hãy thêm phương thức OnDebug () ...

    public Service1()
    {
        InitializeComponent();
    }

    public void OnDebug()
    {
        OnStart(null);
    }

    protected override void OnStart(string[] args)
    {
        // your code to do something
    }

    protected override void OnStop()
    {
    }

Làm thế nào nó hoạt động

Về cơ bản, bạn phải tạo một public void OnDebug()cuộc gọi OnStart(string[] args)như được bảo vệ và không thể truy cập bên ngoài. Các void Main()chương trình được thêm vào với #iftiền xử lý với#DEBUG .

Visual Studio xác định DEBUGnếu dự án được biên dịch trong chế độ Gỡ lỗi. Điều này sẽ cho phép phần gỡ lỗi (bên dưới) thực thi khi điều kiện là đúng

Service1 myService = new Service1();
myService.OnDebug();
System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);

Và nó sẽ chạy giống như một ứng dụng console, một khi mọi thứ ổn, bạn có thể thay đổi chế độ Releasevà phần thông thường elsesẽ kích hoạt logic


Tôi đang tìm kiếm câu trả lời này, dunno tại sao nó được xếp hạng thấp như vậy. Giải thích mã để giúp đỡ người khác hoặc có thể nhận xét nhiều hơn;)
Vinod Srivastav

14

CẬP NHẬT

Cách tiếp cận này là dễ nhất:

http://www.codeproject.com/KB/dotnet/DebugWinService.aspx

Tôi để lại câu trả lời ban đầu của tôi dưới đây cho hậu thế.


Các dịch vụ của tôi có xu hướng có một lớp đóng gói Timer vì tôi muốn dịch vụ kiểm tra định kỳ xem có công việc nào để nó thực hiện không.

Chúng tôi mới lên lớp và gọi StartEventLoop () trong quá trình khởi động dịch vụ. (Lớp này cũng có thể dễ dàng được sử dụng từ ứng dụng bảng điều khiển.)

Hiệu ứng phụ tuyệt vời của thiết kế này là các đối số mà bạn thiết lập Timer có thể được sử dụng để có độ trễ trước khi dịch vụ thực sự bắt đầu hoạt động, do đó bạn có thời gian để đính kèm trình gỡ lỗi theo cách thủ công.

ps Làm cách nào để đính kèm trình gỡ lỗi thủ công vào quy trình đang chạy ...?

using System;
using System.Threading;
using System.Configuration;    

public class ServiceEventHandler
{
    Timer _timer;
    public ServiceEventHandler()
    {
        // get configuration etc.
        _timer = new Timer(
            new TimerCallback(EventTimerCallback)
            , null
            , Timeout.Infinite
            , Timeout.Infinite);
    }

    private void EventTimerCallback(object state)
    {
        // do something
    }

    public void StartEventLoop()
    {
        // wait a minute, then run every 30 minutes
        _timer.Change(TimeSpan.Parse("00:01:00"), TimeSpan.Parse("00:30:00");
    }
}

Ngoài ra, tôi thường làm như sau (đã được đề cập trong các câu trả lời trước nhưng với các trình biên dịch có điều kiện [#if] để giúp tránh nó bắn trong bản dựng Phát hành).

Tôi đã ngừng làm theo cách này vì đôi khi chúng tôi quên xây dựng trong Bản phát hành và bị lỗi trình gỡ lỗi trong một ứng dụng chạy trên bản demo của khách hàng (lúng túng!).

#if DEBUG
if (!System.Diagnostics.Debugger.IsAttached)
{
    System.Diagnostics.Debugger.Break();
}
#endif

Điều gì xảy ra khi // do somethingmất hơn 30 phút để hoàn thành?
Vinod Srivastav

13

static void Main()
{
#if DEBUG
                // Run as interactive exe in debug mode to allow easy
                // debugging.

                var service = new MyService();
                service.OnStart(null);

                // Sleep the main thread indefinitely while the service code
                // runs in .OnStart

                Thread.Sleep(Timeout.Infinite);
#else
                // Run normally as service in release mode.

                ServiceBase[] ServicesToRun;
                ServicesToRun = new ServiceBase[]{ new MyService() };
                ServiceBase.Run(ServicesToRun);
#endif
}

[Xin lỗi về việc không có lời giải thích với mã - sự cố đánh dấu] Nên chạy bình thường từ MS Visual Studio (F5) trong các bản dựng gỡ lỗi. Vẫn chạy như một dịch vụ bình thường trong các bản dựng phát hành.
Thomas Bratt

Kết hợp điều này với giải pháp trên của Christian K. để sử dụng thuộc tính "Môi trường.UserInteractive" và giải pháp thực sự sạch sẽ và đơn giản.
Ben Robbins

OnStartprotectedvà bạn không thể sửa đổi cấp độ truy cập :(
Eduard Luca

10

Bạn cũng có thể bắt đầu dịch vụ thông qua dấu nhắc lệnh (sc.exe).

Cá nhân, tôi sẽ chạy mã dưới dạng một chương trình độc lập trong giai đoạn gỡ lỗi và khi hầu hết các lỗi được giải quyết, hãy chuyển sang chạy như một dịch vụ.


10

Những gì tôi đã từng làm là có một công tắc dòng lệnh sẽ khởi động chương trình như một dịch vụ hoặc như một ứng dụng thông thường. Sau đó, trong IDE của tôi, tôi sẽ đặt công tắc để tôi có thể chuyển qua mã của mình.

Với một số ngôn ngữ bạn thực sự có thể phát hiện nếu nó chạy trong IDE và tự động thực hiện chuyển đổi này.

Ngôn ngữ của bạn đang sử dụng là gì?


9

Sử dụng thư viện TopShelf .

Tạo một ứng dụng bảng điều khiển sau đó định cấu hình thiết lập trong Chính của bạn

class Program
    {
        static void Main(string[] args)
        {
            HostFactory.Run(x =>
            {

                // setup service start and stop.
                x.Service<Controller>(s =>
                {
                    s.ConstructUsing(name => new Controller());
                    s.WhenStarted(controller => controller.Start());
                    s.WhenStopped(controller => controller.Stop());
                });

                // setup recovery here
                x.EnableServiceRecovery(rc =>
                {
                    rc.RestartService(delayInMinutes: 0);
                    rc.SetResetPeriod(days: 0);
                });

                x.RunAsLocalSystem();
            });
        }
}

public class Controller
    {
        public void Start()
        {

        }

        public void Stop()
        {

        }
    }

Để gỡ lỗi dịch vụ của bạn, chỉ cần nhấn F5 trong studio hình ảnh.

Để cài đặt dịch vụ, nhập cmd "console.exe install"

Sau đó, bạn có thể bắt đầu và dừng dịch vụ trong trình quản lý dịch vụ windows.


Cấp phép của họ quá khó hiểu
l --'''''--------- '' '' '' '' '' '' '

Họ sử dụng Giấy phép Apache afaik. Topshelf là cách dễ nhất mà tôi đã sử dụng để phát triển và gỡ lỗi các dịch vụ windows. Siêu dễ sử dụng. Phát triển như một ứng dụng giao diện điều khiển. Cài đặt như một dịch vụ với một chuyển đổi dòng lệnh. Rất khuyến khích.
cướp

TopShelf giúp tôi tiết kiệm hàng tấn thời gian. Thx
L_7337

8

Tôi nghĩ nó phụ thuộc vào hệ điều hành bạn đang sử dụng, Vista khó hơn rất nhiều khi gắn vào Dịch vụ, do sự tách biệt giữa các phiên.

Hai tùy chọn tôi đã sử dụng trong quá khứ là:

  • Sử dụng GFlags (trong Công cụ gỡ lỗi cho Windows) để thiết lập trình gỡ lỗi vĩnh viễn cho một quy trình. Điều này tồn tại trong khóa đăng ký "Tùy chọn thực thi tệp hình ảnh" và cực kỳ hữu ích. Tôi nghĩ bạn sẽ cần phải điều chỉnh cài đặt Dịch vụ để bật "Tương tác với Máy tính để bàn". Tôi sử dụng điều này cho tất cả các loại gỡ lỗi, không chỉ các dịch vụ.
  • Tùy chọn khác, là tách mã một chút, để phần dịch vụ có thể hoán đổi cho nhau khi khởi động ứng dụng thông thường. Bằng cách đó, bạn có thể sử dụng cờ dòng lệnh đơn giản và khởi chạy như một quy trình (chứ không phải là Dịch vụ), giúp việc gỡ lỗi dễ dàng hơn nhiều.

Hi vọng điêu nay co ich.


+1 cho GFlags. Điều này đặc biệt hữu ích nếu bạn không thể sửa đổi mã nguồn (hoặc nếu bạn không có nó).
Chris Gillum

6

Tôi muốn có thể gỡ lỗi mọi khía cạnh của dịch vụ của mình, bao gồm mọi khởi tạo trong OnStart (), trong khi vẫn thực thi nó với hành vi dịch vụ đầy đủ trong khuôn khổ của SCM ... không có chế độ "bảng điều khiển" hoặc "ứng dụng".

Tôi làm điều này bằng cách tạo ra một dịch vụ thứ hai, trong cùng một dự án, để sử dụng để gỡ lỗi. Dịch vụ gỡ lỗi, khi được khởi động như bình thường (tức là trong plugin dịch vụ MMC), tạo quy trình lưu trữ dịch vụ. Điều này cung cấp cho bạn một quy trình để đính kèm trình gỡ lỗi mặc dù bạn chưa bắt đầu dịch vụ thực sự của mình. Sau khi gắn trình gỡ lỗi vào quy trình, hãy bắt đầu dịch vụ thực sự của bạn và bạn có thể đột nhập vào bất cứ nơi nào trong vòng đời dịch vụ, bao gồm cả OnStart ().

Vì nó yêu cầu xâm nhập mã rất tối thiểu, dịch vụ gỡ lỗi có thể dễ dàng được đưa vào dự án thiết lập dịch vụ của bạn và dễ dàng xóa khỏi bản phát hành sản xuất của bạn bằng cách nhận xét một dòng mã và xóa một trình cài đặt dự án.

Chi tiết:

1) Giả sử bạn đang thực hiện MyService, cũng tạo ra MyServiceDebug. Thêm cả hai vào ServiceBasemảng Program.csnhư vậy:

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    static void Main()
    {
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[] 
        { 
            new MyService(),
            new MyServiceDebug()
        };
        ServiceBase.Run(ServicesToRun);
    }

2) Thêm dịch vụ thực VÀ dịch vụ gỡ lỗi vào trình cài đặt dự án cho dự án dịch vụ:

nhập mô tả hình ảnh ở đây

Cả hai dịch vụ (thực và gỡ lỗi) đều được bao gồm khi bạn thêm đầu ra dự án dịch vụ vào dự án thiết lập cho dịch vụ. Sau khi cài đặt, cả hai dịch vụ sẽ xuất hiện trong plugin MMC service.msc.

3) Bắt đầu dịch vụ gỡ lỗi trong MMC.

4) Trong Visual Studio, đính kèm trình gỡ lỗi vào quy trình được bắt đầu bởi dịch vụ gỡ lỗi.

5) Bắt đầu dịch vụ thực sự và tận hưởng gỡ lỗi.


5

Khi tôi viết một dịch vụ, tôi đặt tất cả logic dịch vụ vào một dự án dll và tạo hai "máy chủ" gọi vào dll này, một là dịch vụ Windows và một là một ứng dụng dòng lệnh.

Tôi sử dụng ứng dụng dòng lệnh để gỡ lỗi và đính kèm trình gỡ lỗi vào dịch vụ thực chỉ cho các lỗi tôi không thể sao chép trong ứng dụng dòng lệnh.

Tôi sử dụng phương pháp này chỉ cần nhớ rằng bạn phải kiểm tra tất cả mã trong khi chạy trong một dịch vụ thực, trong khi công cụ dòng lệnh là một công cụ gỡ lỗi tốt, đó là một môi trường khác và nó không hoạt động chính xác như một dịch vụ thực.


4

Khi phát triển và gỡ lỗi một dịch vụ Windows, tôi thường chạy nó như một ứng dụng bảng điều khiển bằng cách thêm một tham số khởi động / bàn điều khiển và kiểm tra điều này. Làm cho cuộc sống dễ dàng hơn nhiều.

static void Main(string[] args) {
    if (Console.In != StreamReader.Null) {
        if (args.Length > 0 && args[0] == "/console") {
            // Start your service work.
        }
    }
}

Cho đến khi bạn phải gỡ lỗi các vấn đề cụ thể của dịch vụ.
leppie

Đúng, sau đó bạn phải đính kèm trình gỡ lỗi vào quy trình dịch vụ thực tế. Nhưng trong hầu hết các trường hợp, lỗi sẽ xuất hiện một trong hai cách và phát triển dễ dàng hơn nhiều.
Maurice

4

Làm thế nào về Debugger.Break () trong dòng đầu tiên?


2

Để gỡ lỗi Windows Services, tôi kết hợp GFlags và tệp .reg được tạo bởi regedit.

  1. Chạy GFlags, chỉ định tên exe và vsjitdebugger
  2. Chạy regedit và đi đến vị trí nơi GFlags đặt tùy chọn của mình
  3. Chọn "Xuất khóa" từ menu tập tin
  4. Lưu tệp đó ở đâu đó với phần mở rộng .reg
  5. Bất cứ lúc nào bạn muốn gỡ lỗi dịch vụ: doubleclick trên tệp .reg
  6. Nếu bạn muốn dừng gỡ lỗi, doubleclick trên tệp .reg thứ hai

Hoặc lưu các đoạn mã sau và thay thế servicename.exe bằng tên thực thi mong muốn.


debugon.reg:

Windows Registry Editor Phiên bản 5,00

[HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows NT \ CurrentVersion \ Tùy chọn thực thi tệp hình ảnh \ servicename.exe]
"Toàn cầu" = "0x00000000"
"Trình gỡ lỗi" = "vsjitdebugger.exe"

debugoff.reg:

Windows Registry Editor Phiên bản 5,00

[HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows NT \ CurrentVersion \ Tùy chọn thực thi tệp hình ảnh \ servicename.exe]
"Toàn cầu" = "0x00000000"

Điều này vẫn hoạt động trên Win 7 / Win 2008? Đó là cách tiếp cận từ support.microsoft.com/kb/824344 nhưng nó phụ thuộc vào các dịch vụ tương tác và tôi nghĩ rằng họ đã bị giết? Nó luôn được sử dụng là tùy chọn ưa thích của tôi (vì các vấn đề khởi động có thể xuất hiện trong quá trình sản xuất, trong đó việc chèn Debugger.Break () vào mã có thể không phải là một tùy chọn).
bến tàu7

1

Đối với lập trình công cụ nhỏ thông thường, tôi đã thực hiện một mẹo rất đơn giản để dễ dàng gỡ lỗi dịch vụ của mình:

Khi bắt đầu dịch vụ, tôi kiểm tra tham số dòng lệnh "/ gỡ lỗi". Nếu dịch vụ được gọi với tham số này, tôi không thực hiện khởi động dịch vụ thông thường mà thay vào đó hãy khởi động tất cả các trình nghe và chỉ hiển thị hộp thông báo "Đang gỡ lỗi, nhấn ok để kết thúc".

Vì vậy, nếu dịch vụ của tôi được khởi động theo cách thông thường, nó sẽ bắt đầu như dịch vụ, nếu nó được bắt đầu với tham số / gỡ lỗi dòng lệnh thì nó sẽ hoạt động như một chương trình bình thường.

Trong VS tôi sẽ chỉ thêm / gỡ lỗi làm tham số gỡ lỗi và khởi động chương trình dịch vụ trực tiếp.

Bằng cách này tôi có thể dễ dàng gỡ lỗi cho hầu hết các vấn đề nhỏ. Tất nhiên, một số thứ vẫn sẽ cần được gỡ lỗi như dịch vụ, nhưng với 99% thì điều này là đủ tốt.



1

Tôi sử dụng một biến thể trên câu trả lời của JOP. Sử dụng các tham số dòng lệnh, bạn có thể đặt chế độ gỡ lỗi trong IDE với các thuộc tính dự án hoặc thông qua trình quản lý dịch vụ Windows.

protected override void OnStart(string[] args)
{
  if (args.Contains<string>("DEBUG_SERVICE"))
  {
    Debugger.Break();
  }
  ...
}


1

Chỉ cần đặt trình gỡ lỗi của bạn ở bất cứ đâu và đính kèm Visualstudio khi khởi động

#if DEBUG
    Debugger.Launch();
#endif

Ngoài ra, bạn cần khởi động VS với tư cách là Quản trị viên và bạn cần cho phép, rằng một quy trình có thể tự động được gỡ lỗi bởi một người dùng khác nhau (như được giải thích ở đây ):

reg add "HKCR\AppID{E62A7A31-6025-408E-87F6-81AEB0DC9347}" /v AppIDFlags /t REG_DWORD /d 8 /f

1

Sử dụng dự án C # của Windows Service Template để tạo một ứng dụng dịch vụ mới https://github.com/HarpyWar/windows-service-template

Có chế độ điều khiển / dịch vụ tự động được phát hiện, trình cài đặt / trình cài đặt tự động của dịch vụ của bạn và một số tính năng được sử dụng nhiều nhất được bao gồm.


1

Đây là phương pháp đơn giản mà tôi đã sử dụng để kiểm tra dịch vụ, không có bất kỳ phương pháp "Gỡ lỗi" bổ sung nào và với các Thử nghiệm đơn vị VS tích hợp.

[TestMethod]
public void TestMyService()
{
    MyService fs = new MyService();

    var OnStart = fs.GetType().BaseType.GetMethod("OnStart", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);

    OnStart.Invoke(fs, new object[] { null });
}

// As an extension method
public static void Start(this ServiceBase service, List<string> parameters)
{
     string[] par = parameters == null ? null : parameters.ToArray();

     var OnStart = service.GetType().GetMethod("OnStart", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);

     OnStart.Invoke(service, new object[] { par });
}

1
static class Program
{
    static void Main()
    {
        #if DEBUG

        // TODO: Add code to start application here

        //    //If the mode is in debugging
        //    //create a new service instance
        Service1 myService = new Service1();

        //    //call the start method - this will start the Timer.
        myService.Start();

        //    //Set the Thread to sleep
        Thread.Sleep(300000);

        //    //Call the Stop method-this will stop the Timer.
        myService.Stop();

         #else
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[] 
        { 
            new Service1() 
        };

        ServiceBase.Run(ServicesToRun);
         #endif
    }
}

cái này dễ hơn chỉ cần thay đổi cài đặt cấu hình giải pháp để gỡ lỗi, chạy dự án / giải pháp, thêm điểm dừng khi bạn đi.
Bahamut

0

Bạn có hai tùy chọn để thực hiện gỡ lỗi.

  1. tạo một tệp nhật ký: Cá nhân tôi thích một tệp nhật ký riêng như tệp văn bản hơn là sử dụng nhật ký ứng dụng hoặc nhật ký sự kiện. Nhưng điều này sẽ khiến bạn tốn rất nhiều thời gian, bởi vì vẫn khó để biết vị trí lỗi chính xác của chúng ta ở đâu
  2. Chuyển đổi ứng dụng sang ứng dụng console: điều này sẽ cho phép bạn, tất cả các công cụ gỡ lỗi mà chúng ta có thể sử dụng trong VS.

Vui lòng tham khảo bài viết trên blog NÀY mà tôi đã tạo cho chủ đề này.


0

Chỉ cần dán

Debugger.Break();

bất kỳ nơi nào trong mã của bạn.

Ví dụ ,

internal static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        private static void Main()
        {
            Debugger.Break();
            ServiceBase[] ServicesToRun;
            ServicesToRun = new ServiceBase[]
            {
                new Service1()
            };
            ServiceBase.Run(ServicesToRun);
        }
    }

Nó sẽ đánh Debugger.Break();khi bạn chạy chương trình của bạn.


0

Tùy chọn tốt nhất là sử dụng không gian tên ' System.Diagnostics '.

Gửi mã của bạn vào nếu khối khác cho chế độ gỡ lỗi và chế độ phát hành như được hiển thị bên dưới để chuyển giữa chế độ gỡ lỗi và chế độ phát hành trong phòng thu trực quan,

#if DEBUG  // for debug mode
       **Debugger.Launch();**  //debugger will hit here
       foreach (var job in JobFactory.GetJobs())
            {
                //do something 
            }

#else    // for release mode
      **Debugger.Launch();**  //debugger will hit here
     // write code here to do something in Release mode.

#endif
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.