Reputation: 31
I made a extension to a game that I made which automatically presses a button when you press "Search Pixel" But each time I press the button whether it succeeds or fails, the memory for the application rises by about 30 Mega Bytes. Starts at 4.5 MB then once the button is click, it rises to 33.3 MB here is the code
{
public Form1()
{
InitializeComponent();
}
private const UInt32 MOUSEEVENTF_LEFTDOWN = 0x0002;
private const UInt32 MOUSEEVENTF_LEFTUP = 0x0004;
[DllImport("user32.dll")]
private static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint dwData, uint dwExtraInf);
[DllImport("user32.dll")]
private static extern bool SetCursorPos(int x, int y);
private void OnButtonSearchPixelClick(object sender, EventArgs e)
{
// 01. get hex value from input field
string inputHexColorCode = textBox1.Text;
SearchPixel(inputHexColorCode);
}
private bool SearchPixel(string hexcode)
{
// Take an image from the screen
// Bitmap bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height); // Create an empty bitmap with the size of the current screen
Bitmap bitmap = new Bitmap(SystemInformation.VirtualScreen.Width, SystemInformation.VirtualScreen.Height); // Create an empty bitmap with the size of all connected screen
Graphics graphics = Graphics.FromImage(bitmap as System.Drawing.Image); // Create a new graphics objects that can capture the screen
graphics.CopyFromScreen(0, 0, 0, 0, bitmap.Size); // Screenshot moment → screen content to graphics object
Color desiredPixelColor = ColorTranslator.FromHtml(hexcode);
// Go one to the right and then check from top to bottom every pixel (next round -> go one to right and go down again)
for (int x = 0; x < SystemInformation.VirtualScreen.Width; x++)
{
for (int y = 0; y < SystemInformation.VirtualScreen.Height; y++)
{
// Get the current pixels color
Color currentPixelColor = bitmap.GetPixel(1550, 412);
// Finally compare the pixels hex color and the desired hex color (if they match, we found a pixel)
if (desiredPixelColor == currentPixelColor)
{
DoubleClickAtPosition(1387, 414);
return true;
}
}
}
return false;
}
private void DoubleClickAtPosition(int posX, int posY)
{
SetCursorPos(posX, posY);
Click();
Thread.Sleep(250);
Click();
}
private new void Click()
{
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
}
}
}
This code is from a video that I watched. If you need me to do anything like post the video or post any other code just let me know please I really need help. I also wanna just say that I'm really new to coding in c#.
Upvotes: 2
Views: 506
Reputation: 2482
Windows Forms uses a single threaded apartment model. This means everything in Windows Forms, from rendering to processing Windows messages (like mouse and keyboard events), occurs in a single thread.
You may have noticed in all of your WinForms applications you can see a [STAThreadAttribute]
in your Program.cs
. The 'STA' stands for 'Single Thread Apartment'.
Because WinForms runs in a single thread apartment, any intensive code you run in your WinForms STA thread will block WinForms from rendering your form and processing its mouse and keyboard events as it's only able to do one thing at a time.
The solution here is to run your intensive SearchPixel()
function in a separate thread to the WinForms STA thread.
There are many ways to achieve this, such as a BackgroundWorker
, or a hard Thread
. However the easiest and most acceptable way would be to use a Task
which is automatically deferred to a thread pool.
This is as simple as changing your code like so:
private void OnButtonSearchPixelClick(object sender, EventArgs e)
{
// 01. get hex value from input field
string inputHexColorCode = textBox1.Text;
Task.Run(() => {
SearchPixel(inputHexColorCode);
});
}
As your code will be running in a thread other than WinForms' STA thread it is not possible to interact with any object that WinForms is using without doing an invoke.
For example:
Task.Run(() => {
label1.Text = "Test";
});
Will cause an InvalidOperationException
, as you're attempting to access an object that WinForms expects to only be accessed in its single thread apartment.
You must tell the STA thread to invoke an action, which will cause the code to be run in the STA thread. Like so:
Task.Run(() => {
//Code in here will run in a thread pool
this.Invoke((Action)delegate {
//Code in here will run in the STA thread
label1.Text = "Test";
});
});
Upvotes: 4