I'm currently writing a small utility library to help improve performance when writing to the console, and I'm facing an issue where it fails to actually output any text. Below is my code:
public static class QuickDraw
{
private static short Width => (short)Console.WindowWidth;
private static short Height => (short)Console.WindowHeight;
private static SafeFileHandle Handle =>
Kernel32.CreateFile("$CONOUT", 0x40000000, 2, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
private static Kernel32.Coord Cursor =>
new Kernel32.Coord((short)Console.CursorLeft, (short)Console.CursorTop);
private static Kernel32.SmallRect WriteRegion =>
new Kernel32.SmallRect() { Left = 0, Top = 0, Right = Width, Bottom = Height };
private static Kernel32.Coord BufferSize =>
new Kernel32.Coord(Width, Height);
private static Kernel32.Coord BufferCoord =>
new Kernel32.Coord(0, 0);
public static void Write(char[] text, ConsoleColor fg, ConsoleColor bg)
{
Kernel32.CharInfo[] buffer = new Kernel32.CharInfo[Width * Height];
Kernel32.Coord cursor = Cursor;
for (int i = 0; i < text.Length; i++)
{
if (text[i] == '\n')
{
cursor.X = 0;
cursor.Y++;
}
else
{
int index = (cursor.Y * Width) + cursor.X;
// Set character
buffer[index].Char.AsciiChar = (byte)text[i];
// Set color
// (Crazy heckin bitwise crap, don't touch.)
buffer[index].Attributes = (short)((int)fg | ((int)bg | (2 << 4)));
// Increment cursor
cursor.X++;
}
// Make sure that cursor does not exceed bounds of window
if (cursor.X >= Width)
{
cursor.X = 0;
cursor.Y++;
}
if (cursor.Y >= Height)
{
cursor.Y = 0;
}
}
var writeRegion = WriteRegion;
Kernel32.WriteConsoleOutput(Handle, buffer, BufferSize, BufferCoord, ref writeRegion);
Console.SetCursorPosition(cursor.X, cursor.Y);
}
}
// Taken from https://stackoverflow.com/a/2754674/7937949
internal static class Kernel32
{
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public 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)]
public 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)
{
X = x;
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(2)] 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've mostly taken the implementation from this post, adding a new class to simplify its use. I'm not entirely sure where my issue is, as my debugger is not working right, but I'm pretty sure it's in my implementation in QuickDraw.
When I try to use QuickDraw.Write(), the cursor moves to the end of whatever string it was trying to print, but nothing actually shows up. What am I doing wrong?
First of all, you didn't copy the code right. There are some mistakes.
in CharInfo change
[FieldOffset(2)] public CharUnion Char;
to
[FieldOffset(0)] public CharUnion Char;
Change $CONOUT to this CONOUT$
First set the Attribute and then the AsciiChar
And of course, if you want the correct foreground-/background color representation then you have to delete the 2 << 4. I don't even know why you put it there.
Related
GetConsoleScreenBufferInfoEx invalid return value
I am trying to change the console's color palette. To get this done I first need to Get my ConsoleScreenBufferInfoEx and then Set it. The problem is that I can't even get a valid ConsoleScreenBufferInfoEx from the STD_OUTPUT_HANDLE.
The code below throws this error message:
System.ArgumentException: 'Value does not fall within the expected range.'
The handle is valid and yet I get this error. I have quadruple-checked every data type and related pinvoke entry - everything is looking good to me. There is no sample code for GetConsoleScreenBufferInfoEx and I haven't been able to find a working solution yet.
My sources:
pinvoke: ConsoleFunctions (kernel32)
msdocs: CONSOLE_SCREEN_BUFFER_INFOEX structure
msdocs: COLORREF
Example App (.NET Core 3.1):
For this code to work, the project's build properties must allow unsafe code.
[Properties -> Build -> Allow unsafe code]
using Microsoft.Win32.SafeHandles;
using System;
using System.Drawing;
using System.Runtime.InteropServices;
namespace ScreenBufferInfoExample
{
class Program
{
[DllImport("kernel32.dll", SetLastError = true)]
public static extern SafeFileHandle GetStdHandle(int nStdHandle);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern unsafe bool GetConsoleScreenBufferInfoEx(SafeFileHandle hConsoleOutput,
out CONSOLE_SCREEN_BUFFER_INFO_EX ConsoleScreenBufferInfo);
static void Main()
{
SafeFileHandle stdOut = GetStdHandle(-11);
if(stdOut.IsInvalid)
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
CONSOLE_SCREEN_BUFFER_INFO_EX info = new CONSOLE_SCREEN_BUFFER_INFO_EX();
info.cbSize = (uint)Marshal.SizeOf(info);
if(!GetConsoleScreenBufferInfoEx(stdOut, out info)) {
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());// <- this gets thrown
// System.ArgumentException: 'Value does not fall within the expected range.'
}
Console.ReadKey(true);
}
}
[StructLayout(LayoutKind.Sequential)]
struct CONSOLE_SCREEN_BUFFER_INFO_EX
{
public uint cbSize;
public COORD dwSize;
public COORD dwCursorPosition;
public ushort wAttributes;
public SMALL_RECT srWindow;
public COORD dwMaximumWindowSize;
public ushort wPopupAttributes;
public bool bFullscreenSupported;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public COLORREF[] ColorTable;
}
[StructLayout(LayoutKind.Sequential)]
struct COORD
{
public short X;
public short Y;
}
[StructLayout(LayoutKind.Sequential)]
struct SMALL_RECT
{
public short Left;
public short Top;
public short Right;
public short Bottom;
}
[StructLayout(LayoutKind.Sequential)]
struct COLORREF
{
public uint ColorDWORD;
public COLORREF(int r, int g, int b)
: this(Color.FromArgb(r, g, b)) { }
public COLORREF(Color color)
{
ColorDWORD = (uint)color.R
+ (((uint)color.G) << 8)
+ (((uint)color.B) << 16);
}
public Color GetColor()
{
return Color.FromArgb((int)(0x000000FFU & ColorDWORD),
(int)(0x0000FF00U & ColorDWORD) >> 8,
(int)(0x00FF0000U & ColorDWORD) >> 16);
}
public void SetColor(Color color)
{
ColorDWORD = (uint)color.R
+ (((uint)color.G) << 8)
+ (((uint)color.B) << 16);
}
}
}
pinvoke.net is often useful, but not always right.
As you need to pass information in and out of the GetConsoleScreenBufferInfoEx method, the parameter cannot be out, but needs to be ref.
So the correct declaration would be
[DllImport("kernel32.dll", SetLastError = true)]
public static extern unsafe bool GetConsoleScreenBufferInfoEx(SafeFileHandle hConsoleOutput,
ref CONSOLE_SCREEN_BUFFER_INFO_EX ConsoleScreenBufferInfo);
called as GetConsoleScreenBufferInfoEx(stdOut, ref info).
(I just corrected the page on pinvoke.net accordingly)
I'm making a console game in C# in Visual Studio and I want to make the game to be in the full console window.
I resized the buffer and window to be the same size, but it looks like the space after the scrollbars remained, which will probably be really annoying to look at. I wonder if there's any way to remove that blank space in C#, or at least add grayed-out scrollbars. I saw a similar thread, but it was in C++.
Here's a reproducible example:
using Microsoft.Win32.SafeHandles;
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace ConsoleTest1
{
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, Y;
public Coord(short x, short y)
{
X = x;
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, Top, Right, Bottom;
public SmallRect(short width, short height)
{
Left = Top = 0;
Right = width;
Bottom = height;
}
}
[STAThread]
static void Main(string[] args)
{
short width = 50, height = 20;
SafeFileHandle handle = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
CharInfo[] buffer = new CharInfo[width * height];
SmallRect writeRegion = new SmallRect(width, height);
Console.SetWindowSize(width, height);
Console.SetBufferSize(width, height);
for (int i = 0; i < buffer.Length; ++i)
{
buffer[i].Attributes = 0xb0;
buffer[i].Char.UnicodeChar = ' ';
}
WriteConsoleOutput(handle, buffer, new Coord(width, height), new Coord(0, 0), ref writeRegion);
Console.ReadKey();
}
}
}
I don't really know how to do this or even if it's possible to remove the black space in this language.
Finally, after a lot of head-scratching, I think I've solved this issue. Firstly, I had to add some additional WinAPI methods:
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GetStdHandle(int nStdHandle);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool GetConsoleScreenBufferInfoEx(
IntPtr hConsoleOutput,
ref ConsoleScreenBufferInfoEx ConsoleScreenBufferInfo);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetConsoleScreenBufferInfoEx(
IntPtr hConsoleOutput,
ref ConsoleScreenBufferInfoEx ConsoleScreenBufferInfoEx);
and structs:
[StructLayout(LayoutKind.Sequential)]
private struct ConsoleScreenBufferInfoEx
{
public uint cbSize;
public Coord dwSize;
public Coord dwCursorPosition;
public short wAttributes;
public SmallRect srWindow;
public Coord dwMaximumWindowSize;
public ushort wPopupAttributes;
public bool bFullscreenSupported;
public Colorref black, darkBlue, darkGreen, darkCyan, darkRed, darkMagenta, darkYellow, gray, darkGray, blue, green, cyan, red, magenta, yellow, white;
}
[StructLayout(LayoutKind.Sequential)]
private struct Colorref
{
public uint ColorDWORD;
}
After that, it was time to modify the part where the buffer and window were resized:
Console.SetWindowSize(width - 2, height);
Console.SetBufferSize(width, height);
IntPtr stdHandle = GetStdHandle(-11);
ConsoleScreenBufferInfoEx bufferInfo = new ConsoleScreenBufferInfoEx();
bufferInfo.cbSize = (uint)Marshal.SizeOf(bufferInfo);
GetConsoleScreenBufferInfoEx(stdHandle, ref bufferInfo);
++bufferInfo.srWindow.Right;
++bufferInfo.srWindow.Bottom;
SetConsoleScreenBufferInfoEx(stdHandle, ref bufferInfo);
And that's it! No more ugly black space (at least on my computer, haven't tested it on other Windows versions).
I have developed a simple Console application to poll an Xbox Controller using xinput. I would like to use the values obtained from one of the thumbsticks to move the mouse. I am able to get the x and y values from the thumbstick, but when I use those values to SendInput() (using the User32.dll), the mouse does not move and the return value is 0.
According to Microsoft, "If the function returns zero, the input was already blocked by another thread."
How do I find the other thread that is blocking it? It is just a simple Console Application (exe) started by Visual Studio that prints the x and y values to the screen and attempts to move the mouse.
long x = controller.x; // values from the controller
long y = controller.y; // these are checked and do contain numbers
INPUT mouseMoveInput = new INPUT();
mouseMoveInput.type = 0; // mouse
mouseMoveInput.mi.dx = x;
mouseMoveInput.mi.dy = y;
mouseMoveInput.mi.mouseData = 0;
mouseMoveInput.mi.dwFlags = MOUSEEVENTF_MOVE;
var result = SendInput(1, ref mouseMoveInput, Marshal.SizeOf(new INPUT());
// result always returns 0
Am I missing something? Should this work?
Here are declarations:
[StructLayout(LayoutKind.Explicit)]
public struct MOUSEINPUT
{
[FieldOffset(0)]
public long X;
[FieldOffset(8)]
public long Y;
[FieldOffset(16)]
public uint MouseData;
[FieldOffset(20)]
public uint Flags;
[FieldOffset(24)]
public uint Time;
[FieldOffset(28)]
public IntPtr ExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
internal struct KEYBOARDINPUT
{
public ushort Vk;
public ushort Scan;
public uint Flags;
public uint Time;
public IntPtr ExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
internal struct HARDWAREINPUT
{
public uint Msg;
public ushort ParamL;
public ushort ParamH;
}
[StructLayout(LayoutKind.Explicit)]
public struct INPUT
{
[FieldOffset(0)]
public uint type;
[FieldOffset(4)]
public MOUSEINPUT mi;
[FieldOffset(4)]
public KEYBOARDINPUT ki;
[FieldOffset(4)]
public HARDWAREINPUT hi;
}
UPDATE: using mouse-event does work, but this function is deprecated. Is there a problem with using it anyway since it works?
There is something odd I'm getting with the struct sizes:
Size of tagINPUT: 40
Size of mouseMoveInput: 40
Size of MOUSEINPUT: 32
Size of uint: 4
But if tagINPUT consists of MOUSEINPUT and uint then shouldn't it's size be 36?
The 2nd parameter of SendInput should be a pointer to an array, not a ref parameter, and especially not a ref directly to the struct.
I would also use explicit layout only for the struct that actually needs it, and let the rest be sequential. It's easier.
This code works for me:
const int INPUT_MOUSE = 0;
const int MOUSEEVENTF_MOVE = 0x0001;
[DllImport("user32.dll", SetLastError = true)]
private static extern uint SendInput(uint numberOfInputs, [MarshalAs(UnmanagedType.LPArray)] INPUT[] inputs, int sizeOfInputStructure);
void Main()
{
INPUT mouseMoveInput = new INPUT();
mouseMoveInput.type = INPUT_MOUSE;
mouseMoveInput.mi.dx = 10;
mouseMoveInput.mi.dy = 10;
mouseMoveInput.mi.mouseData = 0;
mouseMoveInput.mi.dwFlags = MOUSEEVENTF_MOVE;
var result = SendInput(1, new INPUT[] { mouseMoveInput}, Marshal.SizeOf(mouseMoveInput));
if(result == 0) {
throw new Win32Exception();
}
}
[StructLayout(LayoutKind.Sequential)]
public struct MOUSEINPUT
{
public int dx;
public int dy;
public uint mouseData;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
public struct KEYBDINPUT
{
public ushort wVk;
public ushort wScan;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
public struct HARDWAREINPUT
{
public uint Msg;
public ushort ParamL;
public ushort ParamH;
}
[StructLayout(LayoutKind.Explicit)]
public struct INPUT
{
[FieldOffset(0)]
public uint type;
[FieldOffset(4)]
public MOUSEINPUT mi;
[FieldOffset(4)]
public KEYBDINPUT ki;
[FieldOffset(4)]
public HARDWAREINPUT hi;
}
I found out, that there is a static property in the System.Windows.SystemParameters class that declares the color the user chose for his Windows overall.
But there is a second possibility for the user that enables him to enable or disable, whether the taskbar/windows bar should use that same color.
I was unable to find a key for that in the SystemParameters-class.
I believe there is a registry value to find the colour and it is probably inside:
HKEY_CURRENT_USER\Control Panel\Colors
However on my system I have colours on the taskbar disabled and that colour value doesn't seem to appear in this key.
A work around would be to combine the answers to the following two questions:
TaskBar Location
How to Read the Colour of a Screen Pixel
You need the following imports:
[DllImport("shell32.dll")]
private static extern IntPtr SHAppBarMessage(int msg, ref APPBARDATA data);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
private static extern int BitBlt(IntPtr hDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop);
The following structs:
private struct APPBARDATA
{
public int cbSize;
public IntPtr hWnd;
public int uCallbackMessage;
public int uEdge;
public RECT rc;
public IntPtr lParam;
}
private struct RECT
{
public int left, top, right, bottom;
}
And the following constant:
private const int ABM_GETTASKBARPOS = 5;
Then you can call the following two methods:
public static Rectangle GetTaskbarPosition()
{
APPBARDATA data = new APPBARDATA();
data.cbSize = Marshal.SizeOf(data);
IntPtr retval = SHAppBarMessage(ABM_GETTASKBARPOS, ref data);
if (retval == IntPtr.Zero)
{
throw new Win32Exception("Please re-install Windows");
}
return new Rectangle(data.rc.left, data.rc.top, data.rc.right - data.rc.left, data.rc.bottom - data.rc.top);
}
public static Color GetColourAt(Point location)
{
using (Bitmap screenPixel = new Bitmap(1, 1, PixelFormat.Format32bppArgb))
using (Graphics gdest = Graphics.FromImage(screenPixel))
{
using (Graphics gsrc = Graphics.FromHwnd(IntPtr.Zero))
{
IntPtr hSrcDC = gsrc.GetHdc();
IntPtr hDC = gdest.GetHdc();
int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, location.X, location.Y, (int)CopyPixelOperation.SourceCopy);
gdest.ReleaseHdc();
gsrc.ReleaseHdc();
}
return screenPixel.GetPixel(0, 0);
}
}
Like this:
Color taskBarColour = GetColourAt(GetTaskbarPosition().Location);
I am running a c# application that makes use of the sendinputs method in windows to mimic keystrokes. It works fine when I am remoted into the instance, but as soon as I log out it throws an exception. Can anyone offer any thoughts on why this is happening, and how to avoid it?
UPDATE::
here is some sample code that can reproduce the issue.
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace ConsoleApplication6
{
class Program
{
class SendKey
{
[StructLayout(LayoutKind.Sequential)]
private struct MOUSEINPUT
{
public int dx;
public int dy;
public int mouseData;
public int dwFlags;
public int time;
public int dwExtraInfo;
};
[StructLayout(LayoutKind.Sequential)]
private struct KEYBDINPUT
{
public short wVk;
public short wScan;
public int dwFlags;
public int time;
public int dwExtraInfo;
};
[StructLayout(LayoutKind.Sequential)]
private struct HARDWAREINPUT
{
public int uMsg;
public short wParamL;
public short wParamH;
};
[StructLayout(LayoutKind.Explicit)]
private struct INPUT
{
[FieldOffset(0)]
public int type;
[FieldOffset(4)]
public MOUSEINPUT no;
[FieldOffset(4)]
public KEYBDINPUT ki;
[FieldOffset(4)]
public HARDWAREINPUT hi;
};
[DllImport("user32.dll")]
private extern static void SendInput(int nInputs, ref INPUT pInputs, int cbsize);
[DllImport("user32.dll", EntryPoint = "MapVirtualKeyA")]
private extern static int MapVirtualKey(int wCode, int wMapType);
private const int INPUT_KEYBOARD = 1;
private const int KEYEVENTF_KEYDOWN = 0x0;
private const int KEYEVENTF_KEYUP = 0x2;
private const int KEYEVENTF_EXTENDEDKEY = 0x1;
private void Send(Keys key, bool isEXTEND)
{
INPUT inp = new INPUT();
inp.type = INPUT_KEYBOARD;
inp.ki.wVk = (short)key;
inp.ki.wScan = (short)MapVirtualKey(inp.ki.wVk, 0);
inp.ki.dwFlags = ((isEXTEND) ? (KEYEVENTF_EXTENDEDKEY) : 0x0) | KEYEVENTF_KEYDOWN;
inp.ki.time = 0;
inp.ki.dwExtraInfo = 0;
SendInput(1, ref inp, Marshal.SizeOf(inp));
System.Threading.Thread.Sleep(100);
inp.ki.dwFlags = ((isEXTEND) ? (KEYEVENTF_EXTENDEDKEY) : 0x0) | KEYEVENTF_KEYUP;
SendInput(1, ref inp, Marshal.SizeOf(inp));
}
static void Main()
{
while(true)
{
System.Threading.Thread.Sleep(2000);
SendKey s = new SendKey();
s.Send(Keys.Z, false);
s.Send(Keys.Left, true);
}
}
}
}
}
if you build this and just run it on a free windows server ec2 instance with notepad in focus it will print zzzzzz continuously while you are remoted into the instance, however, as soon as you remote out of the instance, it will stop printing until you log back in.