มีวิธีที่ง่ายกว่าในการก้าวไปข้างหน้ารหัสกว่าจะเริ่มบริการผ่านทาง Windows Service Control Manager แล้วแนบตัวดีบักกับเธรดหรือไม่ มันค่อนข้างยุ่งยากและฉันก็สงสัยว่าถ้ามีวิธีที่ตรงไปตรงมามากกว่านี้
มีวิธีที่ง่ายกว่าในการก้าวไปข้างหน้ารหัสกว่าจะเริ่มบริการผ่านทาง Windows Service Control Manager แล้วแนบตัวดีบักกับเธรดหรือไม่ มันค่อนข้างยุ่งยากและฉันก็สงสัยว่าถ้ามีวิธีที่ตรงไปตรงมามากกว่านี้
คำตอบ:
หากฉันต้องการแก้จุดบกพร่องของบริการอย่างรวดเร็วฉันก็แค่เข้าไปข้างDebugger.Break()
ใน เมื่อถึงบรรทัดนั้นมันจะทำให้ฉันกลับไปที่ VS อย่าลืมลบบรรทัดนั้นเมื่อคุณทำเสร็จแล้ว
อัปเดต:เป็นอีกทางเลือกสำหรับ#if DEBUG
pragmas คุณยังสามารถใช้Conditional("DEBUG_SERVICE")
แอตทริบิวต์
[Conditional("DEBUG_SERVICE")]
private static void DebugMode()
{
Debugger.Break();
}
ในของคุณOnStart
เพียงแค่เรียกวิธีนี้:
public override void OnStart()
{
DebugMode();
/* ... do the rest */
}
ที่นั่นรหัสจะเปิดใช้งานในระหว่างการสร้างการดีบักเท่านั้น ในขณะที่คุณอยู่ที่นั่นอาจเป็นประโยชน์ในการสร้าง Build Configuration แยกต่างหากสำหรับการดีบักบริการ
ฉันยังคิดว่ามี "รุ่น" แยกต่างหากสำหรับการดำเนินการตามปกติและเป็นบริการเป็นวิธีที่จะไป แต่มันจำเป็นจริงๆที่จะอุทิศสวิตช์บรรทัดคำสั่งแยกต่างหากเพื่อวัตถุประสงค์นั้นหรือไม่?
คุณไม่สามารถทำได้:
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
นอกเหนือจากการถูกเรียกใช้เป็นบริการ (ไม่ใช่แบบโต้ตอบ)?
System.Diagnostics.Debugger.IsAttached
Environment.UserInteractive
เมื่อฉันตั้งค่าโครงการบริการใหม่ไม่กี่สัปดาห์ที่ผ่านมาฉันพบโพสต์นี้ ในขณะที่มีคำแนะนำที่ดีมากมายฉันยังไม่พบวิธีแก้ปัญหาที่ฉันต้องการ: ความเป็นไปได้ที่จะเรียกคลาสบริการ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);
}
นี่คือรหัสทั้งหมดที่จำเป็น แต่ฉันยังเขียนคำแนะนำด้วยคำอธิบาย
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
บางครั้งมันเป็นสิ่งสำคัญในการวิเคราะห์สิ่งที่เกิดขึ้นในช่วงเริ่มต้นของการบริการ การเชื่อมต่อกับกระบวนการไม่ได้ช่วยที่นี่เพราะคุณไม่ได้เร็วพอที่จะแนบดีบักเกอร์ในขณะที่บริการเริ่มต้นขึ้น
คำตอบสั้น ๆ คือฉันใช้โค้ด 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 " ปรากฏ คลิกตามที่แสดงในภาพหน้าจอ: Yes, debug Servicename.exe
หลังจากนั้นโดยเฉพาะใน Windows 7 UAC อาจแจ้งให้คุณป้อนข้อมูลประจำตัวของผู้ดูแลระบบ ป้อนพวกเขาและดำเนินการต่อไปYes:
หลังจากนั้นหน้าต่างดีบักเกอร์ Just-In-Time Visual Studio ที่รู้จักกันดีจะปรากฏขึ้น มันจะถามคุณว่าคุณต้องการดีบักหรือไม่โดยใช้ดีบักเกอร์ delected ก่อนที่คุณจะคลิกYes,เลือกที่คุณไม่ต้องการที่จะเปิดตัวอย่างใหม่ (ตัวเลือกที่ 2) - ตัวอย่างใหม่จะไม่เป็นประโยชน์ที่นี่เพราะรหัสที่มาจะไม่สามารถแสดงได้ ดังนั้นคุณเลือกอินสแตนซ์ Visual Studio ที่คุณเปิดก่อนหน้านี้แทน:
หลังจากที่คุณได้คลิกYes,หลังจากที่ในขณะ Visual Studio จะแสดงขวาลูกศรสีเหลืองในสายที่Debugger.Launch
คำสั่งและคุณสามารถที่จะแก้ปัญหารหัสของคุณ (วิธีMyInitOnStart
ซึ่งมีการเริ่มต้นของคุณ)
การกดF5ดำเนินการต่อเนื่องทันทีจนกว่าจะถึงจุดพักถัดไปที่คุณเตรียมไว้
คำแนะนำ:เพื่อให้บริการการทำงานที่เลือกDebug -> แยกออกทั้งหมด สิ่งนี้ช่วยให้คุณสามารถเรียกใช้ไคลเอนต์ที่สื่อสารกับบริการหลังจากที่เริ่มต้นอย่างถูกต้องและคุณเสร็จสิ้นการดีบักรหัสเริ่มต้น หากคุณกดShift+F5 (หยุดการดีบัก) สิ่งนี้จะยุติการให้บริการ แทนที่จะทำเช่นนี้คุณควรใช้แผงควบคุมการบริการเพื่อหยุด
สังเกตได้ว่า
ถ้าคุณสร้างปล่อยแล้วรหัสการแก้ปัญหาจะถูกลบออกโดยอัตโนมัติและการบริการการทำงานได้ตามปกติ
ฉันใช้Debugger.Launch()
ซึ่งจะเริ่มต้นและแนบดีบัก ฉันได้ทดสอบDebugger.Break()
เช่นกันซึ่งใช้งานไม่ได้เนื่องจากไม่มีการเชื่อมต่อดีบักเกอร์ในการเริ่มต้นบริการ (ทำให้"ข้อผิดพลาด 1067: กระบวนการยุติโดยไม่คาดคิด" )
RequestAdditionalTime
ตั้งค่าการหมดเวลาที่นานขึ้นสำหรับการเริ่มต้นบริการ (ซึ่งไม่ได้เป็นการหน่วงเวลาโค้ดเอง แต่จะดำเนินการทันทีด้วยDebugger.Launch
คำสั่ง) มิฉะนั้นการหมดเวลาเริ่มต้นสำหรับการเริ่มบริการสั้นเกินไปและการเริ่มบริการล้มเหลวหากคุณไม่ได้โทรbase.Onstart(args)
เร็วพอจากตัวดีบั๊ก ในทางปฏิบัติหมดเวลา 10 นาทีเพื่อหลีกเลี่ยงการที่คุณเห็นข้อความ " บริการไม่ตอบสนอง ... "ทันทีหลังจากเริ่มดีบัก
เมื่อคุณชินกับมันแล้ววิธีนี้ง่ายมากเพราะคุณเพียงแค่ต้องเพิ่ม 4 บรรทัดไปยังรหัสบริการที่มีอยู่ซึ่งจะช่วยให้คุณได้รับการควบคุมและแก้ไขข้อบกพร่องได้อย่างรวดเร็ว
base.RequestAdditionalTime(600000)
จะป้องกันการควบคุมบริการจากการยกเลิกบริการเป็นเวลา 10 นาทีหากไม่ได้โทรbase.OnStart(args)
ภายในช่วงเวลานั้น) นอกจากนั้นฉันยังจำได้ว่า UAC จะยกเลิกหากคุณไม่ป้อนข้อมูลประจำตัวของผู้ดูแลระบบในระยะเวลาหนึ่ง (ฉันไม่รู้ว่ากี่วินาที แต่ฉันคิดว่าคุณต้องป้อนภายในหนึ่งนาที UAC อื่นจะยกเลิก) ซึ่งจะยุติเซสชันการดีบัก
สิ่งที่ฉันมักจะทำคือแค็ปซูลลอจิกของบริการในคลาสที่แยกต่างหากและเริ่มจากคลาส 'นักวิ่ง' คลาสนักวิ่งนี้สามารถให้บริการจริงหรือเพียงแค่คอนโซลแอปพลิเคชัน ดังนั้นทางออกของคุณมี (atleast) 3 โครงการ:
/ConsoleRunner
/....
/ServiceRunner
/....
/ApplicationLogic
/....
วิดีโอ 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()
โปรแกรมจะเพิ่มด้วยการ#if
preprocessor #DEBUG
กับ
Visual Studio กำหนดDEBUG
ว่าโครงการรวบรวมในโหมดดีบั๊กซึ่งจะอนุญาตให้ส่วนดีบั๊ก (ด้านล่าง) ทำงานเมื่อเงื่อนไขเป็นจริง
Service1 myService = new Service1();
myService.OnDebug();
System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
และมันจะทำงานเหมือนกับแอปพลิเคชั่นคอนโซลเมื่อสิ่งต่าง ๆ ดีขึ้นคุณสามารถเปลี่ยนโหมดได้Release
และelse
ส่วนปกติจะเรียกใช้ตรรกะ
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 นาทีจึงจะเสร็จสมบูรณ์
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
}
OnStart
คือprotected
และคุณไม่สามารถแก้ไขระดับการเข้าถึง :(
คุณยังสามารถเริ่มบริการผ่านทางพรอมต์คำสั่ง (sc.exe)
โดยส่วนตัวฉันจะเรียกใช้รหัสเป็นโปรแกรมแบบสแตนด์อโลนในขั้นตอนการดีบักและเมื่อข้อบกพร่องส่วนใหญ่ถูกยกเลิกให้เปลี่ยนเป็นการทำงานเป็นบริการ
สิ่งที่ฉันเคยทำคือการมีสวิตช์บรรทัดคำสั่งซึ่งจะเริ่มโปรแกรมไม่ว่าจะเป็นบริการหรือเป็นโปรแกรมปกติ จากนั้นใน IDE ของฉันฉันจะตั้งสวิตช์เพื่อให้ฉันสามารถผ่านรหัสของฉัน
ด้วยบางภาษาคุณสามารถตรวจพบว่ามันทำงานใน IDE จริงหรือไม่และทำสวิตช์นี้โดยอัตโนมัติ
คุณใช้ภาษาอะไร
ใช้ไลบรารี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
ฉันคิดว่ามันขึ้นอยู่กับสิ่งที่คุณใช้ระบบปฏิบัติการ, Vista เป็นเรื่องยากมากที่จะแนบไปกับบริการเนื่องจากการแยกระหว่างเซสชัน
ตัวเลือกสองตัวที่ฉันเคยใช้ในอดีตคือ:
หวังว่านี่จะช่วยได้
ฉันชอบที่จะสามารถดีบักทุกแง่มุมของบริการของฉันรวมถึงการเริ่มต้นใน 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) เริ่มบริการจริงและเพลิดเพลินกับการแก้ไขข้อบกพร่อง
เมื่อฉันเขียนบริการฉันใส่ตรรกะของบริการทั้งหมดในโครงการ dll และสร้าง "โฮสต์" สองอันที่เรียกใช้เป็น dll นี้หนึ่งคือบริการของ Windows และอีกอันคือแอปพลิเคชันบรรทัดคำสั่ง
ฉันใช้แอปพลิเคชันบรรทัดคำสั่งสำหรับการดีบักและแนบดีบักเกอร์กับบริการจริงสำหรับข้อบกพร่องเท่านั้นฉันไม่สามารถสร้างซ้ำในแอปพลิเคชันบรรทัดคำสั่ง
ฉันใช้วิธีนี้เพียงจำไว้ว่าคุณต้องทดสอบโค้ดทั้งหมดในขณะที่ทำงานในบริการจริงในขณะที่เครื่องมือบรรทัดคำสั่งเป็นตัวช่วยในการดีบั๊กที่ดีมันเป็นสภาพแวดล้อมที่แตกต่างกัน
เมื่อพัฒนาและแก้ไขจุดบกพร่องของบริการ Windows โดยทั่วไปฉันจะเรียกใช้เป็นแอปพลิเคชันคอนโซลโดยเพิ่มพารามิเตอร์เริ่มต้น / console และตรวจสอบสิ่งนี้ ทำให้ชีวิตง่ายขึ้นมาก
static void Main(string[] args) {
if (Console.In != StreamReader.Null) {
if (args.Length > 0 && args[0] == "/console") {
// Start your service work.
}
}
}
วิธีการเกี่ยวกับ Debugger.Break () ในบรรทัดแรก?
ในการแก้ปัญหา Windows Services ฉันรวม GFlags และไฟล์. reg ที่สร้างโดย regedit
หรือบันทึกตัวอย่างต่อไปนี้และแทนที่ 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"
สำหรับการเขียนโปรแกรมสิ่งเล็ก ๆ เป็นประจำฉันได้ทำเคล็ดลับง่าย ๆ เพื่อแก้จุดบกพร่องบริการของฉันได้อย่างง่ายดาย:
เมื่อเริ่มบริการฉันจะตรวจสอบพารามิเตอร์บรรทัดคำสั่ง "/ debug" หากมีการเรียกใช้บริการด้วยพารามิเตอร์นี้ฉันจะไม่เริ่มต้นบริการตามปกติ แต่ให้เริ่มต้นฟังทั้งหมดและเพียงแสดงกล่องข้อความ "กำลังดำเนินการแก้ไขข้อผิดพลาดกดตกลงเพื่อสิ้นสุด"
ดังนั้นหากบริการของฉันเริ่มต้นตามปกติมันจะเริ่มต้นเป็นบริการหากเริ่มต้นด้วยพารามิเตอร์บรรทัดคำสั่ง / ดีบักมันจะทำหน้าที่เหมือนโปรแกรมปกติ
ใน VS ฉันจะเพิ่ม / debug เป็นพารามิเตอร์การดีบักและเริ่มโปรแกรมบริการโดยตรง
วิธีนี้ฉันสามารถดีบักปัญหาเล็ก ๆ ส่วนใหญ่ได้อย่างง่ายดาย แน่นอนว่าบางสิ่งยังคงต้องมีการดีบั๊กเป็นบริการ แต่สำหรับ 99% นี่ดีพอ
#if DEBUG
System.Diagnostics.Debugger.Break();
#endif
ฉันใช้ความหลากหลายในคำตอบของ JOP การใช้พารามิเตอร์บรรทัดคำสั่งคุณสามารถตั้งค่าโหมดการดีบักใน IDE ด้วยคุณสมบัติโครงการหรือผ่านตัวจัดการบริการของ Windows
protected override void OnStart(string[] args)
{
if (args.Contains<string>("DEBUG_SERVICE"))
{
Debugger.Break();
}
...
}
สำหรับการแก้ไขปัญหาในโปรแกรม 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
เพียงแค่ใส่ดีบักเกอร์มื้อกลางวันที่ใดก็ได้และแนบ 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
ใช้โปรเจ็กต์ Windows Service Template C # เพื่อสร้างแอปบริการใหม่https://github.com/HarpyWar/windows-service-template
มีการตรวจพบโหมดคอนโซล / บริการโดยอัตโนมัติตัวติดตั้งอัตโนมัติ / ตัวติดตั้งบริการของคุณและคุณสมบัติที่ใช้งานบ่อยที่สุดรวมอยู่ด้วย
นี่เป็นวิธีง่าย ๆ ที่ฉันใช้ในการทดสอบบริการโดยไม่มีวิธี "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 });
}
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
}
}
คุณมีสองตัวเลือกในการทำการดีบัก
โปรดอ้างอิงโพสต์บล็อกนี้ที่ฉันสร้างขึ้นสำหรับหัวข้อ
เพียงแค่วาง
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();
เมื่อคุณเรียกใช้โปรแกรมของคุณ
ตัวเลือกที่ดีที่สุดคือการใช้เนมสเปซ ' 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