This question has bugged me for a while now, and I realize it's hard to describe what I am looking for. I want to be able to reserve a row for text input in a C# Console Application, while still allowing other information to be updated in the remaining rows. More specifically, I'd like to make a small mud game where the game is updated even while the user is busy making input. It's important that the input doesn't block the information flow.
I'd like to achieve the effect of the user writing input to the last visible row in the screen, while the other text append as usual, but not scrolling down my line of input, nor overwrite it.
If I would describe this in terms of Forms, I'd imagine the equivalent of having a multi-line textbox as the upper portion for the information, with a single-line textbox at the bottom for the input.
One option that you could try, is to directly manipulate the console buffer to render your game area and use the Console.SetCursorPosition to position the cursor to the input line where you use Console.ReadLine for example to take the user input.
Since the direct manipulation of the buffer does not affect the cursor position and is independent of the Console Read/Write functionality you can have a thread updating the Console buffer which covers the first 24 lines and the 25 line is waiting for input. If I get some time I will try put together a sample of what I mean, but in the meantime you can reference the other answers I have provided for a pointer to writing directly to the Console buffer.
How can I write fast colored output to Console?
Deleting previously written lines in Console
Of course you will want to write some nice wrapper functions to make this easy to work with, I always think about doing this, I just don't do enough work with the console so that I actually get down and do something.
Update: Added a small example of updating the console in a thread while still accepting user input. Just type 'quit' to stop it running. Note the the ConsoleBuffer class is not ideal, I am not closing the console handle, it was just a quick piece of code for the demo.
using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
using System.Threading;
namespace ConsoleDemo
{
class Program
{
static void Main(string[] args)
{
Thread t = new Thread(new ThreadStart(UpdateConsole));
t.IsBackground=true;
t.Start();
string input;
do
{
Console.SetCursorPosition(0, 23);
Console.Write("Command: ");
input = Console.ReadLine();
ConsoleBuffer.ClearArea(0, 21, 80, 3);
Console.SetCursorPosition(0, 22);
Console.Write(input);
} while (!string.Equals(input, "quit", StringComparison.OrdinalIgnoreCase));
}
static void UpdateConsole()
{
int i = 0;
Random rnd = new Random();
while (true)
{
string s = new string((char)(65 + (i % 26)),1);
for (short x = 0; x < 80; ++x)
{
for (short y = 0; y < 20; ++y)
{
ConsoleBuffer.WriteAt(x, y, s);
ConsoleBuffer.SetAttribute(x, y, (short)(rnd.Next(15)+1));
}
}
Thread.Sleep(500);
i++;
}
}
}
public class ConsoleBuffer
{
private static SafeFileHandle _hBuffer = null;
static ConsoleBuffer()
{
_hBuffer = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
if (_hBuffer.IsInvalid)
{
throw new Exception("Failed to open console buffer");
}
}
public static void WriteAt(short x, short y, string value)
{
int n = 0;
WriteConsoleOutputCharacter(_hBuffer, value, value.Length, new Coord(x, y), ref n);
}
public static void SetAttribute(short x, short y, short attr)
{
SetAttribute( x, y, new short[] { attr });
}
public static void SetAttribute(short x, short y, short[] attrs)
{
int n = 0;
WriteConsoleOutputAttribute(_hBuffer, attrs, attrs.Length, new Coord(x, y), ref n);
}
public static void ClearArea(short left, short top, short width, short height, char ch = ' ')
{
ClearArea(left, top, width, height, new CharInfo() { Char = new CharUnion() { UnicodeChar = ch } });
}
public static void ClearArea(short left, short top, short width, short height)
{
ClearArea(left, top, width, height, new CharInfo() { Char = new CharUnion() { AsciiChar = 32 } });
}
private static void ClearArea(short left, short top, short width, short height, CharInfo charAttr)
{
CharInfo[] buf = new CharInfo[width * height];
for (int i = 0; i < buf.Length; ++i)
{
buf[i] = charAttr;
}
SmallRect rect = new SmallRect() { Left = left, Top = top, Right = (short)(left + width), Bottom = (short)(top + height) };
WriteConsoleOutput(_hBuffer, buf,
new Coord() { X = width, Y = height },
new Coord() { X = 0, Y = 0 },
ref rect);
}
[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)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool WriteConsoleOutput(
SafeFileHandle hConsoleOutput,
CharInfo[] lpBuffer,
Coord dwBufferSize,
Coord dwBufferCoord,
ref SmallRect lpWriteRegion);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool WriteConsoleOutputCharacter(
SafeFileHandle hConsoleOutput,
string lpCharacter,
int nLength,
Coord dwWriteCoord,
ref int lpumberOfCharsWritten);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool WriteConsoleOutputAttribute(
SafeFileHandle hConsoleOutput,
short[] lpAttributes,
int nLength,
Coord dwWriteCoord,
ref int lpumberOfAttrsWritten);
[StructLayout(LayoutKind.Sequential)]
struct Coord
{
public short X;
public short Y;
public Coord(short X, short Y)
{
this.X = X;
this.Y = Y;
}
};
[StructLayout(LayoutKind.Explicit)]
struct CharUnion
{
[FieldOffset(0)]
public char UnicodeChar;
[FieldOffset(0)]
public byte AsciiChar;
}
[StructLayout(LayoutKind.Explicit)]
struct CharInfo
{
[FieldOffset(0)]
public CharUnion Char;
[FieldOffset(2)]
public short Attributes;
}
[StructLayout(LayoutKind.Sequential)]
struct SmallRect
{
public short Left;
public short Top;
public short Right;
public short Bottom;
}
}
}
The dotNet Console supports SetCursorPosition() and you also use the old DOS trick of ending a line with \r instead of \n\r.
But multi-threading and Append doesn't sound like a good combination.
Look at these .NET bindings for curses
http://www.mono-project.com/Libraries#Curses
ncurses is obviously a UNIX invention, but the API's are said to be mostly cross-platform (I haven't tried the .NET bindings myself, but have had very good results working with ncurses in general).
This will absolutely contain the goods you need and more
Related
This question already has an answer here:
Using WriteConsoleOutput to write Unicode with c#
(1 answer)
Closed 2 years ago.
Having looked at this question and modifying the code to print characters like '☺', '☻' or '█', I can't see how to get these characters to actually draw properly with WriteConsoleOutput. (There is no problem when using a stream or Console.Write but I can't individually control the character colour with these solutions so they're unsuitable.)
It seems there was a solution here for C++ but the "L" macro they talk about down the bottom does not apply to C#.
Finally, having checked the suggestions here, I can't see anything wrong with the code I ended up with:
class Program
{
[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
);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool WriteConsoleOutputW(
SafeFileHandle hConsoleOutput,
CharInfo[] lpBuffer,
Coord dwBufferSize,
Coord dwBufferCoord,
ref SmallRect lpWriteRegion
);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetConsoleOutputCP(uint wCodePageID);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetConsoleCP(uint wCodePageID);
[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;
}
static void Main(string[] args)
{
//SetConsoleOutputCP(65001);
//SetConsoleCP(65001);
SafeFileHandle fileHandle = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
Console.TreatControlCAsInput = true;
//Console.OutputEncoding = Encoding.UTF8;
Console.OutputEncoding = System.Text.Encoding.Unicode;
Console.CursorVisible = false;
short screenWidth = 40;
short screenHeight = 20;
Console.SetWindowSize(screenWidth + 1, screenHeight + 1);
Console.SetBufferSize(screenWidth + 1, screenHeight + 1);
bool running = true;
while (running)
{
CharInfo[] buf = new CharInfo[screenWidth * screenHeight];
SmallRect rect = new SmallRect() { Left = 0, Top = 0, Right = screenWidth, Bottom = screenHeight };
for (int i = 0; i < buf.Length; ++i)
{
buf[i].Attributes = 6;
buf[i].Char.UnicodeChar = '☺';
}
bool b = WriteConsoleOutputW(
fileHandle,
buf,
new Coord() { X = screenWidth, Y = screenHeight },
new Coord() { X = 0, Y = 0 },
ref rect
);
Console.SetCursorPosition(0, 0);
}
}
}
I seem to have ruled out all combinations of CharSet, A/W DLLImport suffixes, code pages, Console properties & source-file encoding.
(Surely it can't be impossible to fast-print coloured smiley-face characters to a C# console. Somebody tell me I've missed something silly here; I've spent about 2 hours Googling & trying to emulate something Castle Adventure managed in 1984 with ease.)
How can I get the aforementioned '☺' to print anything other than a '?' or a ':'?
This seems to be a duplicate of Using WriteConsoleOutput to write Unicode with c#
The solution to that was to add the CharSet = CharSet.Unicode attribute to both the CharInfo and CharUnion structs.
My C# calls a C++DLL in order to obtain data from a device. Data are returned through a callback function which has a window handle as input and one integer and two structs as by ref input values.
When the callback is triggered, the DLL is supposed to return 5 records, i.e. to be triggered 5 times. The first record arrives with all proper values but then the app crashes (0xC0000005: Access violation reading location 0x00000000), so my guess is that it crashes right before sending the second set of data.
I don’t have access to the source code of the DLL but I have access to the C++ app that demonstrates its usage.
This is how I declare the delegate and import the function that contains the callback as a member:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void onGetParaminfoHandler( IntPtr handle, ref int hIndex, ref PATINFO pi, ref PARAMINFO pri);
private static onGetParaminfoHandler ogp; //--declaration reference of the callback to be used
[DllImport("DataTraffic.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
private static extern void StartUpLoad(IntPtr handle, onGetParaminfoHandler myCallback);
Here’s the declaration of the two structs, PATINFO and PARAMINFO:
[StructLayout(LayoutKind.Sequential)]
public struct PATINFO
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
string szDeviceInfo;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
string szUserName;//name
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
string szTime;
[MarshalAs(UnmanagedType.U4)]
uint nID;
[MarshalAs(UnmanagedType.U4)]
uint nGender;//Sex 1 = male 0: Female
[MarshalAs(UnmanagedType.U4)]
uint nStandard;//Standard 1:ers 2:knudson 3:usa 4: Other
[MarshalAs(UnmanagedType.U4)]
uint nAge;//Age
[MarshalAs(UnmanagedType.U4)]
uint nHeight;//Height cm
[MarshalAs(UnmanagedType.U4)]
uint nWeight;//weight kg
[MarshalAs(UnmanagedType.Bool)]
bool BBDT;//whether relaxing True=Diastolic
[MarshalAs(UnmanagedType.Bool)]
bool bSmoker;//Smoking true= Smoking
};
[StructLayout(LayoutKind.Sequential)]
public struct PARAMINFO
{
//[MarshalAs(UnmanagedType.R4)]
float fffvc;//Maximum force capacity
//[MarshalAs(UnmanagedType.R4)]
float fFEV1;//a second amount
//[MarshalAs(UnmanagedType.R4)]
float fPEF;//Peak flow rate
//[MarshalAs(UnmanagedType.R4)]
float fFEV1Per;//FEV1/FVC
//[MarshalAs(UnmanagedType.R4)]
float fFEF25;//25% capacity
//[MarshalAs(UnmanagedType.R4)]
float fFEF750;//50% capacity
//[MarshalAs(UnmanagedType.R4)]
float fFEF75;//75% capacity
//[MarshalAs(UnmanagedType.R4)]
float fFEF2575;//25-75% capacity
//[MarshalAs(UnmanagedType.R4)]
float fFET;//exhalation Time
//[MarshalAs(UnmanagedType.R4)]
float fEVOL;//Extrapolation Volume
//[MarshalAs(UnmanagedType.R4)]
float fEOTV;//End of test volume
//[MarshalAs(UnmanagedType.R4)]
float fPEFT;//Peak Time
};
Here’s how I call the callback function:
public static void StartDataupload(IntPtr handle)
{
ogp = new onGetParaminfoHandler(GetParaminfo); //--keep reference of the delegate
GC.KeepAlive(ogp); //-- not much help from this one
StartUpLoad(handle, ogp);
}
And the callback itself:
private static void GetParaminfo(IntPtr handle, ref int hIndex,ref PATINFO pi, ref PARAMINFO pri)
{ enter code here
if (hIndex >= 1)
{
PATINFO pat = new PATINFO();
pat = pi;
PARAMINFO par = new PARAMINFO();
par = pri;
int error = Marshal.GetLastWin32Error();
if (error != 0)
throw new Win32Exception(error);
}
Some more info: the LastWin32Error returns large, random numbers which of course translate to Unknown errors. I suspect that between the first and the second call of the callback by the DLL, some pointer gets lost and so do I.
Here’s how the sample code (which works fine) in C calls the callback:
StartUpLoad(GetSafeHwnd(), GetParamInfo);
And part of the callback function of the sample C program which also uses the same DLL:
static void GetParamInfo(HWND hWnd, int &nIndex, PATINFO &PatientInf, PARAMINFO &ParamInf)
{
if(nIndex >= 1)
{
PATINFO *pPatientInf = new PATINFO;
PARAMINFO *pParamInf = new PARAMINFO;
*pPatientInf = PatientInf;
*pParamInf = ParamInf;
PostMessage(hWnd, WM_RECEIVEONECASE,
reinterpret_cast<WPARAM>(pPatientInf),
reinterpret_cast<LPARAM>(pParamInf));
}
else if(nIndex == FLAG_UPLOAD_STOP)
{
EnableOk(hWnd);
PostMessage(hWnd, WM_RECEIVECOMPLETE, PatientInf.nID, 0);
}
Any hints as to where to focus, would be greatly appreciated.
edit: here is the dllexport function signature:
__declspec(dllexport) void StartUpLoad(HWND hwnd, void (*pVoid)(HWND hWnd, int &nIndex, PATINFO &PatientInf, PARAMINFO &ParamInf));
and here are the definitions in C for the two structures:
typedef struct PATINFO
{
char szDeviceInfo[20];
char szUserName[20];
char szTime[20];
UINT nID;
UINT nGender;
UINT nStandard;
UINT nAge;
UINT nHeight;
UINT nWeight;
BOOL bBDT;
BOOL bSmoker;
PATINFO()
{
memset(szDeviceInfo, 0, sizeof(szDeviceInfo));
memset(szUserName, 0, sizeof(szUserName));
memset(szTime, 0, sizeof(szTime));
nID = 1;
nGender = 1;
nStandard = 2;
nAge = 20;
nHeight = 160;
nWeight = 50;
bBDT = FALSE;
bSmoker = FALSE;
};
}PATINFO;
typedef struct PARAMINFO
{
float fFVC;
float fFEV1;
float fPEF;
float fFEV1Per;//FEV1/FVC
float fFEF25;
float fFEF50;
float fFEF75;
float fFEF2575;
float fFET;
float fEVOL;
float fEOTV;
float fPEFT;
PARAMINFO()
{
fFVC = 0.0;
fFEV1 = 0.0;
fPEF = 0.0;
fFEV1Per = 0.0;
fFEF25 = 0.0;
fFEF50 = 0.0;
fFEF75 = 0.0;
fFEF2575 = 0.0;
fFET = 0.0f;
fEVOL = 0.0f;
fEOTV = 0.0f;
fPEFT = 0.0f;
};
}PARAMINFO;
EDIT2: the C handler OnReceiveOneCase is the following:
void CWaitDlg::OnReceiveOneCase(WPARAM wParam, LPARAM lParam)
{
PATINFO *pPatientInfo = reinterpret_cast<PATINFO *>(wParam);
PARAMINFO *pParamInfo = reinterpret_cast<PARAMINFO *>(lParam);
int nListCount = 0;
CString strNo("\0");
CString strFVC("\0");
strNo.Format("%d", pPatientInfo->nID);
strFVC.Format("%.2f", pParamInfo->fFVC);
nListCount = m_UploadList.GetItemCount();
m_UploadList.InsertItem(nListCount, strNo);
m_UploadList.SetItemText(nListCount, 1, pPatientInfo->szTime);
m_UploadList.SetItemText(nListCount, 2, strFVC);
++m_nCaseCount;
delete pPatientInfo;
delete pParamInfo;
}
Okay, I am looking for a function or something that will read the color of a certain pixel on my monitor, and when that color is detected, another function will be enabled. I figure using RGB. All help appreciated. Thank You.
This is the most efficient: It grabs a pixel at the location of the cursor, and doesn't rely on only having one monitor.
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Diagnostics;
namespace FormTest
{
public partial class Form1 : Form
{
[DllImport("user32.dll")]
static extern bool GetCursorPos(ref Point lpPoint);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern int BitBlt(IntPtr hDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop);
public Form1()
{
InitializeComponent();
}
private void MouseMoveTimer_Tick(object sender, EventArgs e)
{
Point cursor = new Point();
GetCursorPos(ref cursor);
var c = GetColorAt(cursor);
this.BackColor = c;
if (c.R == c.G && c.G < 64 && c.B > 128)
{
MessageBox.Show("Blue");
}
}
Bitmap screenPixel = new Bitmap(1, 1, PixelFormat.Format32bppArgb);
public Color GetColorAt(Point location)
{
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);
}
}
}
Now, obviously, you don't have to use the cursor's current location, but this is the general idea.
EDIT:
Given the above GetColorAt function you can poll a certain pixel on the screen in a safe, performance friendly way like this:
private void PollPixel(Point location, Color color)
{
while(true)
{
var c = GetColorAt(location);
if (c.R == color.R && c.G == color.G && c.B == color.B)
{
DoAction();
return;
}
// By calling Thread.Sleep() without a parameter, we are signaling to the
// operating system that we only want to sleep long enough for other
// applications. As soon as the other apps yield their CPU time, we will
// regain control.
Thread.Sleep()
}
}
You can wrap that in a Thread if you want, or execute it from a Console application. "Whatever suits your fancy," I guess.
Most answers here use the very same source of that pixel (desktop dc).
The key function is GetPixel.
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetWindowDC(IntPtr window);
[DllImport("gdi32.dll", SetLastError = true)]
public static extern uint GetPixel(IntPtr dc, int x, int y);
[DllImport("user32.dll", SetLastError = true)]
public static extern int ReleaseDC(IntPtr window, IntPtr dc);
public static Color GetColorAt(int x, int y)
{
IntPtr desk = GetDesktopWindow();
IntPtr dc = GetWindowDC(desk);
int a = (int) GetPixel(dc, x, y);
ReleaseDC(desk, dc);
return Color.FromArgb(255, (a >> 0) & 0xff, (a >> 8) & 0xff, (a >> 16) & 0xff);
}
I think this is the cleanest and quickest way.
Note:
If you have modified the default text size among the Display Settings on Windows to increase readability on a high resolution display, the coordinate parameters of GetPixel() need to be adjusted the same way. For example, if the cursor location is (x,y) with 150% of text size on Windows 7, you need to call GetPixel(x*1.5, y*1.5) to get the color of the pixel under the cursor.
This function is shorter and can achieve the same result using System.Drawing, without Pinvoke.
Color GetColorAt(int x, int y)
{
Bitmap bmp = new Bitmap(1, 1);
Rectangle bounds = new Rectangle(x, y, 1, 1);
using (Graphics g = Graphics.FromImage(bmp))
g.CopyFromScreen(bounds.Location, Point.Empty, bounds.Size);
return bmp.GetPixel(0, 0);
}
Please check this two different functions I have used in one of my previous projects :
1) This function takes snapshot of Desktop
private void CaptureScreenAndSave(string strSavePath)
{
//SetTitle("Capturing Screen...");
Bitmap bmpScreenshot;
Graphics gfxScreenshot;
bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height,System.Drawing.Imaging.PixelFormat.Format32bppArgb);
gfxScreenshot = Graphics.FromImage(bmpScreenshot);
gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
MemoryStream msIn = new MemoryStream();
bmpScreenshot.Save(msIn, System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders()[0], null);
msIn.Close();
byte[] buf = msIn.ToArray();
MemoryStream msOut = new MemoryStream();
msOut.Write(buf, 0, buf.Length);
msOut.Position = 0;
Bitmap bmpOut = new Bitmap(msOut);
try
{
bmpOut.Save(strSavePath, System.Drawing.Imaging.ImageFormat.Bmp);
//SetTitle("Capturing Screen Image Saved...");
}
catch (Exception exp)
{
}
finally
{
msOut.Close();
}
}
2) This function takes an image in input and calculates RGB average of pixel range given.
double GetRGBAverageForPixelRange( int istartRange, int iEndRange, Bitmap oBitmap )
{
double dRetnVal = 0 ;
Color oTempColor ;
int i, j ;
for( int iCounter = istartRange ; iCounter < iEndRange ; iCounter++ )
{
i = (iCounter % (oBitmap.Width));
j = ( iCounter / ( oBitmap.Width ) ) ;
if (i >= 0 && j >= 0 && i < oBitmap.Width && j < oBitmap.Height )
{
oTempColor = oBitmap.GetPixel(i, j);
dRetnVal = dRetnVal + oTempColor.ToArgb();
}
}
return dRetnVal ;
}
This two functions together might solve your problem. Happy Coding :)
EDIT : Please note that GetPixel is very slow function. I will think twice befor using it.
As far as I know the easiest way to do this is to:
take a screenshot
look at the bitmap and get the pixel color
Edit
There is probably no way to "wait" until the pixel changes to a certain color. Your program will probably have to just loop and check it every so often until it sees the color.
For example:
while(!IsPixelColor(x, y, color))
{
//probably best to add a sleep here so your program doesn't use too much CPU
}
DoAction();
EDIT 2
Here is some sample code you can modify. This code just changes the color of a label based on the current color in a given pixel. This code avoids the handle leak mentioned.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Runtime.InteropServices;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern int BitBlt(IntPtr hDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop);
Thread t;
int x, y;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
x = 20;
y = 50;
t = new Thread(update);
t.Start();
}
private void update()
{
Bitmap screenCopy = new Bitmap(1, 1);
using (Graphics gdest = Graphics.FromImage(screenCopy))
{
while (true)
{
//g.CopyFromScreen(new Point(0, 0), new Point(0, 0), new Size(256, 256));
using (Graphics gsrc = Graphics.FromHwnd(IntPtr.Zero))
{
IntPtr hSrcDC = gsrc.GetHdc();
IntPtr hDC = gdest.GetHdc();
int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, x, y, (int)CopyPixelOperation.SourceCopy);
gdest.ReleaseHdc();
gsrc.ReleaseHdc();
}
Color c = Color.FromArgb(screenCopy.GetPixel(0, 0).ToArgb());
label1.ForeColor = c;
}
}
}
}
}
This line uses About 10 ms.
int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, location.X, location.Y, (int)CopyPixelOperation.SourceCopy);
I have a small application I wrote that simply displays a preview of webcam or my capture card. At the moment it works exactly how I want it to, with the exception that the capture card displays at a much lower framerate than I'd like it to.
Here is my relevant code:
private const int WM_CAP_DRIVER_CONNECT = 1034;
private const int WM_CAP_SET_PREVIEW = 1074;
private const int WM_CAP_SET_PREVIEWRATE = 1076;
private const int WM_CAP_SET_SCALE = 1077;
private const int WS_CHILD = 1073741824;
private const int WS_VISIBLE = 268435456;
private const short SWP_NOMOVE = 2;
private const short SWP_NOZORDER = 4;
private const short HWND_BOTTOM = 1;
private const int iDevice = 0;
private int hHwnd;
private int previewRate = 34;
private int width = 640;
private int height = 480;
[DllImport("user32.dll", EntryPoint="SendMessageA")]
public static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);
[DllImport("user32.dll", EntryPoint="SetWindowPos")]
static extern int SetWindowPos(int hwnd, int hWndInsertAfter, int x, int y, int cx, int cy, int wFlags);
[DllImport("user32.dll")]
static extern bool DestroyWindow(int hndw);
[DllImport("avicap32.dll")]
public static extern int capCreateCaptureWindow(string lpszWindowName, int dwStyle, int X, int Y, int nWidth, int nHeight, int hwndParent, int nID);
private void OpenPreviewWindow()
{
hHwnd = capCreateCaptureWindow(iDevice.ToString(), (WS_VISIBLE | WS_CHILD), 0, 0, width, height, Handle.ToInt32(), 0);
// Connect to device
if (SendMessage(hHwnd, WM_CAP_DRIVER_CONNECT, iDevice, 0) != -1)
{
SendMessage(hHwnd, WM_CAP_SET_SCALE, 1, 0);
SendMessage(hHwnd, WM_CAP_SET_PREVIEWRATE, previewRate, 0);
SendMessage(hHwnd, WM_CAP_SET_PREVIEW, 1, 0);
SetWindowPos(hHwnd, HWND_BOTTOM, 0, 0, width, height, (SWP_NOMOVE | SWP_NOZORDER));
}
else
{
DestroyWindow(hHwnd);
}
}
When I preview the capture card in an application like FMLE, it previews at 30 FPS, which is my target framerate (~34 milliseconds per frame,) however when I use my application to preview it's closer to 10-15 FPS. I should probably also note that my program will preview 30 FPS from my webcam. What could be causing the problem with the capture card, and how can I fix it?
Use DirectShow to view the webcam.
An example would be: http://www.codeproject.com/KB/audio-video/WebcamUsingDirectShowNET.aspx
or http://www.codeproject.com/KB/miscctrl/webcam_c_sharp.aspx
Okay, I am looking for a function or something that will read the color of a certain pixel on my monitor, and when that color is detected, another function will be enabled. I figure using RGB. All help appreciated. Thank You.
This is the most efficient: It grabs a pixel at the location of the cursor, and doesn't rely on only having one monitor.
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Diagnostics;
namespace FormTest
{
public partial class Form1 : Form
{
[DllImport("user32.dll")]
static extern bool GetCursorPos(ref Point lpPoint);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern int BitBlt(IntPtr hDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop);
public Form1()
{
InitializeComponent();
}
private void MouseMoveTimer_Tick(object sender, EventArgs e)
{
Point cursor = new Point();
GetCursorPos(ref cursor);
var c = GetColorAt(cursor);
this.BackColor = c;
if (c.R == c.G && c.G < 64 && c.B > 128)
{
MessageBox.Show("Blue");
}
}
Bitmap screenPixel = new Bitmap(1, 1, PixelFormat.Format32bppArgb);
public Color GetColorAt(Point location)
{
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);
}
}
}
Now, obviously, you don't have to use the cursor's current location, but this is the general idea.
EDIT:
Given the above GetColorAt function you can poll a certain pixel on the screen in a safe, performance friendly way like this:
private void PollPixel(Point location, Color color)
{
while(true)
{
var c = GetColorAt(location);
if (c.R == color.R && c.G == color.G && c.B == color.B)
{
DoAction();
return;
}
// By calling Thread.Sleep() without a parameter, we are signaling to the
// operating system that we only want to sleep long enough for other
// applications. As soon as the other apps yield their CPU time, we will
// regain control.
Thread.Sleep()
}
}
You can wrap that in a Thread if you want, or execute it from a Console application. "Whatever suits your fancy," I guess.
Most answers here use the very same source of that pixel (desktop dc).
The key function is GetPixel.
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetWindowDC(IntPtr window);
[DllImport("gdi32.dll", SetLastError = true)]
public static extern uint GetPixel(IntPtr dc, int x, int y);
[DllImport("user32.dll", SetLastError = true)]
public static extern int ReleaseDC(IntPtr window, IntPtr dc);
public static Color GetColorAt(int x, int y)
{
IntPtr desk = GetDesktopWindow();
IntPtr dc = GetWindowDC(desk);
int a = (int) GetPixel(dc, x, y);
ReleaseDC(desk, dc);
return Color.FromArgb(255, (a >> 0) & 0xff, (a >> 8) & 0xff, (a >> 16) & 0xff);
}
I think this is the cleanest and quickest way.
Note:
If you have modified the default text size among the Display Settings on Windows to increase readability on a high resolution display, the coordinate parameters of GetPixel() need to be adjusted the same way. For example, if the cursor location is (x,y) with 150% of text size on Windows 7, you need to call GetPixel(x*1.5, y*1.5) to get the color of the pixel under the cursor.
This function is shorter and can achieve the same result using System.Drawing, without Pinvoke.
Color GetColorAt(int x, int y)
{
Bitmap bmp = new Bitmap(1, 1);
Rectangle bounds = new Rectangle(x, y, 1, 1);
using (Graphics g = Graphics.FromImage(bmp))
g.CopyFromScreen(bounds.Location, Point.Empty, bounds.Size);
return bmp.GetPixel(0, 0);
}
Please check this two different functions I have used in one of my previous projects :
1) This function takes snapshot of Desktop
private void CaptureScreenAndSave(string strSavePath)
{
//SetTitle("Capturing Screen...");
Bitmap bmpScreenshot;
Graphics gfxScreenshot;
bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height,System.Drawing.Imaging.PixelFormat.Format32bppArgb);
gfxScreenshot = Graphics.FromImage(bmpScreenshot);
gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
MemoryStream msIn = new MemoryStream();
bmpScreenshot.Save(msIn, System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders()[0], null);
msIn.Close();
byte[] buf = msIn.ToArray();
MemoryStream msOut = new MemoryStream();
msOut.Write(buf, 0, buf.Length);
msOut.Position = 0;
Bitmap bmpOut = new Bitmap(msOut);
try
{
bmpOut.Save(strSavePath, System.Drawing.Imaging.ImageFormat.Bmp);
//SetTitle("Capturing Screen Image Saved...");
}
catch (Exception exp)
{
}
finally
{
msOut.Close();
}
}
2) This function takes an image in input and calculates RGB average of pixel range given.
double GetRGBAverageForPixelRange( int istartRange, int iEndRange, Bitmap oBitmap )
{
double dRetnVal = 0 ;
Color oTempColor ;
int i, j ;
for( int iCounter = istartRange ; iCounter < iEndRange ; iCounter++ )
{
i = (iCounter % (oBitmap.Width));
j = ( iCounter / ( oBitmap.Width ) ) ;
if (i >= 0 && j >= 0 && i < oBitmap.Width && j < oBitmap.Height )
{
oTempColor = oBitmap.GetPixel(i, j);
dRetnVal = dRetnVal + oTempColor.ToArgb();
}
}
return dRetnVal ;
}
This two functions together might solve your problem. Happy Coding :)
EDIT : Please note that GetPixel is very slow function. I will think twice befor using it.
As far as I know the easiest way to do this is to:
take a screenshot
look at the bitmap and get the pixel color
Edit
There is probably no way to "wait" until the pixel changes to a certain color. Your program will probably have to just loop and check it every so often until it sees the color.
For example:
while(!IsPixelColor(x, y, color))
{
//probably best to add a sleep here so your program doesn't use too much CPU
}
DoAction();
EDIT 2
Here is some sample code you can modify. This code just changes the color of a label based on the current color in a given pixel. This code avoids the handle leak mentioned.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Runtime.InteropServices;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern int BitBlt(IntPtr hDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop);
Thread t;
int x, y;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
x = 20;
y = 50;
t = new Thread(update);
t.Start();
}
private void update()
{
Bitmap screenCopy = new Bitmap(1, 1);
using (Graphics gdest = Graphics.FromImage(screenCopy))
{
while (true)
{
//g.CopyFromScreen(new Point(0, 0), new Point(0, 0), new Size(256, 256));
using (Graphics gsrc = Graphics.FromHwnd(IntPtr.Zero))
{
IntPtr hSrcDC = gsrc.GetHdc();
IntPtr hDC = gdest.GetHdc();
int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, x, y, (int)CopyPixelOperation.SourceCopy);
gdest.ReleaseHdc();
gsrc.ReleaseHdc();
}
Color c = Color.FromArgb(screenCopy.GetPixel(0, 0).ToArgb());
label1.ForeColor = c;
}
}
}
}
}
This line uses About 10 ms.
int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, location.X, location.Y, (int)CopyPixelOperation.SourceCopy);