none
Unable to get Window events for VisStudio main window?

    Question

  • Hello,
    I have a C# VisStudio Add-in that implements a WindowActivated handler; the event handler is correctly hooked to the WindowEvents.WindowActivated event.

    It seems that Window events for the main application window are not included -- is that true?
    Is there any way to get events, such as got focus/lost focus, for the main application window?

    Thanks for any tips or reference...
    Tuesday, July 07, 2009 12:07 PM

Answers

  • Thanks Bert,

    I completely forgot about IVsBroadcastMessageEvents. The documentation for IVsBroadcaseMessageEvents.OnBroadcastMessage even mentions that WM_ACTIVATEAPP is passed to this event. This is definately a cleaner option that subclassing the IDE's main HWND.

    Note, you'll need to install the Visual Studio SDK, and reference the following assemblies:

       Microsoft.VisualStudio.Shell.Interop.dll
       Microsoft.VisualStudio.Shell.9.0.dll
       Microsoft.VisualStudio.OLE.Interop.dll

    AutoSaveAddin (new and improved)
    =======================

    using

     

    System;
    using System.Windows.Forms;
    using Extensibility;
    using EnvDTE;
    using EnvDTE80;

    using
    Microsoft.VisualStudio.Shell.Interop;
    using Microsoft.VisualStudio.Shell;
    using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider;

    namespace
    AutoSaveAddin
    {
       public class Connect : IDTExtensibility2, IVsBroadcastMessageEvents
       {
          private const int WM_ACTIVATEAPP = 0x001C;
          private DTE2 applicationObject;
          private AddIn addInInstance;
          private IVsShell vsShell;
          private uint abmCookie;

          public
    Connect() {}

          public
    void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
          {
             applicationObject = (
    DTE2)application;
             addInInstance = (
    AddIn)addInInst;

             ServiceProvider
    sp = new ServiceProvider((IOleServiceProvider)applicationObject);
             vsShell = (
    IVsShell)sp.GetService(typeof(SVsShell));
             vsShell.AdviseBroadcastMessages(
    this, out abmCookie);
          }

          public
    void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)
          {
             if (abmCookie != 0)
             {
                if (vsShell != null)
                   vsShell.UnadviseBroadcastMessages(abmCookie);
                abmCookie = 0;
             }
          }

          public
    void OnAddInsUpdate(ref Array custom) {}
          public void OnStartupComplete(ref Array custom) {}
          public void OnBeginShutdown(ref Array custom) {}

          public
    int OnBroadcastMessage(uint msg, IntPtr wParam, IntPtr lParam)
          {
             if (msg==WM_ACTIVATEAPP && 0==(int)wParam)
                applicationObject.ExecuteCommand(
    "SaveAll", "");

             return
    0;
          }
       }
    }

    Sincerely,
    Ed Dore
    Thursday, July 09, 2009 5:43 PM
    Moderator

All replies

  • I don't believe there's a way to track this using VS automation or VS SDK APIs. You might be able to subclass the MainWindow.Hwnd, and watch for the WM_NCACTIVATE message.

    However, I'd be curious to know why you want (or need) to track application activation on the main window of the IDE.

    THanks,
    Ed Dore
    Tuesday, July 07, 2009 7:54 PM
    Moderator
  • However, I'd be curious to know why you want (or need) to track application activation on the main window of the IDE.

    THanks,
    Ed Dore
    The editor I use 95% of the time has a great feature -- "Save files on loss of focus".
    If VisStudio has that, I haven't found it, so I thought I would implement it, as I take it for granted, now.

    Subclassing MainWindow.Hwnd seems like a lot of work (particularly in a C# Add-in) for something that should be pretty straightforward in VS automation.
    • Edited by Number8 Tuesday, July 07, 2009 8:36 PM
    Tuesday, July 07, 2009 8:31 PM
  • I agree. It would be nice if the IDE actually fired the WindowActivated event for the main IDE window. However, implementing this by subclassing the main IDE's HWND isn't terribly difficult. The following took @5 minutes to implement:

    using

     

    System;
    using System.Windows.Forms;
    using Extensibility;
    using EnvDTE;
    using EnvDTE80;

    namespace
    AutoSaveAddin
    {
       public class SubclassedWindow : NativeWindow
       {
          private const int WM_ACTIVATEAPP = 0x001C;
          private DTE2 dte;

          public
    SubclassedWindow(int hWnd, DTE2 dte)
          {
             this.dte = dte;
             AssignHandle((
    IntPtr)hWnd);
          }

          protected
    override void WndProc(ref Message m)
          {
             if (m.Msg == WM_ACTIVATEAPP)
             {
                if (0 == (int)m.WParam)
                   dte.ExecuteCommand(
    "SaveAll", "");
             }
             base.WndProc(ref m);
          }
       }

       public
    class Connect : IDTExtensibility2
       {
          private DTE2 applicationObject;
          private AddIn addInInstance;
          private SubclassedWindow subclassedMainWindow;

          public
    Connect() {}

          public
    void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
          {
             applicationObject = (
    DTE2)application;
             addInInstance = (
    AddIn)addInInst;
             subclassedMainWindow =
    new SubclassedWindow(applicationObject.MainWindow.HWnd, applicationObject);
          }

          public
    void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)
          {
             if (subclassedMainWindow != null)
             {
                subclassedMainWindow.ReleaseHandle();
                subclassedMainWindow =
    null;
             }
          }

          public
    void OnAddInsUpdate(ref Array custom) {}
          public void OnStartupComplete(ref Array custom) {}
          public void OnBeginShutdown(ref Array custom) {}
       }
    }

    Sincerely,
    Ed Dore
    Tuesday, July 07, 2009 10:50 PM
    Moderator
  • A solution without subclassing would be implementing IVsBroadcastMessageEvents. (You can register implementations via IVsShell.AdviseBroadcastMessages)
    Wednesday, July 08, 2009 11:56 AM
  • Thanks Bert,

    I completely forgot about IVsBroadcastMessageEvents. The documentation for IVsBroadcaseMessageEvents.OnBroadcastMessage even mentions that WM_ACTIVATEAPP is passed to this event. This is definately a cleaner option that subclassing the IDE's main HWND.

    Note, you'll need to install the Visual Studio SDK, and reference the following assemblies:

       Microsoft.VisualStudio.Shell.Interop.dll
       Microsoft.VisualStudio.Shell.9.0.dll
       Microsoft.VisualStudio.OLE.Interop.dll

    AutoSaveAddin (new and improved)
    =======================

    using

     

    System;
    using System.Windows.Forms;
    using Extensibility;
    using EnvDTE;
    using EnvDTE80;

    using
    Microsoft.VisualStudio.Shell.Interop;
    using Microsoft.VisualStudio.Shell;
    using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider;

    namespace
    AutoSaveAddin
    {
       public class Connect : IDTExtensibility2, IVsBroadcastMessageEvents
       {
          private const int WM_ACTIVATEAPP = 0x001C;
          private DTE2 applicationObject;
          private AddIn addInInstance;
          private IVsShell vsShell;
          private uint abmCookie;

          public
    Connect() {}

          public
    void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
          {
             applicationObject = (
    DTE2)application;
             addInInstance = (
    AddIn)addInInst;

             ServiceProvider
    sp = new ServiceProvider((IOleServiceProvider)applicationObject);
             vsShell = (
    IVsShell)sp.GetService(typeof(SVsShell));
             vsShell.AdviseBroadcastMessages(
    this, out abmCookie);
          }

          public
    void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)
          {
             if (abmCookie != 0)
             {
                if (vsShell != null)
                   vsShell.UnadviseBroadcastMessages(abmCookie);
                abmCookie = 0;
             }
          }

          public
    void OnAddInsUpdate(ref Array custom) {}
          public void OnStartupComplete(ref Array custom) {}
          public void OnBeginShutdown(ref Array custom) {}

          public
    int OnBroadcastMessage(uint msg, IntPtr wParam, IntPtr lParam)
          {
             if (msg==WM_ACTIVATEAPP && 0==(int)wParam)
                applicationObject.ExecuteCommand(
    "SaveAll", "");

             return
    0;
          }
       }
    }

    Sincerely,
    Ed Dore
    Thursday, July 09, 2009 5:43 PM
    Moderator
  • Thanks for the code.
    I was trying to figure out how to use IVsBroadcastMessageEvents, and this was the step I was missing:
     ServiceProvider sp = new ServiceProvider((IOleServiceProvider)applicationObject);
    That is, getting the ServiceProvider interface from the DTE2 object.

    Seems like a much cleaner solution to the problem than subclassing.


    Thursday, July 09, 2009 8:27 PM