File: Notification\VSNotificationServiceFactory.cs
Web Access
Project: src\src\VisualStudio\Core\Def\Microsoft.VisualStudio.LanguageServices_yayxzhyh_wpftmp.csproj (Microsoft.VisualStudio.LanguageServices)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
 
using System;
using System.Composition;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Notification;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
 
namespace Microsoft.VisualStudio.LanguageServices.Implementation.Notification;
 
[ExportWorkspaceServiceFactory(typeof(INotificationService), ServiceLayer.Host), Shared]
internal class VSNotificationServiceFactory : IWorkspaceServiceFactory
{
    private readonly IVsUIShell _uiShellService;
 
    private static readonly object s_gate = new();
 
    private static VSDialogService s_singleton;
 
    [ImportingConstructor]
    [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
    public VSNotificationServiceFactory(SVsServiceProvider serviceProvider)
        => _uiShellService = (IVsUIShell)serviceProvider.GetService(typeof(SVsUIShell));
 
    public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices)
    {
        lock (s_gate)
        {
            s_singleton ??= new VSDialogService(_uiShellService);
        }
 
        return s_singleton;
    }
 
    private class VSDialogService : INotificationService, INotificationServiceCallback
    {
        private readonly IVsUIShell _uiShellService;
 
        /// <summary>
        /// For testing purposes only.  If non-null, this callback will be invoked instead of showing a dialog.
        /// </summary>
        public Action<string, string, NotificationSeverity> NotificationCallback { get; set; }
 
        public VSDialogService(IVsUIShell uiShellService)
            => _uiShellService = uiShellService;
 
        public void SendNotification(
            string message,
            string title = null,
            NotificationSeverity severity = NotificationSeverity.Warning)
        {
            if (NotificationCallback != null)
            {
                // invoke the callback
                NotificationCallback(message, title, severity);
            }
            else
            {
                _uiShellService.EnableModeless(0);
                try
                {
                    var icon = SeverityToIcon(severity);
                    _uiShellService.ShowMessageBox(
                        dwCompRole: 0, // unused, as per MSDN documentation
                        rclsidComp: Guid.Empty, // unused
                        pszTitle: null, // use a null title since the title just becomes another line in the regular message
                        pszText: message,
                        pszHelpFile: null,
                        dwHelpContextID: 0, // required to be 0, as per MSDN documentation
                        msgbtn: OLEMSGBUTTON.OLEMSGBUTTON_OK,
                        msgdefbtn: OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST,
                        msgicon: icon,
                        fSysAlert: 0, // Not system modal
                        pnResult: out var dialogResult);
                }
                finally
                {
                    // if ShowMessageBox() throws we need to ensure that the UI isn't forever stuck in a modal state
                    _uiShellService.EnableModeless(1);
                }
            }
        }
 
        public bool ConfirmMessageBox(string message, string title = null, NotificationSeverity severity = NotificationSeverity.Warning)
        {
            _uiShellService.EnableModeless(0);
            try
            {
                var icon = SeverityToIcon(severity);
                _uiShellService.ShowMessageBox(
                    dwCompRole: 0, // unused, as per MSDN documentation
                    rclsidComp: Guid.Empty, // unused
                    pszTitle: null, // use a null title since the title just becomes another line in the regular message
                    pszText: message,
                    pszHelpFile: null,
                    dwHelpContextID: 0, // required to be 0, as per MSDN documentation
                    msgbtn: OLEMSGBUTTON.OLEMSGBUTTON_YESNO,
                    msgdefbtn: OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST,
                    msgicon: icon,
                    fSysAlert: 0, // Not system modal
                    pnResult: out var dialogResult);
 
                // The dialogResult is 6 when the Yes button is clicked.
                return dialogResult == 6;
            }
            finally
            {
                // if ShowMessageBox() throws we need to ensure that the UI isn't forever stuck in a modal state
                _uiShellService.EnableModeless(1);
            }
        }
 
        private static OLEMSGICON SeverityToIcon(NotificationSeverity severity)
        {
            OLEMSGICON result;
            switch (severity)
            {
                case NotificationSeverity.Information:
                    result = OLEMSGICON.OLEMSGICON_INFO;
                    break;
                case NotificationSeverity.Warning:
                    result = OLEMSGICON.OLEMSGICON_WARNING;
                    break;
                default:
                    // Error
                    result = OLEMSGICON.OLEMSGICON_CRITICAL;
                    break;
            }
 
            return result;
        }
    }
}