วิธีที่ง่ายกว่าในการดีบักบริการ Windows


325

มีวิธีที่ง่ายกว่าในการก้าวไปข้างหน้ารหัสกว่าจะเริ่มบริการผ่านทาง Windows Service Control Manager แล้วแนบตัวดีบักกับเธรดหรือไม่ มันค่อนข้างยุ่งยากและฉันก็สงสัยว่าถ้ามีวิธีที่ตรงไปตรงมามากกว่านี้


ฉันสร้างตั๋ว User Voice นี้ พิจารณาการลงคะแนนให้มัน: visualstudio.uservoice.com/forums/121579-visual-studio-ide/ …
David

คำตอบ:


271

หากฉันต้องการแก้จุดบกพร่องของบริการอย่างรวดเร็วฉันก็แค่เข้าไปข้างDebugger.Break()ใน เมื่อถึงบรรทัดนั้นมันจะทำให้ฉันกลับไปที่ VS อย่าลืมลบบรรทัดนั้นเมื่อคุณทำเสร็จแล้ว

อัปเดต:เป็นอีกทางเลือกสำหรับ#if DEBUGpragmas คุณยังสามารถใช้Conditional("DEBUG_SERVICE")แอตทริบิวต์

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

ในของคุณOnStartเพียงแค่เรียกวิธีนี้:

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

ที่นั่นรหัสจะเปิดใช้งานในระหว่างการสร้างการดีบักเท่านั้น ในขณะที่คุณอยู่ที่นั่นอาจเป็นประโยชน์ในการสร้าง Build Configuration แยกต่างหากสำหรับการดีบักบริการ


45
หรือคุณสามารถใช้ Debugger.Launch () คุณจะต้องรวมคำสั่งการใช้สำหรับ Systems.Diagnostics namespace
Omar Kooheji

1
โพสต์บล็อกของคุณทำงานได้ดีและบันทึกวันของฉัน :) แต่ Debugger.Break () ไม่ได้ผลสำหรับฉัน ดูเหมือนว่า. Net จะข้ามฟังก์ชัน DebugMode ด้วยเหตุผลบางประการที่เกี่ยวข้องกับการเพิ่มประสิทธิภาพ
Bizhan

3
Debugger.Launch () ใช้ได้กับฉันเมื่อ Debugger.Break () ไม่ทำงาน (การออกจากกระบวนการด้วยรหัส 255)
Oliver Bock

พวกคุณทำงานนี้อย่างไร ไม่มีอะไรเกิดขึ้น. ฉันได้ลองใช้ Break () และ Launch () แล้ว
4thSpace

13
@ 4thSpace: 1. สร้างตัวติดตั้งสำหรับบริการของคุณเพื่อให้คุณสามารถติดตั้งบริการของคุณได้ 2. เพิ่มบรรทัด Debugger.Launch (); ที่จุดเริ่มต้นของหน้าหลักของคุณ () 3. สร้างรหัสของคุณในโหมดดีบั๊ก 4. เขียนทับ dll ที่ติดตั้งด้วย debug-dll 5. เริ่มบริการจากแผงบริการ Windows ตอนนี้ป๊อปอัปปรากฏขึ้นเพื่อขอให้คุณแนบกับดีบักเกอร์ วิธีนี้ใช้ได้ผลสำหรับฉัน หวังว่าสำหรับคุณเช่นกัน
ffonz

210

ฉันยังคิดว่ามี "รุ่น" แยกต่างหากสำหรับการดำเนินการตามปกติและเป็นบริการเป็นวิธีที่จะไป แต่มันจำเป็นจริงๆที่จะอุทิศสวิตช์บรรทัดคำสั่งแยกต่างหากเพื่อวัตถุประสงค์นั้นหรือไม่?

คุณไม่สามารถทำได้:

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

นั่นจะมี "ประโยชน์" ที่คุณสามารถเริ่มต้นแอปของคุณผ่าน doubleclick (ตกลงถ้าคุณต้องการจริงๆ) และคุณสามารถกดF5ใน Visual Studio (โดยไม่จำเป็นต้องแก้ไขการตั้งค่าโครงการเพื่อรวม/consoleตัวเลือกนั้น)

ในทางเทคนิคการEnvironment.UserInteractiveตรวจสอบว่าWSF_VISIBLEตั้งค่าสถานะสำหรับสถานีหน้าต่างปัจจุบัน แต่มีเหตุผลอื่นที่มันจะกลับมาfalseนอกเหนือจากการถูกเรียกใช้เป็นบริการ (ไม่ใช่แบบโต้ตอบ)?


ที่ดี! ก่อนหน้านี้ฉันใช้เมธอด "ifde #" เพื่อเริ่มต้นเป็นแอปพลิเคชันหากการดีบักหรือบริการ สิ่งนี้นำไปสู่แอปที่ไม่สามารถเรียกใช้เป็นบริการได้หากคุณต้องการแก้ปัญหา แต่โซลูชันของคุณแก้ปัญหาได้และปล่อยให้แอปสามารถทำงานได้ในบริการ / แอปทั้งสี่และการเปิดตัว / ดีบั๊ก
Jonas

29
หากคุณไม่ต้องการให้โปรแกรมทำงานเมื่อมันเป็นดับเบิลคลิก (ผู้ใช้อาจได้รับสับสนและเรียกใช้อินสแตนซ์หลาย ฯลฯ ) แล้วคุณสามารถใช้แทนSystem.Diagnostics.Debugger.IsAttached Environment.UserInteractive
Blorgbeard ออก

5
แต่มีเหตุผลอื่นใดบ้างที่จะส่งคืนเท็จนอกเหนือจากการถูกเรียกใช้เป็นบริการ (ไม่ใช่แบบโต้ตอบ) ฉันสามารถนึกถึงหนึ่ง: งานที่กำหนดเวลาไว้ซึ่งไม่ได้เชื่อมต่อกับคอนโซล
โฮแกน

7
ฉันใช้พารามิเตอร์บรรทัดคำสั่งสำหรับกรณีนี้ - ติดตั้งเพื่อติดตั้งบริการ - ติดตั้งเพื่อถอนการติดตั้งบริการและ - โต้ตอบเพื่อเรียกใช้บริการเป็นแอปพลิเคชัน ฉันเพิ่ม - โต้ตอบกับตัวเลือกโครงการ (การดีบัก> อาร์กิวเมนต์คำสั่ง) ดังนั้นฉันสามารถดีบักจาก VS ได้อย่างง่ายดาย การคลิกสองครั้งจะไม่สร้างอินสแตนซ์ที่ใช้งานที่ไม่ต้องการเนื่องจากต้องมีการโต้ตอบ แค่ 2 เซ็นต์ของฉัน
Emir Akaydın

@ EmirAkaydınใช่จริง ๆ แล้วฉันมีพารามิเตอร์บรรทัดคำสั่งเป็น "สำรอง" เช่นกัน อย่างไรก็ตามฉันต้องการมีอินสแตนซ์ "แบบโต้ตอบ" เมื่อดับเบิลคลิกและไม่และข้อความแสดงข้อผิดพลาดเกี่ยวกับความจริงที่ว่าบริการไม่สามารถเริ่มต้นได้ เป้าหมายแตกต่างกันฉันเดา ;-)
Christian.K

123

เมื่อฉันตั้งค่าโครงการบริการใหม่ไม่กี่สัปดาห์ที่ผ่านมาฉันพบโพสต์นี้ ในขณะที่มีคำแนะนำที่ดีมากมายฉันยังไม่พบวิธีแก้ปัญหาที่ฉันต้องการ: ความเป็นไปได้ที่จะเรียกคลาสบริการOnStartและOnStopวิธีการต่างๆโดยไม่ต้องปรับเปลี่ยนคลาสบริการ

วิธีแก้ปัญหาที่ฉันเกิดขึ้นกับการEnvironment.Interactiveใช้โหมดการเลือกทำงานตามที่แนะนำโดยคำตอบอื่น ๆ ของโพสต์นี้

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

RunInteractiveผู้ช่วยที่ใช้สะท้อนที่จะเรียกการป้องกันOnStartและOnStopวิธีการ:

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);
}

นี่คือรหัสทั้งหมดที่จำเป็น แต่ฉันยังเขียนคำแนะนำด้วยคำอธิบาย


ทำให้เป็นวิธีการขยายที่ดีสำหรับ ServiceBase [] ฉันมีบริการหลายอย่างในโซลูชันของฉันดังนั้นแทนที่จะมีคลาสพื้นฐานสำหรับ Program.cs ฉันเพิ่งเรียก servicesToRun.RunInteractive (args) ทางออกที่ดี @Anders!
David Keaveny

3
ทางออกที่ดีแน่นอน ฉันสร้างส่วนขยายอย่างง่ายสำหรับ ServiceBase [] ตามที่ David แนะนำซึ่งอนุญาตให้เรียกใช้บริการด้วยรหัสเดียว: pastebin.com/F0fhhG2R
Funbit

4
+1 อดีตเพื่อนร่วมงานของฉันสร้างคลาสพื้นฐาน "EasyRunService" (ซึ่งสืบทอด ServiceProcess) ซึ่งทำสิ่งเดียวกันค่อนข้างมาก แต่ไม่ต้องการการสะท้อนกลับ (เนื่องจาก OnStart อยู่ในคลาสฐาน) มันทำให้การดีบักบริการ windows เป็นเรื่องง่าย
sondergard

3
@ Chazt3n ตรวจสอบให้แน่ใจว่าชนิดเอาต์พุตโปรเจ็กต์ของคุณถูกตั้งค่าเป็น "Console Application" สำหรับการติดตั้งบริการมันไม่สำคัญว่าจะเลือกชนิดเอาท์พุทลักษณะการทำงานเหมือนกัน
Funbit

2
ยังเป็นทางออกที่ยอดเยี่ยม! สิ่งเดียวที่ฉันจะเพิ่ม (ดังแสดงในwalk through) คือเพื่อให้แน่ใจว่าคุณเข้าสู่คุณสมบัติของโครงการและเปลี่ยนประเภทเอาต์พุตเป็นConsole Applicationก่อนที่คุณจะพยายามรวบรวมและเรียกใช้ Project Properties -> Application -> Output type -> Console Applicationค้นหาได้ที่ นอกจากนี้เพื่อให้ทำงานได้อย่างถูกต้องสำหรับฉันฉันต้องทำงานโดยใช้startคำสั่ง Ex: C:\"my app name.exe" -serviceจะไม่ทำงานสำหรับฉัน ฉันใช้แทนC:\start /wait "" "my app name.exe" -service
Arvo Bowen

47

บางครั้งมันเป็นสิ่งสำคัญในการวิเคราะห์สิ่งที่เกิดขึ้นในช่วงเริ่มต้นของการบริการ การเชื่อมต่อกับกระบวนการไม่ได้ช่วยที่นี่เพราะคุณไม่ได้เร็วพอที่จะแนบดีบักเกอร์ในขณะที่บริการเริ่มต้นขึ้น

คำตอบสั้น ๆ คือฉันใช้โค้ด 4 บรรทัดต่อไปนี้:

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

สิ่งเหล่านี้จะถูกแทรกลงในOnStartวิธีการให้บริการดังนี้:

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);
}

สำหรับผู้ที่ยังไม่เคยทำมาก่อนฉันได้รวมคำแนะนำอย่างละเอียดไว้ด้านล่างเพราะคุณสามารถติดขัดได้ง่าย คำแนะนำต่อไปนี้อ้างถึงWindows 7x64และVisual Studio 2010 Team Editionแต่ควรใช้ได้กับสภาพแวดล้อมอื่นเช่นกัน


สำคัญ:ปรับใช้บริการในโหมด "ด้วยตนเอง" (การใช้InstallUtilยูทิลิตี้จากพรอมต์คำสั่ง VS หรือเรียกใช้โครงการติดตั้งบริการที่คุณได้เตรียมไว้) เปิด Visual Studio ก่อนที่คุณจะเริ่มบริการและโหลดโซลูชันที่มีซอร์สโค้ดของบริการ - ตั้งค่าเบรกพอยต์เพิ่มเติมตามที่คุณต้องการใน Visual Studio - จากนั้นเริ่มบริการผ่านแผงควบคุมบริการ

เนื่องจากDebugger.Launchรหัสนี้จะทำให้กล่องโต้ตอบ "มีข้อยกเว้น Microsoft .NET Framework ที่ไม่สามารถจัดการได้เกิดขึ้นในServicename.exe " ปรากฏ คลิกตามที่แสดงในภาพหน้าจอ:Elevate Yes, debug Servicename.exe
FrameworkException

หลังจากนั้นโดยเฉพาะใน Windows 7 UAC อาจแจ้งให้คุณป้อนข้อมูลประจำตัวของผู้ดูแลระบบ ป้อนพวกเขาและดำเนินการต่อไปYes:

UACPrompt

หลังจากนั้นหน้าต่างดีบักเกอร์ Just-In-Time Visual Studio ที่รู้จักกันดีจะปรากฏขึ้น มันจะถามคุณว่าคุณต้องการดีบักหรือไม่โดยใช้ดีบักเกอร์ delected ก่อนที่คุณจะคลิกYes,เลือกที่คุณไม่ต้องการที่จะเปิดตัวอย่างใหม่ (ตัวเลือกที่ 2) - ตัวอย่างใหม่จะไม่เป็นประโยชน์ที่นี่เพราะรหัสที่มาจะไม่สามารถแสดงได้ ดังนั้นคุณเลือกอินสแตนซ์ Visual Studio ที่คุณเปิดก่อนหน้านี้แทน: VSDebuggerPrompt

หลังจากที่คุณได้คลิกYes,หลังจากที่ในขณะ Visual Studio จะแสดงขวาลูกศรสีเหลืองในสายที่Debugger.Launchคำสั่งและคุณสามารถที่จะแก้ปัญหารหัสของคุณ (วิธีMyInitOnStartซึ่งมีการเริ่มต้นของคุณ) VSDebuggerBreakpoint

การกดF5ดำเนินการต่อเนื่องทันทีจนกว่าจะถึงจุดพักถัดไปที่คุณเตรียมไว้

คำแนะนำ:เพื่อให้บริการการทำงานที่เลือกDebug -> แยกออกทั้งหมด สิ่งนี้ช่วยให้คุณสามารถเรียกใช้ไคลเอนต์ที่สื่อสารกับบริการหลังจากที่เริ่มต้นอย่างถูกต้องและคุณเสร็จสิ้นการดีบักรหัสเริ่มต้น หากคุณกดShift+F5 (หยุดการดีบัก) สิ่งนี้จะยุติการให้บริการ แทนที่จะทำเช่นนี้คุณควรใช้แผงควบคุมการบริการเพื่อหยุด

สังเกตได้ว่า

  • ถ้าคุณสร้างปล่อยแล้วรหัสการแก้ปัญหาจะถูกลบออกโดยอัตโนมัติและการบริการการทำงานได้ตามปกติ

  • ฉันใช้Debugger.Launch()ซึ่งจะเริ่มต้นและแนบดีบัก ฉันได้ทดสอบDebugger.Break()เช่นกันซึ่งใช้งานไม่ได้เนื่องจากไม่มีการเชื่อมต่อดีบักเกอร์ในการเริ่มต้นบริการ (ทำให้"ข้อผิดพลาด 1067: กระบวนการยุติโดยไม่คาดคิด" )

  • RequestAdditionalTimeตั้งค่าการหมดเวลาที่นานขึ้นสำหรับการเริ่มต้นบริการ (ซึ่งไม่ได้เป็นการหน่วงเวลาโค้ดเอง แต่จะดำเนินการทันทีด้วยDebugger.Launchคำสั่ง) มิฉะนั้นการหมดเวลาเริ่มต้นสำหรับการเริ่มบริการสั้นเกินไปและการเริ่มบริการล้มเหลวหากคุณไม่ได้โทรbase.Onstart(args)เร็วพอจากตัวดีบั๊ก ในทางปฏิบัติหมดเวลา 10 นาทีเพื่อหลีกเลี่ยงการที่คุณเห็นข้อความ " บริการไม่ตอบสนอง ... "ทันทีหลังจากเริ่มดีบัก

  • เมื่อคุณชินกับมันแล้ววิธีนี้ง่ายมากเพราะคุณเพียงแค่ต้องเพิ่ม 4 บรรทัดไปยังรหัสบริการที่มีอยู่ซึ่งจะช่วยให้คุณได้รับการควบคุมและแก้ไขข้อบกพร่องได้อย่างรวดเร็ว


1
จากความอยากรู้อยากเห็นคุณรู้หรือไม่ว่ามีการหมดเวลาสำหรับการโต้ตอบกับผู้ใช้กับพรอมต์ผู้ใช้ Debugger.Launch ()?
ชีฟ

1
ตามที่อธิบายไว้base.RequestAdditionalTime(600000)จะป้องกันการควบคุมบริการจากการยกเลิกบริการเป็นเวลา 10 นาทีหากไม่ได้โทรbase.OnStart(args)ภายในช่วงเวลานั้น) นอกจากนั้นฉันยังจำได้ว่า UAC จะยกเลิกหากคุณไม่ป้อนข้อมูลประจำตัวของผู้ดูแลระบบในระยะเวลาหนึ่ง (ฉันไม่รู้ว่ากี่วินาที แต่ฉันคิดว่าคุณต้องป้อนภายในหนึ่งนาที UAC อื่นจะยกเลิก) ซึ่งจะยุติเซสชันการดีบัก
Matt

2
ฉันพบสิ่งนี้เป็นวิธีที่ดีที่สุดสำหรับการดีบักข้อความ CustomCommand +1
Justin

40

สิ่งที่ฉันมักจะทำคือแค็ปซูลลอจิกของบริการในคลาสที่แยกต่างหากและเริ่มจากคลาส 'นักวิ่ง' คลาสนักวิ่งนี้สามารถให้บริการจริงหรือเพียงแค่คอนโซลแอปพลิเคชัน ดังนั้นทางออกของคุณมี (atleast) 3 โครงการ:

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

1
ฉันเคยใช้วิธีการนี้เช่นกัน แต่ฉันคิดว่าการรวมกันของสิ่งนี้และคำตอบข้างต้นเป็นวิธีการรักษา
RobS

27

วิดีโอ YouTubeนี้โดย Fabio Scopelอธิบายวิธีการดีบักบริการ Windows ค่อนข้างดี ... วิธีการที่แท้จริงในการเริ่มต้นที่ 4:45 ในวิดีโอ ...

นี่คือรหัสที่อธิบายในวิดีโอ ... ในไฟล์ Program.cs ของคุณเพิ่มรายการสำหรับส่วนดีบั๊ก ...

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

        }
    }
}

ในไฟล์ Service1.cs ของคุณเพิ่มเมธอด OnDebug () ...

    public Service1()
    {
        InitializeComponent();
    }

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

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

    protected override void OnStop()
    {
    }

มันทำงานอย่างไร

โดยทั่วไปคุณต้องสร้างสิ่งpublic void OnDebug()ที่เรียกOnStart(string[] args)ว่ามันได้รับการป้องกันและไม่สามารถเข้าถึงได้จากภายนอก void Main()โปรแกรมจะเพิ่มด้วยการ#ifpreprocessor #DEBUGกับ

Visual Studio กำหนดDEBUGว่าโครงการรวบรวมในโหมดดีบั๊กซึ่งจะอนุญาตให้ส่วนดีบั๊ก (ด้านล่าง) ทำงานเมื่อเงื่อนไขเป็นจริง

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

และมันจะทำงานเหมือนกับแอปพลิเคชั่นคอนโซลเมื่อสิ่งต่าง ๆ ดีขึ้นคุณสามารถเปลี่ยนโหมดได้Releaseและelseส่วนปกติจะเรียกใช้ตรรกะ


ฉันกำลังมองหาคำตอบนี้ไม่ได้ว่าทำไมมันถึงได้อันดับต่ำ อธิบายรหัสเพื่อช่วยผู้อื่นหรืออาจเป็นการคอมเม้นท์เพิ่มเติม)
Vinod Srivastav

14

UPDATE

วิธีนี้ง่ายที่สุด:

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

ฉันทิ้งคำตอบดั้งเดิมไว้ด้านล่างเพื่อลูกหลาน


บริการของฉันมักจะมีคลาสที่ห่อหุ้มตัวจับเวลาเนื่องจากฉันต้องการให้บริการตรวจสอบเป็นระยะ ๆ ว่ามีงานใดบ้างที่ต้องทำ

เราเพิ่มคลาสใหม่และเรียกใช้ StartEventLoop () ในระหว่างการเริ่มบริการ (คลาสนี้สามารถใช้งานได้ง่ายจากแอพคอนโซลด้วย)

ผลข้างเคียงที่ดีของการออกแบบนี้คือข้อโต้แย้งที่คุณตั้งค่าตัวจับเวลาสามารถใช้เพื่อให้เกิดความล่าช้าก่อนที่บริการจะเริ่มทำงานจริงเพื่อให้คุณมีเวลาแนบตัวดีบักเกอร์ด้วยตนเอง

ps วิธีแนบตัวดีบั๊กกับกระบวนการทำงาน ...

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");
    }
}

ฉันเคยทำสิ่งต่อไปนี้ (ได้กล่าวถึงแล้วในคำตอบก่อนหน้า แต่ด้วยแฟล็กคอมไพเลอร์แบบมีเงื่อนไข [#if] เพื่อช่วยหลีกเลี่ยงการถูกไล่ออกในการสร้าง Release)

ฉันหยุดทำเช่นนี้เพราะบางครั้งเราลืมที่จะสร้างใน Release และมีการดีบักเกอร์ในแอพที่ทำงานบนไคลเอนต์สาธิต (น่าอาย!)

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

จะเกิดอะไรขึ้นเมื่อ// do somethingใช้เวลามากกว่า 30 นาทีจึงจะเสร็จสมบูรณ์
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
}

[ขออภัยที่ไม่มีคำอธิบายเกี่ยวกับรหัส - ปัญหา markdown] ควรทำงานได้ตามปกติจาก MS Visual Studio (F5) ในการสร้างการดีบัก ยังคงทำงานเป็นบริการปกติในรุ่นบิลด์
Thomas Bratt

รวมสิ่งนี้กับโซลูชันข้างต้นโดย Christian K. เพื่อใช้คุณสมบัติ "Environment.UserInteractive" และโซลูชันนั้นสะอาดและเรียบง่าย
Ben Robbins

OnStartคือprotectedและคุณไม่สามารถแก้ไขระดับการเข้าถึง :(
Eduard Luca

10

คุณยังสามารถเริ่มบริการผ่านทางพรอมต์คำสั่ง (sc.exe)

โดยส่วนตัวฉันจะเรียกใช้รหัสเป็นโปรแกรมแบบสแตนด์อโลนในขั้นตอนการดีบักและเมื่อข้อบกพร่องส่วนใหญ่ถูกยกเลิกให้เปลี่ยนเป็นการทำงานเป็นบริการ


10

สิ่งที่ฉันเคยทำคือการมีสวิตช์บรรทัดคำสั่งซึ่งจะเริ่มโปรแกรมไม่ว่าจะเป็นบริการหรือเป็นโปรแกรมปกติ จากนั้นใน IDE ของฉันฉันจะตั้งสวิตช์เพื่อให้ฉันสามารถผ่านรหัสของฉัน

ด้วยบางภาษาคุณสามารถตรวจพบว่ามันทำงานใน IDE จริงหรือไม่และทำสวิตช์นี้โดยอัตโนมัติ

คุณใช้ภาษาอะไร


9

ใช้ไลบรารีTopShelf

สร้างแอปพลิเคชันคอนโซลจากนั้นกำหนดค่าการตั้งค่าในหน้าหลักของคุณ

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()
        {

        }
    }

ในการแก้ปัญหาบริการของคุณเพียงแค่กด F5 ใน visual studio

หากต้องการติดตั้งบริการให้พิมพ์ cmd "console.exe install"

จากนั้นคุณสามารถเริ่มและหยุดบริการในตัวจัดการบริการ windows


ใบอนุญาตของพวกเขาสับสนเกินไปที่จะเข้าใจ
l

พวกเขาใช้ Apache License afaik Topshelf เป็นวิธีที่ง่ายที่สุดที่ฉันเคยใช้ในการพัฒนาและแก้ไขข้อบกพร่องของบริการ windows ใช้งานง่ายสุด ๆ พัฒนาเป็นแอปพลิเคชั่นคอนโซล ติดตั้งเป็นบริการด้วยสวิตช์บรรทัดคำสั่งเดียว แนะนำเป็นอย่างยิ่ง
ปล้น

TopShelf ช่วยฉันได้หลายครั้ง ขอบคุณ
L_7337

8

ฉันคิดว่ามันขึ้นอยู่กับสิ่งที่คุณใช้ระบบปฏิบัติการ, Vista เป็นเรื่องยากมากที่จะแนบไปกับบริการเนื่องจากการแยกระหว่างเซสชัน

ตัวเลือกสองตัวที่ฉันเคยใช้ในอดีตคือ:

  • ใช้ GFlags (ในเครื่องมือดีบั๊กสำหรับ Windows) เพื่อตั้งค่าดีบักเกอร์ถาวรสำหรับกระบวนการ มีอยู่ในคีย์รีจิสทรี "ตัวเลือกการดำเนินการไฟล์ภาพ" และมีประโยชน์อย่างเหลือเชื่อ ฉันคิดว่าคุณจะต้องปรับแต่งการตั้งค่าบริการเพื่อเปิดใช้งาน "การโต้ตอบกับเดสก์ท็อป" ฉันใช้สิ่งนี้สำหรับการดีบักทุกประเภทไม่ใช่เฉพาะบริการ
  • ตัวเลือกอื่นคือการแยกรหัสบิตเพื่อให้ส่วนบริการนั้นสามารถเปลี่ยนได้ด้วยการเริ่มต้นแอปปกติ ด้วยวิธีนี้คุณสามารถใช้การตั้งค่าสถานะบรรทัดคำสั่งอย่างง่ายและเรียกใช้เป็นกระบวนการ (แทนบริการ) ซึ่งทำให้การดีบักง่ายขึ้นมาก

หวังว่านี่จะช่วยได้


+1 สำหรับ GFlags สิ่งนี้มีประโยชน์อย่างยิ่งหากคุณไม่สามารถแก้ไขซอร์สโค้ด (หรือถ้าคุณไม่มี)
Chris Gillum

6

ฉันชอบที่จะสามารถดีบักทุกแง่มุมของบริการของฉันรวมถึงการเริ่มต้นใน OnStart () ในขณะที่ยังคงดำเนินการกับพฤติกรรมบริการเต็มรูปแบบภายในกรอบของ SCM ... ไม่มีโหมด "คอนโซล" หรือ "แอป"

ฉันทำได้โดยการสร้างบริการที่สองในโครงการเดียวกันเพื่อใช้สำหรับการดีบัก บริการดีบักเมื่อเริ่มต้นตามปกติ (เช่นในปลั๊กอิน MMC ของบริการ) จะสร้างกระบวนการโฮสต์บริการ สิ่งนี้ช่วยให้คุณสามารถแนบดีบักเกอร์ได้แม้ว่าคุณจะยังไม่ได้เริ่มบริการจริง หลังจากเชื่อมต่อดีบักเกอร์เข้ากับกระบวนการให้เริ่มบริการจริงของคุณและคุณสามารถเจาะเข้าไปที่ใดก็ได้ในวงจรชีวิตของบริการรวมถึง OnStart ()

เนื่องจากมันต้องการการบุกรุกของรหัสที่น้อยมากบริการตรวจแก้จุดบกพร่องจึงสามารถรวมอยู่ในโครงการตั้งค่าบริการของคุณได้อย่างง่ายดายและถูกลบออกจากรุ่นผลิตของคุณได้อย่างง่ายดายโดยการใส่รหัสบรรทัดเดียวและลบโปรแกรมติดตั้งโครงการเดียว

รายละเอียด:

1) สมมติว่าคุณกำลังนำไปใช้MyServiceสร้างMyServiceDebugด้วย เพิ่มทั้งสองลงในServiceBaseอาร์เรย์Program.csดังนี้:

    /// <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) เพิ่มบริการจริงและบริการดีบักในตัวติดตั้งโครงการสำหรับโครงการบริการ:

ป้อนคำอธิบายรูปภาพที่นี่

บริการทั้งสอง (ของจริงและดีบัก) ถูกรวมไว้เมื่อคุณเพิ่มเอาท์พุทโครงการบริการไปยังโครงการติดตั้งสำหรับบริการ หลังจากติดตั้งแล้วบริการทั้งสองจะปรากฏใน service.msc MMC plugin

3) เริ่มบริการ debug ใน MMC

4) ใน Visual Studio ให้แนบดีบักเกอร์กับกระบวนการที่เริ่มโดยบริการตรวจแก้จุดบกพร่อง

5) เริ่มบริการจริงและเพลิดเพลินกับการแก้ไขข้อบกพร่อง


5

เมื่อฉันเขียนบริการฉันใส่ตรรกะของบริการทั้งหมดในโครงการ dll และสร้าง "โฮสต์" สองอันที่เรียกใช้เป็น dll นี้หนึ่งคือบริการของ Windows และอีกอันคือแอปพลิเคชันบรรทัดคำสั่ง

ฉันใช้แอปพลิเคชันบรรทัดคำสั่งสำหรับการดีบักและแนบดีบักเกอร์กับบริการจริงสำหรับข้อบกพร่องเท่านั้นฉันไม่สามารถสร้างซ้ำในแอปพลิเคชันบรรทัดคำสั่ง

ฉันใช้วิธีนี้เพียงจำไว้ว่าคุณต้องทดสอบโค้ดทั้งหมดในขณะที่ทำงานในบริการจริงในขณะที่เครื่องมือบรรทัดคำสั่งเป็นตัวช่วยในการดีบั๊กที่ดีมันเป็นสภาพแวดล้อมที่แตกต่างกัน


4

เมื่อพัฒนาและแก้ไขจุดบกพร่องของบริการ Windows โดยทั่วไปฉันจะเรียกใช้เป็นแอปพลิเคชันคอนโซลโดยเพิ่มพารามิเตอร์เริ่มต้น / console และตรวจสอบสิ่งนี้ ทำให้ชีวิตง่ายขึ้นมาก

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

จนกว่าคุณจะต้องแก้ปัญหาเฉพาะของบริการ
leppie

จริงแล้วคุณต้องแนบดีบักเกอร์กับกระบวนการบริการจริง แต่ในกรณีส่วนใหญ่ข้อผิดพลาดจะปรากฏขึ้นอย่างใดอย่างหนึ่งและการพัฒนาง่ายขึ้นมาก
Maurice


2

ในการแก้ปัญหา Windows Services ฉันรวม GFlags และไฟล์. reg ที่สร้างโดย regedit

  1. เรียกใช้ GFlags โดยระบุชื่อ exe และ vsjitdebugger
  2. เรียกใช้ regedit และไปที่ตำแหน่งที่ GFlags ตั้งค่าตัวเลือกของเขา
  3. เลือก "ส่งออกคีย์" จากเมนูไฟล์
  4. บันทึกไฟล์นั้นที่อื่นด้วยนามสกุล. reg
  5. เมื่อใดก็ตามที่คุณต้องการแก้ปัญหาบริการ: คลิกสองครั้งที่ไฟล์. reg
  6. หากคุณต้องการหยุดการดีบักให้คลิกสองครั้งที่ไฟล์. reg ที่สอง

หรือบันทึกตัวอย่างต่อไปนี้และแทนที่ servicename.exe ด้วยชื่อปฏิบัติการที่ต้องการ


debugon.reg:

Windows Registry Editor เวอร์ชัน 5.00

[HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows NT \ CurrentVersion \ Image ตัวเลือกการดำเนินการของไฟล์ \ servicename.exe]
"GlobalFlag" = "0x00000000"
"ดีบักเกอร์" = "vsjitdebugger.exe"

debugoff.reg:

Windows Registry Editor เวอร์ชัน 5.00

[HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows NT \ CurrentVersion \ Image ตัวเลือกการดำเนินการของไฟล์ \ servicename.exe]
"GlobalFlag" = "0x00000000"

สิ่งนี้ยังใช้งานได้กับ Win 7 / Win 2008 หรือไม่ มันเป็นวิธีการจากsupport.microsoft.com/kb/824344แต่ขึ้นอยู่กับบริการแบบโต้ตอบและฉันคิดว่าพวกเขาถูกฆ่าตาย? มันเคยเป็นตัวเลือกที่ฉันชอบ (เนื่องจากปัญหาการเริ่มต้นอาจเกิดขึ้นในการผลิตซึ่งการแทรก Debugger.Break () ลงในรหัสอาจไม่ใช่ตัวเลือก)
piers7

1

สำหรับการเขียนโปรแกรมสิ่งเล็ก ๆ เป็นประจำฉันได้ทำเคล็ดลับง่าย ๆ เพื่อแก้จุดบกพร่องบริการของฉันได้อย่างง่ายดาย:

เมื่อเริ่มบริการฉันจะตรวจสอบพารามิเตอร์บรรทัดคำสั่ง "/ debug" หากมีการเรียกใช้บริการด้วยพารามิเตอร์นี้ฉันจะไม่เริ่มต้นบริการตามปกติ แต่ให้เริ่มต้นฟังทั้งหมดและเพียงแสดงกล่องข้อความ "กำลังดำเนินการแก้ไขข้อผิดพลาดกดตกลงเพื่อสิ้นสุด"

ดังนั้นหากบริการของฉันเริ่มต้นตามปกติมันจะเริ่มต้นเป็นบริการหากเริ่มต้นด้วยพารามิเตอร์บรรทัดคำสั่ง / ดีบักมันจะทำหน้าที่เหมือนโปรแกรมปกติ

ใน VS ฉันจะเพิ่ม / debug เป็นพารามิเตอร์การดีบักและเริ่มโปรแกรมบริการโดยตรง

วิธีนี้ฉันสามารถดีบักปัญหาเล็ก ๆ ส่วนใหญ่ได้อย่างง่ายดาย แน่นอนว่าบางสิ่งยังคงต้องมีการดีบั๊กเป็นบริการ แต่สำหรับ 99% นี่ดีพอ



1

ฉันใช้ความหลากหลายในคำตอบของ JOP การใช้พารามิเตอร์บรรทัดคำสั่งคุณสามารถตั้งค่าโหมดการดีบักใน IDE ด้วยคุณสมบัติโครงการหรือผ่านตัวจัดการบริการของ Windows

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

1

สำหรับการแก้ไขปัญหาในโปรแกรม Windows Service ที่มีอยู่ให้ใช้ 'Debugger.Break ()' ตามที่คนอื่นแนะนำ

สำหรับโปรแกรม Windows Service ใหม่ฉันขอแนะนำให้ใช้วิธีการของ James Michael Hare http://geekswithblogs.net/BlackRabbitCoder/archive/2011/03/01/c-toolbox-debug-able-self-installable-windows-service-template- redux.aspx


1

เพียงแค่ใส่ดีบักเกอร์มื้อกลางวันที่ใดก็ได้และแนบ Visualstudio เมื่อเริ่มต้น

#if DEBUG
    Debugger.Launch();
#endif

นอกจากนี้คุณต้องเริ่มต้น VS เป็น Administatrator และคุณต้องอนุญาตว่ากระบวนการสามารถดีบักโดยอัตโนมัติโดยผู้ใช้ที่แตกต่างกัน (ตามที่อธิบายไว้ที่นี่ ):

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

1

ใช้โปรเจ็กต์ Windows Service Template C # เพื่อสร้างแอปบริการใหม่https://github.com/HarpyWar/windows-service-template

มีการตรวจพบโหมดคอนโซล / บริการโดยอัตโนมัติตัวติดตั้งอัตโนมัติ / ตัวติดตั้งบริการของคุณและคุณสมบัติที่ใช้งานบ่อยที่สุดรวมอยู่ด้วย


1

นี่เป็นวิธีง่าย ๆ ที่ฉันใช้ในการทดสอบบริการโดยไม่มีวิธี "Debug" เพิ่มเติมใด ๆ และด้วยการทดสอบหน่วย VS แบบรวม

[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
    }
}

ง่ายกว่านี้ เพียงแค่เปลี่ยนการตั้งค่าโซลูชันเป็นดีบั๊กรันโปรเจ็กต์ / โซลูชันเพิ่มเบรกพอยต์ตามที่คุณไป
Bahamut

0

คุณมีสองตัวเลือกในการทำการดีบัก

  1. สร้างไฟล์บันทึก: โดยส่วนตัวแล้วฉันชอบไฟล์บันทึกแยกต่างหากเช่นไฟล์ข้อความแทนที่จะใช้แอปพลิเคชันหรือบันทึกเหตุการณ์ แต่สิ่งนี้จะทำให้คุณเสียค่าใช้จ่ายมากในนามของเวลาเพราะมันยังคงยากที่จะคิด
  2. แปลงแอปพลิเคชันเป็นคอนโซลแอปพลิเคชัน: สิ่งนี้จะช่วยให้คุณเครื่องมือการดีบักทั้งหมดที่เราสามารถใช้ใน VS

โปรดอ้างอิงโพสต์บล็อกนี้ที่ฉันสร้างขึ้นสำหรับหัวข้อ


0

เพียงแค่วาง

Debugger.Break();

ที่ใดในรหัสคุณ

ตัวอย่างเช่น ,

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);
        }
    }

มันจะตีDebugger.Break();เมื่อคุณเรียกใช้โปรแกรมของคุณ


0

ตัวเลือกที่ดีที่สุดคือการใช้เนมสเปซ ' System.Diagnostics '

ใส่รหัสของคุณหากบล็อกอื่นสำหรับโหมด debug และโหมด release ตามที่แสดงด้านล่างเพื่อสลับระหว่างโหมด debug และ release ใน visual studio

#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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.