Get Result of Private Property TcpClient.BeginConnect, IAsyncResult in VB.NET - vb.net

I have an application in VB.NET When I run the application in Visual Studio 2010 and mouseover an IAsyncResult, I see the protected property Result. I would like to read the value of the property in the application. How can I do that?
Imports System.Net
Imports System.Net.Sockets
...
Friend Function StartSendGo() As String
'Declarations
Dim strSendMachineName As String = "DEV001"
Dim intSendPort As Integer = 50035
Dim socketclient As New System.Net.Sockets.TcpClient()
Dim rslt As IAsyncResult = tcpClient.BeginConnect(strSendMachineName, intSendPort, New AsyncCallback(AddressOf ConnectCallback), socketclient)
Dim blnSuccess = rslt.AsyncWaitHandle.WaitOne(intTimeOutConnect, True)
'HERE is where I need rslt.Result.Message
End Function
Public Function ConnectCallback()
'Placeholder
End Function
When I mouseover rslt, VS shows that it is of type
System.Net.Sockets.Socket+MultipleAddressConnectAsyncResult I have never seen a plus (+) in a type before, and I am not able to declare a variable of that type. If I expand the properties, there is a protected property Result, which has a property Message with a value of "No connection could be made because the target machine actively refused it 192.0.0.10:50035". I need access to that message. I would also like to access addresses, but that is less important.

I found a solution - to use Reflection to read the value of the private property.
'Imports
Imports System.Reflection
'Call functions that write to rslt
rslt = tcpClient.BeginConnect(strSendMachineName, intSendPort, New AsyncCallback(AddressOf ConnectCallback), socketclient)
blnSuccess = rslt.AsyncWaitHandle.WaitOne(intTimeOutConnect, True)
'Use Reflection
'Get Type
Dim myType As Type = rslt.GetType()
'Get properties
Dim myPropertyInfo As PropertyInfo() = myType.GetProperties((BindingFlags.NonPublic Or BindingFlags.Instance))
'The order of the properties is not guaranteed. Find by name.
For Each pi As PropertyInfo In myPropertyInfo
If pi.Name = "Result" Then
'TODO Add check for nothing.
'Assign to Exception-type variable.
exException = pi.GetValue(rslt, Nothing)
End If
Next

Related

How do I save objects in Configuration of Winforms application?

I'm trying to implement functionality to allow the user to select his favorite forms. Favorite forms are forms he/she needs quick access to. To avoid browsing for too long through the ToolStripMenu's.
I try to save a reference to a form in the application configuration. But I'm getting the error
Value of type 'System.Windows.Forms.Form' cannot be converted to
'String'.
Public Sub SetSetting(ByVal pstrKey As String, ByVal frmFavorite As Form)
Dim keyExists As Boolean = False
For Each strKey As String In configuration.AppSettings.Settings.AllKeys
If strKey.Equals(pstrKey) Then
configuration.AppSettings.Settings.Item(pstrKey).Value = frmFavorite
keyExists = True
End If
Next
If Not keyExists Then
configuration.AppSettings.Settings.Add(pstrKey, frmFavorite)
End If
configuration.Save(ConfigurationSaveMode.Modified)
ConfigurationManager.RefreshSection("appSettings")
End Sub
You can only store string values in the application config file, no objects.
But just store the name of the Form in the config file.
When starting your application create the form via reflection like shown in this Object Factory example.
Public Class ObjectFactory
Public Shared Function CreateAnObject(ByVal ObjectName As String) As Object
Dim Assem = [Assembly].GetExecutingAssembly()
Dim myType As Type = Assem.GetType(ObjectName.Trim)
Dim o As Object = Nothing
Try
o = Activator.CreateInstance(myType)
Catch oEx As TargetInvocationException
MessageBox.Show(oEx.ToString)
End Try
Return o
End Function
End Class
...
Dim formName as String = configuration.AppSettings.Settings.Item(<YourSettingKey>)
Dim oForm As Form = _
ObjectFactory.CreateAnObject(formName)

Entity Framework : Why this code doesn't work

I'm using Entity Framework 6.0, DbContext. I'm using this method to copy an object and some related children:
Imports System.Data.Objects
Imports System.Data.Objects.DataClasses
Imports System.Runtime.CompilerServices
Public Module Entities
<Extension()>
Public Function CloneEntity(Of T As Class)(entity As T, context As ObjectContext, Optional include As List(Of IncludeEntity) = Nothing, Optional copyKeys As Boolean = False) As T
Return CloneEntityHelper(entity, context, include, copyKeys)
End Function
Private Function CloneEntityHelper(Of T As Class)(entity As T, context As ObjectContext, Optional include As List(Of IncludeEntity) = Nothing, Optional copyKeys As Boolean = False) As T
If include Is Nothing Then include = New List(Of IncludeEntity)()
Dim myType = entity.GetType()
Dim methodInfo = context.GetType().GetMethod("CreateObject").MakeGenericMethod(myType)
Dim result = methodInfo.Invoke(context, Nothing)
Dim propertyInfo = entity.GetType().GetProperties()
For Each info In propertyInfo
Dim attributes = info.GetCustomAttributes(GetType(EdmScalarPropertyAttribute), False).ToList()
For Each attr As EdmScalarPropertyAttribute In attributes
If (Not copyKeys) AndAlso attr.EntityKeyProperty
Continue For
End If
info.SetValue(result, info.GetValue(entity, Nothing), Nothing)
Next
If info.PropertyType.Name.Equals("EntityCollection`1", StringComparison.OrdinalIgnoreCase) Then
Dim shouldInclude = include.SingleOrDefault(Function(i) i.Name.Equals(info.Name, StringComparison.OrdinalIgnoreCase))
If shouldInclude Is Nothing Then Continue For
Dim relatedChildren = info.GetValue(entity, Nothing)
Dim propertyType As Type = relatedChildren.GetType().GetGenericArguments().First()
Dim genericType As Type = GetType(EntityCollection(Of ))
Dim boundType = genericType.MakeGenericType(propertyType)
Dim children = Activator.CreateInstance(boundType)
For Each child In relatedChildren
Dim cloneChild = CloneEntityHelper(child, context, shouldInclude.Children, shouldInclude.CopyKeys)
children.Add(cloneChild)
Next
info.SetValue(result, children, Nothing)
End If
Next
Return result
End Function
Public Class IncludeEntity
Public Property Name As String
Public Property Children As New List(Of IncludeEntity)
Public Property CopyKeys As Boolean
Public Sub New(propertyName As String, ParamArray childNodes() As String)
Name = propertyName
Children = childNodes.Select(Function(n) new IncludeEntity(n)).ToList()
End Sub
End Class
End Module
Now I'm using the code like below :
Dim litm, newitm As New MyObject
Dim inc = New List(Of IncludeEntity)()
inc.Add(New IncludeEntity("Child_list"))
litm=context.MyObjects.FirstOrDefault
newitm = litm.CloneEntity(CType(context, Entity.Infrastructure.IObjectContextAdapter).ObjectContext,include:=inc)
The code is executed without errors, but nothing is copied, so newitm is empty.
I have inspected the code and found that this line on the CloneEntity function :
Dim myType = entity.GetType()
Is producing a strange type.
I'm expecting that the type will be of MyObject type, but instead this return :
MyObject_F2FFE64DA472EB2B2BDF7E143DE887D3845AD9D1731FD3107937062AC0C2E4BB
This line too :
Dim result = methodInfo.Invoke(context, Nothing)
produces the same strange type.
I don't know if this is the problem, but this is the only strange thing I have noticed.
Can you help me to find out why this code doesn't work?
Thank you!
Entity framework, like many other ORMs will build a proxy type for your entities so that it can intercept calls to:
Lazy load the contents of any collection contained as properties within your entity, when you access those collection properties.
Detect that you have made changes to the properties of an instance as part of dirty checking, so that it will know which objects are dirty and need to be saved to the database when you invoke SaveChanges.
Refer for example to EF returning proxy class instead of actual entity or Working with Proxies.
If you want to find out the underlying type of your entity that is wrapped by the proxy, i.e. the one that would match with the type you are looking for (e.g. MyObject), you can do that using a method in the object context:
var underlyingType = ObjectContext.GetObjectType(entity.GetType());

Calling System.IO.ReadAllBytes by string name

This post is related to Visual Basic .NET 2010
So, I'm wondering if there's any way to call a function from a library such as System.ReadAllBytes by string name.
I've been trying Assembly.GetExecutingAssembly().CreateInstance and System.Activator.CreateInstance followed by CallByName(), but none of them seemed to work.
Example of how I tried it:
Dim Inst As Object = Activator.CreateInstance("System.IO", False, New Object() {})
Dim Obj As Byte() = DirectCast(CallByName(Inst, "ReadAllBytes", CallType.Method, new object() {"C:\file.exe"}), Byte())
Help is (as always) much appreciated
It is System.IO.File.ReadAllBytes(), you missed the "File" part. Which is a Shared method, the CallByName statement is not flexible enough to permit calling such methods. You will need to use the more universal Reflection that's available in .NET. Which looks like this for your specific example, spelled out for clarity:
Imports System.Reflection
Module Module1
Sub Main()
Dim type = GetType(System.IO.File)
Dim method = type.GetMethod("ReadAllBytes")
Dim result = method.Invoke(Nothing, New Object() {"c:\temp\test.bin"})
Dim bytes = DirectCast(result, Byte())
End Sub
End Module

.Net Dynamically Load DLL

I am trying to write some code that will allow me to dynamically load DLLs into my application, depending on an application setting. The idea is that the database to be accessed is set in the application settings and then this loads the appropriate DLL and assigns it to an instance of an interface for my application to access.
This is my code at the moment:
Dim SQLDataSource As ICRDataLayer
Dim ass As Assembly = Assembly. _
LoadFrom("M:\MyProgs\WebService\DynamicAssemblyLoading\SQLServer\bin\Debug\SQLServer.dll")
Dim obj As Object = ass.CreateInstance(GetType(ICRDataLayer).ToString, True)
SQLDataSource = DirectCast(obj, ICRDataLayer)
MsgBox(SQLDataSource.ModuleName & vbNewLine & SQLDataSource.ModuleDescription)
I have my interface (ICRDataLayer) and the SQLServer.dll contains an implementation of this interface. I just want to load the assembly and assign it to the SQLDataSource object.
The above code just doesn't work. There are no exceptions thrown, even the Msgbox doesn't appear.
I would've expected at least the messagebox appearing with nothing in it, but even this doesn't happen!
Is there a way to determine if the loaded assembly implements a specific interface. I tried the below but this also doesn't seem to do anything!
For Each loadedType As Type In ass.GetTypes
If GetType(ICRDataLayer).IsAssignableFrom(loadedType) Then
Dim obj1 As Object = ass.CreateInstance(GetType(ICRDataLayer).ToString, True)
SQLDataSource = DirectCast(obj1, ICRDataLayer)
End If
Next
EDIT: New code from Vlad's examples:
Module CRDataLayerFactory
Sub New()
End Sub
' class name is a contract,
' should be the same for all plugins
Private Function Create() As ICRDataLayer
Return New SQLServer()
End Function
End Module
Above is Module in each DLL, converted from Vlad's C# example.
Below is my code to bring in the DLL:
Dim SQLDataSource As ICRDataLayer
Dim ass As Assembly = Assembly. _
LoadFrom("M:\MyProgs\WebService\DynamicAssemblyLoading\SQLServer\bin\Debug\SQLServer.dll")
Dim factory As Object = ass.CreateInstance("CRDataLayerFactory", True)
Dim t As Type = factory.GetType
Dim method As MethodInfo = t.GetMethod("Create")
Dim obj As Object = method.Invoke(factory, Nothing)
SQLDataSource = DirectCast(obj, ICRDataLayer)
EDIT: Implementation based on Paul Kohler's code
Dim file As String
For Each file In Directory.GetFiles(baseDir, searchPattern, SearchOption.TopDirectoryOnly)
Dim assemblyType As System.Type
For Each assemblyType In Assembly.LoadFrom(file).GetTypes
Dim s As System.Type() = assemblyType.GetInterfaces
For Each ty As System.Type In s
If ty.Name.Contains("ICRDataLayer") Then
MsgBox(ty.Name)
plugin = DirectCast(Activator.CreateInstance(assemblyType), ICRDataLayer)
MessageBox.Show(plugin.ModuleName)
End If
Next
I get the following error with this code:
Unable to cast object of type 'SQLServer.CRDataSource.SQLServer' to type 'DynamicAssemblyLoading.ICRDataLayer'.
The actual DLL is in a different project called SQLServer in the same solution as my implementation code. CRDataSource is a namespace and SQLServer is the actual class name of the DLL.
The SQLServer class implements ICRDataLayer, so I don't understand why it wouldn't be able to cast it.
Is the naming significant here, I wouldn't have thought it would be.
Final Working code
Contents of PluginUtility:
enter code here Public Shared Function GetInstances1(Of Type)(ByVal baseDir As String, ByVal searchPattern As String) As System.Type()
Dim tmpInstances As New List(Of Type)
Try
Dim file As String
For Each file In Directory.GetFiles(baseDir, searchPattern, SearchOption.TopDirectoryOnly)
Dim assemblyType As System.Type
For Each assemblyType In Assembly.LoadFrom(file).GetTypes
Dim s As System.Type() = assemblyType.GetInterfaces
Return s.ToArray()
Next
Next
Catch exp As TargetInvocationException
If (Not exp.InnerException Is Nothing) Then
Throw exp.InnerException
End If
End Try
End Function
Code to load the DLL:
enter code here
Dim basedir As String = "M:\MyProgs\WebService\DynamicAssemblyLoading\SQLServer\bin\Debug\"
Dim searchPattern As String = "*SQL*.dll"
Dim plugin As CRDataLayer.ICRDataLayer
Try
Dim file As String
For Each file In Directory.GetFiles(baseDir, searchPattern, SearchOption.TopDirectoryOnly)
Dim assemblyType As System.Type
For Each assemblyType In Assembly.LoadFrom(file).GetExportedTypes
If assemblyType.GetInterface("CRDataLayer.ICRDataLayer") IsNot Nothing Then
plugin = DirectCast(Activator.CreateInstance(assemblyType), CRDataLayer.ICRDataLayer)
MessageBox.Show(plugin.ModuleDescription)
End If
Next
Next
Catch exp As TargetInvocationException
If (Not exp.InnerException Is Nothing) Then
Throw exp.InnerException
End If
Catch ex As Exception
MsgBox(ex.Message)
Clipboard.SetText(ex.Message)
End Try
Version 2 - This sample loads up a DLL from it current directory.
There are 2 projects, 1 console application project and a "module" project (the module 'coppies' its DLL to the working directory of the console app).
The sample below simply demonstrates dynamically loading a DLL that implements an interface. The IModule interface just reports its name. PlugInUtility.GetInstances(Of IModule)(Environment.CurrentDirectory, "*.Module.dll") will create an instance of any IModule instance found in a DLL within the current directory ending with ".Module.dll". It's a reflected VB.NET version straight out of Mini SQL Query.
With that in mind something like:
Dim modules As IModule() = PlugInUtility.GetInstances(Of ICRDataLayer)(Environment.CurrentDirectory, "*.Server.dll")
Should satisfy your requirement. Then you just need to chose which one to execute!
The code:
In "VB.LoaderDemo Colsole App"
' IModule.vb
Public Interface IModule
Property ModuleName() As String
End Interface
' PlugInUtility.vb
Imports System.IO
Imports System.Reflection
Public Class PlugInUtility
Public Shared Function GetInstances(Of T)(ByVal baseDir As String, ByVal searchPattern As String) As T()
Dim tmpInstances As New List(Of T)
Try
Dim file As String
For Each file In Directory.GetFiles(baseDir, searchPattern, SearchOption.TopDirectoryOnly)
Dim assemblyType As Type
For Each assemblyType In Assembly.LoadFrom(file).GetTypes()
If (Not assemblyType.GetInterface(GetType(T).FullName) Is Nothing) Then
tmpInstances.Add(DirectCast(Activator.CreateInstance(assemblyType), T))
End If
Next
Next
Catch exp As TargetInvocationException
If (Not exp.InnerException Is Nothing) Then
Throw exp.InnerException
End If
End Try
Return tmpInstances.ToArray()
End Function
End Class
' MainModule.vb
Module MainModule
Sub Main()
Dim plugins As IModule() = PlugInUtility.GetInstances(Of IModule)(Environment.CurrentDirectory, "*.Module.dll")
Dim m As IModule
For Each m In plugins
Console.WriteLine(m.ModuleName)
Next
End Sub
End Module
In "Sample1 DLL" (references 'VB.LoaderDemo' for IModule)
Imports VB.LoaderDemo
Public Class MyModule1
Implements IModule
Dim _name As String
Public Sub New()
_name = "Sample 1, Module 1"
End Sub
Public Property ModuleName() As String Implements IModule.ModuleName
Get
Return _name
End Get
Set(ByVal value As String)
_name = value
End Set
End Property
End Class
The output is:
> Sample 1, Module 1
Is the type ICRDataLayer defined in the DLL you are going to load? If so, you seem to already reference the DLL in your project settings.
You need to work with just reflection:
Dim obj As Object = ass.CreateInstance("ICRDataLayer", True)
Dim t as Type = obj.GetType()
Dim method as MethodInfo = t.GetMethod("DoSomething")
method.Invoke(obj, ...)
Edit: If ICRDataLayer is implemented in the application, and the plugin just implements the interface, you need the plugin to provide a factory for you: (sorry for C# code, I am not familiar with VB.NET's syntax)
// in each of plugins:
static class CRDataLayerFactory // class name is a contract,
{ // should be the same for all plugins
static ICRDataLayer Create()
{
return new CRDataLayerImplementation();
}
}
The application's code should look like this:
Dim factory As Object = ass.CreateInstance("CRDataLayerFactory", True)
Dim t as Type = factory.GetType()
Dim method as MethodInfo = t.GetMethod("Create")
Dim obj as Object = method.Invoke(factory, null)
SQLDataSource = DirectCast(obj, ICRDataLayer)
A few things to look for in your code
Debug through and check that the
assembly is loaded correctly, in case
it fails due to dependency checking
Instead of using GetType, use GetExportedType so you have smaller subset to iterate through
The CreateInstance should use your loadedType rather than the interface (you cant create object from an interface)
Personally, I dont like naming my variable ass, I would shorten it to assem instead :)

What did VB replace the function "Set" with?

I've found several aspx codes for forms which include the use of a "Set" function. When I try them out on the hosting server, I get an error message that "Set is no longer supported". Anyone know what replaced the "Set" command?
More specifically, how do I change this:
Dim mail
Set mail = Server.CreateObject("CDONTS.NewMail")
mail.To = EmailTo
mail.From = EmailFrom
mail.Subject = Subject
mail.Body = Body
mail.Send
to be VB.NET compatible?
If you mean the VB6 syntax
Set obj = new Object
then you can simply remove the Set
obj = new Object()
Set is a keyword in VB6, with the intrudction of VB.NET the keyword, as used in this context, was removed.
Formerly, Set was used to indicate that an object reference was being assigned (Let was the default). Because default properties no longer are supported unless they accept parameters, these statements have been removed.
Module Module1
Sub Main()
Dim person As New Person("Peter")
Dim people As New People()
people.Add(person)
'Use the default property, provided we have a parameter'
Dim p = people("Peter")
End Sub
End Module
Public Class People
Private _people As New Dictionary(Of String, Person)
Public Sub Add(ByVal person As Person)
_people.Add(person.Name, person)
End Sub
Default Public ReadOnly Property Person(ByVal name As String) As Person
Get
Return _people(name)
End Get
End Property
End Class
Public Class Person
Private _name As String
Public Sub New(ByVal name As String)
_name = name
End Sub
Public ReadOnly Property Name() As String
Get
Return _name
End Get
End Property
End Class
Some things to remember for .Net:
NEVER use Server.CreateObject() in .Net code. Ever.
NEVER Dim a variable without giving it an explicit type. Except for new Option Infer linq types
NEVER use the Set keyword. Except when defining a property.
In fact, in .Net you can get rid probably of the CDONTS dependancy entirely, as .Net has a built-in mail support:
Dim smtp As New System.Net.SmtpClient()
Dim message As New System.Net.MailMessage(EmailFrom, EmailTo, Subject, Body)
smtp.Send(message)