Deribbonizer for Classic File Explorer


hdmi

Jack of all trades – master of none
Pro User
VIP
Local time
6:34 AM
Posts
3,632
Location
Belgium
OS
11 Home
As the title says, this tool (written by me) removes the ribbon from each newly opened File Explorer window if you are using ExplorerPatcher settings > File Explorer > Control Interface > Windows 10 Ribbon (or this tweak—Option Three).

Please be aware that I haven't yet tested the program for very long yet. So, use it at your own risk.

To compile the program, copy-paste the code below to a new batch (.bat) file, then run the batch file. It uses the built-in C# 5 compiler of .NET Framework 4.8.1, which is available in Windows 11 by default. After you run the batch file, you should be able to find the file Deribbonizer.exe in the same folder.

I designed Deribbonizer such a way that it runs as a console application silently in the background. To quit the application, you can simply run it again on the command line with a /quit parameter switch (or simply create a new desktop shortcut, and put the command in its target field).

Deribbonizer.exe /quit

Also note, if certain types of errors occur, the program logs them to a log file in the same folder.

How it works​

Deribbonizer uses Windows built-in UI Automation capabilities to listen for specific Automation events that Windows fires when a window opens. This approach is vastly more efficient compared to constant polling of System.Activator.CreateInstance(Type.GetTypeFromProgID("Shell.Application")).Windows collection object, with some minor caveat.​
To prevent the application's memory usage from growing forever, I made an attempt at implementing Garbage Collection in a more optimized manner. It helps to slow down the memory growth for sure, but doesnt completely fix the problem. So, I had to come up with an additional strategy. That is, to make the application automatically restart itself after it sees that its memory usage got a little bit too big.​
I know that someone who has better knowledge about how to write a mod for Windhawk should be able to make my program obsolete, BUT... my approach doesn't require to hook into explorer.exe, and, you don't need to install anything for it to work. Another reason why I decided to use UI Automation for this purpose was to open the road for future ideas on customizing the classic File Explorer (and various other things...) to add advanced functionalities to it without the need to learn how to write my own mod for Windhawk.​

Here is the code:
Batch:
// 2>NUL&@ECHO OFF&CLS&GOTO batch
/*
:batch
FOR /F "tokens=* USEBACKQ" %%F IN (`REG QUERY "HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" /v InstallPath`) DO SET "InstallPath=%%F"
SET InstallPath=%InstallPath:*REG_SZ =%
FOR /F "tokens=* Eol= " %%F IN ("%InstallPath%") DO SET "InstallPath=%%F"
IF EXIST "%InstallPath%csc.exe" ("%InstallPath%csc.exe" /out:"%~dp0\Deribbonizer.exe" /target:winexe "%~dpnx0" /lib:%InstallPath%WPF /r:UIAutomationTypes.dll,UIAutomationClient.dll) ELSE (ECHO ERROR - Missing csc.exe)
PAUSE&EXIT
*/

using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Automation;
using System.Windows.Forms;

class Program
{
    private const int HWND_MESSAGE = -3; // Message-Only Window
    private const uint WM_CLOSE = 0x0010; // Close message
    private const string WINDOW_CLASS_NAME = "MessageOnlyWindowClass";
    private const string ERROR_LOG_FILE = "error_log.txt";

    private static bool isQuitMsg;
    private static IntPtr hWndFind;
    private static IntPtr hWndMsg;
    private static Process process;

    [DllImport("user32.dll")]
    private static extern bool PostMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);

    [DllImport("user32.dll", SetLastError = true)]
    private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    [DllImport("user32.dll")]
    private static extern IntPtr DefWindowProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);

    [DllImport("user32.dll", SetLastError = true)]
    private static extern ushort RegisterClassEx(ref WNDCLASSEX lpwcx);

    [DllImport("user32.dll", SetLastError = true)]
    private static extern IntPtr CreateWindowEx(
        int dwExStyle, string lpClassName, string lpWindowName,
        int dwStyle, int x, int y, int nWidth, int nHeight,
        IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam);

    [DllImport("user32.dll")]
    private static extern bool DestroyWindow(IntPtr hWnd);

    private static IntPtr WindowProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
    {
        if (msg == WM_CLOSE)
        {
            isQuitMsg = true; // static
            try
            {
                if (process == null || process.HasExited)
                {
                    Environment.Exit(0);
                }
                if (hWndMsg != IntPtr.Zero)
                {
                    DestroyWindow(hWndMsg);
                    hWndMsg = IntPtr.Zero;
                }
                Automation.RemoveAllEventHandlers();
                GarbageCollect();
                process.Dispose();
                process = null;
                Environment.Exit(0);
            }
            catch (Exception ex)
            {
                LogError(ex);
            }
            Environment.Exit(0);
        }
        return DefWindowProc(hWnd, msg, wParam, lParam);
    }

    // ******************************** Main ********************************
    static void Main(string[] args)
    {
        process = Process.GetCurrentProcess(); // static
        bool isQuitCommand = args.Length == 1 && args[0].Equals("/quit", StringComparison.OrdinalIgnoreCase);
        if (!isQuitCommand && args.Length != 0)
        {
            process.Dispose();
            Environment.Exit(0);
        }
        hWndFind = FindWindow(WINDOW_CLASS_NAME, "MessageWindow"); // static
        if (isQuitCommand)
        {
            Abort();
        }
        RegisterAndCreateWindow();
        AddAutomationEventHandler();
        if (hWndFind != IntPtr.Zero)
        {
            if (!PostMessage(hWndFind, WM_CLOSE, IntPtr.Zero, IntPtr.Zero))
            {
                LogError(new Exception("PostMessage failed in Main()"));
            }
            hWndFind = IntPtr.Zero;
        }
        System.Windows.Forms.Timer gcTimer = new System.Windows.Forms.Timer();
        gcTimer.Interval = 30000; // 30 seconds
        gcTimer.Tick += (sender, e) =>
        {
            try
            {
                if (isQuitMsg || process == null || process.HasExited)
                {
                    return;
                }
                Cleanup();
            }
            catch (Exception ex)
            {
                LogError(ex);
            }
        };
        gcTimer.Start();
        Application.Run();
    }
    // **********************************************************************

    private static void RegisterAndCreateWindow()
    {
        WindowProcDelegate windowProc = WindowProc;
        IntPtr functionPointer = Marshal.GetFunctionPointerForDelegate(windowProc);
        WNDCLASSEX wndClass = new WNDCLASSEX
        {
            cbSize = (uint)Marshal.SizeOf(typeof(WNDCLASSEX)),
            style = 0,
            lpfnWndProc = functionPointer,
            cbClsExtra = 0,
            cbWndExtra = 0,
            hInstance = IntPtr.Zero,
            hIcon = IntPtr.Zero,
            hCursor = IntPtr.Zero,
            hbrBackground = IntPtr.Zero,
            lpszMenuName = null,
            lpszClassName = WINDOW_CLASS_NAME,
            hIconSm = IntPtr.Zero
        };
        if (RegisterClassEx(ref wndClass) == 0)
        {
            Abort();
        }
        hWndMsg = CreateWindowEx(0, WINDOW_CLASS_NAME, "MessageWindow", 0, 0, 0, 0, 0, (IntPtr)HWND_MESSAGE,
                                 IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); // static
        if (hWndMsg != IntPtr.Zero)
        {
            return;
        }
        Abort();
    }

    private static void AddAutomationEventHandler()
    {
        Automation.AddAutomationEventHandler(
            WindowPattern.WindowOpenedEvent,
            AutomationElement.RootElement,
            TreeScope.Children,
            (sender, e) =>
            {
                var element = sender as AutomationElement;
                if (element.Current.ClassName == "CabinetWClass")
                {
                    PropertyCondition condition = new PropertyCondition(AutomationElement.ClassNameProperty, "UIRibbonCommandBarDock");
                    AutomationElement child = element.FindFirst(TreeScope.Children, condition);
                    if (child != null)
                    {
                        condition = new PropertyCondition(AutomationElement.ClassNameProperty, "UIRibbonCommandBar");
                        child = child.FindFirst(TreeScope.Children, condition);
                        if (child != null)
                        {
                            IntPtr hWndBar = (IntPtr)child.Current.NativeWindowHandle;
                            child = null;
                            if (hWndBar == IntPtr.Zero)
                            {
                                LogError(new Exception("hWndBar is IntPtr.Zero — UI element not found."));
                            }
                            else
                            {
                                if (!PostMessage(hWndBar, WM_CLOSE, IntPtr.Zero, IntPtr.Zero))
                                {
                                    LogError(new Exception("PostMessage failed in AddAutomationEventHandler()"));
                                }
                            }
                        }
                    }
                    condition = null;
                }
                element = null;
                try
                {
                    if (isQuitMsg || process == null || process.HasExited)
                    {
                        return;
                    }
                    Cleanup();
                }
                catch (Exception ex)
                {
                    LogError(ex);
                }
            }
        );
    }

    private static void Cleanup()
    {
        process.Refresh();
        if (process.WorkingSet64 > 39 << 20)
        {
            isQuitMsg = true; // static
            Process processNew = new Process();
            processNew.StartInfo.UseShellExecute = false;
            processNew.StartInfo.FileName = process.ProcessName + ".exe";
            processNew.Start();
            processNew.Dispose();
            processNew = null;
        }
        GarbageCollect();
    }

    private static void GarbageCollect()
    {
        if (GC.TryStartNoGCRegion(50 << 20))
        {
            GC.EndNoGCRegion();
        }
        GC.Collect(GC.MaxGeneration, GCCollectionMode.Optimized);
        GC.WaitForPendingFinalizers();
    }

    private static void Abort()
    {
        if (hWndFind != IntPtr.Zero)
        {
            if (!PostMessage(hWndFind, WM_CLOSE, IntPtr.Zero, IntPtr.Zero))
            {
                LogError(new Exception("PostMessage failed in Abort()"));
            }
        }
        process.Dispose();
        Environment.Exit(0);
    }

    private static void LogError(Exception ex)
    {
        File.AppendAllText(ERROR_LOG_FILE, String.Format("{0} :\n{1}\n", DateTime.Now, ex.Message));
    }
}

[StructLayout(LayoutKind.Sequential)]
struct WNDCLASSEX
{
    public uint cbSize;
    public uint style;
    public IntPtr lpfnWndProc;
    public int cbClsExtra;
    public int cbWndExtra;
    public IntPtr hInstance;
    public IntPtr hIcon;
    public IntPtr hCursor;
    public IntPtr hbrBackground;
    public string lpszMenuName;
    public string lpszClassName;
    public IntPtr hIconSm;
}

delegate IntPtr WindowProcDelegate(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
 
Windows Build/Version
latest Release Preview Channel (RPC)

My Computers

System One System Two

  • OS
    11 Home
    Computer type
    Laptop
    Manufacturer/Model
    Asus TUF Gaming F16 (2024)
    CPU
    i7 13650HX
    Memory
    16GB DDR5
    Graphics Card(s)
    GeForce RTX 4060 Mobile
    Sound Card
    Eastern Electric MiniMax DAC Supreme; Emotiva UMC-200; Astell & Kern AK240
    Monitor(s) Displays
    Sony Bravia XR-55X90J
    Screen Resolution
    3840×2160
    Hard Drives
    512GB SSD internal
    37TB external
    PSU
    Li-ion
    Cooling
    2× Arc Flow Fans, 4× exhaust vents, 5× heatpipes
    Keyboard
    Logitech K800
    Mouse
    Logitech G402
    Internet Speed
    20Mbit/s up, 250Mbit/s down
    Browser
    FF
  • Operating System
    11 Home
    Computer type
    Laptop
    Manufacturer/Model
    Medion S15450
    CPU
    i5 1135G7
    Memory
    16GB DDR4
    Graphics card(s)
    Intel Iris Xe
    Sound Card
    Eastern Electric MiniMax DAC Supreme; Emotiva UMC-200; Astell & Kern AK240
    Monitor(s) Displays
    Sony Bravia XR-55X90J
    Screen Resolution
    3840×2160
    Hard Drives
    2TB SSD internal
    37TB external
    PSU
    Li-ion
    Mouse
    Logitech G402
    Keyboard
    Logitech K800
    Internet Speed
    20Mbit/s up, 250Mbit/s down
    Browser
    FF

Latest Support Threads

Back
Top Bottom