/// <summary> /// Starts processing the given activity via the WorkflowApplication. /// </summary> /// <param name="command">The command to be ran.</param> /// <param name="firstParameter">The first parameter to include (if any).</param> /// <param name="secondParameter">The second parameter to include (if any).</param> /// <param name="thirdParameter">The third parameter to include (if any).</param> private void StartWorkflowScience(string command, string firstParameter, string secondParameter, string thirdParameter /* future use */) { // Now stage the WorkflowApplication, using the SQL instance, wrapped in using to dispose all of the things when done. using (AutoResetEvent syncEvent = new AutoResetEvent(false)) { // NOTE: If the string doesn't - explicitly - match, the .ctor() of the // SwedishCodeActivity will throw, since the string is the key. Also, // no boxing/unboxing required for Dictionary<T,T>; which saves overhead. Dictionary <string, SwedishCodeActivity <object> > newDictionary = new Dictionary <string, SwedishCodeActivity <object> > { // See the 'Activities' folder for examples of Activities that you can use here. { "CheckFreeDiskSpaceActivity", new CheckFreeDiskSpaceActivity() }, { "CopyFilesActivity", new CopyFilesActivity() }, { "CopyNIsAndDLLsActivity", new CopyImagesAndLibrariesActivity() }, { "DateTimeActivity", new UtcDateTimeActivity() }, { "DumpProcessThreadsActivity", new DumpProcessThreadsActivity() }, { "FileExistsActivity", new CheckIfFileExistsActivity() }, { "ForceBlueScreenActivity", new ForceBlueScreenActivity() }, { "GetLoggedOnUsersActivity", new GetCurrentLoggedOnUsersActivity() }, { "GetOsFileVersionActivity", new GetOsFileVersionActivity() }, { "GetProcessIdActivity", new GetProcessIdsActivity() }, { "GetSystemUptimeActivity", new GetSystemUptimeActivity() }, { "GetTimeSkewActivity", new GetSystemTimeSkewActivity() }, { "InstallSysInternalsActivity", new InstallSysInternalsActivity() }, { "ModifyConfigurationFileActivity", new EditConfigurationFileActivity() }, { "PingActivity", new PingResponseActivity() }, { "PortConnectivityActivity", new PortConnectivityActivity() }, { "ReadFileContentsActivity", new ReadFileContentsActivity() }, { "RenameMachineActivity", new RenameMachineActivity() }, { "RestartServiceActivity", new RestartServiceActivity() }, { "SetSymbolsEnvironmentPath", new SetSymbolServerEnvironmentPathActivity() }, { "StartSecureDeleteActivity", new StartSecureDeleteActivity() }, { "WindowsUpdateActivity", new WindowsUpdateActivity() }, { "WebStringActivity", new WebStringActivity() } }; SwedishCodeActivity <object> newSwedishCodeActivity = (SwedishCodeActivity <object>)newDictionary[command]; if (!string.IsNullOrWhiteSpace(firstParameter)) { newSwedishCodeActivity.FirstInArgument = firstParameter; } if (!string.IsNullOrWhiteSpace(secondParameter)) { newSwedishCodeActivity.SecondInArgument = secondParameter; } if (!string.IsNullOrWhiteSpace(thirdParameter)) { newSwedishCodeActivity.ThirdInArgument = thirdParameter; } SqlWorkflowInstanceStore newSqlWorkflowInstanceStore = new SqlWorkflowInstanceStore("Server=192.168.0.252,1433\\SQL2008EXPRESS;Initial Catalog=WorkflowInstanceStore;Integrated Security=SSPI") { HostLockRenewalPeriod = TimeSpan.FromSeconds(1), InstanceCompletionAction = InstanceCompletionAction.DeleteNothing, InstanceLockedExceptionAction = InstanceLockedExceptionAction.AggressiveRetry, RunnableInstancesDetectionPeriod = TimeSpan.FromSeconds(1) // Minimum allowed value. }; InstanceHandle workflowInstanceStoreHandle = newSqlWorkflowInstanceStore.CreateInstanceHandle(); CreateWorkflowOwnerCommand createWorkflowOwnerCommand = new CreateWorkflowOwnerCommand(); InstanceView newInstanceView = newSqlWorkflowInstanceStore.Execute(workflowInstanceStoreHandle, createWorkflowOwnerCommand, TimeSpan.FromSeconds(30)); newSqlWorkflowInstanceStore.DefaultInstanceOwner = newInstanceView.InstanceOwner; WorkflowApplication newWorkflowApplication = new WorkflowApplication(newSwedishCodeActivity, new WorkflowIdentity { // The Dictionary will throw for non-found key before we ever get here, so no need to validate input. Name = command, Version = new Version(0, 1, 0, 0) }) { InstanceStore = newSqlWorkflowInstanceStore, SynchronizationContext = SynchronizationContext.Current }; newWorkflowApplication.Persist(); ResultObject = new object(); newWorkflowApplication.Completed += delegate(WorkflowApplicationCompletedEventArgs e) { if (e.CompletionState == ActivityInstanceState.Faulted) { EventLog.WriteEntry("Felsökning.Tomte.AdminService", $"Workflow {e.InstanceId} has faulted.\nException: {e.TerminationException.GetType().FullName}\nMessage:{e.TerminationException.Message}"); syncEvent.Set(); } else if (e.CompletionState == ActivityInstanceState.Canceled) { EventLog.WriteEntry("Felsökning.Tomte.AdminService", $"Workflow {e.InstanceId} has been canceled."); syncEvent.Set(); } else { // Since the result can be *anything*, let's not treat it like a string. EventLog.WriteEntry("Felsökning.Tomte.AdminService", $"Workflow {e.InstanceId} completed. Result: {e.Outputs["Result"]}"); ResultObject = e.Outputs["Result"]; syncEvent.Set(); } }; newWorkflowApplication.Aborted = delegate(WorkflowApplicationAbortedEventArgs e) { // The workflow aborted, so let's find out why. EventLog.WriteEntry("Felsökning.Tomte.AdminService", $"Workflow {e.InstanceId} has been aborted.\nException: {e.Reason.GetType().FullName}\nMessage:{e.Reason.Message}"); syncEvent.Set(); }; newWorkflowApplication.Idle = delegate(WorkflowApplicationIdleEventArgs e) { // TODO: [FUTURE] Need to handle future persistence maintenance. EventLog.WriteEntry("Felsökning.Tomte.AdminService", $"Workflow {e.InstanceId} has entered the Idle state."); syncEvent.Set(); }; newWorkflowApplication.PersistableIdle = delegate(WorkflowApplicationIdleEventArgs e) { EventLog.WriteEntry("Felsökning.Tomte.AdminService", $"Workflow {e.InstanceId} has entered PersistableIdle."); syncEvent.Set(); // Runtime will persist. return(PersistableIdleAction.Persist); }; newWorkflowApplication.Unloaded = delegate(WorkflowApplicationEventArgs e) { EventLog.WriteEntry("Felsökning.Tomte.AdminService", $"Workflow {e.InstanceId} has been unloaded."); syncEvent.Set(); }; newWorkflowApplication.OnUnhandledException = delegate(WorkflowApplicationUnhandledExceptionEventArgs e) { // Log the unhandled exception. EventLog.WriteEntry("Felsökning.Tomte.AdminService", !string.IsNullOrWhiteSpace(e.UnhandledException.InnerException?.Message) ? $"Workflow {e.InstanceId} has reached an AggregateException in OnUnhandledException.\nException Source: {e.ExceptionSource.DisplayName}\nException Instance ID: {e.ExceptionSourceInstanceId}\nException: {e.UnhandledException.InnerException.GetType().FullName}\nMessage: {e.UnhandledException.InnerException.Message}\nFirstArgument: {firstParameter}\nSecondArgument: {secondParameter}" : $"Workflow {e.InstanceId} has reached OnUnhandledException.\nException Source: {e.ExceptionSource.DisplayName}\nException Instance ID: {e.ExceptionSourceInstanceId}\nException: {e.UnhandledException.GetType().FullName}\nMessage: {e.UnhandledException.Message}\nFirstArgument: {firstParameter}\nSecondArgument: {secondParameter}"); syncEvent.Set(); // Instruct the runtime to terminate the workflow. // The other viable choices here are 'Abort' or 'Cancel' return(UnhandledExceptionAction.Terminate); }; newWorkflowApplication.Run(); // Because a new thread is spawned, we need to wait for it to complete before we can move on. syncEvent.WaitOne(); // Instance MUST be unloaded to update the SQL record. One would think this would happen on the overridden delegate methods (e.g.: Completed,OnUnhandledException, etc.) // but testing has proven this to not be the case. newWorkflowApplication.Unload(TimeSpan.FromSeconds(30)); // Now, we dump the instance owner. DeleteWorkflowOwnerCommand newDeleteWorkflowOwnerCommand = new DeleteWorkflowOwnerCommand(); newSqlWorkflowInstanceStore.Execute( workflowInstanceStoreHandle, newDeleteWorkflowOwnerCommand, TimeSpan.FromSeconds(30)); } GC.Collect(); }
/// <summary> /// Overrides the <see cref="ProcessRecord"/> method inherited from <see cref="Cmdlet"/>. /// </summary> protected override void ProcessRecord() { // Because this isn't an app, we need do some science to get the config value. Configuration dllConfiguration = ConfigurationManager.OpenExeConfiguration(Assembly.GetExecutingAssembly().Location); Assembly assembly = Assembly.LoadFrom(dllConfiguration.AppSettings.Settings["DllDropPath"].Value); // See Public.Activities.Research for examples of Activities that you can use here. ObjectHandle newActivityObject = Activator.CreateInstance(assembly.FullName, this.Type); SwedishCodeActivity <string> unwrappedActivity = (SwedishCodeActivity <string>)newActivityObject.Unwrap(); if (!string.IsNullOrWhiteSpace(this.PassedValue)) { unwrappedActivity.FirstInArgument = this.PassedValue; } AutoResetEvent syncEvent = new AutoResetEvent(false); WorkflowApplication newWorkflowApplication = new WorkflowApplication(unwrappedActivity); newWorkflowApplication.Completed += delegate(WorkflowApplicationCompletedEventArgs e) { if (e.CompletionState == ActivityInstanceState.Faulted) { Console.WriteLine("Workflow {0} Terminated.", e.InstanceId); Console.WriteLine( "Exception: {0}\n{1}", e.TerminationException.GetType().FullName, e.TerminationException.Message); syncEvent.Set(); } else if (e.CompletionState == ActivityInstanceState.Canceled) { Console.WriteLine("Workflow {0} Canceled.", e.InstanceId); syncEvent.Set(); } else { // Since the result can be *anything*, let's not treat it like a string. this.Result = e.Outputs["Result"]; Console.WriteLine( "Workflow {0} Completed at {1}.", e.InstanceId, DateTime.UtcNow); syncEvent.Set(); } }; newWorkflowApplication.Aborted = delegate(WorkflowApplicationAbortedEventArgs e) { // The workflow aborted, so let's find out why. Console.WriteLine("Workflow {0} has been aborted.", e.InstanceId); Console.WriteLine( "Exception: {0}\n{1}", e.Reason.GetType().FullName, e.Reason.Message); syncEvent.Set(); }; newWorkflowApplication.Idle = delegate(WorkflowApplicationIdleEventArgs e) { // NOTE: If the workflow can persist, both Idle and PersistableIdle are called in that order. Console.WriteLine("Workflow {0} Idle.", e.InstanceId); syncEvent.Set(); }; newWorkflowApplication.PersistableIdle = delegate(WorkflowApplicationIdleEventArgs e) { // Runtime will persist. Console.WriteLine("Workflow {0} is in PersistableIdle.", e.InstanceId); syncEvent.Set(); return(PersistableIdleAction.Unload); }; newWorkflowApplication.Unloaded = delegate(WorkflowApplicationEventArgs e) { Console.WriteLine("Workflow {0} Unloaded.", e.InstanceId); syncEvent.Set(); }; newWorkflowApplication.OnUnhandledException = delegate(WorkflowApplicationUnhandledExceptionEventArgs e) { // Display the unhandled exception. Console.WriteLine( "OnUnhandledException in Workflow {0}\n{1}", e.InstanceId, e.UnhandledException.Message); Console.WriteLine( "ExceptionSource: {0} - {1}", e.ExceptionSource.DisplayName, e.ExceptionSourceInstanceId); syncEvent.Set(); // Instruct the runtime to terminate the workflow. // The other viable choices here are 'Abort' or 'Cancel' return(UnhandledExceptionAction.Terminate); }; Console.WriteLine($"Starting Workflow: { newWorkflowApplication.Id }"); newWorkflowApplication.Run(); // Because a new thread is spawned, we need to wait for it to complete before we can move on. syncEvent.WaitOne(); }