I need to read text from a particular location in the console, say 5,5.
If I were to need to write to this location, it would simply be:
Console.SetCursorPosition(5, 5);
Console.Write("My text");
Is there any way i can read in a similar way?
Just to clarify:
I don't want to stop to take an input from the user, there's a chance even that the input won't be from the user, but something previously printed out. I literally want some sort of:
Console.GetCharAtLocation(5,5) or something similar.
Here is a C# code utility that can read what's currently in the Console buffer (not the window, the buffer):
Sample usage:
class Program
{
static void Main(string[] args)
{
// read 10 lines from the top of the console buffer
foreach (string line in ConsoleReader.ReadFromBuffer(0, 0, (short)Console.BufferWidth, 10))
{
Console.Write(line);
}
}
}
Utility:
public class ConsoleReader
{
public static IEnumerable<string> ReadFromBuffer(short x, short y, short width, short height)
{
IntPtr buffer = Marshal.AllocHGlobal(width * height * Marshal.SizeOf(typeof(CHAR_INFO)));
if (buffer == null)
throw new OutOfMemoryException();
try
{
COORD coord = new COORD();
SMALL_RECT rc = new SMALL_RECT();
rc.Left = x;
rc.Top = y;
rc.Right = (short)(x + width - 1);
rc.Bottom = (short)(y + height - 1);
COORD size = new COORD();
size.X = width;
size.Y = height;
const int STD_OUTPUT_HANDLE = -11;
if (!ReadConsoleOutput(GetStdHandle(STD_OUTPUT_HANDLE), buffer, size, coord, ref rc))
{
// 'Not enough storage is available to process this command' may be raised for buffer size > 64K (see ReadConsoleOutput doc.)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
IntPtr ptr = buffer;
for (int h = 0; h < height; h++)
{
StringBuilder sb = new StringBuilder();
for (int w = 0; w < width; w++)
{
CHAR_INFO ci = (CHAR_INFO)Marshal.PtrToStructure(ptr, typeof(CHAR_INFO));
char[] chars = Console.OutputEncoding.GetChars(ci.charData);
sb.Append(chars[0]);
ptr += Marshal.SizeOf(typeof(CHAR_INFO));
}
yield return sb.ToString();
}
}
finally
{
Marshal.FreeHGlobal(buffer);
}
}
[StructLayout(LayoutKind.Sequential)]
private struct CHAR_INFO
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public byte[] charData;
public short attributes;
}
[StructLayout(LayoutKind.Sequential)]
private struct COORD
{
public short X;
public short Y;
}
[StructLayout(LayoutKind.Sequential)]
private struct SMALL_RECT
{
public short Left;
public short Top;
public short Right;
public short Bottom;
}
[StructLayout(LayoutKind.Sequential)]
private struct CONSOLE_SCREEN_BUFFER_INFO
{
public COORD dwSize;
public COORD dwCursorPosition;
public short wAttributes;
public SMALL_RECT srWindow;
public COORD dwMaximumWindowSize;
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool ReadConsoleOutput(IntPtr hConsoleOutput, IntPtr lpBuffer, COORD dwBufferSize, COORD dwBufferCoord, ref SMALL_RECT lpReadRegion);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GetStdHandle(int nStdHandle);
}
A simplified demo that works in Windows 10 for reading a single character from the specified (X, Y) position on the screen. Tested with .NET 4.7.2.
First, here's a line of code which populates the Console with a demo grid. Note that it should render in the top left corner of your screen in order for the demo to work.
static void Populate_Console()
{
Console.Clear();
Console.Write(#"
┌───────┐
1│C D E F│
2│G H I J│
3│K L M N│
4│O P Q R│
└───────┘
2 4 6 8 ".Trim());
}
It should look like this:
Now let's read some characters back. To start you'll need the native console handle for stdout. Here's the P/Invoke method for getting it from Win32:
[DllImport("kernel32", SetLastError = true)]
static extern IntPtr GetStdHandle(int num);
Now for the cool part; this seems to be the only answer on this page so far which uses the ReadConsoleOutputCharacter Win32 function. Although it doesn't let you get the character color attributes, this approach does save all the trouble of dealing with copy rectangles and having to use CreateConsoleScreenBuffer to allocate screen buffers and copy between them.
There are separate Ansi and Unicode versions, and you need to call the proper one depending on the code page that's active in the Console window. I show both P/Invoke signatures here, but for simplicity, in the example I'll just continue with the Ansi version:
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Ansi)]
[return: MarshalAs(UnmanagedType.Bool)] // ̲┌──────────────────^
static extern bool ReadConsoleOutputCharacterA(
IntPtr hStdout, // result of 'GetStdHandle(-11)'
out byte ch, // A̲N̲S̲I̲ character result
uint c_in, // (set to '1')
uint coord_XY, // screen location to read, X:loword, Y:hiword
out uint c_out); // (unwanted, discard)
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)] // ̲┌───────────────────^
static extern bool ReadConsoleOutputCharacterW(
IntPtr hStdout, // result of 'GetStdHandle(-11)'
out Char ch, // U̲n̲i̲c̲o̲d̲e̲ character result
uint c_in, // (set to '1')
uint coord_XY, // screen location to read, X:loword, Y:hiword
out uint c_out); // (unwanted, discard)
You may notice I've stripped down the marshaling on these to the bare minimum needed for the purposes of my example code, which is designed to only fetch one character at a time. Therefore, you will probably find that c_in must always be 1, due to the managed pointer declarations ‘out byte ch’ and ‘out Char ch’.
That's really all you need; calling the appropriate P/Invoke function as described above is mostly self-explanatory if you limit yourself to reading a single character. To show this with a trivial example, I'll finish with the cute demo program, that reads four characters back from the Console, along a diagonal of the grid we drew above.
static void Windows_Console_Readback()
{
var stdout = GetStdHandle(-11);
for (uint coord, y = 1; y <= 4; y++)
{
coord = (5 - y) * 2; // loword <-- X coord to read
coord |= y << 16; // hiword <-- Y coord to read
if (!ReadConsoleOutputCharacterA(
stdout,
out byte chAnsi, // result: single ANSI char
1, // # of chars to read
coord, // (X,Y) screen location to read (see above)
out _)) // result: actual # of chars (unwanted)
throw new Win32Exception();
Console.Write(" " + (Char)chAnsi + " ");
}
}
And there you have it...
This functionality doesn't exist. It's theoretically possible for you to override the input and output streams on the console to keep your own copy of the console buffer that you could read from, but it would be non-trivial (and probably couldn't support all of the edge cases such as an external program hooking into your console and reading/writing to it).
Forget about it, too much trouble, you could read from buffer and get all the current console output, but that would be too much.
My suggestion is to create a ConsoleWriter delegation, you choose how, could be a class or just an static method, and this writer would keep the last line in a property so each time you would Console.WriteLine something you just call your delegation, with your implementation and at the end it calls Console.WriteLine.
As #Servy has stated, there isn't any built-in functionality (that I know of, or can find) that can do what you want. However, there is a work-around (it's a bit of a hack, but it worked).
You can create your own buffer in memory, or on disk. Whenever you output to the Console, also output to your buffer. You can then use your buffer to read from in ways that you couldn't with the Console.
There are two ways to buffer: on disk or in memory. You can use the Console.BufferWidth and Console.BufferHeight properties to find out your buffer size. I found it simpler to do this in memory using an array of strings (each string was a line of output, and there were a number of strings in the array equal to the BufferHeight, if I remember correctly). A colleague ended up doing the same thing on disk.
You'll want to create a method to replace Console.Write and Console.WriteLine, so that you can write to both buffers at once. Something like:
public void MyWrite( string output ) {
Console.Write( output );
Array.Write( output ); // obvious pseudo-code
}
I found it helpful to wrap a Class around the array and implement methods to support it ... you could then implement your GetCharAtLocation( int i, int j ) method, as well as any other functionality you need there.
What about:
class Program {
static void Main( string[ ] args ) {
CustomizedConsole.WriteLine( "Lorem Ipsum" ); //Lorem Ipsum
Console.WriteLine( CustomizedConsole.ReadContent( 6, 5 ) ); //Ipsum
Console.WriteLine( CustomizedConsole.GetCharAtLocation( 0, 0 ) ); //L
}
}
static class CustomizedConsole {
private static List<char> buffer = new List<char>();
private static int lineCharCount = 0;
public static void Write(string s){
lineCharCount += s.Length;
buffer.AddRange( s );
Console.Write( s );
}
public static void WriteLine(string s ) {
for ( int i = 0; i < Console.BufferWidth - lineCharCount - s.Length; i++ )
s += " ";
buffer.AddRange( s );
Console.WriteLine( s );
lineCharCount = 0;
}
public static string ReadContent( int index, int count ) {
return new String(buffer.Skip( index ).Take( count ).ToArray());
}
public static char GetCharAtLocation( int x, int y ) {
return buffer[ Console.BufferHeight * x + y ];
}
}
EDIT :
As said the others this is just a trivial case where there are a lot of other things to improve. But I wrote this only as a starting point.
Related
I am trying to use the WriteConsoleOutputfunction from kernel32.dll, however I cannot get unicode characters to display correctly, they always display as the wrong characters.
I have attempted to use:
Console.OutputEncoding = System.Text.Encoding.UTF8;
Changing this to Encoding.Unicode does not work either.
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetConsoleOutputCP(uint wCodePageID);
public void SetCP(){
SetConsoleOutputCP(65001);
}
I have tried using both of the above, each one individually and none with just about every combination of values.
I have also switched between all fonts (including the true type ones), however none of them seem to display the characters correctly.
Here is the code I am using to use WriteConsoleOutput
[DllImport("kernel32.dll", SetLastError = true, EntryPoint = "WriteConsoleOutputW", CharSet = CharSet.Unicode)]
static extern bool WriteConsoleOutputW(SafeFileHandle hConsoleOutput, CharInfo[] lpBuffer, Coord dwBufferSize, Coord dwBufferCoord, ref SmallRect lpWriteRegion);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern SafeFileHandle CreateFile(string fileName, [MarshalAs(UnmanagedType.U4)] uint fileAccess, [MarshalAs(UnmanagedType.U4)] uint fileShare, IntPtr securityAttributes, [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition, [MarshalAs(UnmanagedType.U4)] int flags, IntPtr template);
private static readonly SafeFileHandle h = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
public static void RegionWrite(string s, int x, int y, int width, int height)
{
if (!h.IsInvalid)
{
int length = width * height;
// Pad any extra space we have
string fill = s + new string(' ', length - s.Length);
// Grab the background and foreground as integers
int bg = (int) Console.BackgroundColor;
int fg = (int) Console.ForegroundColor;
// Make background and foreground into attribute value
short attr = (short)(fg | (bg << 4));
CharInfo[] buf = fill.Select(c =>
{
CharInfo info = new CharInfo();
// Give it our character to write
info.Char.UnicodeChar = c;
// Use our attributes
info.Attributes = attr;
// Return info for this character
return info;
}).ToArray();
// Make everything short so we don't have to cast all the time
short sx = (short) x;
short sy = (short) y;
short swidth = (short) width;
short sheight = (short) height;
// Make a buffer size out our dimensions
Coord bufferSize = new Coord(swidth, sheight);
// Not really sure what this is but its probably important
Coord pos = new Coord(0, 0);
// Where do we place this?
SmallRect rect = new SmallRect() { Left = sx, Top = sy, Right = (short) (sx + swidth), Bottom = (short) (sy + sheight) };
bool b = WriteConsoleOutputW(h, buf, bufferSize, pos, ref rect);
}
else
{
throw new Exception("Console handle is invalid.");
}
}
Using this with standard ASCII characters works perfectly:
RegionWrite("Hello world", 4, 4, 10, 10);
However when I use anything above the standard ASCII range, it fails to display correctly:
RegionWrite("┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬", 4, 4, 10, 10); This outputs as two lines of ',' characters, this makes some sense as the "┬" character has a value of 9516, 9516 % 128 is 44 which is the ascii code for ','.
I know it is physically possible to output these characters as Console.Write("┬┬┬┬") works correctly. I am switching from Console.Write to WriteConsoleOutput as there is a significant performance increase.
Here is the code im using to set code pages:
public void Setup()
{
Console.BufferHeight = Console.WindowHeight;
Console.BufferWidth = Console.WindowWidth;
Console.OutputEncoding = System.Text.Encoding.UTF8;
SetConsoleOutputCP(65001);
DefaultColor();
Console.Clear();
Console.ReadLine();
RegionWrite("┬┬┬┬", 4, 4, 10, 10);
Console.WriteLine("┬┬┬┬");
Console.ReadLine();
}
Here are my structures:
[StructLayout(LayoutKind.Sequential)]
public struct Coord
{
public short X;
public short Y;
public Coord(short X, short Y)
{
this.X = X;
this.Y = Y;
}
}
[StructLayout(LayoutKind.Explicit)]
public struct CharUnion
{
[FieldOffset(0)] public char UnicodeChar;
[FieldOffset(0)] public byte AsciiChar;
}
[StructLayout(LayoutKind.Explicit)]
public struct CharInfo
{
[FieldOffset(0)] public CharUnion Char;
[FieldOffset(2)] public short Attributes;
}
[StructLayout(LayoutKind.Sequential)]
public struct SmallRect
{
public short Left;
public short Top;
public short Right;
public short Bottom;
}
I assume I have screwed up one of the variables of WriteConsoleOutput but after hours of searching for answers i'm really not sure where i've gone wrong. Is there some internal set encoding function I need to use?
nvm fixed it
Simple solution, change
[StructLayout(LayoutKind.Explicit)]
public struct CharUnion
{
[FieldOffset(0)] public char UnicodeChar;
[FieldOffset(0)] public byte AsciiChar;
}
to
[StructLayout(LayoutKind.Explicit, CharSet=CharSet.Unicode)]
public struct CharUnion
{
[FieldOffset(0)] public char UnicodeChar;
[FieldOffset(0)] public byte AsciiChar;
}
This is because it will default to ANSI meaning your unicode characters get automatically turned into ANSI, hence ┬ into ,
i got a native 32 bit dll (no source) which runs as a plugin in an application i use. I've done another native dll myself which will communicate with that plugin in order to create and update the plugin's controls.
From that dll i've exported the functions I need in order to control the plugin from my c# application (with p/invoke).
here's the code:
h file:
#pragma once
#include "include\SpoutControls.h"
extern "C" { __declspec(dllexport) void InitializeControls(char *sendername, int *numControls, char** names, int *types, float* floats, float* toggles, float* press, char** text); }
extern "C" { __declspec(dllexport) bool UpdateControls(const char** text, float *floats, float *toggles, float *press, int *numControls); }
extern "C" { __declspec(dllexport) void CloseControls(); }
//
extern "C" __declspec(dllexport) int ReleaseMemory(float *pArray)
{
delete[] pArray;
//delete[] Usize;
return 0;
};
the cpp:
#include "SpoutControls4vvvv.h"
//SpoutControls and the functions
//CreateControl, OpenControls, CheckControls, CloseControls
//are declared in SpoutControls.h, which comes with the 32 bit plugin dll
SpoutControls spoutcontrols;
void InitializeControls(char *sendername, int *numControls, char** names, int *types, float* floats, float* toggles, float* press, char** text) {
int Vcontrols = numControls[0];
int Tcontrols = numControls[1];
int Pcontrols = numControls[2];
int Scontrols = numControls[3];
int all = Vcontrols + Tcontrols + Pcontrols + Scontrols;
int v=0, t=0, p=0, s = 0;
for (int controlID = 0; controlID < all; controlID++) {
if (types[controlID] == 0) {
spoutcontrols.CreateControl(names[controlID], "float",0.0,1.0, floats[v]);
v++;
}
if (types[controlID] == 1) {
spoutcontrols.CreateControl(names[controlID], "bool", toggles[t]);
t++;
}
if (types[controlID] == 2) {
spoutcontrols.CreateControl(names[controlID], "event", press[p]);
p++;
}
if (types[controlID] == 3) {
spoutcontrols.CreateControl(names[controlID], "text", text[s]);
s++;
}
}
spoutcontrols.OpenControls(sendername);
}
bool UpdateControls(const char** text, float *floats, float *toggles, float *press, int *numControls) {
int Vcontrols = numControls[0];
int Tcontrols = numControls[1];
int Pcontrols = numControls[2];
int Scontrols = numControls[3];
int all = Vcontrols + Tcontrols + Pcontrols + Scontrols;
int v = 0, t = 0, p = 0, s = 0;
if (spoutcontrols.CheckControls(myControls)) {
for (int controlID = 0; controlID < all; controlID++) {
if (myControls[controlID].type == 10) {
floats[v] = myControls[controlID].value;
v++;
}
if (myControls[controlID].type == 0) {
toggles[t] = myControls[controlID].value;
t++;
}
if (myControls[controlID].type == 1) {
press[p] = myControls[controlID].value;
p++;
}
if (myControls[controlID].type == 100) {
text[s] = myControls[controlID].text.data();
s++;
}
}
return true;
}
return false;
}
void CloseControls() {
spoutcontrols.CloseControls();
}
and here's the c# code:
public unsafe class SystemSpoutSenderNode: IDisposable
{
[System.Runtime.InteropServices.DllImport("SpoutControls4vvvv.dll")]
private static extern void InitializeControls(IntPtr sendername, IntPtr numControls,String[] names, IntPtr types, IntPtr floats, IntPtr toggles, IntPtr press, String[] text);
[System.Runtime.InteropServices.DllImport("SpoutControls4vvvv.dll")]
private static extern int CloseControls();
[System.Runtime.InteropServices.DllImport("SpoutControls4vvvv.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern bool UpdateControls([In, Out] String[] text, [In, Out] float[] floats, [In, Out] float[] toggles, [In, Out] float[] press, IntPtr numControls);
[System.Runtime.InteropServices.DllImport("SpoutControls4vvvv.dll")]
private static extern int ReleaseMemory(IntPtr ptr);
public void Evaluate(int SpreadMax)
{
//countControls determines number of controls per type (string,float,toggle,click)
int[] controls = countControls(FType);
//sumControls will just add up all elements in controls
int all = sumControls(controls);
//in my code these arrays will get filled with values, deleted here for readability
String[] names = new String[all];
int[] types = new int[all];
float[] floats = new float[controls[0]];
float[] toggles = new float[controls[1]];
float[] press = new float[controls[2]];
String[] text = new String[controls[3]];
//initialze return arrays
String[] Rtext = new String[controls[3]];
float[] Rfloats = new float[controls[0]];
float[] Rtoggles = new float[controls[1]];
float[] Rpress = new float[controls[2]];
//allocate pointers
IntPtr SndrNamePtr = NativeUtf8FromString(FSenderName);
IntPtr BinPtr = Marshal.AllocHGlobal(4*sizeof(int));
IntPtr TypePtr = Marshal.AllocHGlobal(all*sizeof(int));
IntPtr FloatPtr = Marshal.AllocHGlobal(controls[0]*sizeof(float));
IntPtr TogglePtr = Marshal.AllocHGlobal(controls[1]*sizeof(float));
IntPtr PressPtr = Marshal.AllocHGlobal(controls[2]*sizeof(float));
try
{
//copy control info + defaults to pointer
Marshal.Copy(controls, 0, BinPtr, 4);
Marshal.Copy(types, 0, TypePtr, all);
Marshal.Copy(floats, 0, FloatPtr, controls[0]);
Marshal.Copy(toggles, 0, TogglePtr, controls[1]);
Marshal.Copy(press, 0, PressPtr, controls[2]);
//initialize controls
if (FWrite) InitializeControls(SndrNamePtr,BinPtr,names,TypePtr,FloatPtr,TogglePtr,PressPtr,text);
//update controls
bool changed = UpdateControls(Rtext,Rfloats,Rtoggles,Rpress,BinPtr);
//FF, FT, FS and FP are the outputs in my c# host
if (changed){
for(int j=0; j<controls[0];j++){
FF[j]=Rfloats[j];
}
for(int j=0; j<controls[1];j++){
FT[j]=FloatToBool(Rtoggles[j]);
}
for(int j=0; j<controls[3];j++){
FS[j]=Rtext[j];
}
}
for(int j=0; j<controls[2];j++){
FP[j]=FloatToBool(Rpress[j]);
}
}
finally
{
Marshal.FreeHGlobal(SndrNamePtr);
Marshal.FreeHGlobal(BinPtr);
Marshal.FreeHGlobal(FloatPtr);
Marshal.FreeHGlobal(TogglePtr);
Marshal.FreeHGlobal(PressPtr);
}
}
}
}
public void Dispose()
{
CleanUp();
CloseControls();
}
}
NOTE: the c# code runs without precompiling in a frame-based, c# host environment for graphical programming (vvvv), therefore i've deleted host specific decalarations of inputs (FType,FSenderName) and outputs (FF,FS,FP,FT) to avoid confusion. These will be used to "connect" this code with other functionality. Evaluate will be called every frame by the host.
Now to the actual question(s):
it's working fine so far in 32 bit, but in 64 bit my c# host crashes without any message. after some reading i believe this is due to pointer sizes being different in 32/64bit systems, but i'm not exactly sure what to do/if this actually applies here. I would be very thankful if you could
explain me how (and why) to get this code to run in 64 bit
point out any other mistakes you might spot along the way- i'm completely new to c++ and still a beginner in c#, so i'm pretty confident there's a lot to improve here; especially: memory leaks and passing the values from c++ to c# and vice versa...uiuiui.
I've understood that I shouldn't cast a pointer to an int in 64 bit, so the last thing I've tried is to change from
int Vcontrols = numControls[0];
int Tcontrols = numControls[1];
int Pcontrols = numControls[2];
int Scontrols = numControls[3];
to
int Vcontrols = (INT_PTR)numControls[0];
int Tcontrols = (INT_PTR)numControls[1];
int Pcontrols = (INT_PTR)numControls[2];
int Scontrols = (INT_PTR)numControls[3];
but with no luck, therefore I'm posting my original problem, even if this is a correct improvement(?).
EDIT: thanks to #dkackman for pointing out one unclear point: my cpp code calls functions which come as source code (SpoutControls.h) with the native 32 bit dll. It's not the source for the 32 bit dll itself but declares the functions used to (as far as i can tell) access the same shared memory as the 32 bit dll.
I can also copy paste the code here if this might be the problem?
Also can be found here
thank you.
I am afraid you are out of luck. If your process is 64bit, you won't be able to load that 32bit dll, no matter how much you try.
Can I load a 32 bit DLL into a 64 bit process on Windows?
from https://msdn.microsoft.com/en-us/library/windows/desktop/aa384231(v=vs.85).aspx
On 64-bit Windows, a 64-bit process cannot load a 32-bit dynamic-link
library (DLL).
Without access to its source, your only option would be to convert your host to 32bit or otherwise figure out how to host the 32bit plugin in a 32bit process and use some sort of IPC to communicate with it from a 64bit host process.
So my guess is that this has nothing to do with your wrapper, array passing or interop code.
I need to read text from a particular location in the console, say 5,5.
If I were to need to write to this location, it would simply be:
Console.SetCursorPosition(5, 5);
Console.Write("My text");
Is there any way i can read in a similar way?
Just to clarify:
I don't want to stop to take an input from the user, there's a chance even that the input won't be from the user, but something previously printed out. I literally want some sort of:
Console.GetCharAtLocation(5,5) or something similar.
Here is a C# code utility that can read what's currently in the Console buffer (not the window, the buffer):
Sample usage:
class Program
{
static void Main(string[] args)
{
// read 10 lines from the top of the console buffer
foreach (string line in ConsoleReader.ReadFromBuffer(0, 0, (short)Console.BufferWidth, 10))
{
Console.Write(line);
}
}
}
Utility:
public class ConsoleReader
{
public static IEnumerable<string> ReadFromBuffer(short x, short y, short width, short height)
{
IntPtr buffer = Marshal.AllocHGlobal(width * height * Marshal.SizeOf(typeof(CHAR_INFO)));
if (buffer == null)
throw new OutOfMemoryException();
try
{
COORD coord = new COORD();
SMALL_RECT rc = new SMALL_RECT();
rc.Left = x;
rc.Top = y;
rc.Right = (short)(x + width - 1);
rc.Bottom = (short)(y + height - 1);
COORD size = new COORD();
size.X = width;
size.Y = height;
const int STD_OUTPUT_HANDLE = -11;
if (!ReadConsoleOutput(GetStdHandle(STD_OUTPUT_HANDLE), buffer, size, coord, ref rc))
{
// 'Not enough storage is available to process this command' may be raised for buffer size > 64K (see ReadConsoleOutput doc.)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
IntPtr ptr = buffer;
for (int h = 0; h < height; h++)
{
StringBuilder sb = new StringBuilder();
for (int w = 0; w < width; w++)
{
CHAR_INFO ci = (CHAR_INFO)Marshal.PtrToStructure(ptr, typeof(CHAR_INFO));
char[] chars = Console.OutputEncoding.GetChars(ci.charData);
sb.Append(chars[0]);
ptr += Marshal.SizeOf(typeof(CHAR_INFO));
}
yield return sb.ToString();
}
}
finally
{
Marshal.FreeHGlobal(buffer);
}
}
[StructLayout(LayoutKind.Sequential)]
private struct CHAR_INFO
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public byte[] charData;
public short attributes;
}
[StructLayout(LayoutKind.Sequential)]
private struct COORD
{
public short X;
public short Y;
}
[StructLayout(LayoutKind.Sequential)]
private struct SMALL_RECT
{
public short Left;
public short Top;
public short Right;
public short Bottom;
}
[StructLayout(LayoutKind.Sequential)]
private struct CONSOLE_SCREEN_BUFFER_INFO
{
public COORD dwSize;
public COORD dwCursorPosition;
public short wAttributes;
public SMALL_RECT srWindow;
public COORD dwMaximumWindowSize;
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool ReadConsoleOutput(IntPtr hConsoleOutput, IntPtr lpBuffer, COORD dwBufferSize, COORD dwBufferCoord, ref SMALL_RECT lpReadRegion);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GetStdHandle(int nStdHandle);
}
A simplified demo that works in Windows 10 for reading a single character from the specified (X, Y) position on the screen. Tested with .NET 4.7.2.
First, here's a line of code which populates the Console with a demo grid. Note that it should render in the top left corner of your screen in order for the demo to work.
static void Populate_Console()
{
Console.Clear();
Console.Write(#"
┌───────┐
1│C D E F│
2│G H I J│
3│K L M N│
4│O P Q R│
└───────┘
2 4 6 8 ".Trim());
}
It should look like this:
Now let's read some characters back. To start you'll need the native console handle for stdout. Here's the P/Invoke method for getting it from Win32:
[DllImport("kernel32", SetLastError = true)]
static extern IntPtr GetStdHandle(int num);
Now for the cool part; this seems to be the only answer on this page so far which uses the ReadConsoleOutputCharacter Win32 function. Although it doesn't let you get the character color attributes, this approach does save all the trouble of dealing with copy rectangles and having to use CreateConsoleScreenBuffer to allocate screen buffers and copy between them.
There are separate Ansi and Unicode versions, and you need to call the proper one depending on the code page that's active in the Console window. I show both P/Invoke signatures here, but for simplicity, in the example I'll just continue with the Ansi version:
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Ansi)]
[return: MarshalAs(UnmanagedType.Bool)] // ̲┌──────────────────^
static extern bool ReadConsoleOutputCharacterA(
IntPtr hStdout, // result of 'GetStdHandle(-11)'
out byte ch, // A̲N̲S̲I̲ character result
uint c_in, // (set to '1')
uint coord_XY, // screen location to read, X:loword, Y:hiword
out uint c_out); // (unwanted, discard)
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)] // ̲┌───────────────────^
static extern bool ReadConsoleOutputCharacterW(
IntPtr hStdout, // result of 'GetStdHandle(-11)'
out Char ch, // U̲n̲i̲c̲o̲d̲e̲ character result
uint c_in, // (set to '1')
uint coord_XY, // screen location to read, X:loword, Y:hiword
out uint c_out); // (unwanted, discard)
You may notice I've stripped down the marshaling on these to the bare minimum needed for the purposes of my example code, which is designed to only fetch one character at a time. Therefore, you will probably find that c_in must always be 1, due to the managed pointer declarations ‘out byte ch’ and ‘out Char ch’.
That's really all you need; calling the appropriate P/Invoke function as described above is mostly self-explanatory if you limit yourself to reading a single character. To show this with a trivial example, I'll finish with the cute demo program, that reads four characters back from the Console, along a diagonal of the grid we drew above.
static void Windows_Console_Readback()
{
var stdout = GetStdHandle(-11);
for (uint coord, y = 1; y <= 4; y++)
{
coord = (5 - y) * 2; // loword <-- X coord to read
coord |= y << 16; // hiword <-- Y coord to read
if (!ReadConsoleOutputCharacterA(
stdout,
out byte chAnsi, // result: single ANSI char
1, // # of chars to read
coord, // (X,Y) screen location to read (see above)
out _)) // result: actual # of chars (unwanted)
throw new Win32Exception();
Console.Write(" " + (Char)chAnsi + " ");
}
}
And there you have it...
This functionality doesn't exist. It's theoretically possible for you to override the input and output streams on the console to keep your own copy of the console buffer that you could read from, but it would be non-trivial (and probably couldn't support all of the edge cases such as an external program hooking into your console and reading/writing to it).
Forget about it, too much trouble, you could read from buffer and get all the current console output, but that would be too much.
My suggestion is to create a ConsoleWriter delegation, you choose how, could be a class or just an static method, and this writer would keep the last line in a property so each time you would Console.WriteLine something you just call your delegation, with your implementation and at the end it calls Console.WriteLine.
As #Servy has stated, there isn't any built-in functionality (that I know of, or can find) that can do what you want. However, there is a work-around (it's a bit of a hack, but it worked).
You can create your own buffer in memory, or on disk. Whenever you output to the Console, also output to your buffer. You can then use your buffer to read from in ways that you couldn't with the Console.
There are two ways to buffer: on disk or in memory. You can use the Console.BufferWidth and Console.BufferHeight properties to find out your buffer size. I found it simpler to do this in memory using an array of strings (each string was a line of output, and there were a number of strings in the array equal to the BufferHeight, if I remember correctly). A colleague ended up doing the same thing on disk.
You'll want to create a method to replace Console.Write and Console.WriteLine, so that you can write to both buffers at once. Something like:
public void MyWrite( string output ) {
Console.Write( output );
Array.Write( output ); // obvious pseudo-code
}
I found it helpful to wrap a Class around the array and implement methods to support it ... you could then implement your GetCharAtLocation( int i, int j ) method, as well as any other functionality you need there.
What about:
class Program {
static void Main( string[ ] args ) {
CustomizedConsole.WriteLine( "Lorem Ipsum" ); //Lorem Ipsum
Console.WriteLine( CustomizedConsole.ReadContent( 6, 5 ) ); //Ipsum
Console.WriteLine( CustomizedConsole.GetCharAtLocation( 0, 0 ) ); //L
}
}
static class CustomizedConsole {
private static List<char> buffer = new List<char>();
private static int lineCharCount = 0;
public static void Write(string s){
lineCharCount += s.Length;
buffer.AddRange( s );
Console.Write( s );
}
public static void WriteLine(string s ) {
for ( int i = 0; i < Console.BufferWidth - lineCharCount - s.Length; i++ )
s += " ";
buffer.AddRange( s );
Console.WriteLine( s );
lineCharCount = 0;
}
public static string ReadContent( int index, int count ) {
return new String(buffer.Skip( index ).Take( count ).ToArray());
}
public static char GetCharAtLocation( int x, int y ) {
return buffer[ Console.BufferHeight * x + y ];
}
}
EDIT :
As said the others this is just a trivial case where there are a lot of other things to improve. But I wrote this only as a starting point.
I've successfully read a PE header from an unmanaged module loaded into memory by another process. What I'd like to do now is read the names of this module's exports. Basically, this is what I have so far (I've left out a majority of the PE parsing code, because I already know it works):
Extensions
public static IntPtr Increment(this IntPtr ptr, int amount)
{
return new IntPtr(ptr.ToInt64() + amount);
}
public static T ToStruct<T>(this byte[] data)
{
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
T result = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();
return result;
}
public static byte[] ReadBytes(this Process process, IntPtr baseAddress, int size)
{
int bytesRead;
byte[] bytes = new byte[size];
Native.ReadProcessMemory(process.Handle, baseAddress, bytes, size, out bytesRead);
return bytes;
}
public static T ReadStruct<T>(this Process process, IntPtr baseAddress)
{
byte[] bytes = ReadBytes(process, baseAddress, Marshal.SizeOf(typeof(T)));
return bytes.ToStruct<T>();
}
public static string ReadString(this Process process, IntPtr baseAddress, int size)
{
byte[] bytes = ReadBytes(process, baseAddress, size);
return Encoding.ASCII.GetString(bytes);
}
GetExports()
Native.IMAGE_DATA_DIRECTORY dataDirectory =
NtHeaders.OptionalHeader.DataDirectory[Native.IMAGE_DIRECTORY_ENTRY_EXPORT];
if (dataDirectory.VirtualAddress > 0 && dataDirectory.Size > 0)
{
Native.IMAGE_EXPORT_DIRECTORY exportDirectory =
_process.ReadStruct<Native.IMAGE_EXPORT_DIRECTORY>(
_baseAddress.Increment((int)dataDirectory.VirtualAddress));
IntPtr namesAddress = _baseAddress.Increment((int)exportDirectory.AddressOfNames);
IntPtr nameOrdinalsAddress = _baseAddress.Increment((int)exportDirectory.AddressOfNameOrdinals);
IntPtr functionsAddress = _baseAddress.Increment((int)exportDirectory.AddressOfFunctions);
for (int i = 0; i < exportDirectory.NumberOfFunctions; i++)
{
Console.WriteLine(_process.ReadString(namesAddress.Increment(i * 4), 64));
}
}
When I run this, all I get is a pattern of double question marks, then completely random characters. I know the header is being read correctly, because the signatures are correct. The problem has to lie in the way that I'm iterating over the function list.
The code at this link seems to suggest that the names and ordinals form a matched pair of arrays, counted up to NumberOfNames, and that the functions are separate. So your loop may be iterating the wrong number of times, but that doesn't explain why you're seeing bad strings from the very beginning.
For just printing names, I'm having success with a loop like the one shown below. I think the call to ImageRvaToVa may be what you need to get the correct strings? However I don't know whether that function will work unless you've actually loaded the image by calling MapAndLoad -- that's what the documentation requests, and the mapping did not seem to work in some quick experiments I did using LoadLibrary instead.
Here's the pInvoke declaration:
[DllImport("DbgHelp.dll", CallingConvention = CallingConvention.StdCall), SuppressUnmanagedCodeSecurity]
public static extern IntPtr ImageRvaToVa(
IntPtr NtHeaders,
IntPtr Base,
uint Rva,
IntPtr LastRvaSection);
and here's my main loop:
LOADED_IMAGE loadedImage = ...; // populated with MapAndLoad
IMAGE_EXPORT_DIRECTORY* pIID = ...; // populated with ImageDirectoryEntryToData
uint* pFuncNames = (uint*)
ImageRvaToVa(
loadedImage.FileHeader,
loadedImage.MappedAddress,
pIID->AddressOfNames,
IntPtr.Zero);
for (uint i = 0; i < pIID->NumberOfNames; i++ )
{
uint funcNameRVA = pFuncNames[i];
if (funcNameRVA != 0)
{
char* funcName =
(char*) (ImageRvaToVa(loadedImage.FileHeader,
loadedImage.MappedAddress,
funcNameRVA,
IntPtr.Zero));
var name = Marshal.PtrToStringAnsi((IntPtr) funcName);
Console.WriteLine(" funcName: {0}", name);
}
}
I have a regular dll with the following function exported.
extern "C" __declspec(dllexport) int FindNearestStuff(double _latitude, double _longitude , LocationStruct * locations[])
LocationStruct is very simple
struct LocationStruct
{
long positionIndex;
long item;
};
I'm tryign to call it from c# using
[DllImport("myclever.dll", CharSet = CharSet.None)]
private static extern int FindNearestStuff(double _latitude, double _longitude,
ref LocationStruct [] locations);
It's all cool and funky and I can step into the dll function from the debugger.
Inside the dll the LocationStruct array is populated correctly and all is very good.
The problem I have is when it returns back from the dll, the LocationStruct array is not coming back with the data - just empty values...
What am I missing?
thanks so much for your help - you certainly put me onthe right direction and i really appreciate your assistance!
This is the solution which seems to work for me;
[DllImport("myclever.dll", CharSet = CharSet.None)]
private static extern int FindNearestStuff(double _latitude, double _longitude, IntPtr locations);
public static int FindNearestStuff(double _latitude, double _longitude, LocationStruct[] locations)
{
int returnValue = -1;
LocationStruct temp;
temp.roadIndex = 1;
temp.tdist = 1;
int iStructSize = Marshal.SizeOf(temp);
try
{
IntPtr locationsPtr = IntPtr.Zero;
IntPtr buffer = Marshal.AllocHGlobal(iStructSize * 10);
FindNearestRoads(_latitude, _longitude, buffer);
for (int i = 0; i < 10; i++)
{
IntPtr ptr = new IntPtr(buffer.ToInt32() + iStructSize * i);
locations[i] = (LocationStruct)Marshal.PtrToStructure(ptr, typeof(LocationStruct));
}
returnValue = 0;
}
catch
{
}
return returnValue;
}
I'm not sure that you will be able to do this automatically since C# has no way of knowing how many items are returned in the locations variable (I'm assuming that the return value of FindNearestStuff is the number of entries in locations.)
You will have to manually marshal your data using the Marshall class and a process like this:
[DllImport("myclever.dll", CharSet = CharSet.None)]
private static extern int FindNearestStuff(double _latitude, double _longitude,
out IntPtr locations);
public static LocationStruct[] FindNearestStuff(double latitude, double longitude) {
IntPtr locationsPtr = IntPtr.Zero;
int numLocations = FindNearestStuff(latitude, longitude, out locationsPtr);
LocationsStruct[] locations = new LocationsStruct[numLocations];
for (int i = 0; i < numLocations; i++) {
// locationsPtr is a pointer to the struct, so read the value
// at locationPtr which will be the address of the struct and
// then manually marshal the struct from that address
locaitonsStruct[i] = (LocationStruct)Marshal.PtrToStructure(
Marshal.ReadIntPtr(locationsPtr), typeof(LocationsStruct));
// Move to the location pointer to the next address of a
// pointer to a struct
locationsPtr += IntPtr.Size;
}
return locations;
}
I haven't actually tried this so caveat emptor.