using System;
using System.Diagnostics;
using System.Drawing;
using System.Runtime.InteropServices;
using Microsoft.Win32;
/// <summary>
/// DisplayFusion Function script that enables complete replacement of the Windows taskbar.
///
/// This script:
/// 1. Makes the Windows taskbar transparent (which inherently makes it stop responding to user input)
/// 2. Configures required DisplayFusion and Explorer settings for proper system tray icon visibility
/// 3. Ensures the DisplayFusion taskbar is correctly positioned on the appropriate monitor
/// 4. Monitors and corrects taskbar position during an initialization period
///
/// Compatibility: Works on Windows 7 and later versions
///
/// Setup Recommendations:
/// - Add a "DisplayFusion Starts" trigger to run this script at startup
/// - Add the "Toggle Windows Taskbar" script as a "DisplayFusion Exits" trigger to restore
/// the Windows taskbar when DisplayFusion closes (otherwise, you may need to restart Explorer)
///
/// First-run behavior: The script may restart Explorer and/or DisplayFusion to apply initial settings.
/// </summary>
public static class DisplayFusionFunction
{
// P/Invoke for SetWindowPos since BFS.Window.SetSizeAndLocation doesn't work reliably on DisplayFusion taskbars
[DllImport("user32.dll")]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
// P/Invoke for sending shutdown messages to DisplayFusion for clean restart
[DllImport("user32.dll")]
private static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
private static extern uint RegisterWindowMessage(string lpString);
// Constants for Windows API calls
// SWP_NOZORDER (0x0004): Maintains Z-order
// SWP_NOACTIVATE (0x0010): Doesn't activate the window
// SWP_SHOWWINDOW (0x0040): Displays the window
// SWP_ASYNCWINDOWPOS (0x4000): Posts the request rather than processing synchronously
private const uint SWP_FLAGS = 0x4054;
// HWND_BROADCAST special value to send messages to all top-level windows
private static readonly IntPtr HWND_BROADCAST = new IntPtr(0xffff);
// Enum to track which edge of the monitor the taskbar is positioned on
private enum TaskbarPosition { Bottom, Top, Left, Right }
/// <summary>
/// Main entry point for the DisplayFusion scripted function.
/// Coordinates the entire taskbar replacement process including registry settings,
/// process management, and taskbar positioning.
/// </summary>
/// <param name="windowHandle">Handle passed by DisplayFusion when the script is triggered</param>
public static void Run(IntPtr windowHandle)
{
Log("Script started");
// Check and update registry settings for Explorer and DisplayFusion
// Returns tuple indicating if restarts are needed
var (explorerRestartNeeded, dfRestartNeeded) = UpdateRegistrySettings();
// If needed, restart Explorer before continuing to apply registry changes
if (explorerRestartNeeded)
{
Log("Restarting Explorer to apply registry changes");
RestartExplorer();
}
// If DisplayFusion registry was changed, restart it and exit
// The script will be re-run when DisplayFusion restarts if triggered by startup
if (dfRestartNeeded)
{
Log("Registry changes require DisplayFusion restart");
RestartDisplayFusion();
return;
}
// Find Windows taskbar and make it transparent
IntPtr winTaskbar = BFS.Window.GetWindowByClass("Shell_TrayWnd");
if (winTaskbar == IntPtr.Zero)
{
Log("Error: Could not find Windows taskbar");
return;
}
// Determine which monitor contains the Windows taskbar
// We need this to ensure we create the DisplayFusion taskbar on the same monitor
uint monitorId = BFS.Monitor.GetMonitorIDByWindow(winTaskbar);
Rectangle monitorBounds = BFS.Monitor.GetMonitorBoundsByID(monitorId);
Log($"Windows taskbar found on monitor {monitorId}");
// Set Windows taskbar to completely transparent (0% opacity)
// This effectively disables it while keeping system tray functionality
BFS.Window.SetTransparency(winTaskbar, 0.0m);
Log("Set Windows taskbar to transparent");
// Find existing DisplayFusion taskbar or create a new one
IntPtr dfTaskbar = FindOrCreateTaskbar(monitorId);
if (dfTaskbar == IntPtr.Zero)
{
Log("Error: Could not find or create DisplayFusion taskbar");
return;
}
// Check and fix the initial taskbar position if needed
bool initialCorrected = CheckAndFixTaskbarPosition(dfTaskbar, monitorBounds);
if (initialCorrected)
Log("Initial position successfully corrected");
else
Log("No initial position correction needed");
// Continue monitoring taskbar position for a short period to ensure stability
// Testing has shown that if position issues occur, they happen within ~10 seconds
MonitorTaskbarPosition(monitorId, monitorBounds);
Log("Script execution completed successfully");
}
/// <summary>
/// Logs a message to the DisplayFusion log for debugging and tracking purposes
/// </summary>
private static void Log(string message)
{
BFS.General.LogText(message);
}
/// <summary>
/// Updates registry settings for Explorer and DisplayFusion to optimize taskbar replacement
/// </summary>
/// <returns>
/// Tuple indicating if Explorer and/or DisplayFusion need to be restarted to apply changes
/// </returns>
private static (bool explorerNeeded, bool dfNeeded) UpdateRegistrySettings()
{
bool explorerRestartNeeded = false;
bool dfRestartNeeded = false;
// Check Explorer AutoTray setting - When set to 0, it ensures all system tray icons are visible
// This is critical for taskbar replacement as otherwise some system tray icons might not be
// accessible after making the Windows taskbar transparent
try
{
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer", true))
{
if (key != null)
{
object currentValue = key.GetValue("EnableAutoTray", null);
// If the value doesn't exist, isn't an integer, or isn't set to 0, update it
if (currentValue == null || !(currentValue is int) || ((int)currentValue != 0))
{
Log("Setting EnableAutoTray=0 to show all system tray icons");
key.SetValue("EnableAutoTray", 0, RegistryValueKind.DWord);
explorerRestartNeeded = true;
}
}
}
}
catch (Exception ex)
{
Log($"Error accessing Explorer registry: {ex.Message}");
}
// Check DisplayFusion settings to ensure taskbars appear on all monitors
// including the primary monitor that has the Windows taskbar
try
{
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Binary Fortress Software\DisplayFusion", true))
{
if (key != null)
{
string taskbarsValue = key.GetValue("TaskbarsAllMonitors", "0").ToString();
if (taskbarsValue != "1")
{
Log("Setting DisplayFusion registry values");
// Enable taskbars on all monitors, including primary
key.SetValue("TaskbarsAllMonitors", "1", RegistryValueKind.String);
// Set a faster polling interval for more responsive taskbar updates
key.SetValue("TaskbarPollInterval", "150", RegistryValueKind.String);
dfRestartNeeded = true;
}
}
}
}
catch (Exception ex)
{
Log($"Error accessing DisplayFusion registry: {ex.Message}");
}
return (explorerRestartNeeded, dfRestartNeeded);
}
/// <summary>
/// Gracefully restarts Explorer to apply registry changes.
/// This method specifically targets only the Explorer process running the taskbar,
/// rather than killing all Explorer instances (which would close all open file windows).
/// </summary>
private static void RestartExplorer()
{
try
{
// Find Windows taskbar process
IntPtr taskbarWindow = BFS.Window.GetWindowByClass("Shell_TrayWnd");
if (taskbarWindow != IntPtr.Zero)
{
uint taskbarPid = BFS.Application.GetAppIDByWindow(taskbarWindow);
if (taskbarPid != 0)
{
// Kill only the taskbar's Explorer process, not all Explorer windows
Process.GetProcessById((int)taskbarPid).Kill();
Log("Explorer taskbar process terminated");
}
}
// Start a new Explorer.exe process
Process.Start("explorer.exe");
// Wait for taskbar to reappear, with a timeout of 5 seconds
DateTime timeout = DateTime.Now.AddSeconds(5);
while (DateTime.Now < timeout && BFS.Window.GetWindowByClass("Shell_TrayWnd") == IntPtr.Zero)
{
BFS.General.ThreadWait(250);
}
// Give Explorer a moment to fully initialize
BFS.General.ThreadWait(500);
}
catch (Exception ex)
{
Log($"Error restarting Explorer: {ex.Message}");
// Fallback: try to start Explorer with no error handling
try { Process.Start("explorer.exe"); } catch { }
}
}
/// <summary>
/// Gracefully restarts DisplayFusion to apply registry changes.
/// Uses PowerShell to handle the restart sequence, as there's no direct BFS method
/// that allows restarting DisplayFusion and preserving the script's execution state.
/// </summary>
private static void RestartDisplayFusion()
{
try
{
Log("Restarting DisplayFusion to apply registry changes...");
// Get the path to DisplayFusion executable
string dfPath = BFS.General.GetAppExecutable();
// Create a separate PowerShell process to handle the restart sequence
// This allows our script to exit while the restart process continues
string psScript = $@"
$dfPath = '{dfPath.Replace("'", "''")}';
# Wait for DisplayFusion to exit
while (Get-Process -Name 'DisplayFusion' -ErrorAction SilentlyContinue) {{
Start-Sleep -Milliseconds 100
}};
# Start DisplayFusion again with RestartApp flag
Start-Process -FilePath $dfPath -ArgumentList '-RestartApp 1';
";
// Start PowerShell with the restart script
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = "powershell.exe";
psi.Arguments = $"-NoProfile -ExecutionPolicy Bypass -Command \"{psScript}\"";
psi.WindowStyle = ProcessWindowStyle.Hidden;
psi.CreateNoWindow = true;
Process.Start(psi);
// Send the graceful shutdown message to DisplayFusion
// This uses DisplayFusion's custom window message for clean shutdown
uint message = RegisterWindowMessage("BFS_DISPLAYFUSION_CLOSE");
PostMessage(HWND_BROADCAST, message, IntPtr.Zero, IntPtr.Zero);
Log("Restart signal sent to DisplayFusion");
}
catch (Exception ex)
{
Log($"Error restarting DisplayFusion: {ex.Message}");
}
}
/// <summary>
/// Finds an existing DisplayFusion taskbar on the specified monitor
/// or creates a new one if none exists.
/// </summary>
/// <param name="monitorId">The ID of the monitor where the taskbar should be located</param>
/// <returns>Handle to the DisplayFusion taskbar window, or IntPtr.Zero if failed</returns>
private static IntPtr FindOrCreateTaskbar(uint monitorId)
{
// Look for existing taskbar first
IntPtr dfTaskbar = FindDFTaskbarOnMonitor(monitorId);
if (dfTaskbar != IntPtr.Zero)
{
Log($"Found existing DisplayFusion taskbar on monitor {monitorId}");
return dfTaskbar;
}
// Create new taskbar if not found
Log($"Creating DisplayFusion taskbar on monitor {monitorId}");
BFS.DisplayFusion.EnableTaskbar(monitorId);
// Wait for taskbar creation with a maximum wait time of 5 seconds
// The taskbar creation isn't instant and we need to allow time for it to appear
for (int i = 0; i < 50; i++) // 5 seconds maximum (100ms × 50)
{
BFS.General.ThreadWait(100);
dfTaskbar = FindDFTaskbarOnMonitor(monitorId);
if (dfTaskbar != IntPtr.Zero)
return dfTaskbar;
}
return IntPtr.Zero;
}
/// <summary>
/// Searches for a DisplayFusion taskbar window on the specified monitor.
/// DisplayFusion taskbars have a specific window class name format "DFTaskbar:*"
/// </summary>
/// <param name="monitorId">The ID of the monitor to search for a taskbar</param>
/// <returns>Handle to the DisplayFusion taskbar window, or IntPtr.Zero if not found</returns>
private static IntPtr FindDFTaskbarOnMonitor(uint monitorId)
{
// Iterate through all top-level windows
foreach (IntPtr handle in BFS.Window.GetAllWindowHandles())
{
string windowClass = BFS.Window.GetClass(handle);
// DisplayFusion taskbars have window class names starting with "DFTaskbar:"
if (windowClass.StartsWith("DFTaskbar:") && BFS.Monitor.GetMonitorIDByWindow(handle) == monitorId)
return handle;
}
return IntPtr.Zero;
}
/// <summary>
/// Checks if the taskbar is positioned correctly, and adjusts it if needed.
/// This ensures the DisplayFusion taskbar spans the full width/height of the monitor
/// and is properly aligned to the appropriate edge.
/// </summary>
/// <param name="taskbarWindow">Handle to the DisplayFusion taskbar window</param>
/// <param name="monitorBounds">Rectangle representing the monitor's bounds</param>
/// <returns>True if position was corrected, false if no correction was needed</returns>
private static bool CheckAndFixTaskbarPosition(IntPtr taskbarWindow, Rectangle monitorBounds)
{
if (taskbarWindow == IntPtr.Zero)
return false;
// Get current taskbar bounds and determine which position it's in
Rectangle currentBounds = BFS.Window.GetBounds(taskbarWindow);
TaskbarPosition position = DetermineTaskbarPosition(currentBounds, monitorBounds);
Rectangle targetBounds = new Rectangle(currentBounds.X, currentBounds.Y, currentBounds.Width, currentBounds.Height);
bool needsCorrection = false;
// Calculate correct taskbar position based on which edge it's attached to
// The taskbar should span the full width/height of the monitor and
// be perfectly aligned with the corresponding edge
switch (position)
{
case TaskbarPosition.Left:
// Left taskbar should be aligned with left edge of monitor and span full height
if (currentBounds.X != monitorBounds.X || currentBounds.Height != monitorBounds.Height)
{
targetBounds.X = monitorBounds.X;
targetBounds.Y = monitorBounds.Y;
targetBounds.Height = monitorBounds.Height;
needsCorrection = true;
}
break;
case TaskbarPosition.Right:
// Right taskbar should be aligned with right edge of monitor and span full height
int rightEdge = monitorBounds.Right - currentBounds.Width;
if (currentBounds.X != rightEdge || currentBounds.Height != monitorBounds.Height)
{
targetBounds.X = rightEdge;
targetBounds.Y = monitorBounds.Y;
targetBounds.Height = monitorBounds.Height;
needsCorrection = true;
}
break;
case TaskbarPosition.Top:
// Top taskbar should be aligned with top edge of monitor and span full width
if (currentBounds.Y != monitorBounds.Y || currentBounds.Width != monitorBounds.Width)
{
targetBounds.X = monitorBounds.X;
targetBounds.Y = monitorBounds.Y;
targetBounds.Width = monitorBounds.Width;
needsCorrection = true;
}
break;
case TaskbarPosition.Bottom:
// Bottom taskbar should be aligned with bottom edge of monitor and span full width
int bottomEdge = monitorBounds.Bottom - currentBounds.Height;
if (currentBounds.Y != bottomEdge || currentBounds.Width != monitorBounds.Width)
{
targetBounds.X = monitorBounds.X;
targetBounds.Y = bottomEdge;
targetBounds.Width = monitorBounds.Width;
needsCorrection = true;
}
break;
}
// Apply correction if taskbar isn't flush against monitor edge
// Using SetWindowPos directly as BFS methods don't reliably work with DisplayFusion taskbars
if (needsCorrection)
{
Log($"Correcting taskbar position for {position} taskbar");
try
{
return SetWindowPos(
taskbarWindow,
IntPtr.Zero,
targetBounds.X,
targetBounds.Y,
targetBounds.Width,
targetBounds.Height,
SWP_FLAGS
);
}
catch (Exception ex)
{
Log($"Position correction failed: {ex.Message}");
return false;
}
}
return false;
}
/// <summary>
/// Determines which side of the monitor the taskbar is positioned on
/// based on its dimensions and position relative to the monitor center.
/// </summary>
/// <param name="taskbarBounds">Rectangle representing the taskbar's bounds</param>
/// <param name="monitorBounds">Rectangle representing the monitor's bounds</param>
/// <returns>TaskbarPosition enum value indicating the taskbar's position</returns>
private static TaskbarPosition DetermineTaskbarPosition(Rectangle taskbarBounds, Rectangle monitorBounds)
{
// Check if taskbar is horizontal or vertical based on dimensions
bool isHorizontal = taskbarBounds.Width > taskbarBounds.Height;
if (isHorizontal)
{
// If horizontal, determine if it's at top or bottom based on Y position
// relative to the vertical center of the monitor
return taskbarBounds.Y < monitorBounds.Y + monitorBounds.Height / 2 ?
TaskbarPosition.Top : TaskbarPosition.Bottom;
}
else
{
// If vertical, determine if it's at left or right based on X position
// relative to the horizontal center of the monitor
return taskbarBounds.X < monitorBounds.X + monitorBounds.Width / 2 ?
TaskbarPosition.Left : TaskbarPosition.Right;
}
}
/// <summary>
/// Monitors taskbar position for a short period to ensure it remains properly positioned.
/// Testing has shown that if the taskbar position is going to shift unexpectedly,
/// it typically happens within about 10 seconds of creation. This method monitors for
/// 15 seconds to ensure stability.
/// </summary>
/// <param name="monitorId">The ID of the monitor containing the taskbar</param>
/// <param name="monitorBounds">Rectangle representing the monitor's bounds</param>
private static void MonitorTaskbarPosition(uint monitorId, Rectangle monitorBounds)
{
Log("Beginning position monitoring (15 seconds max)");
DateTime endTime = DateTime.Now.AddSeconds(15);
while (DateTime.Now < endTime)
{
BFS.General.ThreadWait(100);
// Re-find the taskbar to get a fresh handle
// This ensures we're working with the current window even if it was recreated
IntPtr dfTaskbar = FindDFTaskbarOnMonitor(monitorId);
if (dfTaskbar == IntPtr.Zero)
continue;
// Check if position correction was needed
if (CheckAndFixTaskbarPosition(dfTaskbar, monitorBounds))
{
Log("Taskbar position corrected during monitoring");
return; // Exit early on successful correction
}
}
Log("Monitoring complete - no corrections needed");
}
}