افزودن پردازش(Process) در زمان اجرا برای debug کردن در ویژوال استودیو
جمعه 29 مرداد 1395در این مقاله قصد داریم شما را با ویژگی در ویژوال استودیو آشنا کنیم که با استفاده از ان میتوانید بطور همزمان سرویس ها و برنامه هایی که در دل یک برنامه بزرگتر قرار دارد را کامپایل و اجرا کنید.
تست های یکپارچه سازی همواره به اجزاء سیستم مانند پایگاه داده ، سرویس ها و ... بستگی دارد. اگر تست یکپارچه سازی به صورت غیر منتظره ای با شکست رو به رو شود ممکن است که نیاز باشد تست مورد نظر را debug کنیم که شامل اجزایی میباشد که معمولا بخشی از همان vs solution میباشد. حال با این سوال رو به رو میشویم که چگونه میتوان یک server مانند TCP که در یک solution با تست یکپارچه سازی قرار دارد را به طور همزمان با تست موردنظر اجرا کرد. این سوال بسیار منطقی به نظر میرسد چرا که هنگامی که در حال debug کردن یک تست هستید اضافه کردن یک process (پردازش) ، بارها و بارها برای debug کردن میتواند بسیار آزاردهنده باشد.
برای اینکه در یک پروژه اجزائی وابسته بهم (مانند سرویس ها) را ایجاد کنیم یک process را مانند یک پروژه ConsoleApplication در همان solution فراخوانی میکنیم. این کار مانند اضافه کردن یک کلاس کمک کننده میباشد تا بتوان process موردنظر را فراخوانی کنیم.
internal class ProcessInvoker 2. { 3. /// <summary> 4. /// Invokes the host process for test service 5. /// </summary> 6. public static void InvokeDummyService() 7. { 8. var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); 9. 10. 11. ProcessStartInfo info = newProcessStartInfo(Path.Combine(path, "DummyService.exe")); 12. 13. 14. info.UseShellExecute = true; 15. info.WorkingDirectory = path; 16. 17. 18. var process = Process.Start(info); 19. // This is what would attach the process to current VS debugger 20. AttachDebugger.ToProcess(process.Id); 21. } 22. 23. 24. /// <summary> 25. /// Kills the process of service host 26. /// </summary> 27. public static void KillDummyService() 28. { 29. Process.GetProcessesByName("DummyService").ToList().ForEach(x => x.Kill()); 30. } 31. }
حال در متدهای TestInitialize و TestCleanup (ما از NUnit در این نمونه کد استفاده کرده ایم که متدهای TestFixtureSetup وTextFixtureTearDown را در آن اضافه کردیم) میخواهیم process موردنظر را شروع کنیم وrespective process را از بین ببریم . در این تست میخواهیم یک نمونه Dummy service (سرویس خالی) را در یک Console application فراخوانی کنیم. سپس تکمیل تست باعث از بین رفتن process میشود.
1. using System; 2. using System.Collections.Generic; 3. using System.Linq; 4. using System.Reflection; 5. using System.Text; 6. using System.Threading.Tasks; 7. using SampleService.Contracts; 8. using WcfDynamicProxy.Tests.Helper; 9. using NUnit.Framework; 10. 11. 12. namespace WcfDynamicProxy.Tests.Tests 13. { 14. /// <summary> 15. /// Implements the tests for CustomClientProxyFactory class. The tests have falvour of integration 16. /// as we're testing it on dummy service client. 17. /// </summary> 18. [TestFixture] 19. public class CustomClientProxyFactoryTest 20. { 21. /// <summary> 22. /// Setup required before the tests of the fixture will run. 23. /// </summary> 24. [TestFixtureSetUp] 25. public void Init() 26. { 27. ServiceHostProcessInvoker.InvokeDummyService(); 28. } 29. 30. 31. /// <summary> 32. /// Tear down to perform clean when the execution is finished. 33. /// </summary> 34. [TestFixtureTearDown] 35. public void TearDown() 36. { 37. ServiceHostProcessInvoker.KillDummyService(); 38. } 39. 40. 41. /// <summary> 42. /// Case when the method GetDefaultErrorLoggingInterfaceProxy was invoked to get the real 43. /// proxy of a test service, succeeds. 44. /// </summary> 45. [Test] 46. public void GetDefaultWCFClientProxy_RequestingProxyClientGeneration_Succeeds() 47. { 48. var proxy = DynamicProxyWrapperFactory.GetProxyInstanceByInterface < ITestService > ("TestService"); 49. 50. 51. var result = proxy.SayHello(); 52. 53. 54. Assert.IsNotNull(result, "The result was null."); 55. Assert.IsNotEmpty(result, "The result was empty."); 56. } 57. } 58. } 58. }
در این مرحله میخواهیم قسمتی که به این process ضمیمه میشود را debug کنیم. این مرحله کمی دشوار است. ما برای این کار یک افزونه در Visual studio team پیدا کردیم تا process های فرزند را به صورت خودکار به debugger جاری اضافه میکند. اما این عمل تنها با استفاده از کلید "F5" کار میکند و برنامه را debug میکند.
در یکی از سایت های مربوط به برنامه نویسی مطلبی در مورد اضافه کردن یک debugger به یک process در c# مطرح شده بود که بسیار جالب بود و به خوبی کار میکرد ما در اینجا از توضیحات مربوط به آن مطلب ، البته با کمی تغییر استفاده کرده ایم.
1. using System; 2. using System.Runtime.InteropServices; 3. using System.Threading; 4. using System.Threading.Tasks; 5. using System.Linq; 6. using System.Collections.Generic; 7. using EnvDTE; 8. 9. 10. namespace Common 11. { 12. [ComImport, Guid("00000016-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 13. public interface IOleMessageFilter 14. { 15. [PreserveSig] 16. int HandleInComingCall(intdwCallType, IntPtrhTaskCaller, intdwTickCount, IntPtrlpInterfaceInfo); 17. 18. 19. [PreserveSig] 20. int RetryRejectedCall(IntPtrhTaskCallee, intdwTickCount, intdwRejectType); 21. 22. 23. [PreserveSig] 24. int MessagePending(IntPtrhTaskCallee, intdwTickCount, intdwPendingType); 25. } 26. 27. 28. public class MessageFilter: IOleMessageFilter 29. { 30. private const int Handled = 0, RetryAllowed = 2, Retry = 99, Cancel = -1, WaitAndDispatch = 2; 31. 32. 33. int IOleMessageFilter.HandleInComingCall(intdwCallType, IntPtrhTaskCaller, intdwTickCount, IntPtrlpInterfaceInfo) 34. { 35. return Handled; 36. } 37. 38. 39. int IOleMessageFilter.RetryRejectedCall(IntPtrhTaskCallee, intdwTickCount, intdwRejectType) 40. { 41. returndwRejectType == RetryAllowed ? Retry : Cancel; 42. } 43. 44. 45. int IOleMessageFilter.MessagePending(IntPtrhTaskCallee, intdwTickCount, intdwPendingType) 46. { 47. returnWaitAndDispatch; 48. } 49. 50. 51. public static void Register() 52. { 53. CoRegisterMessageFilter(newMessageFilter()); 54. } 55. 56. 57. public static void Revoke() 58. { 59. CoRegisterMessageFilter(null); 60. } 61. 62. 63. private static void CoRegisterMessageFilter(IOleMessageFilternewFilter) 64. { 65. IOleMessageFilteroldFilter; 66. CoRegisterMessageFilter(newFilter, out oldFilter); 67. } 68. 69. 70. [DllImport("Ole32.dll")] 71. private static extern intCoRegisterMessageFilter(IOleMessageFilternewFilter, outIOleMessageFilteroldFilter); 72. } 73. 74. 75. public static class AttachDebugger 76. { 77. public static void ToProcess(intprocessId) 78. { 79. MessageFilter.Register(); 80. var process = GetProcess(processId); 81. if (process != null) 82. { 83. process.Attach(); 84. Console.WriteLine("Attached to {0}", process.Name); 85. } 86. MessageFilter.Revoke(); 87. } 88. private static Process GetProcess(intprocessID) 89. { 90. var dte = (DTE) Marshal.GetActiveObject("VisualStudio.DTE.12.0"); 91. var processes = dte.Debugger.LocalProcesses.OfType < Process > (); 92. returnprocesses.SingleOrDefault(x => x.ProcessID == processID); 93. } 94. } 95. }
نکات قابل توجه :
لازم است که کتابخانه EnvDTE را با استفاده از مسیر AddReference ->Extensions به VS اضافه کنیم. همچنین باید ورژن vs شما با vs در حال استفاده از آن هستید باید متفاوت باشد. بعنوان مثال vs که از آن استفاده میکنیم ویژوال استودیو 2015 میباشد و از ویژوال استودیو 2012 برای انجام آن استفاده میکنیم.
حال بعد از اینکه process اجرا شد در کلاس ProcessInvoker یک کلاس برای صدا زدن AttachDebugger utility اضافه میکنیم. زمانیکه میخواهیم تست را برای debugging اجرا کنیم برنامه با سرعت بالایی اجرا خواهد شد. process در ویژوال استودیو فراخوانی و اضافه میشود و توانایی این را دارد که بتواند کدهای process های دیگر را نیز debug کند.
شما میتوانید به menu –> Debug بروید و process که میخواهید به برنامه اضافه شود را انتخاب کنید و از این طریق نیز process موردنظر را به برنامه اضافه کنید.
- VisualStudio
- 2k بازدید
- 3 تشکر