How do I save an image from CapturePreviewAsync stream? - vb.net

I don't understand much about Async, but I get no errors when running this, and still I don't get an image in C:\temp\test.jpg - what am I doing wrong?
Private Sub WebView22_Click(sender As Object, e As EventArgs) Handles WebView22.Click
grabpic()
End Sub
Public Async Function grabpic() As Threading.Tasks.Task
Dim ms As System.IO.Stream
Await (WebView22.CoreWebView2.CapturePreviewAsync(CoreWebView2CapturePreviewImageFormat.Jpeg, ms))
Dim file As New System.IO.FileStream("C:\temp\test.jpg", IO.FileMode.Create)
ms.CopyTo(file)
ms.Close()
End Function

There are a few problems with your code:
You're not awaiting the graphics() function, and therefore exceptions are being silently swallowed. Also, I'm not exactly sure what triggers the Click event of a WebView2 control (assuming it can actually be triggered). So, instead, you probably should test using a button click, for example.
You're not initializing ms at all, which will cause a NullReferenceException. You probably wanted to create an instance of MemoryStream.
You're calling ms.CopyTo after writing to the stream. At that point, the current position will be at the end of the stream and nothing will be written to file. You either need to set the position at the beginning (i.e., ms.Seek(0, SeekOrigin.Begin) or use ms.WriteTo() instead.
With that, the graphics() method would look something like this:
Public Async Function grabpic() As Task
Using ms As New IO.MemoryStream()
Await WebView22.CoreWebView2.CapturePreviewAsync(
CoreWebView2CapturePreviewImageFormat.Jpeg, ms)
Using file As New IO.FileStream("C:\temp\test.jpg", IO.FileMode.Create)
ms.Seek(0, IO.SeekOrigin.Begin)
ms.CopyTo(file)
End Using
End Using
End Function
However, you don't really need a MemoryStream in this case. You could just write directly to the FileStream. Here's what the final code should look like:
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Await grabpic()
End Sub
Public Async Function grabpic() As Task
Using file As New IO.FileStream("C:\temp\test.jpg", IO.FileMode.Create)
Await WebView22.CoreWebView2.CapturePreviewAsync(
CoreWebView2CapturePreviewImageFormat.Jpeg, file)
End Using
End Function

Related

VB.net Asynchronous call to Url with no response needed

I have a VB.Net page that needs to submit data to a url after a user clicks a button. I don't need any data back from the url, I just need to pass parameters to it and allow the user to continue to the next step without waiting for the url to do it's thing.
I've seen some similar posts for c# using UploadStringTaskAsync, but haven't been able to find a corresponding method for VB.net.
https://learn.microsoft.com/en-us/dotnet/api/system.net.webclient.uploadstringtaskasync?view=net-6.0
I believe I can call the Async method from my existing nonasync methods as I don't need the response back. However, if there is a more elegant approach to do this please let me know.
Update with current code attempting to use thread:
Sub Page_Load(sender As Object, e As EventArgs)
If Not IsPostBack Then
Dim thread As New Thread(AddressOf testSub)
thread.Start()
End If
End Sub
Sub testSub
Using WC As New WebClient WC.UploadString("https://someurl.com?parameter1=testing&parameter2=anothertest", "SOMEDATA")
End Using
End Sub
This runs but unfortunately does not appear to handle any of the parameters. When I post the url directly in the browser it runs as expected. I don't need to send any data other than the querystring as well so I'm not sure if that's breaking the uploadstring. However, when I run it via debugger I don't see any errors so long as I populate that string for the data with a value.
I might be misunderstanding though when the await call is needed. While I don't need any data back, the external url can take up to 5 minutes to process. I'm wondering if it's taking too long and timing out after that thread is started.
You could run it in its own thread.
Imports System.Net
Imports System.Threading
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
' Create a thread to run this in the background
Dim t As New Thread(
Sub()
Using WC As New WebClient
WC.UploadString("https://YOURURL", "YOURDATA")
End Using
End Sub
)
' Start the thread
t.Start()
End Sub
End Class

Webview2 AdditionalBrowserArguments --Kiosk-printing

I'm trying to print using the Webview2 control.
I can print with a dialog using the follwing code:
Await WebView_Browser1.CoreWebView2.ExecuteScriptAsync("window.print();")
But i would like to print silently.
With Edge we can use the parameter --Kiosk-printing like in Chrome.
This is the property i'm looking for: CoreWebView2EnvironmentOptions.AdditionalBrowserArguments
But how can i start the Webview2 control using these parameters?
I've looked up some examples like this: Link
But converting them to VB.NET using https://converter.telerik.com/ doesn't seem to work.
Is it possible using WinForms?
Thank you for your time!
This is what i have so far:
Dim opts As New CoreWebView2EnvironmentOptions With {.AdditionalBrowserArguments = "--kiosk-printing"}
Dim env As CoreWebView2Environment = Await CoreWebView2Environment.CreateAsync(Nothing, Nothing, Nothing), opts)
Dim tsk As Task = WebView_Browser1.EnsureCoreWebView2Async(env)
But is giving me the error: 'Await' can only be used within an Async method. Consider marking this method with the 'Async' modifier and changing its return type to 'Task'.
And i dont know how to use this could somebody please give me an example?
UPDATE
This is the current code that i have but it's not working.
Private Async Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Await InitializeAsync()
End Sub
Private Async Function InitializeAsync() As Task
Dim opts As New CoreWebView2EnvironmentOptions With {.AdditionalBrowserArguments = "--kiosk-printing"}
Dim env = Await CoreWebView2Environment.CreateAsync(Nothing, Nothing, opts)
AddHandler WebView_Browser1.CoreWebView2.FrameNavigationCompleted, AddressOf WebView_Browser1_FrameNavigationCompleted
Await WebView_Browser1.EnsureCoreWebView2Async(env)
End Function
Private Sub WV_CoreWebView2Ready1(sender As Object, e As EventArgs) Handles WebView_Browser1.CoreWebView2InitializationCompleted
WebView_Browser1.CoreWebView2.Navigate("https://www.google.com")
End Sub
I don't know how to use the arguments with my WebView_Browser1.
Can somebody please give me an example of how do deal with this?
Thank you!

vb.net 2010 async report not working in GUI

I know this is going to be virtually impossible to answer without me posting code, so I'll try to give some examples to aid this but...
I have a written dll which does some processing. I have it async tasks which report back status messages, such as the thing it's currently working on. There are multiple threads running each processing a different thing.
Now... when I use my DLL in a console app, the status.report("what I'm doing") works fine. I have a method in my console app with a Console.Writeline(text) which works great.
However... when I use the SAME dll in a gui form, and use the SAME methods from the console within the form to run the SAME processes with the SAME data, the SAME method that works perfectly writing the line to the console is NOT triggered and NO report is even processed by the gui.
Example.
console app:
Imports myDLL
Module Module1
Sub Main
SAE(paramaters).wait()
End Sub
Private Async Function SAE(parameters) as Task
Dim progress_indicator As Progress(Of Integer) = New Progress(Of Integer)(AddressOf DisplayProgress)
Dim progress_text As Progress(Of String) = New Progress(Of String)(AddressOf textProgress)
Dim complete As Object = Nothing
complete = Await Task.Run(Function() MyDLL.Process1(other parameters, progress_indicator, progress_text))
End Function
Private Sub DisplayProgress(ByVal percentage As Decimal)
Console.WriteLine("percentage " + Format(percentage, "0.00"))
End Sub
Private Sub textProgress(ByVal text As String)
Console.WriteLine("sub - reporting: " + text)
End Sub
End Module
Public Class myDLL
Public Function SettleAll(other paramaters, progress_indicator As IProgress(Of Integer), status As IProgress(Of String)) As Boolean
Dim aThread As Thread
aThread = New Thread(Sub() _OtherProcess(other parameters, progress_indicator, status))
aThread.Start()
System.Threading.Thread.Sleep(10)
aThread.Join
End Function
Private Sub _OtherProcess(other parameters, progress_indicator, status))
Loop
'Do Some stuff...
status.Report("Report back this it's working on this, that or the other")
progress_indicator.Report(SomePercentageProgressVariable))
End Loop
End Function
End Class
Now... when I use this, I get messages in the console window as I expect. However... in the gui... when I copy the SAE method and put the Sub Main code into a button click like this:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
SAE(paramaters).wait()
End Sub
... and I change the following methods:
Private Sub DisplayProgress(ByVal percentage As Decimal)
Debug.Print("percentage " + Format(percentage, "0.00"))
End Sub
Private Sub textProgress(ByVal text As String)
TextBox1.AppendText(text)
Debug.Print("sub - reporting: " + text)
End Sub
NOTHING at all happens...
The DLL is doing the processing, but there's no reporting.
Think I solved it.
If I change the button on_click method to an Async method llike this:
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Await SAE(paramaters)
End Sub
It seems to work

VB.Net + WebService: main form unresponsive on load

I have a small VB.Net project with link to sql using web service (SOAP).
I have to make sure that all forms are totally responsive no matter what, and it's working pretty well. My only problem is on loading the application!
The main start-up form has only single line of code:
Private Async Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Await objWebService.GetCurrentSessionsAsync
End Sub
But while this "awaitable" code is being executed the form is unresponsive, frozen and wait cursor is displayed.
Any idea on what might be causing this issue and how to handle it?
In regard to your answer, the code can be much cleaner if you don't combine different programming patterns, check this out:
Private Async Sub frmMain_Load(sender As Object,
e As EventArgs) Handles MyBase.Load
Dim res = Await GetCurrentSessionsAsync()
End Sub
Private Async Function GetCurrentSessionsAsync() As Task(Of com.services.Server)
Try
Return Await Task.Factory.
StartNew(Function() objWebService.GetCurrentSessions)
Catch ex As Exception
Glob.ErrorLog("GetCurrentSessions", ex, True)
Return New com.services.Server
End Try
End Function
References:
try-catch (C# Reference)
Async Return Types (C# and Visual Basic)
The key problem is that Async does not magically make your method asynchronous. It only lets compiler know that your method will have Await keywords, and that the code needs to be converted into a state machine. Any code that is not awaited is executed synchronously, even if the method is marked as Async. Consider the following example:
Private Async Sub Form1_Load(sender As Object,
e As EventArgs) Handles MyBase.Load
Await LongRunning1() 'opens the form, then asynchronously changes
'Text property after 2 seconds
End Sub
Private Async Function LongRunning1() As Task
Await Task.Factory.StartNew(Sub() Threading.Thread.Sleep(2000))
Me.Text = "Finished LongRunning1"
End Function
Here a long running process, Thread.Sleep as an example, is wrapped into a Task, and there is an Await keyword. It tells the compiler to wait for the statements inside the task to finish, before executing the next line. Without the Await, the Text property would be set immediately.
Now suppose you have some long running synchronous code in your Async method:
Private Async Sub Form1_Load(sender As Object,
e As EventArgs) Handles MyBase.Load
Await LongRunning2() 'synchronously waits 2 seconds, opens the form,
'then asynchronously changes Text property after 2 seconds
End Sub
Private Async Function LongRunning2() As Task
Threading.Thread.Sleep(2000)
Await LongRunning1()
Me.Text = "Finished LongRunning2"
End Function
Notice in this case it synchronously waits for the Thread.Sleep to finish, so for the end user you app appears as hanging. Bottom line is - you have to know which method calls can be long running, and wrap them into a task based await model. Otherwise you may be seeing the problem you are seeing.
If this sounds too complicated, you can fire up a background worker (.NET 2.0+), or use TPL (.NET 4.0+) to start a task. If you wish to go into lower level, threading is available since .NET 1.1. Then display some wait/progress window/overlay on top of the form/control, for which the functionality is not yet available. Check these out:
Loading data from DB asynchronously in win forms
Await async with event handler
Thanks to #PanagiotisKanavos for pointing me in the right direction.
So here what it is (I have to say that the answer of Neolisk and Panagiotis led me to the solution):
What made my loading form unresponsive is what appeared to be a bug in web services, only the first call of my web service would produce this issue. So If the first call was made after form load, on another event, I would face same problem.
To fix this, I changed the way I call my first method through web service using TaskCompletionSource variable, and calling my first method using Thread. I'll post my before/after code to be sure I delivered my fix clearly.
Before:
Private Async Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim res = Await objWebService.GetCurrentSessionsAsync
End Sub
After:
Private Async Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim res = Await GetCurrentSessionsAsync()
End Sub
Dim _tcServer As New TaskCompletionSource(Of MyProject.com.services.Server)
Private Async Function GetCurrentSessionsAsync() As Task(Of com.services.Server)
Try
Dim th As New System.Threading.Thread(AddressOf GetCurrentSessions)
th.Start()
Return Await _tcServer.Task
Catch ex As Exception
Return New MyProject.com.services.Server
End Try
End Function
Private Sub GetCurrentSessions()
Try
Dim res = objWebService.GetCurrentSessions
_tcServer.SetResult(res)
Catch ex As Exception
Glob.ErrorLog("GetCurrentSessions", ex, True)
End Try
End Sub
I hope this can help others in the future.
Thank you.

VB.NET DownloadDataAsync:

I am having the worst trouble getting around a bug, and am hoping that I can get some advice on this site. In short, I am trying to make an asynchronous web service call from my VB.NET application. But my "client_DownloadDataCompleted" callback is NEVER being called when the download is complete.
Here is my complete code:
Public Sub BeginAsyncDownload(ByVal Url As String)
Dim waiter As System.Threading.AutoResetEvent = New System.Threading.AutoResetEvent(False)
Dim client As WebClient = New WebClient()
'client_DownloadDataCompleted method gets called when the download completes.
AddHandler client.DownloadDataCompleted, AddressOf client_DownloadDataCompleted
Dim uri As Uri = New Uri(Url)
Downloading = True 'Class variable defined elsewhere
client.DownloadDataAsync(uri, waiter)
End Sub
Private Sub client_DownloadDataCompleted(ByVal sender As Object, ByVal e As AsyncCompletedEventArgs)
MessageBox.Show("Download Completed")
Downloading = False
Debug.Print("Downloaded")
End Sub
Again, the client_DownloadDataCompleted method is never being called. I have also tried using the method:
Private Sub client_DownloadDataCompleted(ByVal sender As Object, ByVal e As DownloadDataCompletedEventArgs)
With no luck. What I really need is that "Downloading" variable to be switched off when the download is complete.
Thanks in advance!
Brett
The client (Webclient) should be declared outside the BeginAsyncDownload subroutine, so it has a form/class level visibility. Please refer to the following code:
Public Class Form1
Dim client as New WebClient()
Private Sub BeginAsyncDownload(ByVal Url As String)
AddHandler client.DownloadDataCompleted, AddressOf client_DownloadDataCompleted
Dim uri As Uri = New Uri(Url)
Downloading = True 'Class variable defined elsewhere
client.DownloadDataAsync(uri, waiter)
End Sub
Private Sub client_DownloadStringCompleted(ByVal sender As Object, ByVal e As System.Net.DownloadStringCompletedEventArgs)
MessageBox.Show("Download Completed")
Downloading = False
Debug.Print("Downloaded")
End Sub
This is a tough one. I spent a little time on this and wasn't able to figure out why it wasn't getting called, sorry.
If you aren't able to get this to work, I have some code on CodePlex that includes a WebHelper class that might help you. I tried to make it as easy to use as WebClient but with all the power of HttpWebRequest.
The project is called BizArk. I wrote it just as a repository of code for myself. Feel free to just use the bits you want, I don't have any particular interest in how the code is used (as long as it's not used for evil :).
In what context are you invoking the webclient? WebClient will pick up your SynchronizationContext.Current and post its completion callback to it.
If you are using WinForms and your UI thread is blocked it will never be able to process your callback.