Memory leak in XmlSerializer as used in VB.net - vb.net

I seem to have a memory leak when using xmlSerializer in VB.net 2010.
The code below is the guts of a function which is called every 20 seconds or so forever.
Uncommenting the line 'serializer.Serialize(writer, response)' sees the memory usage climb.
Using writer As New StreamWriter(fileName)
Dim request = invoker.CreateRequest()
Dim response = invoker.Invoke(authorization, request)
If Not response Is Nothing Then 'maybe token timeout
Dim serializer = New XmlSerializer(GetType(TResponse)) ', New XmlRootAttribute("TResponse"))
Try
' serializer.Serialize(writer, response)
Catch e As InvalidOperationException
Dim msg As String = "ERROR: Failed to serialize the response:" & vbCrLf & e.Message
AddToRtfEventLog(category.ToUpper & " DATA " & msg, Drawing.Color.Red)
End Try
serializer = Nothing
AddToStatus(Convert.ToString("Response saved as ") & fileName & vbCrLf, Drawing.Color.LightGray, True)
End If
Return response
End Using
From internet reading, I gather this is a bug in the VB xmlSerializer and a possible solution is to use it in a caching manner. Unfortunately I have no idea how to do that so I wonder if someone can help me out here.
Firstly, am I doing it right in the first place (it works fine, just the memory issue) and
Secondly, if I am, can you show me how to fix the leak please?
Thanks
Malcom
Part 2-
With help from Jehof, I've now got the following cache class in place:
Public NotInheritable Class XmlSerializerCache
Private Sub New()
End Sub
Private Shared ReadOnly cache As New Dictionary(Of String, XmlSerializer)()
Public Shared Function Create(type As Type, root As XmlRootAttribute) As XmlSerializer
Dim key = [String].Format(CultureInfo.InvariantCulture, "{0}:{1}", type, root.ElementName)
If Not cache.ContainsKey(key) Then
cache.Add(key, New XmlSerializer(type, root))
End If
Return cache(key)
End Function
End Class
and have replaced the calls with :
Dim xmlRootAttribute = New XmlRootAttribute("TResponse")
Dim serializer = XmlSerializerCache.Create(GetType(TResponse), XmlRootAttribute)
Try
serializer.Serialize(writer, response)
Thats has improved the leak but is the correct way to do this? To me it seems that I am recreating the cache with each call?

Related

VB.net POST method using HTTPclient

Hello I am trying to access a webform with data. However my code does not have error but its not working. It is supposed to send me a text message using the Web API provided by the telco carrier. Any assistance and advise will be greatly appreciated.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Try
Dim client As New HttpClient
Dim url = $"some url"
Dim data2 = "{""username"": ""someuname"", ""password"":""somepass"", ""msisdn:""some number"", ""content:""Hello, this is a sample broadcast"", ""shortcode_mask:""somemask""}"
Dim payload = Newtonsoft.Json.JsonConvert.SerializeObject(data2)
Dim buffer = Encoding.UTF8.GetBytes(payload)
Dim bytes = New Net.Http.ByteArrayContent(buffer)
bytes.Headers.ContentType = New Net.Http.Headers.MediaTypeHeaderValue("application/json")
Dim request = client.PostAsync(url, bytes)
Catch Ex As Exception
Console.WriteLine()
End Try
End Sub
End Class
There are a few issues in your code. Let me start with the most important one first:
Catch Ex As Exception
Console.WriteLine()
End Try
This code means: If an error occurs, throw away all useful information about the error that could help me find the cause, then write an empty line to the console and pretend nothing bad happened.
Don't do that. Remove the whole try-catch block, then re-run your code again. If you get an exception: That's great, because .NET is now telling you what is wrong with your code! If you understand the error message, use it to fix your code. If you don't understand it, feel free to ask the community on StackOverflow.
Second issue:
This line
Dim request = client.PostAsync(url, bytes)
starts an asynchronous web request. You don't wait for the result, so you don't know whether it succeeded or not.
You're supposed to Await async methods, but if you aren't familiar with the async/await pattern yet, I won't be able to give you all the necessary background in the single StackOverflow answer. Until you have familiarized youself with async/await, you can synchronously wait for the result by accessing the Result property of the Task that has been returned:
Dim response = client.PostAsync(url, bytes).Result
response.EnsureSuccessStatusCode()
Third issue: You JSON-serialize something which is already JSON. If your data is already JSON, just use it as the payload without calling SerializeObject.
Your data2 is a string. When you use the JSON library to serialize it, it probably isn't in the JSON structure that your URL endpoint expects. Create a Type, fill it with data, and pass that to your JsonCoverter
Thank you Heinzi, here is the final working code based on your concepts:
Public Class Form1
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim client As New HttpClient
Dim url = $"some url"
Dim data2 = "
{""username"": ""some uname"", ""password"": ""somepass"", ""msisdn"": ""somenumber"", ""content"": ""Hello, this is a sample broadcast"", ""shortcode_mask"" :""somemask""}"
Dim buffer = Encoding.UTF8.GetBytes(data2)
Dim bytes = New ByteArrayContent(buffer)
bytes.Headers.ContentType = New Headers.MediaTypeHeaderValue("application/json")
Dim request1 = Await client.PostAsync(url, bytes)
Dim response = client.PostAsync(url, bytes).Result
response.EnsureSuccessStatusCode()
End Sub
End Class
I can now receive text messages :)

vb.net How to declare thread and call functions with it

I'm trying to use a thread to translate every text found in the Windows Forms to make my system multi-language.
I have a separate class named 'Language' with a sub and a function, sub reads a language source file, and function translates by receiving and returning a string.
Then I have my first Windows Form where I declare my thread:
Dim ThreadTraductor As New Thread(AddressOf ...) 'don't know how to do it
Dim cultureInfo As New System.Globalization.CultureInfo(ConfigurationManager.AppSettings('en').ToString)
ThreadTraductor.CurrentCulture = cultureInfo
ThreadTraductor.CurrentUICulture = cultureInfo
Basically I'm creating this thread to have a background process translating every Windows Form that's opened during execution, problem is I don't know how to declare it properly since I don't want to include any parameter when declaring, but I want the thread to be called from different Forms with parameters to translate, and also I want the thread to use my translate method inside Language class, is that possible? How?
Please assist, I haven't use threads before.
Public Class Lenguaje 'Class used to read language source file and translate strings
Public dicIdioma As New Dictionary(Of String, String)
Public Sub LeerArchivo(ByVal Culture As String)
Dim vectorAux() As String
dicIdioma.Clear()
Try
Dim LectorArchivo As New StreamReader("C:\Users\Joaqo\Desktop\Dorian VB\DorianBdBv1.0\UI\bin\Debug\" + Culture + ".txt")
Dim line As String
While Not LectorArchivo.Peek = -1
line = LectorArchivo.ReadLine()
vectorAux = line.Split(":")
dicIdioma.Add(vectorAux(0), vectorAux(1))
End While
Catch ex As System.IO.FileNotFoundException
MessageBox.Show("No se encuentra el archivo de idioma.")
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub
Public Function Traducir(ByRef frase As String) As String
Dim StringAux As String
For Each Item As String In dicIdioma.Keys
If Item = Char.ToLower(frase(0)) & frase.Substring(1) Then
StringAux = Char.ToUpper(dicIdioma.Item(Item)(0)) & dicIdioma.Item(Item).Substring(1)
frase = StringAux.Replace("_", " ")
ElseIf Item.Replace("_", " ") = Char.ToLower(frase(0)) & frase.Substring(1) Then
StringAux = Char.ToUpper(dicIdioma.Item(Item)(0)) & dicIdioma.Item(Item).Substring(1)
frase = StringAux.Replace("_", " ")
End If
Next
Return frase
End Function
End Class
Then I iterate every text object in my Windows Forms to translate them:
For Each Item As Label In Me.Controls.OfType(Of Label)()
Item.Text = Traductor.Traducir(Item.Text)
Next
For Each Item As Button In Me.Controls.OfType(Of Button)()
Item.Text = Traductor.Traducir(Item.Text)
Next
And it works just fine, but I'd be calling Traductor, Lenguaje's instance declared on my first interface, from the whole app, isn't that wrong somehow?
I was told that I should use Culture and CultureUI for this, but I'm not familiarized with that. What do you think? Sorry if I'm missing something, it's my first question here.

How do I upload data to a server with valid SSL certificate using a client vb.net program?

I am working on a program and uploads a shipping manifest to a the shippers website. When I try to upload, I get a nondescript error back from their server, and when checking with the shipper, they tell me that "there is an issue with the SSL" I am using.
I've spent quite a bit of time piecing together code that, from what I seem to understand, is supposed to work, but I'm not making any progress. As far as I know everything else is fine with the upload, but there is a problem with my SSL certificate
If I understand what this code is supposed to do correctly, I should get a certificate from the shippers website, which allows certification to my program for a space of time during which I can upload the data. I'm really not sure that this is what my code is doing at all, but the only code examples I have seen show it something like this.
Here's my code with the URLs changed:
'This references a custom class that compiles the manifest I'm going to upload
Dim StringToUpload As String = Compile_Manifest(MyDate, UseTestDB)
Dim webClient As New System.Net.WebClient
webClient.Credentials = System.Net.CredentialCache.DefaultCredentials
'From what I understand,
'this is supposed to set up properties used in next section of code
System.Net.ServicePointManager.SecurityProtocol = Net.SecurityProtocolType.Ssl3
System.Net.ServicePointManager.ServerCertificateValidationCallback = _
AddressOf AcceptAllCertifications
'I can see that this reaches the server,
'but I don't know how it relates to the next section of code
'that actually uploads the manifest
Dim ServerRequest As System.Net.WebRequest = _
System.Net.WebRequest.Create("https://www.certify.some-shippper.com:443/somefolder")
Dim ServerResponse As System.Net.WebResponse
ServerResponse = ServerRequest.GetResponse()
ServerResponse.Close()
'This code works for the upload of the manifest,
'and it seems the above code is unrelated and does not use a SSL certificate.
'When this code runs I get the same error back from the shippers server,
'indicating an issue with my SSL, with or without the two sections of code above.
Dim StrResult As String = ""
Dim WrappedString As String = TransmitPLD.WrapPldFile(StringToUpload)
'This references a custom class that wraps the data to upload
'in information from the shipper.
Dim ByesToUpload As Byte() = _
System.Web.HttpUtility.UrlEncodeToBytes(WrappedString, _
System.Text.ASCIIEncoding.ASCII)
Dim Result As Byte() = _
webClient.UploadData("https://www.certify.some-shippper.com:443/somefolder", _
ByesToUpload)
StrResult = System.Web.HttpUtility.UrlDecode(Result, _
System.Text.ASCIIEncoding.ASCII)
MessageBox.Show(StrResult)
So it turns out I went about it the wrong way. I needed to upload my data through System.Net.WebRequest and it takes care of the certificates for me. Not implementing all the parts of the code I needed, it didn't handle the retrieval of the shipper's certificate.
In case anyone else gets confused about the matter like I did, here's my working code for anyone to see, adapt and use. My resource for fixing the code (and by that I mean starting from scratch) was the MSDN page for the WebRequest class, and it has code examples much the same as what I have below in C++, C#, and VB.NET and here is the link.
First there are some global variables that need to be set and class that needs to be created for to store the upload response:
' This is set in the function that Upload function
' and uploads the data in the ReadCallback sub
Private Shared WrappedString As String
' This is used to wait for the callback in the Upload function
Private Shared allDone As New Threading.ManualResetEvent(False)
Friend Class RequestState
' This class stores the request state of the request.
Public request As Net.WebRequest
Public Sub New()
request = Nothing
End Sub ' New
End Class ' RequestState
Then there is a sub needed for the upload part web request which will be called further below in the upload function:
Private Shared Sub ReadCallback(asynchronousResult As IAsyncResult)
Try
Dim myRequestState As RequestState = CType(asynchronousResult.AsyncState, RequestState)
Dim myWebRequest As Net.WebRequest = myRequestState.request
' End the request.
Dim streamResponse As IO.Stream = myWebRequest.EndGetRequestStream(asynchronousResult)
' Convert the string into a byte array.
Dim byteArray As Byte() = System.Text.Encoding.ASCII.GetBytes(WrappedString)
' Write the data to the stream.
streamResponse.Write(byteArray, 0, byteArray.Length)
streamResponse.Close()
' Allow the main thread to resume.
allDone.Set()
Catch ex As Exception
Throw New Exception("Error in " & Reflection.MethodBase.GetCurrentMethod.Name.ToString & " **" & ex.Message, ex)
End Try
End Sub ' ReadCallback
Finally, this is the function that should be called to upload the data, which uses all the code above:
Public Shared Function Upload(ByVal MyDate As Date) As String
Dim StrResult As String = ""
UploadSucess = False
Try
' This is my code that builds the manifest that I want to upload
Dim StringToUpload As String = Compile_PLD200(MyDate)
WrappedString = TransmitPLD.WrapPldFile(StringToUpload)
Dim myWebRequest As Net.WebRequest
myWebRequest = Net.WebRequest.Create("https://www.some.website.com:443/someplace")
' Create an instance of the RequestState and assign
' myWebRequest to it's request field.
Dim myRequestState As New RequestState()
myRequestState.request = myWebRequest
myWebRequest.ContentType = "multipart/mixed; boundary=BOUNDARY"
myRequestState.request.Method = "POST"
' Start the asynchronous 'BeginGetRequestStream' method call.
Dim r As IAsyncResult = CType(myWebRequest.BeginGetRequestStream(AddressOf ReadCallback, myRequestState), IAsyncResult)
' Pause the current thread until the async operation completes.
allDone.WaitOne()
' Send the Post and get the response.
Dim myWebResponse As Net.WebResponse = myWebRequest.GetResponse()
Dim streamResponse As IO.Stream = myWebResponse.GetResponseStream()
Dim streamRead As New IO.StreamReader(streamResponse)
Dim readBuff(256) As [Char]
Dim count As Integer = streamRead.Read(readBuff, 0, 256)
While count > 0
Dim outputData As New [String](readBuff, 0, count)
Console.WriteLine(outputData)
count = streamRead.Read(readBuff, 0, 256)
StrResult += outputData
End While
' Close the Stream Object.
streamResponse.Close()
streamRead.Close()
' Release the HttpWebResponse Resource.
myWebResponse.Close()
Catch ex As Exception
Throw New Exception("Error in " & Reflection.MethodBase.GetCurrentMethod.Name.ToString & " **" & ex.Message, ex)
End Try
Return StrResult
End Function ' Upload
Again here is the MSDN page for the WebRequest class which has a code example too.
Hope this helps anyone who was stuck like I was. And any criticisms as to the implementation of the code are welcome. This just happen to do what I want, I can't say it is the most efficient implementation.

non blocking webrequests vb.net

I am making a program that must process about 5000 strings as quickly as possible. about 2000 of these strings must be translated via a webrequest to mymemory.translated.net. (see code below, JSON part removed since not needed here)
Try
url = "http://api.mymemory.translated.net/get?q=" & Firstpart & "!&langpair=de|it&de=somemail#christmas.com"
request = DirectCast(WebRequest.Create(url), HttpWebRequest)
response = DirectCast(request.GetResponse(), HttpWebResponse)
myreader = New StreamReader(response.GetResponseStream())
Dim rawresp As String
rawresp = myreader.ReadToEnd()
Debug.WriteLine("Raw:" & rawresp)
Catch ex As Exception
MessageBox.Show(ex.ToString)
End Try
the code itself is working fine, problem is it is a blocking code and needs about 1 second per string. Thats more then half an hour for all my strings. i would need to convert this code to a non blocking one and make multiple calls on the same time. Could somebody please tell me how i could do that? I was thinking of a background worker but that wouldnt speed things up.. it would just execute the code on a different thread...
thanks!
The problem is you aren't just being held back by the maximum number of concurrent operations. HttpWebRequests are throttled by nature (I believe the default policy allows only 2 at any given time), so you have to override that behaviour too. Please refer to the code below.
Imports System.Diagnostics
Imports System.IO
Imports System.Net
Imports System.Threading
Imports System.Threading.Tasks
Public Class Form1
''' <summary>
''' Test entry point.
''' </summary>
Private Sub Form1_Load() Handles MyBase.Load
' Generate enough words for us to test thoroughput.
Dim words = Enumerable.Range(1, 100) _
.Select(Function(i) "Word" + i.ToString()) _
.ToArray()
' Maximum theoretical number of concurrent requests.
Dim maxDegreeOfParallelism = 24
Dim sw = Stopwatch.StartNew()
' Capture information regarding current SynchronizationContext
' so that we can perform thread marshalling later on.
Dim uiScheduler = TaskScheduler.FromCurrentSynchronizationContext()
Dim uiFactory = New TaskFactory(uiScheduler)
Dim transformTask = Task.Factory.StartNew(
Sub()
' Apply the transformation in parallel.
' Parallel.ForEach implements clever load
' balancing, so, since each request won't
' be doing much CPU work, it will spawn
' many parallel streams - likely more than
' the number of CPUs available.
Parallel.ForEach(words, New ParallelOptions With {.MaxDegreeOfParallelism = maxDegreeOfParallelism},
Sub(word)
' We are running on a thread pool thread now.
' Be careful not to access any UI until we hit
' uiFactory.StartNew(...)
' Perform transformation.
Dim url = "http://api.mymemory.translated.net/get?q=" & word & "!&langpair=de|it&de=somemail#christmas.com"
Dim request = DirectCast(WebRequest.Create(url), HttpWebRequest)
' Note that unless you specify this explicitly,
' the framework will use the default and you
' will be limited to 2 parallel requests
' regardless of how many threads you spawn.
request.ServicePoint.ConnectionLimit = maxDegreeOfParallelism
Using response = DirectCast(request.GetResponse(), HttpWebResponse)
Using myreader As New StreamReader(response.GetResponseStream())
Dim rawresp = myreader.ReadToEnd()
Debug.WriteLine("Raw:" & rawresp)
' Transform the raw response here.
Dim processed = rawresp
uiFactory.StartNew(
Sub()
' This is running on the UI thread,
' so we can access the controls,
' i.e. add the processed result
' to the data grid.
Me.Text = processed
End Sub, TaskCreationOptions.PreferFairness)
End Using
End Using
End Sub)
End Sub)
transformTask.ContinueWith(
Sub(t As Task)
' Always stop the stopwatch.
sw.Stop()
' Again, we are back on the UI thread, so we
' could access UI controls if we needed to.
If t.Status = TaskStatus.Faulted Then
Debug.Print("The transformation errored: {0}", t.Exception)
Else
Debug.Print("Operation completed in {0} s.", sw.ElapsedMilliseconds / 1000)
End If
End Sub,
uiScheduler)
End Sub
End Class
If you want to send 10 parallel requests, you must create 10 BackgroundWorkers. Or manually create 10 threads. Then iterate, and whenever a worker/thread is done, give it a new task.
I do not recommend firing 5000 parallel threads/workers, you must be careful:
A load like that could be interpreted as spamming or an attack by the server. Don't overdo it, maybe talk to translated.net and ask them about the workload they accept.
Also think about what your machine and your internet upstream can handle.
I would create a Task for every request, so you can have a Callback for every call using ContinueWith:
For Each InputString As String In myCollectionString
Tasks.Task(Of String).Factory.StartNew(Function(inputString)
Dim request As HttpWebRequest
Dim myreader As StreamReader
Dim response As HttpWebResponse
Dim rawResp As String = String.Empty
Try
Dim url As String = "http://api.mymemory.translated.net/get?q=" & inputString & "!&langpair=de|it&de=somemail#christmas.com"
request = DirectCast(WebRequest.Create(url), HttpWebRequest)
response = DirectCast(request.GetResponse(), HttpWebResponse)
myreader = New StreamReader(response.GetResponseStream())
rawResp = myreader.ReadToEnd()
Debug.WriteLine("Raw:" & rawResp)
Catch ex As Exception
MessageBox.Show(ex.ToString)
End Try
Return rawResp
End Function, CancellationToken.None, _
Tasks.TaskCreationOptions.None).ContinueWith _
(Sub(task As Tasks.Task(Of String))
'Dom something with result
Console.WriteLine(task.Result)
End Sub)
Next

VB.Net Sockets Invoke

I am using vb.net 2010 and I have created a program that uses sockets to transfer data between our windows server and a unix server. The code was originally from a Microsoft sample project hence my little understanding of it.
Everything was fine until I had the idea of changing the program into a service. The Invoke command is not accessable from a service. I think I understand why but more importantly how do I get around it or fix it?
' need to call Invoke before can update UI elements
Dim args As Object() = {command, data}
Invoke(_processInStream, args)
Someone please help I am desperate to finish this program so I can move on :)
Below is the rest of the class, there is a server socket class too but I didnt want to complicate things?
Public Class srvMain
' start the InStream code to receive data control.Invoke callback, used to process the socket notification event on the GUI's thread
Delegate Sub ProcessSocketCommandHandler(ByVal command As NotifyCommandIn, ByVal data As Object)
Dim _processInStream As ProcessSocketCommandHandler
' network communication
Dim WithEvents _serverPRC As New ServerSocket
Dim _encryptDataIn() As Byte
Dim myConn As SqlConnection
Dim _strsql As String = String.Empty
Protected Overrides Sub OnStart(ByVal args() As String)
' watch for filesystem changes in 'FTP Files' folder
Watch()
' hookup Invoke callback
_processInStream = New ProcessSocketCommandHandler(AddressOf ProcessSocketCommandIn)
' listen for Ultimate sending signatures
_serverPRC.Start(My.Settings.listen_port_prc)
myConn = New SqlConnection(My.Settings.Mill_SQL_Connect)
End Sub
Protected Overrides Sub OnStop()
' Add code here to perform any tear-down necessary to stop your service.
End Sub
' this is where we will break the data down into arrays
Private Sub processDataIn(ByVal data As Object)
Try
If data Is Nothing Then
Throw New Exception("Stream empty!")
End If
Dim encdata As String
' decode to string and perform split(multi chars not supported)
encdata = Encoding.Default.GetString(data)
_strsql = encdata
myConn.Open()
Dim commPrice As New SqlCommand(_strsql, myConn)
Dim resPrice As SqlDataReader = commPrice.ExecuteReader
'********************************THIS MUST BE DYNAMIC FOR MORE THAN ONE NATIONAL
If resPrice.Read = True And resPrice("ats" & "_price") IsNot DBNull.Value Then
'If resPrice("ats" & "_price") Is DBNull.Value Then
' cannot find price so error
'natPrice = ""
'natAllow = 2
'End If
natPrice = resPrice("ats" & "_price")
natAllow = resPrice("ats" & "_allow")
Else
' cannot find price so error
natPrice = ""
natAllow = 2
End If
myConn.Close()
' substring not found therefore must be a pricing query
'MsgBox("string: " & encdata.ToString)
'natPrice = "9.99"
Catch ex As Exception
ErrHandle("4", "Process Error: " + ex.Message + ex.Data.ToString)
Finally
myConn.Close() ' dont forget to close!
End Try
End Sub
'========================
'= ServerSocket methods =
'========================
' received a socket notification for receiving from Ultimate
Private Sub ProcessSocketCommandIn(ByVal command As NotifyCommandIn, ByVal data As Object)
' holds the status message for the command
Dim status As String = ""
Select Case command
Case NotifyCommandIn.Listen
'status = String.Format("Listening for server on {0} ...", CStr(data))
status = "Waiting..."
Case NotifyCommandIn.Connected
'status = "Connected to Ultimate" ' + CStr(data)
status = "Receiving..."
Case NotifyCommandIn.Disconnected
status = "Waiting..." ' disconnected from Ultimate now ready...
Case NotifyCommandIn.ReceivedData
' store the encrypted data then process
processDataIn(data)
End Select
End Sub
' called from socket object when a network event occurs.
Private Sub NotifyCallbackIn(ByVal command As NotifyCommandIn, ByVal data As Object) Handles _serverPRC.Notify
' need to call Invoke before can update UI elements
Dim args As Object() = {command, data}
Invoke(_processInStream, args)
End Sub
End Class
Any help is appreciated
Many thanks
Invoke is a member of System.Windows.Forms.Form, and it is used to make sure that a certain method is invoked on the UI thread. This is a necessity in case the method in question touches UI controls.
In this case it looks like you simply can call the method directly, i.e.
instead of
Dim args As Object() = {command, data}
Invoke(_processInStream, args)
you can simply write
ProcessSocketCommandIn(command, data)
Also, in this case you can get rid of the _processInStream delegate instance.