<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<HTML lang="en">
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252">
<link href="style.css" rel="stylesheet" type="text/css">
<title>4.6.13. Working with the Notepad++ 'Replace' dialog</title>
<script src="show_matches.js" type="text/javascript"></script>
</HEAD>
<BODY>
<h3>4.6.13. Working with the Notepad++ 'Replace' dialog</h3>

<p>Let's imagine you want to invoke the Notepad++ 'Replace' dialog, populate its 'Find what' and 'Replace with' fields with specific strings, check or uncheck the search-related checkboxes, and finally press the 'Find Next' button. And you want to do all of this programmatically.</p>
<p>The first tools that come to my mind to do such things are AutoIt ( <code>https://www.autoitscript.com/</code> ) and AutoHotKey ( <code>https://www.autohotkey.com/</code> ).</p>
<p>But let's consider something simpler. For example, NirCmd from NirSoft ( <code>https://www.nirsoft.net/</code> ).</p>
<p>First, let's create the following NppExec script:</p>

<blockquote>
  <pre>env_set local PATH = $(SYS.PATH);C:\tools\NirSoft  // TODO: specify path to the folder with nircmd.exe here!
npp_sendmsg WM_COMMAND IDM_SEARCH_REPLACE  // showing the Replace dialog
nircmd script &quot;$(NPP_DIRECTORY)\replace.ncl&quot;  // TODO: you may specify other folder than $(NPP_DIRECTORY)</pre>
</blockquote>

<p>Then, as the NppExec script above mentions &quot;$(NPP_DIRECTORY)\replace.ncl&quot;, let's create a file named &quot;replace.ncl&quot; under your Notepad++ folder. It will be a simple non-Unicode text file with the following lines:</p>

<blockquote>
  <pre>// set 'Find what' text:
dlg &quot;notepad++.exe&quot; &quot;Replace&quot; settext 1601 &quot;Find what&quot;
// set 'Replace with' text:
dlg &quot;notepad++.exe&quot; &quot;Replace&quot; settext 1602 &quot;Replace with&quot;
// uncheck 'Whole word' checkbox:
win child title &quot;Replace&quot; sendmsg id 1603 0x00F1 0x0000 0
// uncheck 'Match case' checkbox:
win child title &quot;Replace&quot; sendmsg id 1604 0x00F1 0x0000 0
// check 'Wrap around' checkbox:
win child title &quot;Replace&quot; sendmsg id 1606 0x00F1 0x0001 0
// uncheck 'Backward direction' checkbox:
win child title &quot;Replace&quot; sendmsg id 1722 0x00F1 0x0000 0
// click 'Regular expression' button:
win child title &quot;Replace&quot; sendmsg id 1605 0x00F5 0 0
// uncheck '. matches newline' checkbox:
win child title &quot;Replace&quot; sendmsg id 1703 0x00F1 0x0000 0
// click 'Find Next' button:
win child title &quot;Replace&quot; sendmsg id 1 0x00F5 0 0</pre>
</blockquote>

<p>This file uses a hard-coded string &quot;Replace&quot; as the title of the Notepad++ 'Replace' dialog. If you are using a non-English localization, you will need to replace the word &quot;Replace&quot; with the localization-dependent title (such as &quot;Remplacer&quot; for French).</p>
<p>Also, this file uses several magic numbers such as 1601 and 1602, 0x00F1 and 0x00F5, 0x0000 and 0x0001. Let me explain what they mean.</p>
<p>To deal with controls (elements) of a dialog, we need to know the corresponding control identifiers (control IDs). Here is what we can see inside the Notepad++ source file <code>https://github.com/notepad-plus-plus/notepad-plus-plus/blob/master/PowerEditor/src/ScintillaComponent/FindReplaceDlg_rc.h</code> :</p>
<blockquote>
  <pre>#define	IDFINDWHAT						1601
#define	IDREPLACEWITH					1602
#define	IDWHOLEWORD						1603
#define	IDMATCHCASE						1604
#define IDREGEXP						1605
#define	IDWRAP							1606</pre>
</blockquote>
<p>I believe this explains where the magic numbers of 1601, 1602, and so on, came from. These are the control IDs. Another way to get these IDs is to use a specific program such as Property Edit ( <code>https://mh-nexus.de/en/downloads.php?product=Property%20Edit</code> ).</p>
<p>Now, knowing the control IDs, we want to send some specific messages to these controls. The BM_SETCHECK message is responsible for checking/unchecking a checkbox control; the BM_CLICK message is responsible for clicking a button. When we look inside a Windows header file, &quot;winuser.h&quot;, we can see that BM_SETCHECK is defined as 0x00F1 and BM_CLICK is defined as 0x00F5.</p>
<p>Finally, the values of 0x0000 and 0x0001 in this context are parameters of the BM_SETCHECK (0x00F1) message. Their meanings with respect to the BM_SETCHECK message are: 0x0000 is BST_UNCHECKED and 0x0001 is BST_CHECKED. So, &quot;0x00F1 0x0001 0&quot; checks a checkbox control, whereas &quot;0x00F1 0x0000 0&quot; unchecks it. You may read more here: <code>https://docs.microsoft.com/windows/win32/controls/bm-setcheck</code></p>
<p>Important note: as NppExec's scripts are executed in a separate thread, the script <em>continues its execution</em> when Notepad++'s dialog is shown. It allows to use NirCmd (as well as AutoHotKey or any other tool) to interact with the Notepad++'s dialog by pressing its buttons and so on. Correspondingly, it's also possible to close the dialog and then execute other commands from the same NppExec's script.</p>

<blockquote>
  <pre> </pre>
</blockquote>

<p>Now, after warming up, let's create a similar AutoHotKey script.</p>
<p>AutoHotKey provides more abilities and more control over what we are doing, so it is recommended. We will use AutoHotKey 2.0 (refer to <code>https://www.autohotkey.com/docs/v2/v2-changes.htm</code> for details).</p>
<p>The new NppExec script to invoke AutoHotKey will be quite similar to the one that invokes NirCmd. We will additionally pass a handle to the Notepad++ main window as an input parameter to the .ahk script. This is to ensure that we will be dealing with the current Notepad++ window (as there may be other running instances of Notepad++):</p>

<blockquote>
  <pre>set local AutoHotKeyDir = C:\tools\AutoHotKey  // TODO: specify path to the AutoHotKey folder here!
env_set local PATH = $(SYS.PATH);$(AutoHotKeyDir)  // adding to %PATH%
npp_sendmsg WM_COMMAND IDM_SEARCH_REPLACE  // showing the Replace dialog
AutoHotkey32.exe &quot;$(AutoHotKeyDir)\NppReplace.ahk&quot; $(NPP_HWND)  // TODO: you may place your .ahk script under a different folder
</pre>
</blockquote>

<p>Now, let's write the .ahk script that will do all the magic. Let's create a file named &quot;NppReplace.ahk&quot; under the folder specified in the NppExec script above (in this example, it is &quot;$(AutoHotKeyDir)\NppReplace.ahk&quot;). This file uses the AutoHotKey 2.0 syntax:</p>

<blockquote>
  <pre>if A_Args.Length &lt; 1
{
  MsgBox(&quot;Usage: &quot; A_ScriptName &quot; &lt;NPP_HWND&gt;&quot;,, 0x30) ; 0x30 - exclamation
  Exit
}

ReplaceDlgTitle := &quot;Replace&quot;
idNppWnd := Integer(A_Args[1])
idReplaceDlg := 0

idDlgList := WinGetList(ReplaceDlgTitle &quot; ahk_exe notepad++.exe&quot;)
for idDlg in idDlgList
{
  idParent := DllCall(&quot;user32\GetParent&quot;, &quot;Ptr&quot;, idDlg, &quot;Ptr&quot;)
  if idParent = idNppWnd
  {
    idReplaceDlg := idDlg
    break
  }
}

if idReplaceDlg = 0
{
  MsgBox(&quot;Could not find a dialog with a title `&quot;&quot; ReplaceDlgTitle &quot;`&quot; for NPP_HWND=&quot; Format(&quot;0x{:X}&quot;, idNppWnd),, 0x10) ; 0x10 - error
  Exit
}

GetDlgItem(idDlg, idItem)
{
  return DllCall(&quot;user32\GetDlgItem&quot;, &quot;Ptr&quot;, idDlg, &quot;Int&quot;, idItem, &quot;Ptr&quot;)
}

ClickButton(idButton)
{
  SendMessage(0x00F5, 0, 0, idButton)
}

idFindWhat := GetDlgItem(idReplaceDlg, 1601)
idReplaceWith := GetDlgItem(idReplaceDlg, 1602)
idWholeWord := GetDlgItem(idReplaceDlg, 1603)
idMatchCase := GetDlgItem(idReplaceDlg, 1604)
idWrapAround := GetDlgItem(idReplaceDlg, 1606)
idBackwardDirection := GetDlgItem(idReplaceDlg, 1722)
idRegularExpression := GetDlgItem(idReplaceDlg, 1605)
idDotMatchesNewline := GetDlgItem(idReplaceDlg, 1703)
idFindNext := GetDlgItem(idReplaceDlg, 1)

ControlSetText(&quot;Find What&quot;, idFindWhat)
ControlSetText(&quot;Replace With&quot;, idReplaceWith)
ControlSetChecked(0, idWholeWord)
ControlSetChecked(0, idMatchCase)
ControlSetChecked(1, idWrapAround)
ControlSetChecked(0, idBackwardDirection)
ClickButton(idRegularExpression)
ControlSetChecked(0, idDotMatchesNewline)
ClickButton(idFindNext)

Exit
</pre>
</blockquote>

<blockquote>
  <pre> </pre>
</blockquote>

<p>Note: If you want to find or replace some string programmatically <em>without showing the Replace dialog</em>, NppExec proposes the following commands for this: SCI_FIND and SCI_REPLACE. The actual behavior of these commands completely depend on the &lt;flags&gt; parameter specified. The description and examples are available by typing any of the following:</p>

<blockquote>
  <pre>help sci_find
help sci_replace
help all</pre>
</blockquote>

<blockquote>
  <pre> </pre>
</blockquote>

<p>See also: Clipboard, keystrokes and much more <a href="4.6.8.html">[4.6.8]</a>.</p>

<blockquote>
  <pre> </pre>
</blockquote>

</BODY>
</HTML>
