/* Reload the ribbon on Office Applications. * * Word (and some other Office applications) only seem to reload the ribbon when changing between different Word * windows. So, if two Word windows are open then WM_ACTIVATE is sent to each one to simulate this. If only a single * window is open then a new one is created and closed before sending WM_ACTIVATE. * * Copyright 2019 Raising the Floor - International * * Licensed under the New BSD license. You may not use this file except in * compliance with this License. * * The R&D leading to these results received funding from the * Department of Education - Grant H421A150005 (GPII-APCP). However, * these results do not necessarily represent the policy of the * Department of Education, and you should not assume endorsement by the * Federal Government. * * You may obtain a copy of the License at * https://github.com/GPII/universal/blob/master/LICENSE.txt */ namespace GPIIOffice { using System; using System.Runtime.InteropServices; using System.Threading.Tasks; #if GOT_OFFICE // GOT_OFFICE can be defined during development if the Office PIAs are installed. // (Put it in a project and add a reference to Microsoft.Office.Interop.Word) using Word = Microsoft.Office.Interop.Word; #endif public class RibbonReload { private const int WM_ACTIVATE = 0x6; private const int HWND_TOPMOST = -1; private const int HWND_NOTOPMOST = -2; private const int SWP_NOMOVE = 0x0002; private const int SWP_NOSIZE = 0x0001; [DllImport("user32.dll")] private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, IntPtr lParam); [DllImport("user32.dll")] private static extern bool SetWindowPos(int hWnd, int hWndInsertAfter, int x, int y, int cx, int cy, int uFlags); [DllImport("user32.dll")] private static extern bool LockSetForegroundWindow(uint uLockCode); #if STANDALONE static void Main(string[] args) { new RibbonReload().Invoke(new { applicationName = "Word" }).Wait(); } #endif /// /// Entry point for edge.js /// /// object containing 'applicationName' /// true on success public async Task Invoke(dynamic input) { try { return ReloadRibbon(input.applicationName, input.applicationObject); } catch (Exception) { return false; } } /// /// Reloads the ribbon for the given Office application. /// /// The Office application; Word, Excel /// An instance of a office COM object (for testing) static bool ReloadRibbon(string applicationName, dynamic applicationObject = null) { // Prevent Word from stealing the focus. LockSetForegroundWindow(1); // Get the running instance. #if GOT_OFFICE Word.Application app = Marshal.GetActiveObject("Word.Application") as Word.Application; #else dynamic app = applicationObject ?? Marshal.GetActiveObject(applicationName + ".Application"); #endif if (app == null) { // No instance means nothing to update. return true; } int windowCount = app.Windows.Count; // A word window will update the ribbon when it has been activated, but only if another Word window was // active sometime earlier. // A single window will need another window to be activated first. if (windowCount == 1) { #if GOT_OFFICE Word.Window window = app.ActiveWindow; #else dynamic window = app.ActiveWindow; #endif try { // Make this window on top of everything, to hide the flicker of the new window. SetWindowPos(window.Hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); // Create and close a new window window.NewWindow().Close(); } finally { // Stop the window from being over everything. SetWindowPos(window.Hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); } } // Send a message to each Word window to make it think it's been activated, and therefor reload the Ribbon. for (int i = 0; i < windowCount; i++) { SendMessage((IntPtr)app.Windows[i + 1].Hwnd, WM_ACTIVATE, 1, IntPtr.Zero); } return true; } } }