/// <summary> /// Writes an exception to the Visual Studio debug pane. /// </summary> /// <param name="e">The exception.</param> /// <param name="message">Optional text to include before the exception information.</param> public static void Exception(Exception e, string message = null) { if (e == null) { return; } // We're going to build a multi-line message to reduce pressure // on the task/threading in [RaspberryDebugPackage.Log()]. var sb = new StringBuilder(); sb.Append("\n"); if (string.IsNullOrEmpty(message)) { sb.Append($"EXCEPTION: {e.GetType().FullName}: {e.Message}\n"); } else { sb.Append($"EXCEPTION: {e.GetType().FullName}: {message} {e.Message}\n"); } sb.Append(e.StackTrace); sb.Append("\n"); RaspberryDebugPackage.Log(sb.ToString()); }
/// <summary> /// Updates the Visual Studio user interface. This is performed asynchronously /// by default which will result in better performance. /// </summary> /// <param name="synchronously"> /// Optionally specifies that the update should be performed immediately, /// before the method returns. /// </param> internal static void UpdateVisualStudioUI(bool synchronously = false) { ThreadHelper.ThrowIfNotOnUIThread(); var vsShell = (IVsUIShell)RaspberryDebugPackage.GetGlobalService(typeof(IVsUIShell)); if (vsShell != null) { var hr = vsShell.UpdateCommandUI(synchronously ? 1 : 0); Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(hr); } }
/// <summary> /// Initializes the package. /// </summary> /// <param name="cancellationToken">A cancellation token to monitor for initialization cancellation, which can occur when VS is shutting down.</param> /// <param name="progress">A provider for progress updates.</param> /// <returns>The tracking <see cref="Task"/>.</returns> protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress <ServiceProgressData> progress) { Instance = this; dte = (DTE2)(await GetServiceAsync(typeof(SDTE))); // When initialized asynchronously, the current thread may be a background thread at this point. // Do any initialization that requires the UI thread after switching to the UI thread. await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); // Initialize the log panel. var debugWindow = Package.GetGlobalService(typeof(SVsOutputWindow)) as IVsOutputWindow; var generalPaneGuid = VSConstants.GUID_OutWindowDebugPane; debugWindow.GetPane(ref generalPaneGuid, out debugPane); // Intercept the debugger commands and quickly decide whether the startup project is enabled // for Raspberry remote debugging so we can invoke our custom commands instead. We'll just // let the default command implementations do their thing when we're not doing Raspberry // debugging. debugStartCommandEvent = dte.Events.CommandEvents["{5EFC7975-14BC-11CF-9B2B-00AA00573819}", 0x0127]; debugStartWithoutDebuggingCommandEvent = dte.Events.CommandEvents["{5EFC7975-14BC-11CF-9B2B-00AA00573819}", 0x0170]; debugAttachToProcessCommandEvent = dte.Events.CommandEvents["{5EFC7975-14BC-11CF-9B2B-00AA00573819}", 0x00d5]; debugRestartCommandEvent = dte.Events.CommandEvents["{5EFC7975-14BC-11CF-9B2B-00AA00573819}", 0x0128]; debugStartCommandEvent.BeforeExecute += DebugStartCommandEvent_BeforeExecute; debugStartWithoutDebuggingCommandEvent.BeforeExecute += DebugStartWithoutDebuggingCommandEvent_BeforeExecute; debugAttachToProcessCommandEvent.BeforeExecute += AttachToProcessCommandEvent_BeforeExecute; debugRestartCommandEvent.BeforeExecute += DebugRestartCommandEvent_BeforeExecute; // Initialize the new commands. await SettingsCommand.InitializeAsync(this); await DebugStartCommand.InitializeAsync(this); await DebugStartWithoutDebuggingCommand.InitializeAsync(this); await DebugAttachToProcessCommand.InitializeAsync(this); }
/// <summary> /// Writes a line of text to the Visual Studio debug pane. /// </summary> /// <param name="text">Optionally specifies the log text.</param> public static void WriteLine(string text = "") { RaspberryDebugPackage.Log(text + "\n"); }
/// <summary> /// Writes text to the debug pane. /// </summary> /// <param name="text">The text text.</param> public static void Write(string text) { RaspberryDebugPackage.Log(text); }
/// <summary> /// Executes an asynchronous action that does not return a result within the context of a /// Visual Studio progress dialog. You may make nested calls and this may also be called /// from any thread. /// </summary> /// <typeparam name="TResult">The action result type.</typeparam> /// <param name="description">The operation description.</param> /// <param name="action">The action.</param> /// <returns>The tracking <see cref="Task"/>.</returns> public static async Task <TResult> ExecuteWithProgressAsync <TResult>(string description, Func <Task <TResult> > action) { Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(description), nameof(description)); Covenant.Requires <ArgumentNullException>(action != null, nameof(action)); await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); if (progressDialog == null) { Covenant.Assert(operationStack.Count == 0); rootDescription = description; operationStack.Push(description); var dialogFactory = (IVsThreadedWaitDialogFactory)RaspberryDebugPackage.GetGlobalService((typeof(SVsThreadedWaitDialogFactory))); dialogFactory.CreateInstance(out progressDialog); progressDialog.StartWaitDialog( szWaitCaption: progressCaption, szWaitMessage: description, szProgressText: null, varStatusBmpAnim: null, szStatusBarText: $"[{LogName}]{description}", iDelayToShowDialog: 0, fIsCancelable: false, fShowMarqueeProgress: true); } else { Covenant.Assert(operationStack.Count > 0); operationStack.Push(description); progressDialog.UpdateProgress( szUpdatedWaitMessage: progressCaption, szProgressText: description, szStatusBarText: null, iCurrentStep: 0, iTotalSteps: 0, fDisableCancel: true, pfCanceled: out var cancelled); } var orgCursor = Cursor.Current; try { Cursor.Current = Cursors.WaitCursor; return(await action().ConfigureAwait(false)); } finally { await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); Cursor.Current = orgCursor; var currentDescription = operationStack.Pop(); if (operationStack.Count == 0) { progressDialog.EndWaitDialog(out var cancelled); progressDialog = null; rootDescription = null; } else { progressDialog.UpdateProgress( szUpdatedWaitMessage: currentDescription, szProgressText: null, szStatusBarText: rootDescription, iCurrentStep: 0, iTotalSteps: 0, fDisableCancel: true, pfCanceled: out var cancelled); } } }