how can I write DIRECTLY into console, without bothering with stdout in c#? I'm upgrading some old program, which has it's stdout redirected into file (because the output matters), and I need somehow to write directly to console, and that the text won't appear in stdout. Is it possible (without using WinAPI)?
EDIT: I'm aware of the possibility to write to stderr, althrough, is it possible to set cursor position for stderr on console?
You could write to Console.Error, which, although it can be redirected, is separate from stdout.
is it possible to set cursor position for stderr on console?
Edit: Assuming that stderr has not been redirected, see Console.CursorTop and Console.CursorLeft. There's various other members on the Console class that you might find useful.
To answer your question directly, use the Win32 WriteConsole function. As far as I can see, the .NET framework doesn't have methods for writing directly to the console window.
If I run your program and redirect its StandardOutput and StandardError, what do you expect to happen to your writes when there is no console?
For this reason, the answer is likely “you can’t” (except perhaps by using crazy hacks, which probably involve Windows API which you said you didn’t want to use).
Think of the console window as nothing more than a UI element that allows the user to view the stdout/stderr of your program (and to provide stdin). It doesn’t really exist for any other purpose.
I actually ended up implementing the low level WriteConsoleOutput some time ago as part of a terminal based Tetris implementation I wrote, since doing coloured console output with Console.BackgroundColor and Console.Write, is far too slow for full screen refreshes. Doing the raw buffer output is much faster.
Note, this writes text to a location on the screen (eg 5,10), but doesn't update or keep track of the cursor position on it's own - writing with this method will update the text buffer and display the output, but the cursor won't move. You'll need to move and keep track of the console cursor manually with some other method, which shouldn't be too difficult.
Here's my code:
Main interop class:
public static class LowLevelConsole {
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private 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);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool WriteConsoleOutput(
SafeFileHandle hConsoleOutput,
CharInfo[] lpBuffer,
Coord dwBufferSize,
Coord dwBufferCoord,
ref SmallRect lpWriteRegion);
[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 ushort Attributes;
}
[StructLayout(LayoutKind.Sequential)]
public struct SmallRect {
public short Left;
public short Top;
public short Right;
public short Bottom;
}
[STAThread]
public static void Write(string line, CharacterAttribute attribute, short xLoc, short yLoc) {
SafeFileHandle h = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
short writeHeight = 1;
short writeWidth = (short)line.Length;
if (!h.IsInvalid) {
CharInfo[] buf = new CharInfo[writeWidth * writeHeight];
SmallRect rect = new SmallRect() { Left = xLoc, Top = yLoc, Right = (short)(writeWidth + xLoc), Bottom = (short)(writeHeight + yLoc) };
for (int i = 0; i < writeWidth; i++) {
buf[i].Attributes = (ushort)attribute;
buf[i].Char.UnicodeChar = line[i];
}
bool b = WriteConsoleOutput(h, buf, new Coord() { X = writeWidth, Y = writeHeight }, new Coord() { X = 0, Y = 0 }, ref rect);
}
}
[STAThread]
public static bool WriteBuffer(CharInfo[,] buffer) { // returns true of success
SafeFileHandle h = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
if (!h.IsInvalid) {
short BufferWidth = (short)buffer.GetLength(0);
short BufferHeight = (short)buffer.GetLength(1);
CharInfo[] buf = new CharInfo[BufferWidth * BufferHeight];
SmallRect rect = new SmallRect() { Left = 0, Top = 0, Right = BufferWidth, Bottom = BufferHeight };
for (int y = 0; y < BufferHeight; y++) {
for (int x = 0; x < BufferWidth; x++) {
buf[y * BufferWidth + x] = buffer[x, y];
}
}
return WriteConsoleOutput(h, buf, new Coord() { X = BufferWidth, Y = BufferHeight }, new Coord() { X = 0, Y = 0 }, ref rect);
}
return false;
}
}
Character attributes:
[Flags]
public enum CharacterAttribute : ushort {
FOREGROUND_BLUE = 0x0001,
FOREGROUND_GREEN = 0x0002,
FOREGROUND_RED = 0x0004,
FOREGROUND_INTENSITY = 0x0008,
BACKGROUND_BLUE = 0x0010,
BACKGROUND_GREEN = 0x0020,
BACKGROUND_RED = 0x0040,
BACKGROUND_INTENSITY = 0x0080,
COMMON_LVB_LEADING_BYTE = 0x0100,
COMMON_LVB_TRAILING_BYTE = 0x0200,
COMMON_LVB_GRID_HORIZONTAL = 0x0400,
COMMON_LVB_GRID_LVERTICAL = 0x0800,
COMMON_LVB_GRID_RVERTICAL = 0x1000,
COMMON_LVB_REVERSE_VIDEO = 0x4000,
COMMON_LVB_UNDERSCORE = 0x8000
}
Test code:
class Program {
static void Main(string[] args) {
// write to location 0,0
LowLevelConsole.Write("Some test text", CharacterAttribute.BACKGROUND_BLUE | CharacterAttribute.FOREGROUND_RED, 0, 0);
// write to location 5,10
LowLevelConsole.Write("another test at a different location",
CharacterAttribute.FOREGROUND_GREEN | CharacterAttribute.FOREGROUND_BLUE,
5, 10);
Console.ReadLine();
}
}
Related
Mouse stimulation using SendInput works perfectly on MainDisplay. However when I use SendInput for extended screen (e.g. Second screen placed to the left of the main display in my case. Issues is replicable irrespective of the extended display any place around main display but with different resolution then main display):
If I use SendInput on extended screen, the mouse position has offset in both X and Y position, ever so slightly ranging from 40 to 80 points in x and 10 to 20 points in Y based on if X (width) and Y(height) of extended screen is different to main display width/height)
Thanks in advance for any support as to why difference on extended screen
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetCursorPos(ref Win32Point pt);
[DllImport("user32.dll")]
internal static extern bool SetCursorPos(int X, int Y);
[StructLayout(LayoutKind.Sequential)]
internal struct Win32Point
{
public Int32 X;
public Int32 Y;
};
internal enum SendInputEventType : int
{
InputMouse,
InputKeyboard
}
[DllImport("user32.dll", SetLastError = true)]
private static extern uint SendInput(uint nInputs, ref Input pInputs, int cbSize);
public struct Input
{
public uint InputType;
public MouseInput MI;
}
public struct MouseInput
{
public int Dx;
public int Dy;
public uint MouseData;
public uint DwFlags;
public uint Time;
public IntPtr DwExtraInfo;
}
public enum MouseEventInfo
{
mouseEventfMove = 0x0001,
mouseEventfLeftdown = 0x0002,
mouseEventfLeftup = 0x0004,
mouseEventfRightdown = 0x0008,
mouseEventfRightup = 0x0010,
mouseEventfWheel = 0x0800,
mouseEventfAbsolute = 0x8000,
wheelDelta = 0x0078
}
static int CalculateAbsoluteCoordinateX(int x, System.Drawing.Rectangle currentBounds)
{
return ((currentBounds.X + x) * 65536) / (currentBounds.Width);
}
static int CalculateAbsoluteCoordinateY(int y, System.Drawing.Rectangle currentBounds)
{
return (((currentBounds.Y + y) * 65536) / currentBounds.Height);
}
// for me screen at index 0 (screen no 1) is main display. Screen id 2
//placed to the left of the main display as per resolution screen i.e.at
//index 1 (Screen.AllScreens[1]) is extended display and Bound.X is a -ve value
public static int ScreenId = 2;
public static System.Drawing.Rectangle CurrentBounds
{
get
{
return SysForms.Screen.AllScreens[ScreenId - 1].Bounds;
}
}
public static void ClickLeftMouseButton(int x, int y)
{
Input mouseInput = new Input();
mouseInput.InputType = SendInputEventType.InputMouse;
mouseInput.MI.Dx = CalculateAbsoluteCoordinateX(x, CurrentBounds);
mouseInput.MI.Dy = CalculateAbsoluteCoordinateY(y, CurrentBounds);
mouseInput.MI.MouseData = 0;
mouseInput.MI.DwFlags = MouseEventInfo.mouseEventfMove | MouseEventInfo.mouseEventfAbsolute;
SendInput(1, ref mouseInput, Marshal.SizeOf(new INPUT()));
mouseInput.MI.DwFlags = MouseEventInfo.mouseEventfLeftdown;
SendInput(1, ref mouseInput, Marshal.SizeOf(new INPUT()));
mouseInput.MI.DwFlags = MouseEventFlags.mouseEventfLeftup;
SendInput(1, ref mouseInput, Marshal.SizeOf(new INPUT()));
}
//Below is code of the WPF MainWindow for testing. Two buttons with click event.
// For main display with screenid as 1 both setcursor position and sendinput
//work perfectly, as I get the MousePosition, but when I apply this to
//extended screen (currently with two screen, main display is screen 1 in my
//case and screen 2 is extended screen, they put the mouse at two different positions.
//I have my doubts the way I am using the extended screen Bounds.X, but
//haven't will able to fix the issue
int x = 600;
int y = 300;
private void btnSend_Click(object sender, RoutedEventArgs e)
{
SetCursorPos(SysForms.Screen.AllScreens[ScreenId - 1].Bounds.X + x, SysForms.Screen.AllScreens[screenId - 1].Bounds.Y + y);
}
private void btnSend1_Click(object sender, RoutedEventArgs e)
{
ClickLeftMouseButton(x, y);
}
Found the issue. While using SendInput, the conversion of x,y in absolute value must be done in relation to Main/Primary screen.
Thus the changes:
static int CalculateAbsoluteCoordinateX(int x, System.Drawing.Rectangle currentBounds)
{
return ((currentBounds.X + x) * 65536) / (SystemParameters.PrimaryScreenWidth);
}
static int CalculateAbsoluteCoordinateY(int y, System.Drawing.Rectangle currentBounds)
{
return (((currentBounds.Y + y) * 65536) / SystemParameters.PrimaryScreenHeight);
}
In my windows.forms c# application, I have a multi-line textbox with WordWrap = true. After I set Text property to a long string, I need to get all lines produced by wrapping. It is not the same as Lines[] property, because my text does not include new line characters.
I have found solutions using graphics MeasureString function but it seems a little bit extra work considering that the textbox control already did the wrapping - why should I do the same work again?
Is there any way to get the lines into which the textbox wraps the text?
Thank you
Can you check the below solution,
public Form1()
{
InitializeComponent();
textBox1.Text = "This is my text where I want to check how I can get wrapped content as seperate lines automatically !! This is my text which I want to check how I can get wrapped content as seperate lines automatically !!";
}
private void button1_Click(object sender, EventArgs e)
{
bool continueProcess = true;
int i = 1; //Zero Based So Start from 1
int j = 0;
List<string> lines = new List<string>();
while (continueProcess)
{
var index = textBox1.GetFirstCharIndexFromLine(i);
if (index != -1)
{
lines.Add(textBox1.Text.Substring(j, index - j));
j = index;
i++;
}
else
{
lines.Add(textBox1.Text.Substring(j, textBox1.Text.Length - j));
continueProcess = false;
}
}
foreach(var item in lines)
{
MessageBox.Show(item);
}
}
GetFirstCharIndexFromLine Reference
Line numbering in the text box starts at zero. If the lineNumber
parameter is greater than the last line in the text box,
GetFirstCharIndexFromLine returns -1.
GetFirstCharIndexFromLine returns the first character index of a
physical line. The physical line is the displayed line, not the
assigned line. The number of displayed lines can be greater than the
number of assigned lines due to word wrap. For example, if you assign
two long lines to a RichTextBox control and set Multiline and WordWrap
to true, the two long assigned lines result in four physical (or
displayed lines).
A little pinvoking would work:
private const UInt32 EM_GETLINECOUNT = 0xba;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
private void button1_Click(object sender, EventArgs e) {
int numLines = SendMessage(textBox1.Handle,
EM_GETLINECOUNT, IntPtr.Zero, IntPtr.Zero).ToInt32()
MessageBox.Show(numLines.ToString());
}
REVISED ANSWER
I checked the Win32 APIs again and realized it could be done easily. I wrote an extension method so you can do it even easier:
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
static class TextBoxExtensions
{
private const uint EM_FMTLINES = 0x00C8;
private const uint WM_GETTEXT = 0x000D;
private const uint WM_GETTEXTLENGTH = 0x000E;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam, StringBuilder lParam);
public static string[] GetWrappedLines(this TextBox textBox)
{
var handle = textBox.Handle;
SendMessage(handle, EM_FMTLINES, 1, IntPtr.Zero);
var size = SendMessage(handle, WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero).ToInt32();
if (size > 0)
{
var builder = new StringBuilder(size + 1);
SendMessage(handle, WM_GETTEXT, builder.Capacity, builder);
return builder.ToString().Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
}
return new string[0];
}
}
}
usage:
var lines = textBox1.GetWrappedLines();
ORIGINAL ANSWER
WinForm TextBox is actually a wrapper of Windows GDI edit control, which handles text wrapping natively. That being said, even if the TextBox keeps an array of wrapped lines, it is not exposed by public API, not even brought to managed environment (which, if it did, can however be retrieved with reflection). So your best bet is still MeasureString.
To check if particular line is wrapped or not, here is the GDI Function you need to use:
1. [DllImport("user32.dll")]
static extern int DrawText(IntPtr hdc, string lpStr, int nCount, ref Dimension lpRect, int wFormat);
Here are what you need to get things done:
public enum DrawTextFlags
{
CalculateArea = 0x00000400,
WordBreak = 0x00000010,
TextBoxControl = 0x00002000,
Top = 0x00000000,
Left = 0x00000000,
HorizontalCenter = 0x00000001,
Right = 0x00000002,
VerticalCenter = 0x00000004,
Bottom = 0x00000008,
SingleLine = 0x00000020,
ExpandTabs = 0x00000040,
TabStop = 0x00000080,
NoClipping = 0x00000100,
ExternalLeading = 0x00000200,
NoPrefix = 0x00000800,
Internal = 0x00001000,
PathEllipsis = 0x00004000,
EndEllipsis = 0x00008000,
WordEllipsis = 0x00040000,
ModifyString = 0x00010000,
RightToLeft = 0x00020000,
NoFullWidthCharacterBreak = 0x00080000,
HidePrefix = 0x00100000,
PrefixOnly = 0x00200000,
NoPadding = 0x10000000,
}
[StructLayout(LayoutKind.Sequential)]
public struct Dimension
{
public int Left, Top, Right, Bottom;
public Dimension(int left, int top, int right, int bottom)
{
this.Left = left;
this.Right = right;
this.Top = top;
this.Bottom = bottom;
}
public Dimension(Rectangle r)
{
this.Left = r.Left;
this.Top = r.Top;
this.Bottom = r.Bottom;
this.Right = r.Right;
}
public static implicit operator Rectangle(Dimension rc)
{
return Rectangle.FromLTRB(rc.Left, rc.Top, rc.Right, rc.Bottom);
}
public static implicit operator Dimension(Rectangle rc)
{
return new Dimension(rc);
}
public static Dimension Default
{
get { return new Dimension(0, 0, 1, 1); }
}
}
So to know whether a particular line is wrapped or not, you would call the function like this:
Dimension rc = new Dimension(0,0,2,2);
var flag = DrawTextFlags.CalculateArea | DrawTextFlags.TextBoxControl | DrawTextFlags.WordBreak;
DrawText(hdc, line, line.length, ref rc, (int)flag);
Now if height of rc you get after executing this function is greater then your font height or tmHeight if you use TextMetric (that is what minimum required for a line to fit vertically) you can safely assume your line is wrapped.
Apart from this,
You can use the following function as an alternative approach:
static extern bool GetTextExtentExPoint(IntPtr hDc, string str, int nLength,
int nMaxExtent, int[] lpnFit, int[] alpDx, ref Size size);
I am writing a c# console tetris game. Once I got to the part that the application was ready. I got to the part where I had to solve lagging. I am writing out like this:
static void writeCol(string a, ConsoleColor b)
{
ConsoleColor c = Console.ForegroundColor;
Console.ForegroundColor = b;
Console.Write(a);
Console.ForegroundColor = c;
}
So when a new block comes/i want to move somehing:
writeCol(blokk, ConsoleColor.Magenta);
Where blokk is:
private const string blokk = "█";
I have found a way to "write" to the console faster:
using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
namespace ConsoleApplication1
{
class Program
{
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
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);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool WriteConsoleOutput(
SafeFileHandle hConsoleOutput,
CharInfo[] lpBuffer,
Coord dwBufferSize,
Coord dwBufferCoord,
ref SmallRect lpWriteRegion);
[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;
}
[STAThread]
static void Main(string[] args)
{
SafeFileHandle h = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
if (!h.IsInvalid)
{
CharInfo[] buf = new CharInfo[80 * 25];
SmallRect rect = new SmallRect() { Left = 0, Top = 0, Right = 80, Bottom = 25 };
for (byte character = 65; character < 65 + 26; ++character)
{
for (short attribute = 0; attribute < 15; ++attribute)
{
for (int i = 0; i < buf.Length; ++i)
{
buf[i].Attributes = attribute;
buf[i].Char.AsciiChar = character;
}
bool b = WriteConsoleOutput(h, buf,
new Coord() { X = 80, Y = 25 },
new Coord() { X = 0, Y = 0 },
ref rect);
}
}
}
Console.ReadKey();
}
}
}
(This code prints out all the characters from A-Z).
So finnaly the question:
How can i modify this code to take advantage of it?
Thanks in advance. Have a nice day.
EDIT:
I found 1 way but it gives me buggy text. Any ideas?
public static void Writetocol(string s)
{
var kiir = s;
byte[] barr;
kiir = Convert.ToString(kiir);
barr = Encoding.ASCII.GetBytes(kiir);
SafeFileHandle h = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
if (!h.IsInvalid)
{
CharInfo[] buf = new CharInfo[80 * 25];
SmallRect rect = new SmallRect() { Left = 0, Top = 0, Right = 80, Bottom = 25 };
for (short attribute = 0; attribute < 15; ++attribute)
{
for (int i = 0; i < barr.Length; ++i)
{
buf[i].Attributes = attribute;
buf[i].Char.AsciiChar = barr[i];
}
bool b = WriteConsoleOutput(h, buf,
new Coord() { X = 80, Y = 25 },
new Coord() { X = 0, Y = 0 },
ref rect);
}
}
}
It gives me this:
When it should give me this:
(It's written Hungarian if anyone wonders)
You could parse the string you provide to fill the buffer by handling your own linefeeds and position, like so:
static void writeCol(string a, ConsoleColor b)
{
byte x = 0, y = 0;
// parsing to make it fly
// fill the buffer with the string
for(int ci=0; ci<a.Length;ci++)
{
switch (a[ci])
{
case '\n': // newline char, move to next line, aka y=y+1
y++;
break;
case '\r': // carriage return, aka back to start of line
x = 0;
break;
case ' ': // a space, move the cursor to the right
x++;
break;
default:
// calculate where we should be in the buffer
int i = y * 80 + x;
// color
buf[i].Attributes= (short) b;
// put the current char from the string in the buffer
buf[i].Char.AsciiChar = (byte) a[ci];
x++;
break;
}
}
// we handled our string, let's write the whole screen at once
bool success = WriteConsoleOutput(h, buf,
new Coord() { X = 80, Y = 25 },
new Coord() { X = 0, Y = 0 },
ref rect);
}
Notice that I already refactored the safehandle h and the native buffer buf to the static state so we only have this once in the app:
static IntPtr h= GetStdHandle(STD_OUTPUT_HANDLE);
static CharInfo[] buf = new CharInfo[80 * 25];
static SmallRect rect = new SmallRect() { Left = 0, Top = 0, Right = 80, Bottom = 25 };
I have added the GetStdHandle
//http://www.pinvoke.net/default.aspx/kernel32/GetStdHandle.html
const int STD_OUTPUT_HANDLE = -11;
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetStdHandle(int nStdHandle);
You need to change the method signature on WriteConsoleOutput to accept an IntPtr instead of SafeFileHandle in that case.
I tested this method with the following test call:
writeCol(#"
TEST
======
1 test
FuBar", ConsoleColor.Blue);
which gives this result:
So keep in mind to fill the buffer buf at the correct positions first and then call WriteConsoleOutput to copy the buffer to the screen at once. If you call that very often you are back to square one...
Notice that you don't need to write the whole screen. By using different rectangles you can write only parts of the screen.
For this demo I left out all error-checking. That is up to you to check.
You might want to read up on the native calls used from the msdn documentation
When I do this;
Point startpoint = Cursor.Position;
startpoint.Y -= 1;
DoMouse(MOUSEEVENTF.MOVE | MOUSEEVENTF.ABSOLUTE, startpoint);
The mouse doesn't just move up.. it moves a bit to the left as well. But if I do it in a loop, it only moves to the left at the first iteration.
Here is a fully working console program presenting the problem. You have to Add Reference -> .NET -> System.Drawing and System.Windows.Forms to get it to compile.
When starting the program type start to move the mouse up 5 pixels once or type start X (X being a number) to move the mouse up 5 pixels X times. You will see that each new loop the mouse will move a bit to the left; it shouldn't be doing that at all.
using System;
using System.Text.RegularExpressions;
using System.Threading;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace mousemove_temp
{
class Program
{
//Capture user input
static void Main(string[] args)
{
while (true)
{
string s = Console.ReadLine();
switch (s)
{
case("start"):
moveMouseTest(1);
break;
default:
//Get # of times to run function
Match match = Regex.Match(s, #"start (.+)", RegexOptions.IgnoreCase);
if (!match.Success || match.Groups.Count != 2) break;
//Copy # to int
int amnt = -1;
try
{
amnt = Int32.Parse(match.Groups[1].Value);
}
catch (Exception) { break; } //fail
if (amnt <= -1) break; //fail
moveMouseTest(amnt); //aaaawww yeah
break;
}
Thread.Sleep(10);
}
}
//Move the mouse
static void moveMouseTest(int repeat)
{
int countrepeat = 0;
//Loop entire function X times
while (countrepeat < repeat)
{
Point startpoint = Cursor.Position;
int amount = 5; //Move 5 pixels
int counter = 0;
//Move 1 pixel up each loop
while (counter < amount)
{
startpoint.Y -= 1;
DoMouse(MOUSEEVENTF.MOVE | MOUSEEVENTF.ABSOLUTE, startpoint);
counter++;
Thread.Sleep(100); //Slow down so you can see it only jumps left the first time
}
countrepeat++;
Console.WriteLine(String.Format("{0}/{1}", countrepeat, repeat));
Thread.Sleep(1000); //Wait a second before next loop
}
}
/*
* Function stuff
*/
//Control the Mouse
private static object mouselock = new object(); //For use with multithreading
public static void DoMouse(MOUSEEVENTF flags, Point newPoint)
{
lock (mouselock)
{
INPUT input = new INPUT();
MOUSEINPUT mi = new MOUSEINPUT();
input.dwType = InputType.Mouse;
input.mi = mi;
input.mi.dwExtraInfo = IntPtr.Zero;
// mouse co-ords: top left is (0,0), bottom right is (65535, 65535)
// convert screen co-ord to mouse co-ords...
input.mi.dx = newPoint.X * (65535 / Screen.PrimaryScreen.Bounds.Width);
input.mi.dy = newPoint.Y * (65535 / Screen.PrimaryScreen.Bounds.Height);
input.mi.time = 0;
input.mi.mouseData = 0;
// can be used for WHEEL event see msdn
input.mi.dwFlags = flags;
int cbSize = Marshal.SizeOf(typeof(INPUT));
int result = SendInput(1, ref input, cbSize);
if (result == 0)
Console.WriteLine("DoMouse Error:" + Marshal.GetLastWin32Error());
}
}
/*
* Native Methods
*/
[DllImport("user32.dll", SetLastError = true)]
static internal extern Int32 SendInput(Int32 cInputs, ref INPUT pInputs, Int32 cbSize);
[DllImport("user32.dll")]
public static extern bool GetAsyncKeyState(Int32 vKey);
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 28)]
internal struct INPUT
{
[FieldOffset(0)]
public InputType dwType;
[FieldOffset(4)]
public MOUSEINPUT mi;
[FieldOffset(4)]
public KEYBDINPUT ki;
[FieldOffset(4)]
public HARDWAREINPUT hi;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct MOUSEINPUT
{
public Int32 dx;
public Int32 dy;
public Int32 mouseData;
public MOUSEEVENTF dwFlags;
public Int32 time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct KEYBDINPUT
{
public Int16 wVk;
public Int16 wScan;
public KEYEVENTF dwFlags;
public Int32 time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct HARDWAREINPUT
{
public Int32 uMsg;
public Int16 wParamL;
public Int16 wParamH;
}
internal enum InputType : int
{
Mouse = 0,
Keyboard = 1,
Hardware = 2
}
[Flags()]
internal enum MOUSEEVENTF : int
{
MOVE = 0x1,
LEFTDOWN = 0x2,
LEFTUP = 0x4,
RIGHTDOWN = 0x8,
RIGHTUP = 0x10,
MIDDLEDOWN = 0x20,
MIDDLEUP = 0x40,
XDOWN = 0x80,
XUP = 0x100,
VIRTUALDESK = 0x400,
WHEEL = 0x800,
ABSOLUTE = 0x8000
}
[Flags()]
internal enum KEYEVENTF : int
{
EXTENDEDKEY = 1,
KEYUP = 2,
UNICODE = 4,
SCANCODE = 8
}
}
}
Can anybody tell what's going wrong?
You're doing the math wrong and as a result are getting rounding errors.
For example, 65535 / 1920 = 34.1328125. But truncation (because you are dividing an int by an int) is resulting in 34. So if on a 1920x1080 screen you had the mouse all the way at the right, you would get 1920 * (65535 / 1920) = 1920 * 34 = 65280.
This will get you better results:
input.mi.dx = (int)((65535.0f * (newPoint.X / (float)Screen.PrimaryScreen.Bounds.Width)) + 0.5f);
input.mi.dy = (int)((65535.0f * (newPoint.Y / (float)Screen.PrimaryScreen.Bounds.Height)) + 0.5f);
Though if you're determined to use P/Invoke rather than just say
Cursor.Position = new Point(newPoint.X, newPoint.Y);
then you really should use SetCursorPos - http://msdn.microsoft.com/en-us/library/windows/desktop/ms648394(v=vs.85).aspx - since that (along with GetCursorPos) is the API that .NET is using to get and set the cursor position via Cursor.Position.
Simplest way for your project is useful open-source library Windows Input Simulator (C# SendInput Wrapper - Simulate Keyboard and Mouse) on codeplex. Use it!
I want to learn if there is another (faster) way to output text to the console application window using C# .net than with the simple Write, BackgroundColor and ForegroundColor methods and properties? I learned that each cell has a background color and a foreground color, and I would like to cache/buffer/write faster than using the mentioned methods.
Maybe there is some help using the Out buffer, but I don't know how to encode the colors into the stream, if that is where the color data resides.
This is for a retrostyle textbased game I am wanting to implement where I make use of the standard colors and ascii characters for laying out the game.
Please help :)
Update:
The Out and buffer is probably not what I need to mess around with. There seems to be a screen buffer that is owned by the console. I don't know how to access it, maybe I am just out of luck unless I import some dlls.
Update: added a sample
If you are prepared to do some P/Invoke stuff, this might help.
Basically if you get a handle to the console buffer, then you can use the standard Win32 APIs wot manipulate the buffer, even build the the entire buffer off screen and the blit it to the Console.
The only tricky part is getting the handle to the console buffer. I have not tried this in .NET, but in years gone by, you could get the handle to the current console by using CreateFile (you will need to P/Invoke this) and open "CONOUT$" then you can use the handle that is return to pass to the other APIs.
P/Invoke for CreateFile
http://www.pinvoke.net/default.aspx/kernel32/CreateFile.html
And you can use WriteConsoleOutput to move all the characters and their attributes from a memory buffer to the console buffer.
http://msdn.microsoft.com/en-us/library/ms687404(VS.85).aspx
You could probably put together a nice library to provide lower-level access to the console buffer.
Since I am trying to get my .NET up to scratch again I thought I would try my hand at this and see if I could get it to work. Here is a sample that will fill the screen with all the letters A-Z and run through all the forground attributes 0-15. I think you will be impressed with the performance. I'll be honest, I did not spend much time reviewing this code so error checking is zero and there might be a little bug here or there but it should get you going with the rest of the APIs.
using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
namespace ConsoleApplication1
{
class Program
{
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
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);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool WriteConsoleOutputW(
SafeFileHandle hConsoleOutput,
CharInfo[] lpBuffer,
Coord dwBufferSize,
Coord dwBufferCoord,
ref SmallRect lpWriteRegion);
[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 ushort 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;
}
[STAThread]
static void Main(string[] args)
{
SafeFileHandle h = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
if (!h.IsInvalid)
{
CharInfo[] buf = new CharInfo[80 * 25];
SmallRect rect = new SmallRect() { Left = 0, Top = 0, Right = 80, Bottom = 25 };
for (byte character = 65; character < 65 + 26; ++character)
{
for (short attribute = 0; attribute < 15; ++attribute)
{
for (int i = 0; i < buf.Length; ++i)
{
buf[i].Attributes = attribute;
buf[i].Char.AsciiChar = character;
}
bool b = WriteConsoleOutputW(h, buf,
new Coord() { X = 80, Y = 25 },
new Coord() { X = 0, Y = 0 },
ref rect);
}
}
}
Console.ReadKey();
}
}
}
Unicode example
using Microsoft.Win32.SafeHandles;
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace FastConsole
{
class Program
{
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
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);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool WriteConsoleOutputW(
SafeFileHandle hConsoleOutput,
CharInfo[] lpBuffer,
Coord dwBufferSize,
Coord dwBufferCoord,
ref SmallRect lpWriteRegion);
[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 ushort 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;
}
[STAThread]
static void Main(string[] args)
{
SafeFileHandle h = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
if (!h.IsInvalid)
{
CharInfo[] buf = new CharInfo[80 * 25];
SmallRect rect = new SmallRect() { Left = 0, Top = 0, Right = 80, Bottom = 25 };
for (ushort character = 0x2551; character < 0x2551 + 26; ++character)
{
for (short attribute = 0; attribute < 15; ++attribute)
{
for (int i = 0; i < buf.Length; ++i)
{
buf[i].Attributes = attribute;
buf[i].Char.UnicodeChar = character;
}
bool b = WriteConsoleOutputW(h, buf,
new Coord() { X = 80, Y = 25 },
new Coord() { X = 0, Y = 0 },
ref rect);
Console.ReadKey();
}
}
}
Console.ReadKey();
}
}
}
If you look at the implementation of Console's properties for altering console colours, they delegate to the SetConsoleTextAttribute method from kernel32.dll. This method takes character attributes as input to set both the foreground and background colours.
From several MSDN doc pages, each screen buffer (of which a console has one) has a two-dimensional array of character info records, each represented by a CHAR_INFO. This is what determines the colour of each character. You can manipulate this using the SetConsoleTextAttribute method, but this is applied to any new text that is written to the console - you cannot manipulate existing text already on the console.
Unless there is a lower-level hook into the console text colour properties (which doesn't look likely), I think you are stuck using these methods.
One thing you could try is to create a new screen buffer, write to that, and then switch it to be the console's current buffer using SetConsoleActiveScreenBuffer. This may yield faster output as you will be writing all output to an inactive buffer.
I had success using
using (var stdout = Console.OpenStandardOutput(Cols * Rows))
{
// fill
stdout.Write(buffer, 0, buffer.Length);
// rinse and repeat
}
But if anyone can advise me on how I write extended ASCII to this i'd be grateful