Closing application after event - c#

I'm using the .NET WebBrowser in order to render an html page and save that as a bitmap (got the code from another stackoverflow post). The problem is that the thread that actually starts the web browser never closes.
The thread should close when the StartBrowser Method finishes but it never does because Application.Run never ends.
My question is : how do I close the application after the WebBrowser finishes rendering the page and saves the bitmap ? Adding Application.Exit();
inside the DocumentCompleted event doesn't seem to work.
Code:
public class DotNetRenderer : IHtmlRenderer
{
static Bitmap bmp;
Thread th;
public void initializeRenderer()
{
}
public Bitmap renderHtml(string htmlContent)
{
th = new Thread(() => StartBrowser(htmlContent));
th.SetApartmentState(ApartmentState.STA);
th.Start();
th.Join();
return bmp;
}
public static void StartBrowser(string source)
{
var webBrowser = new WebBrowser();
webBrowser.ScrollBarsEnabled = false;
webBrowser.DocumentCompleted +=
webBrowser_DocumentCompleted;
webBrowser.DocumentText = source;
Application.Run();
}
static void webBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
var webBrowser = (WebBrowser)sender;
bmp = new Bitmap(webBrowser.Width, webBrowser.Height);
webBrowser.DrawToBitmap(bmp, new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height));
Application.ExitThread();
}
}
Application.ExitThread() does what I want, but now I don't know why the webBrowser Width and Height are always (250,250) regardless of the input HTML.

Related

Convert HTML file to image using web browser in windows NT service

private static void StartBrowser(string source)
{
var th = new Thread(() =>
{
var webBrowser = new WebBrowser();
webBrowser.ScrollBarsEnabled = false;
webBrowser.DocumentCompleted += webBrowser_DocumentCompleted;
webBrowser.DocumentText = source;
Application.Run();
});
th.SetApartmentState(ApartmentState.STA);
th.Start();
}
static void webBrowser_DocumentCompleted(object sender,WebBrowserDocumentCompletedEventArgs e)
{
var webBrowser = (WebBrowser)sender;
using (Bitmap bitmap = new Bitmap(576,384))
{
webBrowser.DrawToBitmap(bitmap,new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height));
bitmap.Save(#"images/" + "xxx.jpg",System.Drawing.Imaging.ImageFormat.Jpeg);
}
}
Hi Guys,
I am trying to print out an HTML file in a windows service by using the windows application web browser. For some reason, I cannot get a clear image, it draws only 25% of the image.
Note:- There is a barcode image in the upper right corner.

InvalidOperationException while saving a bitmap and using graphics.copyFromScreen parallel-y

I am writing an application to capture the screen using the CopyFromScreen method, and also want to save the image I capture to send over my local network.
So, I am trying store the captured screen on one bitmap, and save another bitmap, which is the previously captured screen, on two threads.
However, this is throwing an InvalidOperationException, which says object is currently in use elsewhere. The exception is thrown by System.Drawing.dll.
I have tried locking, and am using separate bitmaps for saving and capturing the screen. How do I stop this from happening? Relevant code:
Bitmap ScreenCapture(Rectangle rctBounds)
{
Bitmap resultImage = new Bitmap(rctBounds.Width, rctBounds.Height);
using (Graphics grImage = Graphics.FromImage(resultImage))
{
try
{
grImage.CopyFromScreen(rctBounds.Location, Point.Empty, rctBounds.Size);
}
catch (System.InvalidOperationException)
{
return null;
}
}
return resultImage;
}
void ImageEncode(Bitmap bmpSharedImage)
{
// other encoding tasks
pictureBox1.Image = bmpSharedImage;
try
{
Bitmap temp = (Bitmap)bmpSharedImage.Clone();
temp.Save("peace.jpeg");
}
catch (System.InvalidOperationException)
{
return;
}
}
private void button1_Click(object sender, EventArgs e)
{
timer1.Interval = 30;
timer1.Start();
}
Bitmap newImage = null;
private async void timer1_Tick(object sender, EventArgs e)
{
//take new screenshot while encoding the old screenshot
Task tskCaptureTask = Task.Run(() =>
{
newImage = ScreenCapture(_rctDisplayBounds);
});
Task tskEncodeTask = Task.Run(() =>
{
try
{
ImageEncode((Bitmap)_bmpThreadSharedImage.Clone());
}
catch (InvalidOperationException err)
{
System.Diagnostics.Debug.Write(err.Source);
}
});
await Task.WhenAll(tskCaptureTask, tskEncodeTask);
_bmpThreadSharedImage = newImage;
}
I reproduced your problem in a nutshell by creating a simple winforms project with a single button on it.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Task.Run(() => SomeTask());
}
public void SomeTask() //this will result in 'Invalid operation exception.'
{
var myhandle = System.Drawing.Graphics.FromHwnd(Handle);
myhandle.DrawLine(new Pen(Color.Red), 0, 0, 100, 100);
}
}
In order to fix this you need to do the following:
public partial class Form1 : Form
{
private Thread myUIthred;
public Form1()
{
myUIthred = Thread.CurrentThread;
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Task.Run(() => SomeTask());
}
public void SomeTask() // Works Great.
{
if (Thread.CurrentThread != myUIthred) //Tell the UI thread to invoke me if its not him who is running me.
{
BeginInvoke(new Action(SomeTask));
return;
}
var myhandle = System.Drawing.Graphics.FromHwnd(Handle);
myhandle.DrawLine(new Pen(Color.Red), 0, 0, 100, 100);
}
}
The issue is (as Spektre implied) a result of trying to call a UI method from a non-UI thread. The 'BeginInvoke' is actually `this.BeginInvoke' and 'this' is the form which was created by the UI thread and therefore all works.
I do not code in C# so I may be wrong here but I assume you are using Windows...
Accessing any visual components (like GDI Bitmap or window ...) is not safe outside WndProc function. So if you are using GDI bitmap (bitmap with device context) or rendering/accessing any visual component from your window inside any thread then there is your problem. After that any call to WinAPI in your app can throw an exception (even unrelated to graphics)
So try to move any such code into your WndProc function. In case you do not have access to it use any event of your window (like OnTimer or OnIdle).

Cefsharp initializing for the second time throws error

My application needs to invoke (off screen) browser on request and cleanup once it is done.
So I created an offscreen browser
public class OffScreenBrowser
{
private static ChromiumWebBrowser _browser;
private static CefSettings _settings;
public void Load(string url,System.Drawing.Size size)
{
if (Cef.IsInitialized) return;
Init(new BrowserProcessHandler());
_browser = new ChromiumWebBrowser(url) {Size = size};
_browser.NewScreenshot += _browser_NewScreenshot;
}
public System.Windows.Controls.Image BrowserImage { get; set; }
public Action NewScreenShotAction { get; set; }
private void _browser_NewScreenshot(object sender, EventArgs e)
{
var bitmap = _browser.ScreenshotOrNull();
Application.Current.Dispatcher.Invoke(new Action(() =>
{
using (MemoryStream memory = new MemoryStream())
{
bitmap.Save(memory, ImageFormat.Png);
memory.Position = 0;
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
BrowserImage = new System.Windows.Controls.Image() {Source = bitmapImage};
NewScreenShotAction();
}
}));
}
public void Close()
{
_browser.NewScreenshot -= _browser_NewScreenshot;
_browser.Dispose();
_settings.Dispose();
Cef.Shutdown();
}
public static void Init(IBrowserProcessHandler browserProcessHandler)
{
_settings = new CefSettings();
if (!Cef.Initialize(_settings, true, browserProcessHandler))
throw new Exception("Unable to Initialize Cef");
}
}
The idea is-on clicking a button create browser and on clicking another button close the browser
private OffScreenBrowser offScreenBrowser;
private void OPen_OnClick(object sender, RoutedEventArgs e)
{
var address = #"https://www.google.co.uk";
var width = 200;
var height = 100;
var windowSize = new System.Drawing.Size(width, height);
offScreenBrowser = new OffScreenBrowser();
offScreenBrowser.Load(address, windowSize);
offScreenBrowser.NewScreenShotAction = () =>
{
Browser.Content = offScreenBrowser.BrowserImage;
};
}
private void Close_OnClick(object sender, RoutedEventArgs e)
{
offScreenBrowser.Close();
}
On the first click it all works fine. On clicking close it seems like the cleanup is fine.
But when I click the open button for the second time I am getting an exception as below
"An unhandled exception of type 'System.AccessViolationException' occurred in CefSharp.Core.dll
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
What am I doing wrong?
This is a limitation in Chromium framework.
CEF can only be Initialized/Shutdown once per process,
See Need to Know/Limitations for more details, this is a limitation of the underlying Chromium framework.
to solve this you can call the Cef.Initialize in the start form and you need to call it only once in your application
the you can call ChromiumWebBrowser many times inside any for of your application forms
it worked for me

Aforge.NET Webcam Control XAML page

I want to use a webcam in c# code with Aforge.NET, but It didin't work because I don't use forms.
See my code below :
public partial class CapturePage : Page
{
private bool DeviceExist = false;
private FilterInfoCollection videoDevices;
private VideoCaptureDevice videoSource = null;
public CapturePage()
{
InitializeComponent();
getCamList();
startVideo();
}
// get the devices name
private void getCamList()
{
try
{
videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
if (videoDevices.Count == 0)
throw new ApplicationException();
DeviceExist = true;
}
catch (ApplicationException)
{
DeviceExist = false;
}
}
//toggle start and stop button
private void startVideo() // as it's say
{
if (DeviceExist)
{
videoSource = new VideoCaptureDevice(videoDevices[0].MonikerString); // the only one webcam
videoSource.NewFrame += new NewFrameEventHandler(video_NewFrame);
CloseVideoSource();
// videoSource.DesiredFrameSize = new Size(160, 120); // deprecated ?
//videoSource.DesiredFrameRate = 10;
videoSource.Start();
}
else
{
// error
}
}
else
{
if (videoSource.IsRunning)
{
timer1.Enabled = false;
CloseVideoSource();
}
}
}
//eventhandler if new frame is ready
private void video_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
Bitmap img = (Bitmap)eventArgs.Frame.Clone();
//do processing here
pictureBox1.Image = img; // But I can't have pictureBox in xaml ??
}
//close the device safely
private void CloseVideoSource()
{
if (!(videoSource == null))
if (videoSource.IsRunning)
{
videoSource.SignalToStop();
videoSource = null;
}
}
}
}
I tried this code in a form application, and it's work, but with a page, PictureBox are not referenced. Even if I reference it and create one in the code, this didn't work.
Can I, and how, use a PictureBox in a XAML Page ? Or there is an other solution tu use Aforge.net Webcam in Xaml Page ?
I succeded with changing the NewFrame methode like this:
private void video_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
System.Drawing.Image imgforms = (System.Drawing.Bitmap)eventArgs.Frame.Clone();
BitmapImage bi = new BitmapImage();
bi.BeginInit();
MemoryStream ms = new MemoryStream();
imgforms.Save(ms, ImageFormat.Bmp);
ms.Seek(0, SeekOrigin.Begin);
bi.StreamSource = ms;
bi.EndInit();
//Using the freeze function to avoid cross thread operations
bi.Freeze();
//Calling the UI thread using the Dispatcher to update the 'Image' WPF control
Dispatcher.BeginInvoke(new ThreadStart(delegate
{
ImageWebcam.Source = bi; /*frameholder is the name of the 'Image' WPF control*/
}));
}
cheers !
You could use WindowsFormsHost to integrate this Page in your WPF application.
If you need a WPF bitmap you may "convert" it like desribed here: Load a WPF BitmapImage from a System.Drawing.Bitmap

WebBrowser in XNA 4.0 not Navigating

I've been trying to get a WebBrowser to draw to a texture in XNA 4.0, and I've found several guides on how to do it. The problem is when I try to implement it, whether I change the Url property or call Navigate() it just won't load the page. I have a feeling I'm being a bit ignorant about the threading required, since my project is not started as an STA thread, so I create a separate thread to start the web browser and render to a bitmap.
Here's how I start it:
public void LoadTexture(GraphicsDevice gfx, ContentManager content, string filename, float duration = -1f)
{
this.gfx = gfx;
this.filename = filename;
this.duration = duration;
_resetEvent = new AutoResetEvent(false);
Thread thread = new Thread(GetWebScreenshot);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
_resetEvent.WaitOne();
}
And here's GetWebScreenshot:
public void GetWebScreenshot()
{
this.web = new WebBrowser();
this.web.Size = new Size(gfx.Viewport.Width, gfx.Viewport.Height);
this.web.Url = new Uri(this.filename);
while (this.web.ReadyState != WebBrowserReadyState.Complete)
{
if (this.web.ReadyState != WebBrowserReadyState.Uninitialized)
{
Console.WriteLine(this.web.ReadyState.ToString());
}
}
bitmap = new Bitmap(this.gfx.Viewport.Width, this.gfx.Viewport.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
this.web.DrawToBitmap(bitmap, new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height));
this.texture = BitmapToTexture2D(this.gfx, bitmap);
_resetEvent.Set();
}
The ReadyState property never changes from Uninitialized, I've also tried using the DocumentReady event, and that never gets fired. I've also tried Join() instead of AutoResetEvent, but nothing seems to work.
I was right, it was ignorance on my part. The critical thing about ActiveX controls and Single Threaded Apartments is that the message queue needs to be pumped. So now I've restructured my code to the following:
public void LoadTexture(GraphicsDevice gfx, ContentManager content, string filename, float duration = -1f)
{
this.gfx = gfx;
this.filename = filename;
this.duration = duration;
Thread thread = new Thread(GetWebScreenshot);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
}
public void GetWebScreenshot()
{
this.web = new WebBrowser();
this.web.Size = new Size(gfx.Viewport.Width, gfx.Viewport.Height);
this.web.Url = new Uri(this.filename);
this.web.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(web_DocumentCompleted);
Application.Run(); // Starts pumping the message queue (and keeps the thread running)
}
void web_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
Bitmap bitmap = new Bitmap(this.gfx.Viewport.Width, this.gfx.Viewport.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
this.web.DrawToBitmap(bitmap, new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height));
this.texture = HTMLTextureFactoryMachine.BitmapToTexture2D(this.gfx, bitmap);
Application.ExitThread(); // Exits the thread
}
This works no problem.

Categories

Resources