Functions/GenXdev.Webbrowser/Set-RemoteDebuggerPortInBrowserShortcuts.cs

// ################################################################################
// Part of PowerShell module : GenXdev.Webbrowser
// Original cmdlet filename : Set-RemoteDebuggerPortInBrowserShortcuts.cs
// Original author : René Vaessen / GenXdev
// Version : 1.302.2025
// ################################################################################
// Copyright (c) René Vaessen / GenXdev
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ################################################################################
 
 
 
using System;
using System.IO;
using System.Management.Automation;
 
namespace GenXdev.Webbrowser
{
    /// <summary>
    /// <para type="synopsis">
    /// Updates browser shortcuts to enable remote debugging ports.
    /// </para>
    ///
    /// <para type="description">
    /// Modifies Chrome and Edge browser shortcuts to include remote debugging
    /// port parameters. This enables automation scripts to interact with the
    /// browsers through their debugging interfaces. Handles both user-specific
    /// and system-wide shortcuts.
    /// </para>
    ///
    /// <para type="description">
    /// The function:<br/>
    /// - Removes any existing debugging port parameters<br/>
    /// - Adds current debugging ports for Chrome and Edge<br/>
    /// - Updates shortcuts in common locations (Desktop, Start Menu, Quick
    /// Launch)<br/>
    /// - Requires administrative rights for system-wide shortcuts
    /// </para>
    ///
    /// <example>
    /// <para>Update all browser shortcuts with debugging ports</para>
    /// <para>
    /// This example updates all Chrome and Edge shortcuts with their
    /// respective debugging ports.
    /// </para>
    /// <code>
    /// Set-RemoteDebuggerPortInBrowserShortcuts
    /// </code>
    /// </example>
    ///
    /// <example>
    /// <para>Preview changes without applying them</para>
    /// <para>
    /// This example uses WhatIf to preview what changes would be made without
    /// actually modifying any shortcuts.
    /// </para>
    /// <code>
    /// Set-RemoteDebuggerPortInBrowserShortcuts -WhatIf
    /// </code>
    /// </example>
    /// </summary>
    [Cmdlet(VerbsCommon.Set, "RemoteDebuggerPortInBrowserShortcuts",
        SupportsShouldProcess = true)]
    public class SetRemoteDebuggerPortInBrowserShortcutsCommand :
        PSGenXdevCmdlet
    {
        private dynamic shell;
 
        /// <summary>
        /// Initialize the cmdlet by creating the WScript.Shell COM object for
        /// shortcut manipulation
        /// </summary>
        protected override void BeginProcessing()
        {
 
            // Create COM object for Windows Script Host shell automation
            var shellType = Type.GetTypeFromProgID("WScript.Shell");
            shell = Activator.CreateInstance(shellType);
 
            WriteVerbose(
                "Created WScript.Shell COM object for shortcut management"
            );
        }
 
        /// <summary>
        /// Process record - main cmdlet logic that updates browser shortcuts
        /// </summary>
        protected override void ProcessRecord()
        {
 
            // Configure Chrome debugging settings
            var chromePort = InvokeScript<int>(
                "GenXdev.Webbrowser\\Get-ChromeRemoteDebuggingPort"
            );
 
            var chromeParam = $" --remote-allow-origins=* " +
                $"--remote-debugging-port={chromePort}";
 
            WriteVerbose($"Configuring Chrome debugging port: {chromePort}");
 
            // Get environment variables for path construction
            var appData = Environment.GetEnvironmentVariable("AppData");
            var programData =
                Environment.GetEnvironmentVariable("ProgramData");
 
            // Get StartMenu and Desktop paths from Get-KnownFolderPath
            var getKnownFolderScript = ScriptBlock.Create(
                "param($folder) GenXdev.Windows\\Get-KnownFolderPath " +
                "-KnownFolder $folder"
            );
 
            var startMenuPath = getKnownFolderScript.Invoke("StartMenu")
                .Count > 0 ? getKnownFolderScript.Invoke("StartMenu")[0]
                .BaseObject.ToString() : null;
 
            var desktopPath = getKnownFolderScript.Invoke("Desktop")
                .Count > 0 ? getKnownFolderScript.Invoke("Desktop")[0]
                .BaseObject.ToString() : null;
 
            // Define Chrome shortcut paths to process
            var chromePaths = new[]
            {
                Path.Combine(appData, "Microsoft", "Internet Explorer",
                    "Quick Launch", "User Pinned", "TaskBar",
                    "Google Chrome.lnk"),
                Path.Combine(programData, "Microsoft", "Windows",
                    "Start Menu", "Programs", "Google Chrome.lnk"),
                startMenuPath != null ?
                    Path.Combine(startMenuPath, "Google Chrome.lnk") : null,
                desktopPath != null ?
                    Path.Combine(desktopPath, "Google Chrome.lnk") : null
            };
 
            // Update Chrome shortcuts
            foreach (var pathPattern in chromePaths)
            {
 
                if (string.IsNullOrEmpty(pathPattern)) continue;
 
                // Use Get-ChildItem to find shortcuts (handles recursion)
                var getItemScript = ScriptBlock.Create(
                    "param($path) Microsoft.PowerShell.Management\\" +
                    "Get-ChildItem -LiteralPath $path -File -Recurse " +
                    "-ErrorAction SilentlyContinue"
                );
 
                var items = getItemScript.Invoke(pathPattern);
 
                foreach (PSObject item in items)
                {
 
                    var fullName = item.Properties["FullName"]?.Value?
                        .ToString();
 
                    if (string.IsNullOrEmpty(fullName)) continue;
 
                    if (ShouldProcess(
                        fullName,
                        $"Update Chrome shortcut with debug port " +
                        $"{chromePort}"))
                    {
 
                        try
                        {
 
                            // Load the shortcut
                            dynamic shortcut =
                                shell.CreateShortcut(fullName);
 
                            // Get current arguments
                            string currentArgs = shortcut.Arguments ?? "";
 
                            // Remove '222' artifact from arguments
                            currentArgs = currentArgs.Replace("222", "");
 
                            // Remove previous port parameters
                            string cleanedArgs =
                                RemovePreviousPortParam(currentArgs);
 
                            // Add Chrome debugging parameters
                            shortcut.Arguments =
                                $"{cleanedArgs} {chromeParam}".Trim();
 
                            // Save the shortcut
                            shortcut.Save();
 
                            WriteVerbose(
                                $"Updated Chrome shortcut: {fullName}"
                            );
                        }
                        catch (Exception ex)
                        {
 
                            WriteVerbose(
                                $"Failed to update Chrome shortcut: " +
                                $"{fullName} - {ex.Message}"
                            );
                        }
                    }
                }
            }
 
            // Configure Edge debugging settings
            var edgePort = InvokeScript<int>(
                "GenXdev.Webbrowser\\Get-EdgeRemoteDebuggingPort"
            );
 
            var edgeParam = $" --remote-allow-origins=* " +
                $"--remote-debugging-port={edgePort}";
 
            WriteVerbose($"Configuring Edge debugging port: {edgePort}");
 
            // Define Edge shortcut paths to process
            var edgePaths = new[]
            {
                Path.Combine(appData, "Microsoft", "Internet Explorer",
                    "Quick Launch", "User Pinned", "TaskBar",
                    "Microsoft Edge.lnk"),
                Path.Combine(programData, "Microsoft", "Windows",
                    "Start Menu", "Programs", "Microsoft Edge.lnk"),
                startMenuPath != null ?
                    Path.Combine(startMenuPath, "Microsoft Edge.lnk") : null,
                desktopPath != null ?
                    Path.Combine(desktopPath, "Microsoft Edge.lnk") : null
            };
 
            // Update Edge shortcuts
            foreach (var pathPattern in edgePaths)
            {
 
                if (string.IsNullOrEmpty(pathPattern)) continue;
 
                // Use Get-ChildItem to find shortcuts (handles recursion)
                var getItemScript = ScriptBlock.Create(
                    "param($path) Microsoft.PowerShell.Management\\" +
                    "Get-ChildItem -LiteralPath $path -File -Recurse " +
                    "-ErrorAction SilentlyContinue"
                );
 
                var items = getItemScript.Invoke(pathPattern);
 
                foreach (PSObject item in items)
                {
 
                    var fullName = item.Properties["FullName"]?.Value?
                        .ToString();
 
                    if (string.IsNullOrEmpty(fullName)) continue;
 
                    if (ShouldProcess(
                        fullName,
                        $"Update Edge shortcut with debug port {edgePort}"))
                    {
 
                        try
                        {
 
                            // Load the shortcut
                            dynamic shortcut =
                                shell.CreateShortcut(fullName);
 
                            // Get current arguments and remove edge param
                            string currentArgs = shortcut.Arguments ?? "";
                            currentArgs = currentArgs.Replace(edgeParam, "")
                                .Trim();
 
                            // Remove previous port parameters
                            string cleanedArgs =
                                RemovePreviousPortParam(currentArgs);
 
                            // Add Edge debugging parameters
                            shortcut.Arguments =
                                $"{cleanedArgs}{edgeParam}";
 
                            // Save the shortcut
                            shortcut.Save();
 
                            WriteVerbose(
                                $"Updated Edge shortcut: {fullName}"
                            );
                        }
                        catch (Exception ex)
                        {
 
                            WriteVerbose(
                                $"Failed to update Edge shortcut: " +
                                $"{fullName} - {ex.Message}"
                            );
                        }
                    }
                }
            }
        }
 
        /// <summary>
        /// End processing - cleanup logic
        /// </summary>
        protected override void EndProcessing()
        {
        }
 
        /// <summary>
        /// Sanitizes shortcut arguments by removing existing debugging port
        /// settings
        /// </summary>
        /// <param name="arguments">
        /// The current shortcut arguments string to clean
        /// </param>
        /// <returns>Cleaned arguments string</returns>
        private string RemovePreviousPortParam(string arguments)
        {
 
            // Initialize working copy of arguments
            string cleanedArgs = arguments ?? "";
 
            // Find first occurrence of port parameter
            int portParamIndex =
                cleanedArgs.IndexOf("--remote-debugging-port=");
 
            // Continue cleaning while port parameters exist
            while (portParamIndex >= 0)
            {
 
                if (ShouldProcess(
                    $"Removing debug port parameter at position " +
                    $"{portParamIndex}",
                    "Remove port parameter?",
                    "Cleaning shortcut arguments"))
                {
 
                    // Remove port parameter and preserve other arguments
                    cleanedArgs = cleanedArgs.Substring(0, portParamIndex)
                        .Trim() + " " + (portParamIndex + 25 <
                        cleanedArgs.Length ?
                        cleanedArgs.Substring(portParamIndex + 25) : "")
                        .Trim();
 
                    // Remove any remaining port number digits
                    while (cleanedArgs.Length > 0 &&
                        "012345679".IndexOf(cleanedArgs[0]) >= 0)
                    {
 
                        cleanedArgs = cleanedArgs.Length > 1 ?
                            cleanedArgs.Substring(1) : "";
                    }
                }
 
                // Check for additional port parameters
                portParamIndex =
                    cleanedArgs.IndexOf("--remote-debugging-port=");
            }
 
            return cleanedArgs;
        }
    }
}