MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4#region Using directives5 6using System;7using System.Collections;8using System.Collections.Generic;9using System.Diagnostics;10using System.Globalization;11using System.Management.Automation;12using System.Runtime.InteropServices;13using System.Text;14using System.Threading;15using Microsoft.Management.Infrastructure.Generic;16using Microsoft.Management.Infrastructure.Options;17 18#endregion19 20namespace Microsoft.Management.Infrastructure.CimCmdlets21{22 #region Context base class23 24 /// <summary>25 /// Context base class for cross operations26 /// for example, some cmdlets need to query instance first and then27 /// remove instance, those scenarios need context object transferred28 /// from one operation to another.29 /// </summary>30 internal abstract class XOperationContextBase31 {32 /// <summary>33 /// <para>namespace</para>34 /// </summary>35 internal string Namespace36 {37 get38 {39 return this.nameSpace;40 }41 }42 43 protected string nameSpace;44 45 /// <summary>46 /// <para>47 /// Session proxy.48 /// </para>49 /// </summary>50 internal CimSessionProxy Proxy51 {52 get53 {54 return this.proxy;55 }56 }57 58 protected CimSessionProxy proxy;59 }60 61 /// <summary>62 /// Class provides all information regarding the63 /// current invocation to the .NET API.64 /// </summary>65 internal class InvocationContext66 {67 /// <summary>68 /// Initializes a new instance of the <see cref="InvocationContext"/> class.69 /// </summary>70 /// <param name="proxy"></param>71 internal InvocationContext(CimSessionProxy proxy)72 {73 if (proxy != null)74 {75 this.ComputerName = proxy.CimSession.ComputerName;76 this.TargetCimInstance = proxy.TargetCimInstance;77 }78 }79 80 /// <summary>81 /// Initializes a new instance of the <see cref="InvocationContext"/> class.82 /// </summary>83 /// <param name="proxy"></param>84 internal InvocationContext(string computerName, CimInstance targetCimInstance)85 {86 this.ComputerName = computerName;87 this.TargetCimInstance = targetCimInstance;88 }89 90 /// <summary>91 /// <para>92 /// ComputerName of the session93 /// </para>94 /// </summary>95 /// <remarks>96 /// return value could be null97 /// </remarks>98 internal virtual string ComputerName { get; }99 100 /// <summary>101 /// <para>102 /// CimInstance on which the current operation against.103 /// </para>104 /// </summary>105 /// <remarks>106 /// return value could be null107 /// </remarks>108 internal virtual CimInstance TargetCimInstance { get; }109 }110 #endregion111 112 #region Preprocessing of result object interface113 /// <summary>114 /// Defines a method to preprocessing an result object before sending to115 /// output pipeline.116 /// </summary>117 [ComVisible(false)]118 internal interface IObjectPreProcess119 {120 /// <summary>121 /// Performs pre processing of given result object.122 /// </summary>123 /// <param name="resultObject"></param>124 /// <returns>Pre-processed object.</returns>125 object Process(object resultObject);126 }127 #endregion128 129 #region Eventargs class130 /// <summary>131 /// <para>132 /// CmdletActionEventArgs holds a CimBaseAction object133 /// </para>134 /// </summary>135 internal sealed class CmdletActionEventArgs : EventArgs136 {137 /// <summary>138 /// Initializes a new instance of the <see cref="CmdletActionEventArgs"/> class.139 /// </summary>140 /// <param name="action">CimBaseAction object bound to the event.</param>141 public CmdletActionEventArgs(CimBaseAction action)142 {143 this.Action = action;144 }145 146 public readonly CimBaseAction Action;147 }148 149 /// <summary>150 /// OperationEventArgs holds a cancellation object, and an operation.151 /// </summary>152 internal sealed class OperationEventArgs : EventArgs153 {154 /// <summary>155 /// Initializes a new instance of the <see cref="OperationEventArgs"/> class.156 /// </summary>157 /// <param name="operationCancellation">Object used to cancel the operation.</param>158 /// <param name="operation">Async observable operation.</param>159 public OperationEventArgs(IDisposable operationCancellation,160 IObservable<object> operation,161 bool theSuccess)162 {163 this.operationCancellation = operationCancellation;164 this.operation = operation;165 this.success = theSuccess;166 }167 168 public readonly IDisposable operationCancellation;169 public readonly IObservable<object> operation;170 public readonly bool success;171 }172 173 #endregion174 175 /// <summary>176 /// <para>177 /// Wrapper of <see cref="CimSession"/> object.178 /// A CimSessionProxy object can only execute one operation at specific moment.179 /// </para>180 /// </summary>181 internal class CimSessionProxy : IDisposable182 {183 #region static members184 185 /// <summary>186 /// <para>187 /// global operation counter188 /// </para>189 /// </summary>190 private static long gOperationCounter = 0;191 192 /// <summary>193 /// Temporary CimSession cache lock.194 /// </summary>195 private static readonly object temporarySessionCacheLock = new();196 197 /// <summary>198 /// <para>temporary CimSession cache</para>199 /// <para>Temporary CimSession means the session is created by cimcmdlets,200 /// which is not created by <see cref="New-CimSession"/> cmdlet.201 /// Due to some cmdlet, such as <see cref="Remove-CimInstance"/>202 /// might need to split the operation into multiple stages, i.e., query203 /// CimInstance firstly, then remove the CimInstance resulted from query,204 /// such that the temporary CimSession need to be shared between205 /// multiple <see cref="CimSessionProxy"/> objects, introducing a206 /// temporary session cache is necessary to control the lifetime of the207 /// temporary CimSession objects.</para>208 /// <para>209 /// Once the reference count of the CimSession is decreased to 0,210 /// then call Dispose on it.211 /// </para>212 /// </summary>213 private static readonly Dictionary<CimSession, uint> temporarySessionCache = new();214 215 /// <summary>216 /// <para>217 /// Add <see cref="CimSession"/> to temporary cache.218 /// If CimSession already present in cache, then increase the refcount by 1,219 /// otherwise insert it into the cache.220 /// </para>221 /// </summary>222 /// <param name="session">CimSession to be added.</param>223 internal static void AddCimSessionToTemporaryCache(CimSession session)224 {225 if (session != null)226 {227 lock (temporarySessionCacheLock)228 {229 if (temporarySessionCache.ContainsKey(session))230 {231 temporarySessionCache[session]++;232 DebugHelper.WriteLogEx(@"Increase cimsession ref count {0}", 1, temporarySessionCache[session]);233 }234 else235 {236 temporarySessionCache.Add(session, 1);237 DebugHelper.WriteLogEx(@"Add cimsession to cache. Ref count {0}", 1, temporarySessionCache[session]);238 }239 }240 }241 }242 243 /// <summary>244 /// <para>Wrapper function to remove CimSession from cache</para>245 /// </summary>246 /// <param name="session"></param>247 /// <param name="dispose">Whether need to dispose the <see cref="CimSession"/> object.</param>248 private static void RemoveCimSessionFromTemporaryCache(CimSession session,249 bool dispose)250 {251 if (session != null)252 {253 bool removed = false;254 lock (temporarySessionCacheLock)255 {256 if (temporarySessionCache.ContainsKey(session))257 {258 temporarySessionCache[session]--;259 DebugHelper.WriteLogEx(@"Decrease cimsession ref count {0}", 1, temporarySessionCache[session]);260 if (temporarySessionCache[session] == 0)261 {262 removed = true;263 temporarySessionCache.Remove(session);264 }265 }266 }267 // there is a race condition that if268 // one thread is waiting to add CimSession to cache,269 // while current thread is removing the CimSession,270 // then invalid CimSession may be added to cache.271 // Ignored this scenario in CimCmdlet implementation,272 // since the code inside cimcmdlet will not hit this273 // scenario anyway.274 if (removed && dispose)275 {276 DebugHelper.WriteLogEx(@"Dispose cimsession ", 1);277 session.Dispose();278 }279 }280 }281 282 /// <summary>283 /// <para>284 /// Remove <see cref="CimSession"/> from temporary cache.285 /// If CimSession already present in cache, then decrease the refcount by 1,286 /// otherwise ignore.287 /// If refcount became 0, call dispose on the <see cref="CimSession"/> object.288 /// </para>289 /// </summary>290 /// <param name="session">CimSession to be added.</param>291 internal static void RemoveCimSessionFromTemporaryCache(CimSession session)292 {293 RemoveCimSessionFromTemporaryCache(session, true);294 }295 #endregion296 297 #region Event definitions298 299 /// <summary>300 /// Define an Event based on the NewActionHandler.301 /// </summary>302 public event EventHandler<CmdletActionEventArgs> OnNewCmdletAction;303 304 /// <summary>305 /// Event triggered when a new operation is started.306 /// </summary>307 public event EventHandler<OperationEventArgs> OnOperationCreated;308 309 /// <summary>310 /// Event triggered when a new operation is completed,311 /// either success or failed.312 /// </summary>313 public event EventHandler<OperationEventArgs> OnOperationDeleted;314 315 #endregion316 317 #region constructors318 319 /// <summary>320 /// Initializes a new instance of the <see cref="CimSessionProxy"/> class.321 /// </summary>322 /// <remarks>323 /// Then create wrapper object by given CimSessionProxy object.324 /// </remarks>325 /// <param name="computerName"></param>326 public CimSessionProxy(CimSessionProxy proxy)327 {328 DebugHelper.WriteLogEx("protocol = {0}", 1, proxy.Protocol);329 330 CreateSetSession(null, proxy.CimSession, null, proxy.OperationOptions, proxy.IsTemporaryCimSession);331 this.Protocol = proxy.Protocol;332 this.OperationTimeout = proxy.OperationTimeout;333 this.isDefaultSession = proxy.isDefaultSession;334 }335 336 /// <summary>337 /// Initializes a new instance of the <see cref="CimSessionProxy"/> class.338 /// </summary>339 /// <remarks>340 /// Create <see cref="CimSession"/> by given computer name.341 /// Then create wrapper object.342 /// </remarks>343 /// <param name="computerName"></param>344 public CimSessionProxy(string computerName)345 {346 CreateSetSession(computerName, null, null, null, false);347 this.isDefaultSession = computerName == ConstValue.NullComputerName;348 }349 350 /// <summary>351 /// Initializes a new instance of the <see cref="CimSessionProxy"/> class.352 /// </summary>353 /// <remarks>354 /// Create <see cref="CimSession"/> by given computer name355 /// and session options.356 /// Then create wrapper object.357 /// </remarks>358 /// <param name="computerName"></param>359 /// <param name="sessionOptions"></param>360 public CimSessionProxy(string computerName, CimSessionOptions sessionOptions)361 {362 CreateSetSession(computerName, null, sessionOptions, null, false);363 this.isDefaultSession = computerName == ConstValue.NullComputerName;364 }365 366 /// <summary>367 /// Initializes a new instance of the <see cref="CimSessionProxy"/> class.368 /// </summary>369 /// <remarks>370 /// Create <see cref="CimSession"/> by given computer name371 /// and cimInstance. Then create wrapper object.372 /// </remarks>373 /// <param name="computerName"></param>374 /// <param name="cimInstance"></param>375 public CimSessionProxy(string computerName, CimInstance cimInstance)376 {377 DebugHelper.WriteLogEx("ComputerName {0}; cimInstance.CimSessionInstanceID = {1}; cimInstance.CimSessionComputerName = {2}.",378 0,379 computerName,380 cimInstance.GetCimSessionInstanceId(),381 cimInstance.GetCimSessionComputerName());382 383 if (computerName != ConstValue.NullComputerName)384 {385 CreateSetSession(computerName, null, null, null, false);386 return;387 }388 389 Debug.Assert(cimInstance != null, "Caller should verify cimInstance != null");390 391 // computerName is null, fallback to create session from cimInstance392 CimSessionState state = CimSessionBase.GetCimSessionState();393 if (state != null)394 {395 CimSession session = state.QuerySession(cimInstance.GetCimSessionInstanceId());396 if (session != null)397 {398 DebugHelper.WriteLogEx("Found the session from cache with InstanceID={0}.", 0, cimInstance.GetCimSessionInstanceId());399 CreateSetSession(null, session, null, null, false);400 return;401 }402 }403 404 string cimsessionComputerName = cimInstance.GetCimSessionComputerName();405 CreateSetSession(cimsessionComputerName, null, null, null, false);406 this.isDefaultSession = cimsessionComputerName == ConstValue.NullComputerName;407 408 DebugHelper.WriteLogEx("Create a temp session with computerName = {0}.", 0, cimsessionComputerName);409 }410 411 /// <summary>412 /// Initializes a new instance of the <see cref="CimSessionProxy"/> class.413 /// </summary>414 /// <remarks>415 /// Create <see cref="CimSession"/> by given computer name,416 /// session options.417 /// </remarks>418 /// <param name="computerName"></param>419 /// <param name="sessionOptions"></param>420 /// <param name="operOptions">Used when create async operation.</param>421 public CimSessionProxy(string computerName, CimSessionOptions sessionOptions, CimOperationOptions operOptions)422 {423 CreateSetSession(computerName, null, sessionOptions, operOptions, false);424 this.isDefaultSession = computerName == ConstValue.NullComputerName;425 }426 427 /// <summary>428 /// Initializes a new instance of the <see cref="CimSessionProxy"/> class.429 /// </summary>430 /// <remarks>431 /// Create <see cref="CimSession"/> by given computer name.432 /// Then create wrapper object.433 /// </remarks>434 /// <param name="computerName"></param>435 /// <param name="operOptions">Used when create async operation.</param>436 public CimSessionProxy(string computerName, CimOperationOptions operOptions)437 {438 CreateSetSession(computerName, null, null, operOptions, false);439 this.isDefaultSession = computerName == ConstValue.NullComputerName;440 }441 442 /// <summary>443 /// Initializes a new instance of the <see cref="CimSessionProxy"/> class.444 /// </summary>445 /// <remarks>446 /// Create wrapper object by given session object.447 /// </remarks>448 /// <param name="session"></param>449 public CimSessionProxy(CimSession session)450 {451 CreateSetSession(null, session, null, null, false);452 }453 454 /// <summary>455 /// Initializes a new instance of the <see cref="CimSessionProxy"/> class.456 /// </summary>457 /// <remarks>458 /// Create wrapper object by given session object.459 /// </remarks>460 /// <param name="session"></param>461 /// <param name="operOptions">Used when create async operation.</param>462 public CimSessionProxy(CimSession session, CimOperationOptions operOptions)463 {464 CreateSetSession(null, session, null, operOptions, false);465 }466 467 /// <summary>468 /// Initialize CimSessionProxy object.469 /// </summary>470 /// <param name="computerName"></param>471 /// <param name="session"></param>472 /// <param name="sessionOptions"></param>473 /// <param name="options"></param>474 private void CreateSetSession(475 string computerName,476 CimSession cimSession,477 CimSessionOptions sessionOptions,478 CimOperationOptions operOptions,479 bool temporaryCimSession)480 {481 DebugHelper.WriteLogEx("computername {0}; cimsession {1}; sessionOptions {2}; operationOptions {3}.", 0, computerName, cimSession, sessionOptions, operOptions);482 483 lock (this.stateLock)484 {485 this.CancelOperation = null;486 this.operation = null;487 }488 489 InitOption(operOptions);490 this.Protocol = ProtocolType.Wsman;491 this.IsTemporaryCimSession = temporaryCimSession;492 493 if (cimSession != null)494 {495 this.CimSession = cimSession;496 CimSessionState state = CimSessionBase.GetCimSessionState();497 if (state != null)498 {499 CimSessionWrapper wrapper = state.QuerySession(cimSession);500 if (wrapper != null)501 {502 this.Protocol = wrapper.GetProtocolType();503 }504 }505 }506 else507 {508 if (sessionOptions != null)509 {510 if (sessionOptions is DComSessionOptions)511 {512 string defaultComputerName = ConstValue.IsDefaultComputerName(computerName) ? ConstValue.NullComputerName : computerName;513 this.CimSession = CimSession.Create(defaultComputerName, sessionOptions);514 this.Protocol = ProtocolType.Dcom;515 }516 else517 {518 this.CimSession = CimSession.Create(computerName, sessionOptions);519 }520 }521 else522 {523 this.CimSession = CreateCimSessionByComputerName(computerName);524 }525 526 this.IsTemporaryCimSession = true;527 }528 529 if (this.IsTemporaryCimSession)530 {531 AddCimSessionToTemporaryCache(this.CimSession);532 }533 534 this.invocationContextObject = new InvocationContext(this);535 DebugHelper.WriteLog("Protocol {0}, Is temporary session ? {1}", 1, this.Protocol, this.IsTemporaryCimSession);536 }537 538 #endregion539 540 #region set operation options541 542 /// <summary>543 /// Gets or sets a value indicating whether to retrieve localized information for the CIM class.544 /// </summary>545 public bool Amended546 {547 get => OperationOptions.Flags.HasFlag(CimOperationFlags.LocalizedQualifiers);548 549 set550 {551 if (value)552 {553 OperationOptions.Flags |= CimOperationFlags.LocalizedQualifiers;554 }555 else556 {557 OperationOptions.Flags &= ~CimOperationFlags.LocalizedQualifiers;558 }559 }560 }561 562 /// <summary>563 /// Set timeout value (seconds) of the operation.564 /// </summary>565 public uint OperationTimeout566 {567 get568 {569 return (uint)this.OperationOptions.Timeout.TotalSeconds;570 }571 572 set573 {574 DebugHelper.WriteLogEx("OperationTimeout {0},", 0, value);575 576 this.OperationOptions.Timeout = TimeSpan.FromSeconds((double)value);577 }578 }579 580 /// <summary>581 /// Set resource URI of the operation.582 /// </summary>583 public Uri ResourceUri584 {585 get586 {587 return this.OperationOptions.ResourceUri;588 }589 590 set591 {592 DebugHelper.WriteLogEx("ResourceUri {0},", 0, value);593 594 this.OperationOptions.ResourceUri = value;595 }596 }597 598 /// <summary>599 /// Enable/Disable the method result streaming,600 /// it is enabled by default.601 /// </summary>602 public bool EnableMethodResultStreaming603 {604 get605 {606 return this.OperationOptions.EnableMethodResultStreaming;607 }608 609 set610 {611 DebugHelper.WriteLogEx("EnableMethodResultStreaming {0}", 0, value);612 this.OperationOptions.EnableMethodResultStreaming = value;613 }614 }615 616 /// <summary>617 /// Enable/Disable prompt user streaming,618 /// it is enabled by default.619 /// </summary>620 public bool EnablePromptUser621 {622 set623 {624 DebugHelper.WriteLogEx("EnablePromptUser {0}", 0, value);625 if (value)626 {627 this.OperationOptions.PromptUser = this.PromptUser;628 }629 }630 }631 632 /// <summary>633 /// Enable the pssemantics.634 /// </summary>635 private void EnablePSSemantics()636 {637 DebugHelper.WriteLogEx();638 639 // this.options.PromptUserForceFlag...640 // this.options.WriteErrorMode641 this.OperationOptions.WriteErrorMode = CimCallbackMode.Inquire;642 643 // !!!NOTES: Does not subscribe to PromptUser for CimCmdlets now644 // since cmdlet does not provider an approach645 // to let user select how to handle prompt message646 // this can be enabled later if needed.647 this.OperationOptions.WriteError = this.WriteError;648 this.OperationOptions.WriteMessage = this.WriteMessage;649 this.OperationOptions.WriteProgress = this.WriteProgress;650 }651 652 /// <summary>653 /// Set keyonly property.654 /// </summary>655 public SwitchParameter KeyOnly656 {657 set { this.OperationOptions.KeysOnly = value.IsPresent; }658 }659 660 /// <summary>661 /// Set Shallow flag.662 /// </summary>663 public SwitchParameter Shallow664 {665 set666 {667 if (value.IsPresent)668 {669 this.OperationOptions.Flags = CimOperationFlags.PolymorphismShallow;670 }671 else672 {673 this.OperationOptions.Flags = CimOperationFlags.None;674 }675 }676 }677 678 /// <summary>679 /// Initialize the operation option.680 /// </summary>681 private void InitOption(CimOperationOptions operOptions)682 {683 DebugHelper.WriteLogEx();684 685 if (operOptions != null)686 {687 this.OperationOptions = new CimOperationOptions(operOptions);688 }689 else690 {691 this.OperationOptions ??= new CimOperationOptions();692 }693 694 this.EnableMethodResultStreaming = true;695 this.EnablePSSemantics();696 }697 698 #endregion699 700 #region misc operations701 702 /// <summary>703 /// Caller call Detach to retrieve the session704 /// object and control the lifecycle of the CimSession object.705 /// </summary>706 /// <returns></returns>707 public CimSession Detach()708 {709 DebugHelper.WriteLogEx();710 711 // Remove the CimSession from cache but don't dispose it712 RemoveCimSessionFromTemporaryCache(this.CimSession, false);713 CimSession sessionToReturn = this.CimSession;714 this.CimSession = null;715 this.IsTemporaryCimSession = false;716 return sessionToReturn;717 }718 719 /// <summary>720 /// Add a new operation to cache.721 /// </summary>722 /// <param name="operation"></param>723 /// <param name="cancelObject"></param>724 private void AddOperation(IObservable<object> operation)725 {726 DebugHelper.WriteLogEx();727 728 lock (this.stateLock)729 {730 Debug.Assert(this.Completed, "Caller should verify that there is no operation in progress");731 this.operation = operation;732 }733 }734 735 /// <summary>736 /// Remove object from cache.737 /// </summary>738 /// <param name="operation"></param>739 private void RemoveOperation(IObservable<object> operation)740 {741 DebugHelper.WriteLogEx();742 743 lock (this.stateLock)744 {745 Debug.Assert(this.operation == operation, "Caller should verify that the operation to remove is the operation in progress");746 747 this.DisposeCancelOperation();748 749 if (this.operation != null)750 {751 this.operation = null;752 }753 754 if (this.CimSession != null && this.ContextObject == null)755 {756 DebugHelper.WriteLog("Dispose this proxy object @ RemoveOperation");757 this.Dispose();758 }759 }760 }761 762 /// <summary>763 /// <para>764 /// Trigger an event that new action available765 /// </para>766 /// </summary>767 /// <param name="action"></param>768 protected void FireNewActionEvent(CimBaseAction action)769 {770 DebugHelper.WriteLogEx();771 772 CmdletActionEventArgs actionArgs = new(action);773 if (!PreNewActionEvent(actionArgs))774 {775 return;776 }777 778 EventHandler<CmdletActionEventArgs> temp = this.OnNewCmdletAction;779 if (temp != null)780 {781 temp(this.CimSession, actionArgs);782 }783 else784 {785 DebugHelper.WriteLog("Ignore action since OnNewCmdletAction is null.", 5);786 }787 788 this.PostNewActionEvent(actionArgs);789 }790 791 /// <summary>792 /// <para>793 /// Trigger an event that new operation is created794 /// </para>795 /// </summary>796 /// <param name="cancelOperation"></param>797 /// <param name="operation"></param>798 private void FireOperationCreatedEvent(799 IDisposable cancelOperation,800 IObservable<object> operation)801 {802 DebugHelper.WriteLogEx();803 804 OperationEventArgs args = new(805 cancelOperation, operation, false);806 this.OnOperationCreated?.Invoke(this.CimSession, args);807 808 this.PostOperationCreateEvent(args);809 }810 811 /// <summary>812 /// <para>813 /// Trigger an event that an operation is deleted814 /// </para>815 /// </summary>816 /// <param name="operation"></param>817 private void FireOperationDeletedEvent(818 IObservable<object> operation,819 bool success)820 {821 DebugHelper.WriteLogEx();822 this.WriteOperationCompleteMessage(this.operationName);823 OperationEventArgs args = new(824 null, operation, success);825 PreOperationDeleteEvent(args);826 this.OnOperationDeleted?.Invoke(this.CimSession, args);827 828 this.PostOperationDeleteEvent(args);829 this.RemoveOperation(operation);830 this.operationName = null;831 }832 833 #endregion834 835 #region PSExtension callback functions836 837 /// <summary>838 /// <para>839 /// WriteMessage callback840 /// </para>841 /// </summary>842 /// <param name="channel"></param>843 /// <param name="message"></param>844 internal void WriteMessage(uint channel, string message)845 {846 DebugHelper.WriteLogEx("Channel = {0} message = {1}", 0, channel, message);847 try848 {849 CimWriteMessage action = new(channel, message);850 this.FireNewActionEvent(action);851 }852 catch (Exception ex)853 {854 DebugHelper.WriteLogEx("{0}", 0, ex);855 }856 }857 858 /// <summary>859 /// <para>860 /// Write operation start verbose message861 /// </para>862 /// </summary>863 /// <param name="operation"></param>864 /// <param name="parameters"></param>865 internal void WriteOperationStartMessage(string operation, Hashtable parameterList)866 {867 DebugHelper.WriteLogEx();868 StringBuilder parameters = new();869 if (parameterList != null)870 {871 foreach (string key in parameterList.Keys)872 {873 if (parameters.Length > 0)874 {875 parameters.Append(',');876 }877 878 parameters.Append(CultureInfo.CurrentUICulture, $@"'{key}' = {parameterList[key]}");879 }880 }881 882 string operationStartMessage = string.Format(CultureInfo.CurrentUICulture,883 CimCmdletStrings.CimOperationStart,884 operation,885 (parameters.Length == 0) ? "null" : parameters.ToString());886 WriteMessage((uint)CimWriteMessageChannel.Verbose, operationStartMessage);887 }888 889 /// <summary>890 /// <para>891 /// Write operation complete verbose message892 /// </para>893 /// </summary>894 /// <param name="operation"></param>895 internal void WriteOperationCompleteMessage(string operation)896 {897 DebugHelper.WriteLogEx();898 string operationCompleteMessage = string.Format(CultureInfo.CurrentUICulture,899 CimCmdletStrings.CimOperationCompleted,900 operation);901 WriteMessage((uint)CimWriteMessageChannel.Verbose, operationCompleteMessage);902 }903 904 /// <summary>905 /// <para>906 /// WriteProgress callback907 /// </para>908 /// </summary>909 /// <param name="activity"></param>910 /// <param name="currentOperation"></param>911 /// <param name="statusDescription"></param>912 /// <param name="percentageCompleted"></param>913 /// <param name="secondsRemaining"></param>914 public void WriteProgress(string activity,915 string currentOperation,916 string statusDescription,917 uint percentageCompleted,918 uint secondsRemaining)919 {920 DebugHelper.WriteLogEx("activity:{0}; currentOperation:{1}; percentageCompleted:{2}; secondsRemaining:{3}",921 0, activity, currentOperation, percentageCompleted, secondsRemaining);922 923 try924 {925 CimWriteProgress action = new(926 activity,927 (int)this.operationID,928 currentOperation,929 statusDescription,930 percentageCompleted,931 secondsRemaining);932 this.FireNewActionEvent(action);933 }934 catch (Exception ex)935 {936 DebugHelper.WriteLogEx("{0}", 0, ex);937 }938 }939 940 /// <summary>941 /// <para>942 /// WriteError callback943 /// </para>944 /// </summary>945 /// <param name="instance"></param>946 /// <returns></returns>947 public CimResponseType WriteError(CimInstance instance)948 {949 DebugHelper.WriteLogEx("Error:{0}", 0, instance);950 try951 {952 CimWriteError action = new(instance, this.invocationContextObject);953 this.FireNewActionEvent(action);954 return action.GetResponse();955 }956 catch (Exception ex)957 {958 DebugHelper.WriteLogEx("{0}", 0, ex);959 return CimResponseType.NoToAll;960 }961 }962 963 /// <summary>964 /// PromptUser callback.965 /// </summary>966 /// <param name="message"></param>967 /// <param name="prompt"></param>968 /// <returns></returns>969 public CimResponseType PromptUser(string message, CimPromptType prompt)970 {971 DebugHelper.WriteLogEx("message:{0} prompt:{1}", 0, message, prompt);972 try973 {974 CimPromptUser action = new(message, prompt);975 this.FireNewActionEvent(action);976 return action.GetResponse();977 }978 catch (Exception ex)979 {980 DebugHelper.WriteLogEx("{0}", 0, ex);981 return CimResponseType.NoToAll;982 }983 }984 #endregion985 986 #region Async result handler987 988 /// <summary>989 /// <para>990 /// Handle async event triggered by <see cref="CimResultObserver{T}"/>991 /// </para>992 /// </summary>993 /// <param name="observer">Object triggered the event.</param>994 /// <param name="resultArgs">Async result event argument.</param>995 internal void ResultEventHandler(996 object observer,997 AsyncResultEventArgsBase resultArgs)998 {999 DebugHelper.WriteLogEx();1000 switch (resultArgs.resultType)1001 {1002 case AsyncResultType.Completion:1003 {1004 DebugHelper.WriteLog("ResultEventHandler::Completion", 4);1005 1006 AsyncResultCompleteEventArgs args = resultArgs as AsyncResultCompleteEventArgs;1007 this.FireOperationDeletedEvent(args.observable, true);1008 }1009 1010 break;1011 case AsyncResultType.Exception:1012 {1013 AsyncResultErrorEventArgs args = resultArgs as AsyncResultErrorEventArgs;1014 DebugHelper.WriteLog("ResultEventHandler::Exception {0}", 4, args.error);1015 1016 using (CimWriteError action = new(args.error, this.invocationContextObject, args.context))1017 {1018 this.FireNewActionEvent(action);1019 }1020 1021 this.FireOperationDeletedEvent(args.observable, false);1022 }1023 1024 break;1025 case AsyncResultType.Result:1026 {1027 AsyncResultObjectEventArgs args = resultArgs as AsyncResultObjectEventArgs;1028 DebugHelper.WriteLog("ResultEventHandler::Result {0}", 4, args.resultObject);1029 object resultObject = args.resultObject;1030 if (!this.isDefaultSession)1031 {1032 AddShowComputerNameMarker(resultObject);1033 }1034 1035 if (this.ObjectPreProcess != null)1036 {1037 resultObject = this.ObjectPreProcess.Process(resultObject);1038 }1039#if DEBUG1040 resultObject = PostProcessCimInstance(resultObject);1041#endif1042 CimWriteResultObject action = new(resultObject, this.ContextObject);1043 this.FireNewActionEvent(action);1044 }1045 1046 break;1047 default:1048 break;1049 }1050 }1051 1052 /// <summary>1053 /// This method adds a note property to <paramref name="o"/>,1054 /// which will cause the default PowerShell formatting and output1055 /// to include PSComputerName column/property in the display.1056 /// </summary>1057 /// <param name="o"></param>1058 private static void AddShowComputerNameMarker(object o)1059 {1060 if (o == null)1061 {1062 return;1063 }1064 1065 PSObject pso = PSObject.AsPSObject(o);1066 if (pso.BaseObject is not CimInstance)1067 {1068 return;1069 }1070 1071 PSNoteProperty psShowComputerNameProperty = new(ConstValue.ShowComputerNameNoteProperty, true);1072 pso.Members.Add(psShowComputerNameProperty);1073 }1074 1075#if DEBUG1076 private static readonly bool isCliXmlTestabilityHookActive = GetIsCliXmlTestabilityHookActive();1077 1078 private static bool GetIsCliXmlTestabilityHookActive()1079 {1080 return !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("CDXML_CLIXML_TEST"));1081 }1082 1083 private static object PostProcessCimInstance(object resultObject)1084 {1085 DebugHelper.WriteLogEx();1086 if (isCliXmlTestabilityHookActive && (resultObject is CimInstance))1087 {1088 string serializedForm = PSSerializer.Serialize(resultObject as CimInstance, depth: 1);1089 object deserializedObject = PSSerializer.Deserialize(serializedForm);1090 object returnObject = (deserializedObject is PSObject) ? (deserializedObject as PSObject).BaseObject : deserializedObject;1091 DebugHelper.WriteLogEx("Deserialized object is {0}, type {1}", 1, returnObject, returnObject.GetType());1092 return returnObject;1093 }1094 1095 return resultObject;1096 }1097#endif1098 #endregion1099 1100 #region Async operations1101 1102 /// <summary>1103 /// <para>1104 /// create a cim instance asynchronously1105 /// </para>1106 /// </summary>1107 /// <param name="namespaceName"></param>1108 /// <param name="instance"></param>1109 public void CreateInstanceAsync(string namespaceName, CimInstance instance)1110 {1111 Debug.Assert(instance != null, "Caller should verify that instance != NULL.");1112 DebugHelper.WriteLogEx("EnableMethodResultStreaming = {0}", 0, this.OperationOptions.EnableMethodResultStreaming);1113 this.CheckAvailability();1114 this.TargetCimInstance = instance;1115 this.operationName = CimCmdletStrings.CimOperationNameCreateInstance;1116 this.operationParameters.Clear();1117 this.operationParameters.Add(@"namespaceName", namespaceName);1118 this.operationParameters.Add(@"instance", instance);1119 this.WriteOperationStartMessage(this.operationName, this.operationParameters);1120 CimAsyncResult<CimInstance> asyncResult = this.CimSession.CreateInstanceAsync(namespaceName, instance, this.OperationOptions);1121 ConsumeCimInstanceAsync(asyncResult, new CimResultContext(instance));1122 }1123 1124 /// <summary>1125 /// Delete a cim instance asynchronously.1126 /// </summary>1127 /// <param name="namespaceName"></param>1128 /// <param name="instance"></param>1129 public void DeleteInstanceAsync(string namespaceName, CimInstance instance)1130 {1131 Debug.Assert(instance != null, "Caller should verify that instance != NULL.");1132 DebugHelper.WriteLogEx("namespace = {0}; classname = {1};", 0, namespaceName, instance.CimSystemProperties.ClassName);1133 this.CheckAvailability();1134 this.TargetCimInstance = instance;1135 this.operationName = CimCmdletStrings.CimOperationNameDeleteInstance;1136 this.operationParameters.Clear();1137 this.operationParameters.Add(@"namespaceName", namespaceName);1138 this.operationParameters.Add(@"instance", instance);1139 this.WriteOperationStartMessage(this.operationName, this.operationParameters);1140 CimAsyncStatus asyncResult = this.CimSession.DeleteInstanceAsync(namespaceName, instance, this.OperationOptions);1141 ConsumeObjectAsync(asyncResult, new CimResultContext(instance));1142 }1143 1144 /// <summary>1145 /// Get cim instance asynchronously.1146 /// </summary>1147 /// <param name="namespaceName"></param>1148 /// <param name="instanceId"></param>1149 public void GetInstanceAsync(string namespaceName, CimInstance instance)1150 {1151 Debug.Assert(instance != null, "Caller should verify that instance != NULL.");1152 DebugHelper.WriteLogEx("namespace = {0}; classname = {1}; keyonly = {2}", 0, namespaceName, instance.CimSystemProperties.ClassName, this.OperationOptions.KeysOnly);1153 this.CheckAvailability();1154 this.TargetCimInstance = instance;1155 this.operationName = CimCmdletStrings.CimOperationNameGetInstance;1156 this.operationParameters.Clear();1157 this.operationParameters.Add(@"namespaceName", namespaceName);1158 this.operationParameters.Add(@"instance", instance);1159 this.WriteOperationStartMessage(this.operationName, this.operationParameters);1160 CimAsyncResult<CimInstance> asyncResult = this.CimSession.GetInstanceAsync(namespaceName, instance, this.OperationOptions);1161 ConsumeCimInstanceAsync(asyncResult, new CimResultContext(instance));1162 }1163 1164 /// <summary>1165 /// Modify cim instance asynchronously.1166 /// </summary>1167 /// <param name="namespaceName"></param>1168 /// <param name="instance"></param>1169 public void ModifyInstanceAsync(string namespaceName, CimInstance instance)1170 {1171 Debug.Assert(instance != null, "Caller should verify that instance != NULL.");1172 DebugHelper.WriteLogEx("namespace = {0}; classname = {1}", 0, namespaceName, instance.CimSystemProperties.ClassName);1173 this.CheckAvailability();1174 this.TargetCimInstance = instance;1175 this.operationName = CimCmdletStrings.CimOperationNameModifyInstance;1176 this.operationParameters.Clear();1177 this.operationParameters.Add(@"namespaceName", namespaceName);1178 this.operationParameters.Add(@"instance", instance);1179 this.WriteOperationStartMessage(this.operationName, this.operationParameters);1180 CimAsyncResult<CimInstance> asyncResult = this.CimSession.ModifyInstanceAsync(namespaceName, instance, this.OperationOptions);1181 ConsumeObjectAsync(asyncResult, new CimResultContext(instance));1182 }1183 1184 /// <summary>1185 /// Enumerate cim instance associated with the1186 /// given instance asynchronously.1187 /// </summary>1188 /// <param name="namespaceName"></param>1189 /// <param name="sourceInstance"></param>1190 /// <param name="associationClassName"></param>1191 /// <param name="resultClassName"></param>1192 /// <param name="sourceRole"></param>1193 /// <param name="resultRole"></param>1194 public void EnumerateAssociatedInstancesAsync(1195 string namespaceName,1196 CimInstance sourceInstance,1197 string associationClassName,1198 string resultClassName,1199 string sourceRole,1200 string resultRole)