Piping to notepad

Date:September 8, 2014 / year-entry #215
Tags:code
Orig Link:https://blogs.msdn.microsoft.com/oldnewthing/20140908-00/?p=53
Comments:    26
Summary:In honor of NotepadConf's new KickStarter video, today's Little Program takes its stdin and puts it in a Notepad window. using System; using System.Diagnostics; using System.Windows.Automation; using System.Runtime.InteropServices; class Program { static void Main(string[] args) { // Slurp stdin into a string. var everything = Console.In.ReadToEnd(); // Fire up a brand new Notepad. var process...

In honor of NotepadConf's new KickStarter video, today's Little Program takes its stdin and puts it in a Notepad window.

using System;
using System.Diagnostics;
using System.Windows.Automation;
using System.Runtime.InteropServices;

class Program
{
  static void Main(string[] args)
  {
    // Slurp stdin into a string.
    var everything = Console.In.ReadToEnd();

    // Fire up a brand new Notepad.
    var process = new Process();
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.FileName = @"C:\Windows\System32\notepad.exe";
    process.Start();
    process.WaitForInputIdle();

    // Find the Notepad edit control.
    var edit = AutomationElement.FromHandle(process.MainWindowHandle)
        .FindFirst(TreeScope.Subtree,
                   new PropertyCondition(
                       AutomationElement.ControlTypeProperty,
                       ControlType.Document));

    // Shove the text into that window.
    var nativeHandle = new IntPtr((int)edit.GetCurrentPropertyValue(
                      AutomationElement.NativeWindowHandleProperty));
    SendMessage(nativeHandle, WM_SETTEXT, IntPtr.Zero, everything);
  }

  [DllImport("user32.dll", EntryPoint="SendMessage", CharSet=CharSet.Unicode)]
  static extern IntPtr SendMessage(
    IntPtr windowHandle, int message, IntPtr wParam, string text);
  const int WM_SETTEXT = 0x000C;
}

The comments pretty much lay out the steps. The part that may not be obvious is the part that deals with UI Automation: We take the main Notepad window, then ask UI Automation to find Document element inside it.

From that element, we extract the window handle, then drop to Win32 and send a WM_SET­TEXT message to jam the text into the Notepad window.

If you save this program under the name 2np, then you can do

dir | 2np

and it will open a Notepad window with a directory listing inside it.

Change one line of code, and this program will launch Wordpad instead.


Comments (26)
  1. BOFH says:

    I do a lot of work in the Command Prompt, mangling snippets of data with Perl and regex, this piping stdout directly into Notepad is something I have been thinking of for a long time.

    I'm just a BOFH with programming experience from C on MS-DOS in the 90s, how do I build this C# thingy in Visual Studio?

    I have access to a machine with Visual Studio 2010 Ultimate, I created a C# Console App and pasted in the code into Program.cs.

    The build stops on this error:

    The type or namespace name 'Windows' does not exist in the namespace 'System' (are you missing an assembly reference?)

    I found the References list and the .Net tab, but there is no System.Windows in the list, what am I supposed to do to make the build work?

  2. CarlD says:

    @BOFH You need to add a reference to uiautomationclient.dll.  See msdn.microsoft.com/…/system.windows.automation.automationelement(v=vs.85).aspx.

  3. Eric Wilson says:

    @BOFH: From the Add references dialog, select the .NET tab, then add a reference to the UIAutomationClient component.

  4. BOFH says:

    I had tried that, but it produced other errors instead so I backed out from that route.

    CarlD's link clarifies that it is the way to go so I looked more closely at those errors and realized that I need to add UIAutomationTypes as well to make it work.

    Thanks! :)

  5. laonianren says:

    This functionality is incredibly useful.

    I wrote a GUI text editor years ago that accepts piped input and I still use it almost daily.

    You can build Raymond's app without Visual Studio thus:

    Save the source from above in a file called 2np.cs and, at a command-prompt in the same directory, run

    set FW=C:WindowsMicrosoft.NETFramework64v4.0.30319

    %FW%csc /lib:%FW%WPF /r:UIAutomationClient.dll /r:UIAutomationTypes.dll 2np.cs

    The FW path may well be a bit different on your computer, but you want the folder containing "csc.exe".

  6. Tim says:

    If you don't mind an extra step, Windows comes with a "clip" utility that you copies its stdin to the clipboard. This is what I do when I want to get some data from a command line program into Excel or a text editor.

  7. Azarien says:

    @BOFH: Always read error messages that you get. If you miss UIAutomationTypes, the message is pretty clear:

    The type 'System.Windows.Automation.AutomationProperty' is defined in an assembly that is not referenced. You must add a reference to assembly 'UIAutomationTypes, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'.

  8. notebatch says:

    I used notepad to write a batch file that does sorta kinda the same thing. Usage is "whatever.bat command".

    @ECHO OFF

    FOR /F "delims=:. tokens=1-4" %%t IN ("%TIME: =0%") DO (SET FILENAME="%TEMP%foo-%%t%%u%%v%%w")

    %1 > %FILENAME%

    notepad %FILENAME%

    DEL /F  %FILENAME% 2>NUL

  9. Azarien says:

    I just noticed that 2np doesn't properly work with Unicode text (characters are replaced by "?")

  10. Antonio 'Grijan' says:

    This program can be replaced with just a simple 2-line batch file:

    @findstr "^" > %temp%2np.txt

    @start notepad %temp%2np.txt

    It accepts input from the pipeline (or even the keyboard!), just as Raymond's program:

    dir /b | 2np

    [There is a race condition in the batch file, and it isn't thread-safe, and if you instinctively hit Ctrl+S to save, it saves it back to the temporary file that is probably not what you wanted. -Raymond]
  11. Mike says:

    It would be sweet if we could host these code samples on a git repo.  That way improvements could be published to them, and everyone enjoys the benefits.

  12. Scott says:

    I started with something similar a long time ago.  I've since added the ability to keep text coming in from the pipe to Notepad in near-realtime with EM_SETSEL and EM_REPLACESEL, and deal with oddball tools that output different line endings.

    It's incredibly useful for a host of things.

  13. Antonio 'Grijan' says:

    @Raymond: these things are right, but I don't think they affect the most frequent use of the command: capturing the (long) output of a command so you can read it slowly, search it for keywords and maybe copy portions of it. The race condition doesn't matter too much in practice because this command is interactive in nature, so it isn't probable to run it twice at the same time. If you need to capture a second command's output before closing the first Notepad, you can do that, as far as you understand that the temporary file is, well, temporary, and can be overwritten without warning. And if you want to save the full output to a file (it would have been simpler to use a >file.txt redirection in the first place!), after pressing Ctrl+S and noticing the Save As dialog didn't show, I'd realize I had to use the Save As command.

    I understand the purpose of all this is to have a nice exercise in GUI programming and adding a tool to our toolset. I was just showing an alternative to your code, even if the batch file doesn't do everything that your C# program do.

    [I guess my workflow is not the same as yours. I use Notepad windows to keep track of things in progress (like notes taken during debugging or a list of files that need to be checked). If I hit Ctrl+S, I assume that my notes have been saved. I don't stop and think, "Oh wait, did I create this with 2np? Rats, that didn't actually save it anywhere permanent." -Raymond]
  14. rs says:

    Can we be sure that Notepad (or Wordpad, for that matter) properly updates all of its state variables when other programs manipulate its internal controls? Don't turn this Little Program into a Big Program unless you know. CreateWindow(L"EDIT", everything, …) is probably easier and safer.

    [Actually, we know that it doesn't. Close the Notepad window without editing anything: You are not prompted to save. -Raymond]
  15. Chris says:

    Use a real operating system, and this is just somecommand | gvim –

    </flamebait>

  16. Smeargle235 says:

    I've been using something like this that I wrote for years, only instead of notepad, it writes the input to a text file and opens it in the default web browser.

  17. GreyCloud says:

    Hum got a weird message about a shim error on Windows 8.1 64bit compiling under .net 4.5. (might be something to do with copying the exe to System32 folder?)   Changed project to .net 3.5 and all is well :)

    thanks for this  

  18. Joker_vD says:

    @rs: Have you heard of that fancy "event queue" invention of recent years? Coupled with an innovative "event loop" technique, and given that "event queue" is maintained by the timesharing operating system you use, and the "event loop" is employed by an application run under supervision of the said TS/OS, one can maintain confidence… okay, I am tired of writing this.

    Although, seriously, it's just sending a WM_SETTEXT message to a window. Even X-Windows can do something like this more or less reliably and without causing chaos.

    @Chris: Yep, the whole Windows is defective because its core system component, namely Notepad, doesn't read data from STDIN.

  19. @Chris says:

    So what's the difference between somecommand | gvim – on a real OS and somecommand | 2np on some other OS again?

  20. Innocent Bystander says:

    Lot's of people posting saying how incredibly useful this is. Why? What's the advantage over just piping to a file or just displaying in the command prompt?

  21. Casual Passerby says:

    @Innocent Bystander: here are a few things you can do more easily in a text editor. Long output can be scrolled and searched more easily. You can cut and paste some or all of the output; this is nice if you are answering a support question by email. You can annotate the text to aid in debugging or explaining. You can save the text for future reference.

    The reason I will use this though is that selecting and copying text in the Windows command prompt just works weird (for good reasons no doubt). Even after years of using it I still cut off content on the right side because it does a block select. This just works better.

    @Tim: thanks for the clipboard trick.

  22. DebugErr says:

    Windows.Automation, eh? Never heard of it. But it looks very interesting. Wonder if I get this working with my other favourite editor, Notepad++.

    [I've used the automation namespace in the past, and expect to see again in the future. It's really handy for this sort of thing. -Raymond]
  23. mg says:

    I wonder why you did not use the value pattern to set the text? The value pattern would also update the internal state of the notepad so it would ask you to save when you exit.

    Or were there a hidden agenda to show how to use SendMessage with a string parameter?

    [I really wanted to use the value pattern, but it turns out that multi-line edit controls don't support the value pattern. Curses! -Raymond]
  24. mg says:

    @Raymond: Ok, I were looking at notepad in Windows 7: there the control type is Edit, not Document, and it does support the value pattern.

  25. stephen yeadon says:

    To your example:

    dir | 2np

    You could try:

    dir | clip | notepad

    ctrl V will paste the results into the open notepad window.

    [At the cost of wiping out whatever was on your clipboard already. And you have to sit and wait for the "dir" to complete. -Raymond]

Comments are closed.


*DISCLAIMER: I DO NOT OWN THIS CONTENT. If you are the owner and would like it removed, please contact me. The content herein is an archived reproduction of entries from Raymond Chen's "Old New Thing" Blog (most recent link is here). It may have slight formatting modifications for consistency and to improve readability.

WHY DID I DUPLICATE THIS CONTENT HERE? Let me first say this site has never had anything to sell and has never shown ads of any kind. I have nothing monetarily to gain by duplicating content here. Because I had made my own local copy of this content throughout the years, for ease of using tools like grep, I decided to put it online after I discovered some of the original content previously and publicly available, had disappeared approximately early to mid 2019. At the same time, I present the content in an easily accessible theme-agnostic way.

The information provided by Raymond's blog is, for all practical purposes, more authoritative on Windows Development than Microsoft's own MSDN documentation and should be considered supplemental reading to that documentation. The wealth of missing details provided by this blog that Microsoft could not or did not document about Windows over the years is vital enough, many would agree an online "backup" of these details is a necessary endeavor. Specifics include:

<-- Back to Old New Thing Archive Index