0

I have an ancient DOS-based command-line system that I need to extract a bunch of data from - and can't directly access the data.

Is there a way of using UI Automation, or similar, to write to an already running console window (and read from specific portions of it)? Preferably in C#.

That way I can remote control the system, and iterate over the data that's in it, dumping it out.

Andrew Ducker
  • 5,308
  • 9
  • 37
  • 48
  • https://stackoverflow.com/questions/2613161/how-to-write-to-the-stdin-of-another-app might be a dupe? – Preston Guillot May 28 '15 at 15:55
  • That method won't allow me to get what's on the screen at position X=5,Y=10 (for example). It's a DOS UI, so I need to check what's at a given location. – Andrew Ducker May 28 '15 at 16:08
  • You'd have to go very low level and retrieve the array associated with the applications console. Can't find a good example. Start by reading about the [GetConsoleScreenBufferInfo](https://msdn.microsoft.com/en-us/library/windows/desktop/ms683171%28v=vs.85%29.aspx) API in the MSDN docs. – Idle_Mind May 28 '15 at 16:44
  • Would my answer help: http://stackoverflow.com/questions/12355378/read-from-location-on-console-c-sharp – Simon Mourier May 28 '15 at 16:44
  • As for writing how about good old sendkeys? Writing into the consol buffer doesn't seem like a way to 'control' the programm.. - As for reading, if all else fails you could scrape the screen and translate the pixels to their characters. How big a pain that would be, depends on how much control you have and how weird the program behaves, like changing fonts and colors.. – TaW May 28 '15 at 18:05
  • @SimonMourier - sadly your answer doesn't let me pass in a window handle/process... – Andrew Ducker May 29 '15 at 09:21
  • If you create the process yourself and your process is a console process, then the console will be yours and the other's process output will happen in your console (something like this: http://stackoverflow.com/questions/5362121/reading-other-process-console-output) , that's the only way. – Simon Mourier May 29 '15 at 09:46
  • Thanks for your help - I found an alternate remote-control solution that works well. – Andrew Ducker May 29 '15 at 16:18

1 Answers1

0

This should do it:

    public static IEnumerable<string> GetScreenContents(AutomationElement window)
    {
        var systemButton = window.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "System"));
        var pattern = (ExpandCollapsePattern)systemButton.GetCurrentPattern(ExpandCollapsePattern.Pattern);
        pattern.Expand();
        SendKeys.SendWait("E");
        SendKeys.SendWait("S");
        Thread.Sleep(100);
        pattern.Expand();
        SendKeys.SendWait("E");
        SendKeys.SendWait("Y");
        Thread.Sleep(100);
        var clipText = Clipboard.GetText();

        var screenWidth = 150;

        return Enumerable.Range(0, clipText.Length / screenWidth)
            .Select(i => clipText.Substring(i * screenWidth, screenWidth));
    }

The first 2/3 of it go to a given AutomationElement, find the system button (the top left widget which has a drop-down menu for system functions), and then select "Edit->Select All". It then does this again, only with "Edit->Copy".

The final third takes this text and splits it into chunks the width of the screen (150 characters in my case).

You get the AutomationElement from a given hWnd using the following:

        var window = AutomationElement.FromHandle(hWnd);
Andrew Ducker
  • 5,308
  • 9
  • 37
  • 48