Search code examples
c#notepad++shortcut

Why does Notepad++ disable/hide all macros when I programmatically edit shortcuts.xml?


If I manually edit %appdata%\Notepad++\shortcuts.xml and manually add a find replace macro (See MACRO below), then close shortcuts.xml and then restart Notepad++, the macro is available to run from the Macro Menu button:

enter image description here

I wrote a program that programmatically edits shortcuts.xml and inserts the very same macro. Notepad++ is not up when I run my program. I know my program is working based on diffing copies of manually modified and programmatically modified shortcuts.xml files. However, when I restart Notepad++ after running my program, the Macro button is "dead", i.e., all macros, although present in shortcuts.xml, disappear from the Macro button.

enter image description here

Is Notepad++ somehow sensitive to its shortcuts.xml file being programmatically manipulated?

MACRO Screenshot (text version follows)

shortcuts.xml

Text Version

<Macro name="Redactor" Ctrl="no" Alt="no" Shift="no" Key="0">
  <Action type="0" message="2422" wParam="0" lParam="0" sParam="" />
  <Action type="0" message="2325" wParam="0" lParam="0" sParam="" />
  <Action type="3" message="1700" wParam="0" lParam="0" sParam="" />
  <Action type="3" message="1601" wParam="0" lParam="0" sParam="(?:[0-9]{1,3}\.){3}[0-9]{1,3}" />
  <Action type="3" message="1625" wParam="0" lParam="2" sParam="" />
  <Action type="3" message="1602" wParam="0" lParam="0" sParam="IP-Placeholder" />
  <Action type="3" message="1702" wParam="0" lParam="768" sParam="" />
  <Action type="3" message="1701" wParam="0" lParam="1609" sParam="" />
  <Action type="0" message="2422" wParam="0" lParam="0" sParam="" />
  <Action type="0" message="2325" wParam="0" lParam="0" sParam="" />
  <Action type="3" message="1700" wParam="0" lParam="0" sParam="" />
  <Action type="3" message="1601" wParam="0" lParam="0" sParam="\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b" />
  <Action type="3" message="1625" wParam="0" lParam="2" sParam="" />
  <Action type="3" message="1602" wParam="0" lParam="0" sParam="email-placeholder" />
  <Action type="3" message="1702" wParam="0" lParam="768" sParam="" />
  <Action type="3" message="1701" wParam="0" lParam="1609" sParam="" />
</Macro>

Solution

  • It's working now.

    My original (non-working) code did this:

    // shortcuts.xml is encoded UTF-8, no BOMs
    string shortCutsXmlFilepath = Path.Combine($"{System.Environment.GetEnvironmentVariable("APPDATA")}\\NotePad++", "shortcuts.xml");
    
    XDocument shortCutsXmlDocument = XDocument.Load(shortCutsXmlFilepath);
    
    // manipulate xDocument elements .......
    
    shortCutsXmlDocument.Save(shortCutsXmlFilepath);
    

    The result was that shortcuts.xml had UTF-8 byte order marks (BOM) inserted (EF BB BF) at the beginning of the file. When I next executed Notepad++ and it read shortcuts.xml, it silently failed to read shortcuts.xml and so no macros were displayed under the Macro menu button.

    The Fix

    The fix is to read shortcuts.xml using a StreamReader and write it using a StreamWriter. I explicitly asserted the 'no BOM' option in both so that there's no chance of the saved version of shortcuts.xml having the BOMs inserted (the 'no BOM' option is the default).

    Note: To enable BOM, use new UTF8Encoding(true) instead.

    XDocument shortCutsXmlDocument = LoadNotepadPpShortcutsXmlDocument();
    
    // manipulate xDocument elements .......
    
    SaveNotepadPpShortcutsXmlDocument(shortCutsXmlDocument);
    
    //
    //
    //
    
    private static XDocument LoadNotepadPpShortcutsXmlDocument()
    {
        string shortCutsXmlFilepath = Path.Combine($"{System.Environment.GetEnvironmentVariable("APPDATA")}\\NotePad++", "shortcuts.xml");
    
        if (File.Exists(shortCutsXmlFilepath) == false)
        {
            MessageBox.Show($"Can't find '{shortCutsXmlFilepath}'");
            Application.Exit();
        }
    
        XDocument xDocument = new XDocument();
    
        using (StreamReader stream = new StreamReader(path: shortCutsXmlFilepath, new UTF8Encoding(false)))
        {
            xDocument = XDocument.Load(stream);
        }
    
        return (xDocument);
    }
    
    private static XDocument SaveNotepadPpShortcutsXmlDocument(XDocument document)
    {
        string shortCutsXmlFilepath = Path.Combine($"{System.Environment.GetEnvironmentVariable("APPDATA")}\\NotePad++", "shortcuts.xml");
    
        //todo Verify that Path.Combine($"{System.Environment.GetEnvironmentVariable("APPDATA")}", "\\NotePad++") exists before writing shortcuts.xml
    
        using (StreamWriter stream = new StreamWriter(shortCutsXmlFilepath, append: false, new UTF8Encoding(false)))
        {
            document.Save(stream);
        }
    
        return (document);
    }