3a040d71d3febeb373596a2167e038987dd34f7e
[mono.git] / mcs / class / referencesource / System.Workflow.Runtime / DebugEngine / DebugController.cs
1 // Copyright (c) Microsoft Corp., 2004. All rights reserved.
2 #region Using directives
3
4 using System;
5 using System.IO;
6 using System.Xml;
7 using System.Text;
8 using System.Threading;
9 using System.Reflection;
10 using System.Collections;
11 using System.Diagnostics;
12 using System.Runtime.Remoting;
13 using System.Collections.Generic;
14 using System.Collections.ObjectModel;
15 using System.Runtime.Serialization;
16 using System.Workflow.Runtime;
17 using System.Workflow.Runtime.Hosting;
18 using System.Runtime.InteropServices;
19 using System.Runtime.Remoting.Channels;
20 using System.Workflow.ComponentModel;
21 using System.Workflow.ComponentModel.Compiler;
22 using System.Workflow.ComponentModel.Design;
23 using System.Workflow.ComponentModel.Serialization;
24 using System.Runtime.Serialization.Formatters;
25 using System.Runtime.Remoting.Channels.Ipc;
26 using System.Configuration;
27 using System.Security.Permissions;
28 using System.Globalization;
29 using Microsoft.Win32;
30 using System.Security.AccessControl;
31 using System.Security.Principal;
32 #endregion
33
34 namespace System.Workflow.Runtime.DebugEngine
35 {
36     internal static class RegistryKeys
37     {
38         internal static readonly string ProductRootRegKey = @"SOFTWARE\Microsoft\Net Framework Setup\NDP\v3.0\Setup\Windows Workflow Foundation";
39         internal static readonly string DebuggerSubKey = ProductRootRegKey + @"\Debugger";
40     }
41
42     [Obsolete("The System.Workflow.* types are deprecated.  Instead, please use the new types from System.Activities.*")]
43     public sealed class DebugController : MarshalByRefObject
44     {
45         #region Data members
46
47         private Guid programId;
48         private string hostName;
49         private int attachTimeout;
50         private ProgramPublisher programPublisher;
51         private DebugControllerThread debugControllerThread;
52         private Timer attachTimer;
53         private WorkflowRuntime serviceContainer;
54         private IpcChannel channel;
55         private IWorkflowDebugger controllerConduit;
56         private bool isZombie;
57         private bool isAttached;
58         private ManualResetEvent eventConduitAttached;
59         private InstanceTable instanceTable;
60         private Dictionary<Type, Guid> typeToGuid;
61         private Dictionary<byte[], Guid> xomlHashToGuid;
62         bool isServiceContainerStarting;
63         private const string rootExecutorGuid = "98fcdc7a-8ab4-4fb7-92d4-20f437285729";
64         private object eventLock;
65         private object syncRoot = new object();
66         private static readonly string ControllerConduitTypeName = "ControllerConduitTypeName";
67
68         #endregion
69
70         #region Security related methods
71         private delegate void ExceptionNotification(Exception e);
72
73         internal static void InitializeProcessSecurity()
74         {
75             // Spawn off a separate thread to that does RevertToSelf and adjusts DACLs.
76             // This is because RevertToSelf terminates client impersonation on the thread
77             // that calls it. We do not want to change that on the current thread when 
78             // the runtime is hosted inside ASP.net for example.
79             Exception workerThreadException = null;
80             ProcessSecurity processSecurity = new ProcessSecurity();
81             Thread workerThread = new Thread(new ThreadStart(processSecurity.Initialize));
82
83             processSecurity.exceptionNotification += delegate(Exception e)
84             {
85                 workerThreadException = e;
86             };
87
88             workerThread.Start(); workerThread.Join();
89             if (workerThreadException != null)
90                 throw workerThreadException;
91         }
92
93         private class ProcessSecurity
94         {
95             internal ExceptionNotification exceptionNotification;
96
97             internal void Initialize()
98             {
99                 try
100                 {
101                     // This is needed if the thread calling the method is impersonating
102                     // a client call (ASP.net hosting scenarios).
103                     if (!NativeMethods.RevertToSelf())
104                         Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
105
106                     // Get the DACL for process token. Add TOKEN_QUERY permissions for the Administrators group.
107                     // Set the updated DACL for process token.
108                     RawAcl tokenDacl = GetCurrentProcessTokenDacl();
109                     CommonAce adminsGroupAceForToken = new CommonAce(AceFlags.None, AceQualifier.AccessAllowed, NativeMethods.TOKEN_QUERY, new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null), false, null);
110                     int i = FindIndexInDacl(adminsGroupAceForToken, tokenDacl);
111                     if (i != -1)
112                         tokenDacl.InsertAce(i, adminsGroupAceForToken);
113                     SetCurrentProcessTokenDacl(tokenDacl);
114                 }
115                 catch (Exception e)
116                 {
117                     // Communicate any exceptions from this thread back to the thread
118                     // that spawned it.
119                     if (exceptionNotification != null)
120                         exceptionNotification(e);
121                 }
122             }
123
124             private RawAcl GetCurrentProcessTokenDacl()
125             {
126                 IntPtr hProcess = IntPtr.Zero;
127                 IntPtr hProcessToken = IntPtr.Zero;
128                 IntPtr securityDescriptorPtr = IntPtr.Zero;
129
130                 try
131                 {
132                     hProcess = NativeMethods.GetCurrentProcess();
133
134                     if (!NativeMethods.OpenProcessToken(hProcess, NativeMethods.TOKEN_ALL_ACCESS, out hProcessToken))
135                         Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
136
137                     // Get security descriptor associated with the kernel object, read the DACL and return
138                     // that to the caller.
139                     uint returnLength;
140
141                     NativeMethods.GetKernelObjectSecurity(hProcessToken, NativeMethods.SECURITY_INFORMATION.DACL_SECURITY_INFORMATION, IntPtr.Zero, 0, out returnLength);
142                     int lasterror = Marshal.GetLastWin32Error(); //#pragma warning disable 56523 doesnt recognize 56523
143
144                     securityDescriptorPtr = Marshal.AllocCoTaskMem((int)returnLength);
145
146                     if (!NativeMethods.GetKernelObjectSecurity(hProcessToken, NativeMethods.SECURITY_INFORMATION.DACL_SECURITY_INFORMATION, securityDescriptorPtr, returnLength, out returnLength))
147                         Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
148
149                     byte[] sdBytes = new byte[returnLength];
150                     Marshal.Copy(securityDescriptorPtr, sdBytes, 0, (int)returnLength);
151
152                     RawSecurityDescriptor rawSecurityDescriptor = new RawSecurityDescriptor(sdBytes, 0);
153
154                     return rawSecurityDescriptor.DiscretionaryAcl;
155                 }
156                 finally
157                 {
158                     if (hProcess != IntPtr.Zero && hProcess != (IntPtr)(-1))
159                         if (!NativeMethods.CloseHandle(hProcess))
160                             Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
161
162                     if (hProcessToken != IntPtr.Zero)
163                         if (!NativeMethods.CloseHandle(hProcessToken))
164                             Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
165
166                     if (securityDescriptorPtr != IntPtr.Zero)
167                         Marshal.FreeCoTaskMem(securityDescriptorPtr);
168                 }
169             }
170             private void SetCurrentProcessTokenDacl(RawAcl dacl)
171             {
172                 IntPtr hProcess = IntPtr.Zero;
173                 IntPtr hProcessToken = IntPtr.Zero;
174                 IntPtr securityDescriptorPtr = IntPtr.Zero;
175                 try
176                 {
177                     hProcess = NativeMethods.GetCurrentProcess();
178
179                     if (!NativeMethods.OpenProcessToken(hProcess, NativeMethods.TOKEN_ALL_ACCESS, out hProcessToken))
180                         Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
181
182                     // Get security descriptor associated with the kernel object and modify it.
183                     uint returnLength;
184
185                     NativeMethods.GetKernelObjectSecurity(hProcessToken, NativeMethods.SECURITY_INFORMATION.DACL_SECURITY_INFORMATION, IntPtr.Zero, 0, out returnLength);
186                     int lasterror = Marshal.GetLastWin32Error(); //#pragma warning disable 56523 doesnt recognize 56523
187
188                     securityDescriptorPtr = Marshal.AllocCoTaskMem((int)returnLength);
189
190                     if (!NativeMethods.GetKernelObjectSecurity(hProcessToken, NativeMethods.SECURITY_INFORMATION.DACL_SECURITY_INFORMATION, securityDescriptorPtr, returnLength, out returnLength))
191                         Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
192
193                     byte[] sdBytes = new byte[returnLength];
194                     Marshal.Copy(securityDescriptorPtr, sdBytes, 0, (int)returnLength);
195
196                     RawSecurityDescriptor rawSecurityDescriptor = new RawSecurityDescriptor(sdBytes, 0);
197                     rawSecurityDescriptor.DiscretionaryAcl = dacl;
198
199                     sdBytes = new byte[rawSecurityDescriptor.BinaryLength];
200                     rawSecurityDescriptor.GetBinaryForm(sdBytes, 0);
201                     Marshal.FreeCoTaskMem(securityDescriptorPtr);
202                     securityDescriptorPtr = Marshal.AllocCoTaskMem(rawSecurityDescriptor.BinaryLength);
203                     Marshal.Copy(sdBytes, 0, securityDescriptorPtr, rawSecurityDescriptor.BinaryLength);
204
205                     if (!NativeMethods.SetKernelObjectSecurity(hProcessToken, NativeMethods.SECURITY_INFORMATION.DACL_SECURITY_INFORMATION, securityDescriptorPtr))
206                         Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
207                 }
208                 finally
209                 {
210                     if (hProcess != IntPtr.Zero && hProcess != (IntPtr)(-1))
211                         if (!NativeMethods.CloseHandle(hProcess))
212                             Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
213
214                     if (hProcessToken != IntPtr.Zero)
215                         if (!NativeMethods.CloseHandle(hProcessToken))
216                             Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
217
218                     if (securityDescriptorPtr != IntPtr.Zero)
219                         Marshal.FreeCoTaskMem(securityDescriptorPtr);
220
221                 }
222             }
223
224             // The preferred order in which ACEs are added to DACLs is
225             // documented here: http://search.msdn.microsoft.com/search/results.aspx?qu=Order+of+ACEs+in+a+DACL&View=msdn&st=b.
226             // This routine follows that logic to determine the position of an ACE in the DACL.
227             private int FindIndexInDacl(CommonAce newAce, RawAcl dacl)
228             {
229                 int i = 0;
230                 for (i = 0; i < dacl.Count; i++)
231                 {
232                     if (dacl[i] is CommonAce && ((CommonAce)dacl[i]).SecurityIdentifier.Value == newAce.SecurityIdentifier.Value && dacl[i].AceType == newAce.AceType)
233                     {
234                         i = -1;
235                         break;
236                     }
237
238                     if (newAce.AceType == AceType.AccessDenied && dacl[i].AceType == AceType.AccessDenied && !newAce.IsInherited && !dacl[i].IsInherited)
239                         continue;
240
241                     if (newAce.AceType == AceType.AccessDenied && !newAce.IsInherited)
242                         break;
243
244                     if (newAce.AceType == AceType.AccessAllowed && dacl[i].AceType == AceType.AccessAllowed && !newAce.IsInherited && !dacl[i].IsInherited)
245                         continue;
246
247                     if (newAce.AceType == AceType.AccessAllowed && !newAce.IsInherited)
248                         break;
249
250                 }
251
252                 return i;
253             }
254         }
255         #endregion
256
257         #region Constructor and Lifetime methods
258
259         internal DebugController(WorkflowRuntime serviceContainer, string hostName)
260         {
261             if (serviceContainer == null)
262                 throw new ArgumentNullException("serviceContainer");
263
264             try
265             {
266                 this.programPublisher = new ProgramPublisher();
267             }
268             catch
269             {
270                 // If we are unable to create the ProgramPublisher, this means that VS does not exist on this machine, so we can't debug.
271                 return;
272             }
273
274             this.serviceContainer = serviceContainer;
275             this.programId = Guid.Empty;
276             this.controllerConduit = null;
277             this.channel = null;
278             this.isZombie = false;
279             this.hostName = hostName;
280
281             AppDomain.CurrentDomain.ProcessExit += OnDomainUnload;
282             AppDomain.CurrentDomain.DomainUnload += OnDomainUnload;
283
284             this.serviceContainer.Started += this.Start;
285             this.serviceContainer.Stopped += this.Stop;
286         }
287
288         public override object InitializeLifetimeService()
289         {
290             // We can't use a sponser because VS doesn't like to be attached when the lease renews itself - the 
291             // debugee gets an Access Violation and VS freezes. Returning null implies that the proxy shim will be 
292             // deleted only when the App Domain unloads. However, we will have disconnected the shim so no 
293             // one will be able to attach to it and the same proxy is used everytime a debugger attaches.
294             return null;
295         }
296
297         #endregion
298
299         #region Attach and Detach methods
300
301         internal void Attach(Guid programId, int attachTimeout, int detachPingInterval, out string hostName, out string uri, out int controllerThreadId, out bool isSynchronousAttach)
302         {
303             Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "WDE: DebugController.Attach(): programId = {0}", programId));
304             lock (this.syncRoot)
305             {
306                 hostName = String.Empty;
307                 uri = String.Empty;
308                 controllerThreadId = 0;
309                 isSynchronousAttach = false;
310
311                 // Race condition:
312                 // During the call to Attach() if Uninitialize() is also called, we should ignore the call to Attach() and
313                 // just return. The Zombie flag and lock(this) help us recognize the ----.
314                 if (this.isZombie)
315                     return;
316
317
318                 // Race condition:
319                 // The isAttached flat along with lock(this) catch the ---- where a debugger may have detached which
320                 // we haven't detected yet and another debugger may have attached, so we force detach from the first
321                 // debugger.
322                 if (this.isAttached)
323                     Detach();
324
325
326                 this.isAttached = true;
327
328                 this.programId = programId;
329                 this.debugControllerThread = new DebugControllerThread();
330                 this.instanceTable = new InstanceTable(this.debugControllerThread.ManagedThreadId);
331                 this.typeToGuid = new Dictionary<Type, Guid>();
332                 this.xomlHashToGuid = new Dictionary<byte[], Guid>((IEqualityComparer<byte[]>)new DigestComparer());
333
334                 this.debugControllerThread.RunThread(this.instanceTable);
335
336                 // Publish our MBR object.
337                 IDictionary providerProperties = new Hashtable();
338                 providerProperties["typeFilterLevel"] = "Full";
339                 BinaryServerFormatterSinkProvider sinkProvider = new BinaryServerFormatterSinkProvider(providerProperties, null);
340
341                 Hashtable channelProperties = new Hashtable();
342                 channelProperties["name"] = string.Empty;
343                 channelProperties["portName"] = this.programId.ToString();
344                 SecurityIdentifier si = new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null);
345                 IdentityReference idRef = si.Translate(typeof(NTAccount));
346                 channelProperties["authorizedGroup"] = idRef.ToString();
347                 this.channel = new IpcChannel(channelProperties, null, sinkProvider);
348                 ChannelServices.RegisterChannel(this.channel, true);
349
350                 ObjRef o = RemotingServices.Marshal(this, this.programId.ToString());
351                 hostName = this.hostName;
352
353                 uri = this.channel.GetUrlsForUri(this.programId.ToString())[0];
354                 controllerThreadId = this.debugControllerThread.ThreadId;
355                 isSynchronousAttach = !this.isServiceContainerStarting;
356
357                 this.attachTimeout = attachTimeout;
358                 this.attachTimer = new Timer(AttachTimerCallback, null, attachTimeout, detachPingInterval);
359             }
360         }
361
362         private void Detach()
363         {
364             Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "WDE: DebugController.Detach():"));
365
366             using (new DebuggerThreadMarker())
367             {
368                 lock (this.syncRoot)
369                 {
370
371                     AppDomain.CurrentDomain.AssemblyLoad -= OnAssemblyLoad;
372
373                     // See comments in Attach().
374                     if (this.isZombie || !this.isAttached)
375                         return;
376
377                     this.isAttached = false;
378
379                     // Undone: AkashS - At this point wait for all event handling to complete to avoid exceptions.
380
381                     this.programId = Guid.Empty;
382
383                     if (this.debugControllerThread != null)
384                     {
385                         this.debugControllerThread.StopThread();
386                         this.debugControllerThread = null;
387                     }
388
389                     if (this.attachTimer != null)
390                     {
391                         this.attachTimer.Change(Timeout.Infinite, Timeout.Infinite);
392                         this.attachTimer = null;
393                     }
394
395                     RemotingServices.Disconnect(this);
396                     if (this.channel != null)
397                     {
398                         ChannelServices.UnregisterChannel(this.channel);
399                         this.channel = null;
400                     }
401
402                     this.controllerConduit = null;
403
404                     this.eventConduitAttached.Reset();
405                     this.instanceTable = null;
406                     this.typeToGuid = null;
407                     this.xomlHashToGuid = null;
408
409                     // Do this only after we perform the previous cleanup! Otherwise
410                     // we may get exceptions from the runtime that may cause the cleanup
411                     // to not happen.
412
413                     if (!this.serviceContainer.IsZombie)
414                     {
415                         foreach (WorkflowInstance instance in this.serviceContainer.GetLoadedWorkflows())
416                         {
417                             WorkflowExecutor executor = instance.GetWorkflowResourceUNSAFE();
418                             using (executor.ExecutorLock.Enter())
419                             {
420                                 if (executor.IsInstanceValid)
421                                     executor.WorkflowExecutionEvent -= OnInstanceEvent;
422                             }
423                         }
424
425                         this.serviceContainer.WorkflowExecutorInitializing -= InstanceInitializing;
426                         this.serviceContainer.DefinitionDispenser.WorkflowDefinitionLoaded -= ScheduleTypeLoaded;
427                     }
428                 }
429             }
430         }
431
432         private void AttachTimerCallback(object state)
433         {
434             Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "WDE: DebugController.AttachTimerCallback():"));
435
436             try
437             {
438                 lock (this.syncRoot)
439                 {
440                     // See comments in Attach().
441                     if (this.isZombie || !this.isAttached)
442                         return;
443
444                     if (!Debugger.IsAttached)
445                     {
446                         // The debugger had attached and has now detached, so cleanup, or Attach() was called on the 
447                         // Program Node, but the process of attach failed thereafter and so we were never actually 
448                         // debugged.
449                         this.attachTimer.Change(Timeout.Infinite, Timeout.Infinite);
450                         Detach();
451                     }
452                 }
453             }
454             catch
455             {
456                 // Avoid throwing unhandled exceptions!
457             }
458         }
459
460         private void OnDomainUnload(object sender, System.EventArgs e)
461         {
462             Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "WDE: DebugController.OnDomainUnload():"));
463
464             Stop(null, default(WorkflowRuntimeEventArgs));
465         }
466
467         #endregion
468
469         #region Methods for the DE
470
471         public void AttachToConduit(Uri url)
472         {
473             if (url == null)
474                 throw new ArgumentNullException("url");
475
476             try
477             {
478                 using (new DebuggerThreadMarker())
479                 {
480                     try
481                     {
482                         RegistryKey debugEngineSubKey = Registry.LocalMachine.OpenSubKey(RegistryKeys.DebuggerSubKey);
483                         if (debugEngineSubKey != null)
484                         {
485                             string controllerConduitTypeName = debugEngineSubKey.GetValue(ControllerConduitTypeName, String.Empty) as string;
486                             if (!String.IsNullOrEmpty(controllerConduitTypeName) && Type.GetType(controllerConduitTypeName) != null)
487                                 this.controllerConduit = Activator.GetObject(Type.GetType(controllerConduitTypeName), url.AbsolutePath) as IWorkflowDebugger;
488                         }
489                     }
490                     catch { }
491
492                     if (this.controllerConduit == null)
493                     {
494                         const string controllerConduitTypeFormat = "Microsoft.Workflow.DebugEngine.ControllerConduit, Microsoft.Workflow.DebugController, Version={0}.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35";
495
496                         // Try versions 12.0.0.0, 11.0.0.0, 10.0.0.0
497                         Type controllerConduitType = null;
498                         for (int version = 12; controllerConduitType == null && version >= 10; --version)
499                         {
500                             try
501                             {
502                                 controllerConduitType = Type.GetType(string.Format(CultureInfo.InvariantCulture, controllerConduitTypeFormat, version));
503                             }
504                             catch (TypeLoadException)
505                             {
506                                 // Fall back to next-lower version
507                             }
508                         }
509
510                         if (controllerConduitType != null)
511                         {
512                             this.controllerConduit = Activator.GetObject(controllerConduitType, url.AbsolutePath) as IWorkflowDebugger;
513                         }
514                     }
515                     Debug.Assert(this.controllerConduit != null, "Failed to create Controller Conduit");
516                     if (this.controllerConduit == null)
517                         return;
518
519                     this.eventLock = new object();
520
521                     // Race Condition:
522                     // We hook up to the AssemblyLoad event, the Schedule events and Instance events handler 
523                     // before we iterate over all loaded assemblies. This means that we need to deal with duplicates in the 
524                     // debugee.
525
526                     // Race Condition:
527                     // Further the order in which we hook up handlers/iterate is important to avoid ----s if the events fire
528                     // before the iterations complete. We need to hook and iterate over the assemblies, then the schedule 
529                     // types and finally the instances. This guarantees that we always have all the assemblies when we load
530                     // schedules types and we always have all the schedule types when we load instances.
531
532                     AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoad;
533                     foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
534                     {
535                         if (!assembly.IsDynamic
536                             && !(assembly is System.Reflection.Emit.AssemblyBuilder)
537                             && !(string.IsNullOrEmpty(assembly.Location)))
538                         {
539                             this.controllerConduit.AssemblyLoaded(this.programId, assembly.Location, assembly.GlobalAssemblyCache);
540                         }
541                     }
542                     this.serviceContainer.DefinitionDispenser.WorkflowDefinitionLoaded += ScheduleTypeLoaded;
543
544                     // In here we load all schedule types defined as they are - with no dynamic updates
545                     ReadOnlyCollection<Type> types;
546                     ReadOnlyCollection<Activity> values;
547                     this.serviceContainer.DefinitionDispenser.GetWorkflowTypes(out types, out values);
548                     for (int i = 0; i < types.Count; i++)
549                     {
550                         Type scheduleType = types[i];
551                         Activity rootActivity = values[i];
552                         LoadExistingScheduleType(GetScheduleTypeId(scheduleType), scheduleType, false, rootActivity);
553                     }
554
555                     ReadOnlyCollection<byte[]> keys;
556                     this.serviceContainer.DefinitionDispenser.GetWorkflowDefinitions(out keys, out values);
557                     for (int i = 0; i < keys.Count; i++)
558                     {
559                         byte[] scheduleDefHash = keys[i];
560                         Activity rootActivity = values[i];
561                         Activity workflowDefinition = (Activity)rootActivity.GetValue(Activity.WorkflowDefinitionProperty);
562                         ArrayList changeActions = null;
563                         if (workflowDefinition != null)
564                             changeActions = (ArrayList)workflowDefinition.GetValue(WorkflowChanges.WorkflowChangeActionsProperty);
565                         LoadExistingScheduleType(GetScheduleTypeId(scheduleDefHash), rootActivity.GetType(), (changeActions != null && changeActions.Count != 0), rootActivity);
566                     }
567
568                     this.serviceContainer.WorkflowExecutorInitializing += InstanceInitializing;
569
570                     foreach (WorkflowInstance instance in this.serviceContainer.GetLoadedWorkflows())
571                     {
572                         WorkflowExecutor executor = instance.GetWorkflowResourceUNSAFE();
573                         using (executor.ExecutorLock.Enter())
574                         {
575                             LoadExistingInstance(instance, true);
576                         }
577                     }
578
579                     this.eventConduitAttached.Set();
580                 }
581             }
582             catch (Exception e)
583             {
584                 // Don't throw exceptions to the Runtime. Ignore exceptions that may occur if the debugger detaches 
585                 // and closes the remoting channel.
586                 Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "WDE: Failure in DebugController.AttachToConduit: {0}, Call stack:{1}", e.Message, e.StackTrace));
587             }
588
589         }
590
591         #endregion
592
593         #region Methods for the Runtime
594
595         private void Start(object source, WorkflowRuntimeEventArgs e)
596         {
597             Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "WDE: DebugController.ServiceContainerStarted():"));
598
599             this.isZombie = false;
600             this.isAttached = false;
601             this.eventConduitAttached = new ManualResetEvent(false);
602             this.isServiceContainerStarting = true;
603
604             bool published = this.programPublisher.Publish(this);
605
606             // If the debugger is already attached, then the DE will invoke AttachToConduit() on a separate thread.
607             // We need to wait for that to happen to prevent new instances being created and causing a ----. See
608             // comments in ControllerConduit.ProgramCreated(). However, if the DE never calls AttachToConduit(),
609             // and the detaches instead, we set a wait timeout to that of our Attach Timer.
610             // Note that when we publish the program node, if the debugger is attached, isAttached will be set to true
611             // when the debugger calls Attach() on the Program Node!
612
613             while (published && this.isAttached && !this.eventConduitAttached.WaitOne(attachTimeout, false));
614             this.isServiceContainerStarting = false;
615         }
616
617         private void Stop(object source, WorkflowRuntimeEventArgs e)
618         {
619             Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "WDE: DebugController.ServiceContainerStopped():"));
620
621             try
622             {
623                 lock (this.syncRoot)
624                 {
625                     Detach();
626                     this.programPublisher.Unpublish();
627
628                     // See comments in Attach().
629                     this.isZombie = true;
630                 }
631             }
632             catch
633             {
634                 // Do not throw exceptions back!
635             }
636         }
637
638         internal void Close()
639         {
640             //Unregister from Appdomain event to remove ourselves from GCRoot.
641             AppDomain.CurrentDomain.ProcessExit -= OnDomainUnload;
642             AppDomain.CurrentDomain.DomainUnload -= OnDomainUnload;
643
644             if (!this.isZombie)
645             {
646                 Stop(null, new WorkflowRuntimeEventArgs(false));
647             }
648         }
649
650
651         private void OnInstanceEvent(object sender, WorkflowExecutor.WorkflowExecutionEventArgs e)
652         {
653             switch (e.EventType)
654             {
655                 case WorkflowEventInternal.Completed:
656                     InstanceCompleted(sender, new WorkflowEventArgs(((WorkflowExecutor)sender).WorkflowInstance));
657                     break;
658                 case WorkflowEventInternal.Terminated:
659                     InstanceTerminated(sender, new WorkflowEventArgs(((WorkflowExecutor)sender).WorkflowInstance));
660                     break;
661                 case WorkflowEventInternal.Unloaded:
662                     InstanceUnloaded(sender, new WorkflowEventArgs(((WorkflowExecutor)sender).WorkflowInstance));
663                     break;
664                 case WorkflowEventInternal.Changed:
665                     OnWorkflowChanged(sender, e);
666                     break;
667                 case WorkflowEventInternal.HandlerInvoking:
668                     OnHandlerInvoking(sender, e);
669                     break;
670                 case WorkflowEventInternal.HandlerInvoked:
671                     OnHandlerInvoked(sender, e);
672                     break;
673                 case WorkflowEventInternal.ActivityStatusChange:
674                     OnActivityStatusChanged(sender, (WorkflowExecutor.ActivityStatusChangeEventArgs)e);
675                     break;
676                 case WorkflowEventInternal.ActivityExecuting:
677                     OnActivityExecuting(sender, (WorkflowExecutor.ActivityExecutingEventArgs)e);
678                     break;
679                 default:
680                     return;
681             }
682         }
683
684
685         private void InstanceInitializing(object sender, WorkflowRuntime.WorkflowExecutorInitializingEventArgs e)
686         {
687             try
688             {
689                 if (e.Loading)
690                     LoadExistingInstance(((WorkflowExecutor)sender).WorkflowInstance, true);
691                 else
692                     LoadExistingInstance(((WorkflowExecutor)sender).WorkflowInstance, false);
693             }
694             catch
695             {
696                 // Don't throw exceptions to the Runtime. Ignore exceptions that may occur if the debugger detaches 
697                 // and closes the remoting channel.
698             }
699         }
700
701         private void InstanceCompleted(object sender, WorkflowEventArgs args)
702         {
703             try
704             {
705                 UnloadExistingInstance(args.WorkflowInstance);
706             }
707             catch
708             {
709                 // Don't throw exceptions to the Runtime. Ignore exceptions that may occur if the debugger detaches 
710                 // and closes the remoting channel.
711             }
712         }
713
714         private void InstanceTerminated(object sender, WorkflowEventArgs args)
715         {
716             try
717             {
718                 UnloadExistingInstance(args.WorkflowInstance);
719             }
720             catch
721             {
722                 // Don't throw exceptions to the Runtime. Ignore exceptions that may occur if the debugger detaches 
723                 // and closes the remoting channel.
724             }
725         }
726
727         private void InstanceUnloaded(object sender, WorkflowEventArgs args)
728         {
729             // Treat this as if the instance completed so that it won't show up in the UI anymore.
730             try
731             {
732                 UnloadExistingInstance(args.WorkflowInstance);
733             }
734             catch
735             {
736                 // Don't throw exceptions to the Runtime. Ignore exceptions that may occur if the debugger detaches 
737                 // and closes the remoting channel.
738             }
739         }
740
741         private void ScheduleTypeLoaded(object sender, WorkflowDefinitionEventArgs args)
742         {
743             try
744             {
745                 if (args.WorkflowType != null)
746                 {
747                     Activity rootActivity = ((WorkflowRuntime)sender).DefinitionDispenser.GetWorkflowDefinition(args.WorkflowType);
748                     LoadExistingScheduleType(GetScheduleTypeId(args.WorkflowType), args.WorkflowType, false, rootActivity);
749                 }
750                 else
751                 {
752                     Activity rootActivity = ((WorkflowRuntime)sender).DefinitionDispenser.GetWorkflowDefinition(args.WorkflowDefinitionHashCode);
753                     LoadExistingScheduleType(GetScheduleTypeId(args.WorkflowDefinitionHashCode), rootActivity.GetType(), false, rootActivity);
754                 }
755             }
756             catch
757             {
758                 // Don't throw exceptions to the Runtime. Ignore exceptions that may occur if the debugger detaches 
759                 // and closes the remoting channel.
760             }
761         }
762
763         private void OnActivityExecuting(object sender, WorkflowExecutor.ActivityExecutingEventArgs eventArgs)
764         {
765             if (this.isZombie || !this.isAttached)
766                 return;
767
768             try
769             {
770                 lock (this.eventLock)
771                 {
772                     IWorkflowCoreRuntime workflowCoreRuntime = (IWorkflowCoreRuntime)sender;
773                     Guid scheduleTypeId = GetScheduleTypeId(workflowCoreRuntime);
774
775                     // When the activity starts executing, update its handler list for stepping.
776                     EnumerateEventHandlersForActivity(scheduleTypeId, eventArgs.Activity);
777                     this.controllerConduit.BeforeActivityStatusChanged(this.programId, scheduleTypeId, workflowCoreRuntime.InstanceID, eventArgs.Activity.QualifiedName, GetHierarchicalId(eventArgs.Activity), eventArgs.Activity.ExecutionStatus, GetContextId(eventArgs.Activity));
778                     this.controllerConduit.ActivityStatusChanged(this.programId, scheduleTypeId, workflowCoreRuntime.InstanceID, eventArgs.Activity.QualifiedName, GetHierarchicalId(eventArgs.Activity), eventArgs.Activity.ExecutionStatus, GetContextId(eventArgs.Activity));
779                 }
780             }
781             catch
782             {
783                 // Don't throw exceptions to the Runtime. Ignore exceptions that may occur if the debugger detaches 
784                 // and closes the remoting channel.
785             }
786         }
787
788         private void OnActivityStatusChanged(object sender, WorkflowExecutor.ActivityStatusChangeEventArgs eventArgs)
789         {
790             if (this.isZombie || !this.isAttached)
791                 return;
792
793             try
794             {
795                 lock (this.eventLock)
796                 {
797                     // We will receive an event when Activity.Execute() is about to be called.
798                     if (eventArgs.Activity.ExecutionStatus == ActivityExecutionStatus.Executing)
799                         return;
800
801                     IWorkflowCoreRuntime workflowCoreRuntime = (IWorkflowCoreRuntime)sender;
802                     Guid scheduleTypeId = GetScheduleTypeId(workflowCoreRuntime);
803
804                     // When the activity starts executing, update its handler list for stepping.
805                     if (eventArgs.Activity.ExecutionStatus == ActivityExecutionStatus.Executing)
806                         EnumerateEventHandlersForActivity(scheduleTypeId, eventArgs.Activity);
807
808                     this.controllerConduit.BeforeActivityStatusChanged(this.programId, scheduleTypeId, workflowCoreRuntime.InstanceID, eventArgs.Activity.QualifiedName, GetHierarchicalId(eventArgs.Activity), eventArgs.Activity.ExecutionStatus, GetContextId(eventArgs.Activity));
809                     this.controllerConduit.ActivityStatusChanged(this.programId, scheduleTypeId, workflowCoreRuntime.InstanceID, eventArgs.Activity.QualifiedName, GetHierarchicalId(eventArgs.Activity), eventArgs.Activity.ExecutionStatus, GetContextId(eventArgs.Activity));
810                 }
811             }
812             catch
813             {
814                 // Don't throw exceptions to the Runtime. Ignore exceptions that may occur if the debugger detaches 
815                 // and closes the remoting channel.
816             }
817         }
818
819         private void OnHandlerInvoking(object sender, EventArgs eventArgs)
820         {
821             // Undone: AkashS - We need to remove EnumerateHandlersForActivity() and set the CPDE
822             // breakpoints from here. This is handle the cases where event handlers are modified
823             // at runtime.
824         }
825
826         private void OnHandlerInvoked(object sender, EventArgs eventArgs)
827         {
828             if (this.isZombie || !this.isAttached)
829                 return;
830
831             try
832             {
833                 lock (this.eventLock)
834                 {
835                     IWorkflowCoreRuntime workflowCoreRuntime = sender as IWorkflowCoreRuntime;
836                     this.controllerConduit.HandlerInvoked(this.programId, workflowCoreRuntime.InstanceID, NativeMethods.GetCurrentThreadId(), GetHierarchicalId(workflowCoreRuntime.CurrentActivity));
837                 }
838             }
839             catch
840             {
841                 // Don't throw exceptions to the Runtime. Ignore exceptions that may occur if the debugger detaches 
842                 // and closes the remoting channel.
843             }
844         }
845
846         private void OnWorkflowChanged(object sender, EventArgs eventArgs)
847         {
848             if (this.isZombie || !this.isAttached)
849                 return;
850
851             try
852             {
853                 lock (this.eventLock)
854                 {
855                     IWorkflowCoreRuntime workflowCoreRuntime = (IWorkflowCoreRuntime)sender;
856
857                     // Get cached old root activity.
858                     Activity oldRootActivity = this.instanceTable.GetRootActivity(workflowCoreRuntime.InstanceID);
859
860                     Guid scheduleTypeId = workflowCoreRuntime.InstanceID; // From now on we will treat the instance id as a dynamic schedule type id.
861                     LoadExistingScheduleType(scheduleTypeId, oldRootActivity.GetType(), true, oldRootActivity);
862
863                     // And now reload the instance.
864                     this.instanceTable.UpdateRootActivity(workflowCoreRuntime.InstanceID, oldRootActivity);
865
866                     // The DE will update the schedule type on the thread that is running the instance.
867                     // DE should be called after the instance table entry is replaced.
868                     this.controllerConduit.InstanceDynamicallyUpdated(this.programId, workflowCoreRuntime.InstanceID, scheduleTypeId);
869                 }
870             }
871             catch
872             {
873                 // Don't throw exceptions to the Runtime. Ignore exceptions that may occur if the debugger detaches 
874                 // and closes the remoting channel.
875             }
876         }
877
878         #endregion
879
880         #region Helper methods and properties
881
882         // Callers of this method should acquire the executor lock only if they 
883         // are not being called in the runtime thread.(
884         private void LoadExistingInstance(WorkflowInstance instance, bool attaching)
885         {
886             WorkflowExecutor executor = instance.GetWorkflowResourceUNSAFE();
887             if (!executor.IsInstanceValid)
888                 return;
889             IWorkflowCoreRuntime runtimeService = (IWorkflowCoreRuntime)executor;
890             Activity rootActivity = runtimeService.RootActivity;
891             Guid scheduleTypeId = GetScheduleTypeId(runtimeService);
892
893             // If we are just attaching, need to LoadExistingScheduleType with the dynamic definition and type
894             // since the OnDynamicUpdateEvent has never been executed.
895             if (attaching && runtimeService.IsDynamicallyUpdated)
896                 LoadExistingScheduleType(scheduleTypeId, rootActivity.GetType(), true, rootActivity);
897
898             // Add to the InstanceTable before firing the DE event !
899             this.instanceTable.AddInstance(instance.InstanceId, rootActivity);
900
901             this.controllerConduit.InstanceCreated(this.programId, instance.InstanceId, scheduleTypeId);
902
903             // Take a lock so that SetInitialActivityStatus is always called before next status events.
904             lock (this.eventLock)
905             {
906                 executor.WorkflowExecutionEvent += OnInstanceEvent;
907                 foreach (Activity activity in DebugController.WalkActivityTree(rootActivity))
908                 {
909 #if false
910                     //
911                     ReplicatorActivity replicator = activity as ReplicatorActivity;
912                     if (replicator != null)
913                     {
914                         foreach (Activity queuedChildActivity in replicator.DynamicActivities)
915                             activityQueue.Enqueue(queuedChildActivity);
916                     }
917                     else
918 #endif
919                     UpdateActivityStatus(scheduleTypeId, instance.InstanceId, activity);
920
921                 }
922
923                 ActivityExecutionContext rootExecutionContext = new ActivityExecutionContext(rootActivity);
924                 foreach (ActivityExecutionContext executionContext in DebugController.WalkExecutionContextTree(rootExecutionContext))
925                 {
926                     Activity instanceActivity = executionContext.Activity;
927                     foreach (Activity childInstance in DebugController.WalkActivityTree(instanceActivity))
928                     {
929                         UpdateActivityStatus(scheduleTypeId, instance.InstanceId, childInstance);
930                     }
931                 }
932             }
933         }
934
935         private void UpdateActivityStatus(Guid scheduleTypeId, Guid instanceId, Activity activity)
936         {
937             if (activity == null)
938                 throw new ArgumentNullException("activity");
939
940             // first update its handler list
941             if (activity.ExecutionStatus == ActivityExecutionStatus.Executing)
942                 EnumerateEventHandlersForActivity(scheduleTypeId, activity);
943
944             //report only states different from the initialized
945             if (activity.ExecutionStatus != ActivityExecutionStatus.Initialized)
946             {
947                 Activity contextActivity = ContextActivityUtils.ContextActivity(activity);
948                 int context = ContextActivityUtils.ContextId(contextActivity);
949                 this.controllerConduit.SetInitialActivityStatus(this.programId, scheduleTypeId, instanceId, activity.QualifiedName, GetHierarchicalId(activity), activity.ExecutionStatus, context);
950             }
951
952         }
953
954         private static IEnumerable WalkActivityTree(Activity rootActivity)
955         {
956             if (rootActivity == null || !rootActivity.Enabled)
957                 yield break;
958
959             // Return self
960             yield return rootActivity;
961
962             // Go through the children as well
963             if (rootActivity is CompositeActivity)
964             {
965                 foreach (Activity childActivity in ((CompositeActivity)rootActivity).Activities)
966                 {
967                     foreach (Activity nestedChild in WalkActivityTree(childActivity))
968                         yield return nestedChild;
969                 }
970             }
971         }
972
973         private static IEnumerable WalkExecutionContextTree(ActivityExecutionContext rootContext)
974         {
975             if (rootContext == null)
976                 yield break;
977
978             yield return rootContext;
979
980             foreach (ActivityExecutionContext executionContext in rootContext.ExecutionContextManager.ExecutionContexts)
981             {
982                 foreach (ActivityExecutionContext nestedContext in WalkExecutionContextTree(executionContext))
983                     yield return nestedContext;
984             }
985             yield break;
986         }
987
988         private void UnloadExistingInstance(WorkflowInstance instance)
989         {
990             // Fire DE event before removing from the InstanceTable!
991             this.controllerConduit.InstanceCompleted(this.programId, instance.InstanceId);
992             this.instanceTable.RemoveInstance(instance.InstanceId);
993         }
994
995         private void LoadExistingScheduleType(Guid scheduleTypeId, Type scheduleType, bool isDynamic, Activity rootActivity)
996         {
997             if (rootActivity == null)
998                 throw new InvalidOperationException();
999
1000             using (StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture))
1001             {
1002                 using (XmlWriter xmlWriter = Helpers.CreateXmlWriter(stringWriter))
1003                 {
1004                     WorkflowMarkupSerializer serializer = new WorkflowMarkupSerializer();
1005                     serializer.Serialize(xmlWriter, rootActivity);
1006                     string fileName = null;
1007                     string md5Digest = null;
1008                     Attribute[] attributes = scheduleType.GetCustomAttributes(typeof(WorkflowMarkupSourceAttribute), false) as Attribute[];
1009                     if (attributes != null && attributes.Length == 1)
1010                     {
1011                         fileName = ((WorkflowMarkupSourceAttribute)attributes[0]).FileName;
1012                         md5Digest = ((WorkflowMarkupSourceAttribute)attributes[0]).MD5Digest;
1013                     }
1014
1015                     this.controllerConduit.ScheduleTypeLoaded(this.programId, scheduleTypeId, scheduleType.Assembly.FullName, fileName, md5Digest, isDynamic, scheduleType.FullName, scheduleType.Name, stringWriter.ToString());
1016                 }
1017             }
1018         }
1019
1020         private string GetHierarchicalId(Activity activity)
1021         {
1022             string id = string.Empty;
1023             while (activity != null)
1024             {
1025                 string iterationId = string.Empty;
1026
1027                 Activity contextActivity = ContextActivityUtils.ContextActivity(activity);
1028                 int context = ContextActivityUtils.ContextId(contextActivity);
1029                 iterationId = activity.Name + ((context > 1 && activity == contextActivity) ? "(" + context + ")" : string.Empty);
1030
1031                 id = (id.Length > 0) ? iterationId + "." + id : iterationId;
1032
1033                 activity = activity.Parent;
1034             }
1035
1036             return id;
1037         }
1038
1039         private int GetContextId(Activity activity)
1040         {
1041             Activity contextActivity = ContextActivityUtils.ContextActivity(activity);
1042             return ContextActivityUtils.ContextId(contextActivity);
1043         }
1044
1045         private Guid GetScheduleTypeId(IWorkflowCoreRuntime workflowCoreRuntime)
1046         {
1047             Activity rootActivity = workflowCoreRuntime.RootActivity;
1048
1049             if (workflowCoreRuntime.IsDynamicallyUpdated)
1050                 return workflowCoreRuntime.InstanceID;
1051             else if (string.IsNullOrEmpty(rootActivity.GetValue(Activity.WorkflowXamlMarkupProperty) as string))
1052                 return GetScheduleTypeId(rootActivity.GetType());
1053             else
1054                 return GetScheduleTypeId(rootActivity.GetValue(WorkflowDefinitionDispenser.WorkflowDefinitionHashCodeProperty) as byte[]);
1055         }
1056
1057         private Guid GetScheduleTypeId(Type scheduleType)
1058         {
1059             // We cannot use the GUID from the type because that is not guaranteed to be unique, especially when
1060             // multiple versions are loaded and the stamps a GuidAttribute.
1061             lock (this.typeToGuid)
1062             {
1063                 if (!this.typeToGuid.ContainsKey(scheduleType))
1064                     this.typeToGuid[scheduleType] = Guid.NewGuid();
1065
1066                 return (Guid)this.typeToGuid[scheduleType];
1067             }
1068         }
1069
1070         private Guid GetScheduleTypeId(byte[] scheduleDefHashCode)
1071         {
1072             // We use the same hashtable to store schedule definition to Guid mapping.
1073             lock (this.xomlHashToGuid)
1074             {
1075                 if (!this.xomlHashToGuid.ContainsKey(scheduleDefHashCode))
1076                     this.xomlHashToGuid[scheduleDefHashCode] = Guid.NewGuid();
1077
1078                 return (Guid)this.xomlHashToGuid[scheduleDefHashCode];
1079             }
1080         }
1081
1082         private void OnAssemblyLoad(object sender, AssemblyLoadEventArgs args)
1083         {
1084             // Call assembly load on the conduit for assemblies loaded from disk.
1085             if (args.LoadedAssembly.Location != String.Empty)
1086             {
1087                 try
1088                 {
1089                     this.controllerConduit.AssemblyLoaded(this.programId, args.LoadedAssembly.Location, args.LoadedAssembly.GlobalAssemblyCache);
1090                 }
1091                 catch
1092                 {
1093                     // Don't throw exceptions to the CLR. Ignore exceptions that may occur if the debugger detaches 
1094                     // and closes the remoting channel.
1095                 }
1096             }
1097         }
1098
1099         private void EnumerateEventHandlersForActivity(Guid scheduleTypeId, Activity activity)
1100         {
1101             List<ActivityHandlerDescriptor> handlerMethods = new List<ActivityHandlerDescriptor>();
1102             MethodInfo getInvocationListMethod = activity.GetType().GetMethod("System.Workflow.ComponentModel.IDependencyObjectAccessor.GetInvocationList", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
1103
1104             foreach (EventInfo eventInfo in activity.GetType().GetEvents(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy))
1105             {
1106                 DependencyProperty dependencyEvent = DependencyProperty.FromName(eventInfo.Name, activity.GetType());
1107
1108                 if (dependencyEvent != null)
1109                 {
1110                     try
1111                     {
1112                         MethodInfo boundGetInvocationListMethod = getInvocationListMethod.MakeGenericMethod(new Type[] { dependencyEvent.PropertyType });
1113                         foreach (Delegate handler in (boundGetInvocationListMethod.Invoke(activity, new object[] { dependencyEvent }) as Delegate[]))
1114                         {
1115                             MethodInfo handlerMethodInfo = handler.Method;
1116                             ActivityHandlerDescriptor handlerMethod;
1117                             handlerMethod.Name = handlerMethodInfo.DeclaringType.FullName + "." + handlerMethodInfo.Name;
1118                             handlerMethod.Token = handlerMethodInfo.MetadataToken;
1119                             handlerMethods.Add(handlerMethod);
1120                         }
1121                     }
1122                     catch
1123                     {
1124                     }
1125                 }
1126             }
1127
1128             this.controllerConduit.UpdateHandlerMethodsForActivity(this.programId, scheduleTypeId, activity.QualifiedName, handlerMethods);
1129         }
1130
1131         #endregion
1132     }
1133 }