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).
Also note, if certain types of errors occur, the program logs them to a log file in the same folder.
Here is the code:
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