افزودن پردازش(Process) در زمان اجرا برای debug کردن در ویژوال استودیو

جمعه 29 مرداد 1395

در این مقاله قصد داریم شما را با ویژگی در ویژوال استودیو آشنا کنیم که با استفاده از ان میتوانید بطور همزمان سرویس ها و برنامه هایی که در دل یک برنامه بزرگتر قرار دارد را کامپایل و اجرا کنید.

 افزودن پردازش(Process) در زمان اجرا برای debug کردن در ویژوال استودیو

تست های یکپارچه سازی همواره به اجزاء سیستم مانند پایگاه داده ، سرویس ها و ... بستگی دارد. اگر تست یکپارچه سازی به صورت غیر منتظره ای با شکست رو به رو شود ممکن است که نیاز باشد تست مورد نظر را 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 موردنظر را به برنامه اضافه کنید.

برنامه نویسان

نویسنده 3355 مقاله در برنامه نویسان

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید