Using .Net XmlSerialize for strings with embedded <cr><lf> loses <cr> when deserializing - vb.net

The following (abysmal) code demonstrates how standard serialize/de-serialize in VB loses the CR when on de-serialize. This can be overcome by applying 'XmlAttribute(DataType:="string")' to Description. Why does it do this? I would like to fix this without applying a 'LF' -> 'CR''LF' in every affected class. This is fixing a bug in existing XML files generated without the XmlAttribute!
Imports System.Xml.Serialization
Imports System.Xml
Imports System.IO
Public Class Form1
Public Class MyObject
Public Description As String
End Class
Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim x As New MyObject
x.Description = "Hello" + vbCrLf + "World"
Dim serializer As New XmlSerializer(GetType(MyObject))
Dim writer As StreamWriter = New StreamWriter("c:\temp\test.xml")
serializer.Serialize(writer, x)
writer.Close()
For i As Integer = 0 To x.Description.ToCharArray.Length - 1
Debug.Print(Asc(x.Description.ToCharArray(i, 1)))
Next
Debug.Print("**********************")
Dim reader As New StreamReader("c:\temp\test.xml")
Dim newObj As MyObject = CType(serializer.Deserialize(reader), MyObject)
For i As Integer = 0 To newObj.Description.ToCharArray.Length - 1
Debug.Print(Asc(newObj.Description.ToCharArray(i, 1)))
Next
End Sub
End Class

Take a look at XML deserialization 'standardising' line endings, how to stop it? (.NET). Does that soluton match what you're trying to do?

Related

store setting in relative path to executable aplication

I am developing an application to run from USB, is it like a dock/launcher application but I have a problem.
I want to use My.Settings class to save my app settings, but it saves the setting file in AppData folder e.g. C:\Users\<user_name>\AppData\Local\...\...\user.config
I don't want that. I want to save in a path and name of my defined, e.g. My.Application.Info.DirectoryPath & "\Settings.xml"
How can I achieve this?
Update Example of final XML:
<?xml version="1.0" encoding="utf-8"?>
<conf>
<pos>1</pos>
<btn index="1" value="D:\League of Legends\" perm="true">LeagueClient.exe</btn>
<btn index="2" value="D:\RuneLite\" perm="false">RuneLite.exe</btn>
<btn index="3" value="" perm="false"></btn>
<btn index="4" value="" perm="false"></btn>
</conf>
Full project in Github coming soon!!!
Work way for me :
Imports System.IO
Imports System.Xml
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
ValConfFile()
End Sub
Public Sub ValConfFile()
If File.Exists("config.xml") = False Then : CreateConfXML() : End If
End Sub
Public Sub CreateConfXML()
Dim obj As Object
Dim archivo As Object
Dim x As Integer = 1
obj = CreateObject("Scripting.FileSystemObject")
archivo = obj.CreateTextFile("config.xml", True)
archivo.WriteLine("<?xml version='1.0' encoding='utf-8'?>")
archivo.WriteLine("<conf>")
archivo.WriteLine("<pos>1</pos>")
For x = 1 To 4
archivo.WriteLine("<btn index='" & CStr(x) & "' value='' perm='false'></btn>")
Next
archivo.WriteLine("</conf>")
archivo.Close()
End Sub
End Class
XML Serialization can be used for this, and is actually fairly straightforward. All you need to do is design one or more classes for your data, then apply the appropriate attributes (their name are in the form Xml...Attribute) in order to serialize them the way you want.
In this setup I've used four different attributes:
XmlElement - Usually the most common one. Specifies that a property will be serialized as an element of its own. The resulting name can be customized by setting the ElementName parameter in the constructor.
If used on lists or arrays, it applies to each item in the collection.
XmlRoot - Pretty much the same as XmlElement, but used for the root element (the class itself).
XmlAttribute - Specifies that a property will be serialized as an attribute (name="value") applied to the parent object, instead of as an element inside it.
XmlText - Specifies that a property's value will be serialized as the contents between the tags of the parent object (i.e. <object>property value</object>).
Imports System.IO
Imports System.Xml
Imports System.Xml.Serialization
<XmlRoot(ElementName:="conf")>
Public Class Config
<XmlElement(ElementName:="pos")>
Public Property Position As Integer
<XmlElement(ElementName:="btn")>
Public Property Buttons As New List(Of ConfigButton)
Public Sub New()
End Sub
Public Sub New(ByVal Position As Integer)
Me.Position = Position
End Sub
Public Shared Function Load(ByVal File As String) As Config
Using FStream As New FileStream(File, FileMode.Open, FileAccess.Read, FileShare.Read)
Dim Serializer As New XmlSerializer(GetType(Config))
Return Serializer.Deserialize(FStream)
End Using
End Function
Public Sub Save(ByVal File As String)
Using FStream As New FileStream(File, FileMode.Create, FileAccess.Write, FileShare.None)
Dim Serializer As New XmlSerializer(GetType(Config))
Serializer.Serialize(FStream)
End Using
End Sub
End Class
Public Class ConfigButton
<XmlText()>
Public Property DisplayName As String
<XmlAttribute("index")>
Public Property Index As Integer
<XmlAttribute("perm")>
Public Property Perm As Boolean
<XmlAttribute("value")>
Public Property Value As String
Public Sub New()
End Sub
Public Sub New(ByVal DisplayName As String, ByVal Value As String, ByVal Index As Integer, ByVal Perm As Boolean)
Me.DisplayName = DisplayName
Me.Value = Value
Me.Index = Index
Me.Perm = Perm
End Sub
End Class
Usage example:
Private cfg As Config
'
'Loading the config.
'
Private Sub Form1_Load(sender As Object, e As EventArgs) Handled MyBase.Load
If File.Exists("config.xml") Then
cfg = Config.Load("config.xml")
Else
cfg = New Config()
End If
End Sub
'
'Saving the config.
'
Private Sub Form1_FormClosed(sender As Object, e As EventArgs) Handles Me.FormClosed
cfg.Save("config.xml")
End Sub
Adding a button:
cfg.Buttons.Add(New ConfigButton("RuneLite.exe", "D:\RuneLite\", 2, False))
Iterating buttons:
For Each btn As ConfigButton In cfg.Buttons
MessageBox.Show(btn.DisplayName)
Next
Removing a button at a specific index:
'Removes the fourth button.
cfg.Buttons.RemoveAt(3)

Event 'Load' cannot be found

Getting the error "Event 'Load' cannot be found" referring to "Handles MyBase.Load" Please see attached code. Any help much appreciated!
I have many other applications set up the same way and they all work. However, these were in an older version of Visual Studio.
Option Explicit On
Option Strict On
Imports System, System.IO
Imports System.Text
Public Class Form1
Private Sub cleanXMLDialog_Load(ByVal eventSender As System.Object, ByVal
eventArgs As System.EventArgs) Handles MyBase.Load
Main()
End
End Sub
Public Sub Main()
Dim directories() As String = Directory.GetDirectories("C:\")
Dim files() As String = Directory.GetFiles("C:\", "*.dll")
DirSearch("c:\")
End Sub
Sub DirSearch(ByVal sDir As String)
Dim d As String
Dim f As String
Try
For Each d In Directory.GetDirectories(sDir)
For Each f In Directory.GetFiles(d, "*.xml")
'Dim Response As String = MsgBox(f)
Debug.Write(f)
Next
DirSearch(d)
Next
Catch excpt As System.Exception
Debug.WriteLine(excpt.Message)
End Try
End Sub
End Class
Load should happen without this error.
This is the class declaration:
Public Class Form1
It looks like you intend this to inherit from a windows Form type, but there's nothing here to make that happen.
You may want this:
Public Class Form1 Inherits System.Windows.Forms.Form
but even this is unlikely to really accomplish anything. It's not enough just to inherit from the Form type if you don't have any controls are properties set and don't ever show the form.
Did you accidentally create a Console or Class Library project when you mean to create a WinForms project?

How to load an internal class in end-user compiled code ? (advanced)

I have a main program with two classes
1- A winform that contains two elements :
Memo Edit to type some code;
Button named compile.
The end user may type some VB.Net code in the memo edit and then compile it.
2 - A simple test class :
Code :
Public Class ClassTest
Public Sub New()
MsgBox("coucou")
End Sub
End Class
Now I would like to use the class ClassTest in the code that will be typed in the MemoEdit and then compile it :
When hitting compile I recieve the error :
The reason is that, the compiler can't find the namespace ClassTest
So to summarize :
The class ClassTest is created in the main program
The end user should be able to use it and create a new assembly at run time
Does anyone know how to do that please ?
Thank you in advance for your help.
Code of the WinForm :
Public Class Form1
Private Sub SimpleButtonCompile_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SimpleButtonCompile.Click
Dim Code As String = Me.MemoEdit1.Text
Dim CompilerResult As CompilerResults
CompilerResult = Compile(Code)
End Sub
Public Function Compile(ByVal Code As String) As CompilerResults
Dim CodeProvider As New VBCodeProvider
Dim CodeCompiler As System.CodeDom.Compiler.CodeDomProvider = CodeDomProvider.CreateProvider("VisualBasic")
Dim Parameters As New System.CodeDom.Compiler.CompilerParameters
Parameters.GenerateExecutable = False
Dim CompilerResult As CompilerResults = CodeCompiler.CompileAssemblyFromSource(Parameters, Code)
If CompilerResult.Errors.HasErrors Then
For i = 0 To CompilerResult.Errors.Count - 1
MsgBox(CompilerResult.Errors(i).ErrorText)
Next
Return Nothing
Else
Return CompilerResult
End If
End Function
End Class
Here is the solution :
If the end user wants to use internal classes he should use the command : Assembly.GetExecutingAssembly
The full code will be :
Code :
Imports System.Reflection
Imports System
Public Class EndUserClass
Public Sub New()
Dim Assembly As Assembly = Assembly.GetExecutingAssembly
Dim ClassType As Type = Assembly.GetType(Assembly.GetName().Name & ".ClassTest")
Dim Instance = Activator.CreateInstance(ClassType)
End Sub
End class

Cannot load temporary file in VBCodeProvider

Here's my script code:
Imports System.Diagnostics
Public Class Script
Implements IScript
Public Sub DoWork(w As WebBrowser, f As Form1) Implements IScript.DoWork
w.Navigate("http://www.google.com")
wait("5000")
w.Document.All("input").InvokeMember("click")
w.Document.All("input").SetAttribute("value", "Torrenter is the best!")
wait("2000")
w.Document.All("421").InvokeMember("click")
wait("1000")
End Sub
Public Sub wait(ByVal interval As Integer)
Dim sw As New Stopwatch
sw.Start()
Do While sw.ElapsedMilliseconds < interval
' Allows UI to remain responsive
Application.DoEvents()
Loop
sw.Stop()
End Sub
End Class
In-code:
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
If int1.Text = "1" Then
int1.Text = "0"
Dim script As IScript = GenerateScript(File.ReadAllText(ListBox2.Items.Item(int2).ToString()))
script.DoWork(WebBrowser1, Me) 'Object reference not set to an instance of an object.
int2 = int2 + 1
int1.Text = "1"
End If
End Sub
Why? :(
It's supposed to start the next script after the first was done. I tried 4 methods but I can't understand why.
The problem is that your script code is failing to compile, but then you are trying to instantiate an object from the compiled assembly anyway. Since it failed to compile, the assembly doesn't actually exist, hence the error. If you modify the Return line in the GenerateScript method, so that it shows the compile errors, the actual problem will be more clear:
Dim results As CompilerResults = provider.CompileAssemblyFromSource(parameters, codes)
If results.Errors.HasErrors Then
Dim builder As New StringBuilder()
builder.AppendLine("Script failed to compile due to the following errors:")
For Each i As CompilerError In results.Errors
builder.AppendFormat("Line {0}: {1}", i.Line, i.ErrorText)
builder.AppendLine()
Next
Throw New Exception(builder.ToString())
Else
Return CType(results.CompiledAssembly.CreateInstance("Script"), IScript)
End If
I suspect that one of the reasons it's failing to compile is because the script uses IScript which is undefined. The reason it would complain that it's undefined is for two reasons. First, you declared the IScript interface as nested inside the Form1 class. You should move that outside of the form class so that it's not nested inside of any other type. Second, you are not specifying the full namespace nor importing the namespace in your script. You can automatically add the Imports line to the beginning of the script code before compiling it, like this:
Dim interfaceNamespace As String = GetType(IScript).Namespace
Dim codes As String = "Imports " & interfaceNamespace & Environment.NewLine & code
As I mentioned in the comments above, you really ought to be passing a string array into the CompileAssemblyFromSource method, not a string. I'm not sure how that even compiles, unless that's something having Option Strict Off somehow allows? In any case, it expects an array, so you should really be giving it one, like this:
Dim interfaceNamespace As String = GetType(IScript).Namespace
Dim codeArray() As String = New String() {"Imports " & interfaceNamespace & Environment.NewLine & code}
Dim results As CompilerResults = provider.CompileAssemblyFromSource(parameters, codeArray)
Another obvious reason why the script would fail to compile is because you have it using methods and properties of your Form1 class, as if it were a member of that class. Remember, the Script class defined by the script file source code is a completely separate class in a separate assembly. It will have no reference to the form unless you give it a reference, for instance, you could define the interface like this:
Public Interface IScript
Sub DoWork(f As Form1)
End Interface
Then, in your script, you could do this:
Public Class Script
Implements IScript
Public Sub DoWork(f As Form1) Implements IScript.DoWork
f.WebBrowser1.Navigate("http://www.google.com")
f.wait("5000")
f.wait("4000")
f.WebBrowser1.Document.All("input").InvokeMember("click")
f.WebBrowser1.Document.All("input").SetAttribute("value", "User")
f.wait("2000")
f.WebBrowser1.Document.All("421").InvokeMember("click")
End Sub
End Class
UPDATE
Ok, since you can't get it working, and I don't want this whole conversation to be a total loss, I put together a working project and tested it. Here's what you need to do to get it to work.
Contents of IScript.vb
Public Interface IScript
Sub DoWork(w As WebBrowser)
End Interface
Contents of Form1.vb
Imports Microsoft.VisualBasic
Imports System.CodeDom.Compiler
Imports System.Reflection
Imports System.IO
Imports System.Text
Public Class Form1
Dim int1 As Integer = 0
Dim int2 As Integer = 0
Dim p As Point
Public Function GenerateScript(ByVal code As String) As IScript
Using provider As New VBCodeProvider()
Dim parameters As New CompilerParameters()
parameters.GenerateInMemory = True
parameters.ReferencedAssemblies.Add(GetType(WebBrowser).Assembly.Location)
parameters.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location)
Dim interfaceNamespace As String = GetType(IScript).Namespace
code = "Imports System.Windows.Forms" & Environment.NewLine & "Imports " & interfaceNamespace & Environment.NewLine & code
Dim results As CompilerResults = provider.CompileAssemblyFromSource(parameters, code)
If results.Errors.HasErrors Then
Dim builder As New StringBuilder()
builder.AppendLine("Script failed to compile due to the following errors:")
For Each i As CompilerError In results.Errors
builder.AppendFormat("Line {0}: {1}", i.Line, i.ErrorText)
builder.AppendLine()
Next
Throw New Exception(builder.ToString())
Else
Return CType(results.CompiledAssembly.CreateInstance("Script"), IScript)
End If
End Using
End Function
Public Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
For Each File As FileInfo In New System.IO.DirectoryInfo(Application.StartupPath & "/scripts").GetFiles
If CheckedListBox1.GetItemCheckState(int2) = CheckState.Checked Then
ListBox1.Items.Add(File.FullName)
End If
int2 = int2 + 1
Next
int2 = 0
Dim script As IScript = GenerateScript(File.ReadAllText(ListBox1.Items.Item(int2).ToString()))
script.DoWork(WebBrowser1)
End Sub
End Class
Contents of script file
Imports System.Diagnostics
Public Class Script
Implements IScript
Public Sub DoWork(w As WebBrowser) Implements IScript.DoWork
w.Navigate("http://www.google.com")
wait("5000")
wait("4000")
w.Document.All("input").InvokeMember("click")
w.Document.All("input").SetAttribute("value", "User")
wait("2000")
w.Document.All("421").InvokeMember("click")
End Sub
Public Sub wait(ByVal interval As Integer)
Dim sw As New Stopwatch
sw.Start()
Do While sw.ElapsedMilliseconds < interval
' Allows UI to remain responsive
Application.DoEvents()
Loop
sw.Stop()
End Sub
End Class

paypal API in VB.net

Hey all, i have converted some C# PayPal API Code over to VB.net. I have added that code to a class within my project but i can not seem to access it:
Imports System
Imports com.paypal.sdk.services
Imports com.paypal.sdk.profiles
Imports com.paypal.sdk.util
Namespace GenerateCodeNVP
Public Class GetTransactionDetails
Public Sub New()
End Sub
Public Function GetTransactionDetailsCode(ByVal transactionID As String) As String
Dim caller As New NVPCallerServices()
Dim profile As IAPIProfile = ProfileFactory.createSignatureAPIProfile()
profile.APIUsername = "xxx"
profile.APIPassword = "xxx"
profile.APISignature = "xxx"
profile.Environment = "sandbox"
caller.APIProfile = profile
Dim encoder As New NVPCodec()
encoder("VERSION") = "51.0"
encoder("METHOD") = "GetTransactionDetails"
encoder("TRANSACTIONID") = transactionID
Dim pStrrequestforNvp As String = encoder.Encode()
Dim pStresponsenvp As String = caller.[Call](pStrrequestforNvp)
Dim decoder As New NVPCodec()
decoder.Decode(pStresponsenvp)
Return decoder("ACK")
End Function
End Class
End Namespace
I am using this to access that class:
Private Sub cmdGetTransDetail_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdGetTransDetail.Click
Dim thereturn As String
thereturn =GetTransactionDetailsCode("test51322")
End Sub
But it keeps telling me:
Error 2 Name 'GetTransactionDetailsCode' is not declared.
I'm new at calling classes in VB.net so any help would be great! :o)
David
Solved
Dim payPalAPI As New GenerateCodeNVP.GetTransactionDetails
Dim theReturn As String
theReturn = payPalAPI.GetTransactionDetailsCode("test51322")
You're probably getting that error because you need to call it like this:
Private Sub cmdGetTransDetail_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdGetTransDetail.Click
Dim thereturn As String
Dim myTransaction as new GetTransactionDetails
thereturn = myTransaction.GetTransactionDetailsCode("test51322")
End Sub
Or you can make the function be a public shared function and access it as if it were a static method
Your GetTransactionDetailsCode method is an instance method of the GetTransactionDetails class, which means that you need an instance of the GetTransactionDetails class in order to call the method.
You can do that like this:
Dim instance As New GetTransactionDetails()
thereturn = instance.GetTransactionDetailsCode("test51322")
However, your method doesn't actually use the class instance, so you should change your GetTransactionDetails class to a Module instead.