How to copy the loaded image in webbrowser to picturebox - c#

Is there any way to capture or copy the loaded image from the web browser to the picture box?
The image I am tring to copy is "captcha" image and every request it will change. I need the loaded image in the web browser be the same as the picture box.
I have tried to split the img tag and request the image again. It worked but the picture box image was not the same as the one the web browser shows.
Here is what I have done so far. It contains one web browser, one picture box, one text box, and one button
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.IO;
using System.Net;
namespace arman_dobare_kir_mishavad
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
string str2 = webBrowser1.DocumentText;
string[] strArray2;
strArray2 = Regex.Split(Regex.Split(str2, "<img id=\"content1_imgCaptcha\" src=\"")[1], "\"");
textBox1.Text = strArray2[0];
this.pictureBox1.ImageLocation = "http://www.hashkiller.co.uk" + strArray2[0];
return;
}
public void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
}
}
}

This i quite a tricky one that took me months to solve.
First things first. As you have discovered nearly all captcha images are dynamically generated images, this means that each time you request the image, even though the url (src tag) is the same, a new captcha image will always generated.
Your best bet to solve this is by "snipping" the already loaded captcha image out of your webbrowser. Trust me, this is the best way, if not the only way.
The good news is, it can easily be done with a built in method
webBrowser.DrawToBitmap(Bitmap,Rectangle)
My Sample Code: (how to use webbrowser.DrawToBitmap for an specific Element)
private void button1_Click(object sender, EventArgs e)
{
int CaptchaWidth = getXoffset(webBrowser1.Document.GetElementById("Captch-Element-Name"));
int CaptchaHeight = getYoffset(webBrowser1.Document.GetElementById("Captch-Element-Name"));
Bitmap bitmap = new Bitmap(CaptchaWidth, CaptchaHeight);
webBrowser1.DrawToBitmap(bitmap, new Rectangle(0, 0, CaptchaWidth, CaptchaHeight));
//now load the image into your pictureBox (you might need to convert the bitmap to a image)
}
//Methods to get Co-ordinates Of an Element in your webbrowser
public int getXoffset(HtmlElement el)
{
int xPos = el.OffsetRectangle.Left;
HtmlElement tempEl = el.OffsetParent;
while (tempEl != null)
{
xPos += tempEl.OffsetRectangle.Left;
tempEl = tempEl.OffsetParent;
}
return xPos;
}
public int getYoffset(HtmlElement el)
{
int yPos = el.OffsetRectangle.Top;
HtmlElement tempEl = el.OffsetParent;
while (tempEl != null)
{
yPos += tempEl.OffsetRectangle.Top;
tempEl = tempEl.OffsetParent;
}
return yPos;
}
So, the bad news is that c# has a annoying little bug in the drawtobitmap method (which is mentioned on the msdn site). What happens is sometimes a blank image will be returned when you run it.... yeah... not really what you want when you trying to crack Captchas right!
Luckily! Another stackOverflow user and i spent months working on a bug free version of this method that makes use of native GDI+.
And it works perfectly, so if drawtobitmap doesn't work the way you expect, here is an alternative.
Sample:
[DllImport("user32.dll")]
public static extern bool PrintWindow(IntPtr hwnd, IntPtr hdcBlt, uint nFlags);
public Bitmap CaptureWindow(Control ctl)
{
//Bitmap bmp = new Bitmap(ctl.Width, ctl.Height); // includes borders
Bitmap bmp = new Bitmap(ctl.ClientRectangle.Width, ctl.ClientRectangle.Height); // content only
using (Graphics graphics = Graphics.FromImage(bmp))
{
IntPtr hDC = graphics.GetHdc();
try { PrintWindow(ctl.Handle, hDC, (uint)0); }
finally { graphics.ReleaseHdc(hDC); }
}
return bmp;
}
So you'll simply call:
CaptureWindow(webBrowser1);
This will return a image of the entire webbrowser, then just snip-out the section containing the captcha image.
You can view my questions were i had the similar problems here (most weren't even answered):
Extracting a image from a WebBrowser Control
Screenshot method generates black images
Reset Webbrowser control to update settings
Now that you have the captcha images you'll need to decrypt them. So ask another question, send me the link and ill share my methods.
Im glad you didnt have to endure my nightmare. Don't forget to mark this as a solution, and as useful!

Related

WPF/C# Updating (and disposing of) Bitmap/BitmapSource in ValueChanged event?

I have a small image editing program that opens an image and does fancy stuff to it. In this case, I'm trying to adjust the brightness of the pixels. The problem isn't getting the pixels right, its disposing the Bitmap/BitmapSources being created in the ValueChanged event for my slider.
So basically the user will click a button and it will open up a new window with a slider. This new window makes a copy of the original bitmap for later use.
When the slider's value changes, it will create a new Bitmap from the original with the corresponding brightness increase, create a new BitmapSource from this, and update the image control's source. Even though I'm using a 'using' statement, I still get an "out of memory" exception after sliding for so long.
Any idea why this is? Is there a better workaround that can achieve the same thing? I have included the code below:
using Imagin.Slideshow;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace Imagin
{
/// <summary>
/// Interaction logic for BrightnessWindow.xaml
/// </summary>
public partial class BrightnessWindow : Window
{
public int Increase;
private System.Drawing.Bitmap Bitmap;
private System.Drawing.Bitmap NewBitmap;
private MainWindow ParentWindow;
public BrightnessWindow(MainWindow m)
{
InitializeComponent();
this.ParentWindow = m;
this.Bitmap = (System.Drawing.Bitmap)this.ParentWindow.Document.Bitmap.Clone();
}
private void slider1_ValueChanged(object sender, RoutedEventArgs e)
{
using (System.Drawing.Bitmap b = (System.Drawing.Bitmap)this.ParentWindow.Document.Bitmap.Clone(new RectangleF() { Width = (int)this.ParentWindow.Document.Bitmap.Width, Height = (int)this.ParentWindow.Document.Bitmap.Height, X = 0, Y = 0 }, this.ParentWindow.Document.Bitmap.PixelFormat))
{
this.ParentWindow.SetPixels(b, AdjustmentTypes.Brightness, Convert.ToInt32(this.slider1.Value));
BitmapSource m = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(b.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
this.ParentWindow.image1.Source = m;
this.NewBitmap = (System.Drawing.Bitmap)b.Clone();
}
}
private void applyButton_Click(object sender, RoutedEventArgs e)
{
if (this.NewBitmap != null)
{
this.ParentWindow.Document.Bitmap = this.NewBitmap;
}
this.Close();
}
private void cancelButton_Click(object sender, RoutedEventArgs e)
{
this.ParentWindow.Document.Refresh();
this.Close();
}
}
}
Another thing I was wondering is if there is a better way to alter the MainWindow's controls without having to pass it as a parameter, or is this the preferred approach for that kind of thing? Thanks!
http://msdn.microsoft.com/en-us/library/1dz311e4%28v=vs.110%29.aspx
Every time you use bitmap.GetHbitmap you create a new instance of a gdibitmap. Per the msdn link, "You are responsible for calling the GDI DeleteObject method to free the memory used by the GDI bitmap object. For more information about GDI bitmaps, see Bitmaps in the Windows GDI documentation."
I'd pull that guy out to a variable and call deleteobject on it when you're done with it, otherwise you're just creating n gdibitmaps where n = number of times you move the slider and never disposing of them.
Edit: Highlighting the line:
//Somewhere in the class
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
private void slider1_ValueChanged(object sender, RoutedEventArgs e)
{
using (System.Drawing.Bitmap b = (System.Drawing.Bitmap)this.ParentWindow.Document.Bitmap.Clone(new RectangleF() { Width = (int)this.ParentWindow.Document.Bitmap.Width, Height = (int)this.ParentWindow.Document.Bitmap.Height, X = 0, Y = 0 }, this.ParentWindow.Document.Bitmap.PixelFormat))
{
this.ParentWindow.SetPixels(b, AdjustmentTypes.Brightness, Convert.ToInt32(this.slider1.Value));
IntPtr hbitmap = b.GetHbitmap(); //create variable so we don't orphan the object
BitmapSource m = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(hbitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); //use variable
this.ParentWindow.image1.Source = m;
this.NewBitmap = (System.Drawing.Bitmap)b.Clone();
DeleteObject(hbitmap); //delete gdi object
}
}

Aforge.net Camera Capture & Save image to directory

everyone. I have been stuck here dealing with this bugs for days, but I still couldn't figure it out.
My guess: I think my code has some problem as I did not dispose the object properly after using it, (I'm not very familiar with these concepts of releasing resources, threading) .
I got these code by taking reference of what people did on youtube, but despite me doing exactly the same thing, my code didn't work out nicely.
SITUATION:
I have two picture boxes, left one can take video of me, right one take the snapshot, if you press button1 , you will start the video, clone_button will copy a image i.e. take a snapshot, and save_image should save it to the path reference, however, i get a generic error occured in GDI+ again and again while I'm trying to save it. Also, my debugger seemed to get crazy (i.e. failed to terminate the vshost.exe ) once I ran this program, I have to restart the computer to get my code running again, which is bleak and frustrating.
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.IO;
using System.Drawing.Imaging;
//AForge.Video dll
using AForge.Video;
using AForge.Video.DirectShow;
using AForge.Imaging;
using AForge.Imaging.Filters;
using AForge;
namespace WebCameraCapture
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private FilterInfoCollection CaptureDevice; // list of webcam
private VideoCaptureDevice FinalFrame;
private void Form1_Load(object sender, EventArgs e)
{
CaptureDevice = new FilterInfoCollection(FilterCategory.VideoInputDevice);//constructor
foreach (FilterInfo Device in CaptureDevice)
{
comboBox1.Items.Add(Device.Name);
}
comboBox1.SelectedIndex = 0; // default
FinalFrame = new VideoCaptureDevice();
}
private void button1_Click(object sender, EventArgs e)
{
FinalFrame = new VideoCaptureDevice(CaptureDevice[comboBox1.SelectedIndex].MonikerString);// specified web cam and its filter moniker string
FinalFrame.NewFrame += new NewFrameEventHandler(FinalFrame_NewFrame);// click button event is fired,
FinalFrame.Start();
}
void FinalFrame_NewFrame(object sender, NewFrameEventArgs eventArgs) // must be void so that it can be accessed everywhere.
// New Frame Event Args is an constructor of a class
{
pictureBox1.Image = (Bitmap)eventArgs.Frame.Clone();// clone the bitmap
}
private void From1_CLosing(object sender, EventArgs e)
{
if (FinalFrame.IsRunning==true) FinalFrame.Stop();
}
private void save_Click(object sender, EventArgs e)
{
if (pictureBox2.Image != null)
{
Bitmap varBmp = new Bitmap(pictureBox2.Image);
Bitmap newBitmap = new Bitmap(varBmp);
varBmp.Dispose();
varBmp = null;
varBmp.Save(#"C:\a.png", ImageFormat.Png);
}
else
{ MessageBox.Show("null exception"); }
}
private void clone_Click(object sender, EventArgs e)
{
pictureBox2.Image = (Bitmap)pictureBox1.Image.Clone();
}
}
}
Any AForge.net user can just PRESS the LINK below and try it out. Thanks!
SAMPLE
After having a look at your code, to me it appears that you are disposing of your image right before you save it. Meaning that your program can't save the image, because it doesn't exist anymore. Actually it reads that you've essentially removed the captured image twice, once on dispose, the second when you set it as null.
So if you move the two code segments after the save, it should be working. Granted without using a dialog box to change the name of the file, you'll surely receive an error unless you remove that file after each time it has been created.
private void save_Click(object sender, EventArgs e)
{
if (pictureBox2.Image != null)
{
//Save First
Bitmap varBmp = new Bitmap(pictureBox2.Image);
Bitmap newBitmap = new Bitmap(varBmp);
varBmp.Save(#"C:\a.png", ImageFormat.Png);
//Now Dispose to free the memory
varBmp.Dispose();
varBmp = null;
}
else
{ MessageBox.Show("null exception"); }
}
If you open the task manager, you can watch how much memory your program is soaking up.
Disposing of the memory after you're done using it, gives it back to the system.
You don't have a dispose inside your FinalFrame_NewFrame thread, so when the camera is reading images,
you should see the memory usage continue to climb until you stop the program.
I've added dispose to my thread, putting the memory usage under control, but now I'm debugging my image saves. Because I'm disposing, I can't save the image lol. My program ends up trying to save a null image file and throws the appropriate error.
I'm using a 2nd picurebox just as you are, but using for example, pbox2.image = pbox1.image, doesn't copy the data, it copies the memory location with the image data, so when I dispose pbox1 to free memory, the image data disappears with the memory location.
So, I had the same issue, and it was resolved one, by moving the dispose method, and two, I had to change the path, it didnt want to save to C:, so I put it on my desktop, you may not have had this issue if you were running as admin but I did so for anyone else who sees this, dont save to the root of C:.

PictureBox to Bitmap or Image?

I am trying to change the code http://sites.google.com/site/webcamlibrarydotnet/winfrom-and-csharp-sample-code-and-download from picture box to image or bitmap as I dont want to display any image or plan to display, all I want is that it will output the image to file.
I have tried changing the webcam.cs from PictureBox _FrameImage to Bitmap _FrameImage and PictureBox ImageControl to Bitmap ImageControl and casting (Bitmap) at e.WebCamImage
As well as changing it on the main form:
private void bWebcam_Click(object sender, EventArgs e)
{
WebCam webcam = new WebCam();
Bitmap image = null;
webcam.InitializeWebCam(ref image);
webcam.Start();
webcam.Stop();
FileStream fstream = new FileStream("testWebcam.jpg", FileMode.Create);
image.Save(fstream, System.Drawing.Imaging.ImageFormat.Jpeg);
fstream.Close();
}
Unhappyly it doesnt seem to work so
how could I change it from picture
box to Bitmap or Image or similar
storage before saving it to a file or
save it to file directly ?
The source code I am using is:
http://sites.google.com/site/webcamlibrarydotnet/winfrom-and-csharp-sample-code-and-download
Instead of using the WebCam class, why not just use the WebCamCapture class directly (since you are not displaying this in a form) and handle the ImageCapture event directly. The event argument for the event contains the Image. You could, in the event handler save the image to disk. Alternately, if you want to use the sample and the WebCam class, and you have a form. Use a PictureBox but leave it hidden (set Visible to false) and then just copy the image from there and save to disk when you need to.
Here is some sample code of using the WebCamCapture class instead of the WebCam class. It should be noted that this code is based on the sample code from the link provided in the question. I have kept the style of the sample so that code lines up.
Edit: Adding example of using WebCamCapture instead of WebCam class. This code should be used to modify Form1.cs in the sample code.
// Instead of having WebCam as member variable, have WemCamCapture
WebCamCapture webCam;
// Change the mainWinForm_Load function
private void mainWinForm_Load(object sender, EventArgs e)
{
webCam = new WebCamCapture();
webCam.FrameNumber = ((ulong)(0ul));
webCam.TimeToCapture_milliseconds = 30;
webCam.ImageCaptured += webcam_ImageCaptured;
}
// Add the webcam Image Captured handler to the main form
private void webcam_ImageCaptured(object source, WebcamEventArgs e)
{
Image imageCaptured = e.WebCamImage;
// You can now stop the camera if you only want 1 image
// webCam.Stop();
// Add code here to save image to disk
}
// Adjust the code in bntStart_Click
// (yes I know there is a type there, but to make code lineup I am not fixing it)
private void bntStart_Click(object sender, Event Args e)
{
webCam.Start(0);
}
Using reflector you can see that internally the WebCam class uses a timer to simulate a framerate. Therefore calling start and stop right after each other will never generate an image since the application doesnt handle application events (and therefore the timer tick event) in between starting and stopping. You should register on the ImageChanged event and call stop in there.
Good luck
** Edit: the start logic **
public void Start(ulong FrameNum)
{
try
{
this.Stop();
this.mCapHwnd = capCreateCaptureWindowA("WebCap", 0, 0, 0, this.m_Width, this.m_Height, base.Handle.ToInt32(), 0);
Application.DoEvents();
SendMessage(this.mCapHwnd, 0x40a, 0, 0);
SendMessage(this.mCapHwnd, 0x432, 0, 0);
this.m_FrameNumber = FrameNum;
this.timer1.Interval = this.m_TimeToCapture_milliseconds;
this.bStopped = false;
this.timer1.Start();
}
catch (Exception exception)
{
MessageBox.Show("An error ocurred while starting the video capture. Check that your webcamera is connected properly and turned on.\r\n\n" + exception.Message);
this.Stop();
}
}
In WebCam.cs you have:
public void InitializeWebCam(ref System.Windows.Forms.PictureBox ImageControl)
{
webcam = new WebCamCapture();
webcam.FrameNumber = ((ulong)(0ul));
webcam.TimeToCapture_milliseconds = FrameNumber;
webcam.ImageCaptured += new WebCamCapture.WebCamEventHandler(webcam_ImageCaptured);
_FrameImage = ImageControl;
}
void webcam_ImageCaptured(object source, WebcamEventArgs e)
{
_FrameImage.Image = e.WebCamImage;
}
If you modify the ImageCaptured code you can do what you want: e.WebCamImage is an Image.
for example you could change/add constructor to accept a file name and, in the ImageCaptured event, you could save image to file.

Simple C# Screen sharing application

I am looking to create a very basic screen sharing application in C#. No remote control necessary. I just want a user to be able to broadcast their screen to a webserver.
How should I implement this? (Any pointer in the right direction will be greatly appreciated).
It does NOT need to be high FPS. Would be sufficient to even update ever 5s or so. Do you think it would be sufficient to just upload a screenshot ever 5 seconds to my web server?
I previously blogged about how remote screen sharing software works here, it is not specific to C# but it gives a good fundamental understanding on the topic. Also linked in that article is the remote frame buffer spec which you'll also probably want to read up on.
Basically you will want to take screenshots and you can transmit those screenshots and display them on the other side. You can keep the last screenshot and compare the screenshot in blocks to see which blocks of the screenshot you need to send. You would typically do some sort of compression before sending the data.
To have remote control you can track mouse movement and transmit it and set the pointer position on the other end. Also ditto about keystrokes.
As far as compression goes in C#, you can simply use JpegBitmapEncoder to create your screenshots with Jpeg compression with the quality that you want.
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.QualityLevel = 40;
To compare file blocks you are probably best to create a hash on the old block and the new one, and then check to see if they are the same. You can use any hashing algorithm you want for this.
Here's code to take a screenshot, uncompressed as a bitmap:
public static Bitmap TakeScreenshot() {
Rectangle totalSize = Rectangle.Empty;
foreach (Screen s in Screen.AllScreens)
totalSize = Rectangle.Union(totalSize, s.Bounds);
Bitmap screenShotBMP = new Bitmap(totalSize.Width, totalSize.Height, PixelFormat.
Format32bppArgb);
Graphics screenShotGraphics = Graphics.FromImage(screenShotBMP);
screenShotGraphics.CopyFromScreen(totalSize.X, totalSize.Y, 0, 0, totalSize.Size,
CopyPixelOperation.SourceCopy);
screenShotGraphics.Dispose();
return screenShotBMP;
}
Now just compress it and send it over the wire, and you're done.
This code combines all screens in a multiscreen setup into one image. Tweak as needed.
Well, it can be as simple as taking screenshots, compressing them, and then sending them over the wire. However, there is existing software that already does this. Is this for practice?
I'm looking to do something similar, and I just found this up on CodeProject. I think this will help you.
http://www.codeproject.com/Articles/371955/Motion-JPEG-Streaming-Server
The key player on sharing/replicating a screen is a COM Component called: RPDViewer
Add that com component to your window form and in References as well..
and thin add this code to your form load and you will get the screen replicated in your form:
using RDPCOMAPILib;
using System;
using System.Windows.Forms;
namespace screenSharingAttempt
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
RDPSession x = new RDPSession();
private void Incoming(object Guest)
{
IRDPSRAPIAttendee MyGuest = (IRDPSRAPIAttendee)Guest;
MyGuest.ControlLevel = CTRL_LEVEL.CTRL_LEVEL_INTERACTIVE;
}
//access to COM/firewall will prompt
private void button1_Click(object sender, EventArgs e)
{
x.OnAttendeeConnected += Incoming;
x.Open();
}
//connect
private void button2_Click(object sender, EventArgs e)
{
IRDPSRAPIInvitation Invitation = x.Invitations.CreateInvitation("Trial", "MyGroup", "", 10);
textBox1.Text = Invitation.ConnectionString;
}
//Share screen
private void button4_Click(object sender, EventArgs e)
{
string Invitation = textBox1.Text;// "";// Interaction.InputBox("Insert Invitation ConnectionString", "Attention");
axRDPViewer1.Connect(Invitation, "User1", "");
}
//stop sharing
private void button5_Click(object sender, EventArgs e)
{
axRDPViewer1.Disconnect();
}
}
}

.NET 2.0 WinForm print screen

I would like to print an image of a dialog, as if [alt][Print Scrn] were used. Does the framework allow for this to be done programmatically?
The Graphics.CopyFromScreen(..) method should do what you need.
Here's a good sample I found on the web:
http://www.geekpedia.com/tutorial181_Capturing-screenshots-using-Csharp.html
EDIT: Code sample: (I created it as an extension method)
public static class FormExtensions
{
public static void SaveAsImage(this Form form, string fileName, ImageFormat format)
{
var image = new Bitmap(form.Width, form.Height);
using (Graphics g = Graphics.FromImage(image))
{
g.CopyFromScreen(form.Location, new Point(0, 0), form.Size);
}
image.Save(fileName, format);
}
}
can be used:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
this.SaveAsImage("foo.bmp", ImageFormat.Bmp);
}
}
What you could probably do is use the existing DLL that has that functionality for windows. It looks like you need to grab either some key commands or do it with a form button, and use the User32.dll. Since interop can sometimes be a big pain, I found a resource here that might help you do what you want:
http://www.cornetdesign.com/2005/04/screen-print-capture-in-c-using_08.html
If you really want just the dialog, use Control.DrawToBitmap to get a BMP image from it.

Categories

Resources