Create class method with already existing name - vba

I'm working on some code working with MS Access database in VBA 6. My goal is to make this same code working with PostgreSQL too (this means it should be able to work with both) . The code is working with DAO.DBEngine but I've not found a way to make this class connect to a PostgreSQL database. My solution is to create an other class with the same methods as DAO.DBEngine, for example the original code is using TableDefs and when I'm create a function called TableDefs I got "ambiguous name detected". Is there any way to name two methods class the same or to suppress/avoid this "ambiguous name detected" error ?

Based on the information provided in the link you could create three classes
IDatabase
Option Explicit
Function Hello()
End Function
clsDBAccess
Option Explicit
Implements IDatabase
Function IDatabase_Hello()
MsgBox TypeName(Me)
End Function
clsPostSQL
Implements IDatabase
Function IDatabase_Hello()
MsgBox TypeName(Me)
End Function
And you could use it like that
Option Explicit
Sub Main()
Dim myType As String
Dim oDatbase As IDatabase
' Determine myType
myType = "Access"
' myType = "PostSQL"
Set oDatbase = ClassFactory(myType)
' Your code here
oDatbase.Hello
End Sub
Function ClassFactory(dbType As String) As IDatabase
Dim oDatabase As IDatabase
If dbType = "Access" Then
Set oDatabase = New clsDBAccess
ElseIf dbType = "PostSQL" Then
Set oDatabase = New clsPostSQL
End If
Set ClassFactory = oDatabase
End Function

Related

Compile error: Only user-defined types defined in public object modules can be coerced to or from a variant or passed to late-bound functions

I'm struggling with a little bit of VBa and Excel. I need to create a structure in VBa, which is a Type. The problem I have is, I get an error message when I try to execute the code! I feel I need to explain how I have arrived where I am in case I've made an error.
I have read that to create a type, it needs to be made public. As such I created a new Class (under Class Modules). In Class1, I wrote
Public Type SpiderKeyPair
IsComplete As Boolean
Key As String
End Type
And within ThisWorkbook I have the following
Public Sub Test()
Dim skp As SpiderKeyPair
skp.IsComplete = True
skp.Key = "abc"
End Sub
There is no other code. The issue I have is I get the error message
Cannot define a public user-defined type within an object module
If I make the type private I don't get that error, but of course I can't access any of the type's properties (to use .NET terminology).
If I move the code from Class1 into Module1 it works, but, I need to store this into a collection and this is where it's gone wrong and where I am stuck.
I've updated my Test to
Private m_spiderKeys As Collection
Public Sub Test()
Dim sKey As SpiderKeyPair
sKey.IsComplete = False
sKey.Key = "abc"
m_spiderKeys.Add (sKey) 'FAILS HERE
End Sub
Only user-defined types defined in public object modules can be coerced to or from a variant or passed to late-bound functions
I have looked into this but I don't understand what it is I need to do... How do I add the SpiderKeyPair to my collection?
Had the exact same problem and wasted a lot of time because the error information is misleading. I miss having List<>.
In Visual Basic you can't really treat everything as an object. You have Structures and Classes which have a difference at memory allocation: https://learn.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/data-types/structures-and-classes
A Type is a structure (so are Arrays), so you if you want a "List" of them you better use an Array and all that comes with it.
If you want to use a Collection to store a "List", you need to create a Class for the object to be handled.
Not amazing... but it is what the language has available.
You seem to be missing basics of OOP or mistaking VBA and VB.NET. Or I do not understand what are you trying to do. Anyhow, try the following:
In a module write this:
Option Explicit
Public Sub Test()
Dim skpObj As SpiderKeyPair
Dim m_spiderKeys As New Collection
Dim lngCounter As Long
For lngCounter = 1 To 4
Set skpObj = New SpiderKeyPair
skpObj.Key = "test" & lngCounter
skpObj.IsComplete = CBool(lngCounter Mod 2 = 0)
m_spiderKeys.Add skpObj
Next lngCounter
For Each skpObj In m_spiderKeys
Debug.Print "-----------------"
Debug.Print skpObj.IsComplete
Debug.Print skpObj.Key
Debug.Print "-----------------"
Next skpObj
End Sub
In a class, named SpiderKeyPair write this:
Option Explicit
Private m_bIsComplete As Boolean
Private m_sKey As String
Public Property Get IsComplete() As Boolean
IsComplete = m_bIsComplete
End Property
Public Property Get Key() As String
Key = m_sKey
End Property
Public Property Let Key(ByVal sNewValue As String)
m_sKey = sNewValue
End Property
Public Property Let IsComplete(ByVal bNewValue As Boolean)
m_bIsComplete = bNewValue
End Property
When you run the Test Sub in the module you get this:
Falsch
test1
-----------------
-----------------
Wahr
test2
Pay attention to how you initialize new objects. It happens with the word New. Collections are objects and should be initialized as well with New.

How to call a function which name is stored in a sql table (VB.net)

Maybe someone of you can help me with that problem.
I have written a background task which gets several workers out of a database.
In the database I added to each worker the name of the function which should get called.
But I am not sure how to call that function from vb.net.
It would be awesome if someone of you can give me a hint :)
thanks
Cheers
Arthur
The namespace System.Reflection has numerous methods that enable this functionality, such as this one:
From MSDN, the example in the link above:
Imports System
Imports System.Reflection
Public Class MagicClass
Private magicBaseValue As Integer
Public Sub New()
magicBaseValue = 9
End Sub
Public Function ItsMagic(preMagic As Integer) As Integer
Return preMagic * magicBaseValue
End Function
End Class
Public Class TestMethodInfo
Public Shared Sub Main()
' Get the constructor and create an instance of MagicClass
Dim magicType As Type = Type.GetType("MagicClass")
Dim magicConstructor As ConstructorInfo = magicType.GetConstructor(Type.EmptyTypes)
Dim magicClassObject As Object = magicConstructor.Invoke(New Object(){})
' Get the ItsMagic method and invoke with a parameter value of 100
Dim magicMethod As MethodInfo = magicType.GetMethod("ItsMagic")
Dim magicValue As Object = magicMethod.Invoke(magicClassObject, New Object(){100})
Console.WriteLine("MethodInfo.Invoke() Example" + vbNewLine)
Console.WriteLine("MagicClass.ItsMagic() returned: {0}", magicValue)
End Sub
End Class
' The example program gives the following output:
'
' MethodInfo.Invoke() Example
'
' MagicClass.ItsMagic() returned: 900
I would suggest having a dictionary of delegates in your application code. Store the keys in the database, rather than the function names. When you retrieve the key from the database, look it up in the dictionary and, if present, execute it. You don't want to allow arbitrary methods to be executed based on names stored in a database.
They keys could be strings or integers. I'd prefer the latter just for space savings and ease of lookup, but strings would be easier for debugging, perhaps. So you'd have a dictionary like this:
Private m_WorkerDelegates As New Dictionary(Of String, Action)()
Somewhere else, you'd fill it up with the available workers:
m_WorkerDelegates.Add("worker1", AddressOf WorkerMethod1)
m_WorkerDelegates.Add("worker2", AddressOf WorkerMethod2)
And then, when retrieving from the database, you'd look up the method in your dictionary:
Public Sub ExecuteWorker(ByVal row As DataRow)
Dim key As String = CStr(row("worker_key"))
If Not m_WorkerDelegates.ContainsKey(key) Then
' either throw exception or report the error in some more effective way '
Throw New Exception("Invalid worker key specified")
End If
' actually call the worker method '
m_WorkerDelegates(key)()
End Sub

How can I allow all subroutines within my program to access a certain variable?

In the code below, I want to be able to access the enteredusername and enteredpassword variables from any sub routine. How would I accomplish this?
Using rdr As New FileIO.TextFieldParser("f:\Computing\Spelling Bee\stdnt&staffdtls.csv")
rdr.TextFieldType = FieldType.Delimited
rdr.Delimiters = New String() {","c}
item = rdr.ReadFields()
End Using
Console.Write("Username: ")
enteredusername = Console.ReadLine
Console.Write("Password: ")
Dim info As ConsoleKeyInfo
Do
info = Console.ReadKey(True)
If info.Key = ConsoleKey.Enter Then
Exit Do
End If
If info.Key = ConsoleKey.Backspace AndAlso enteredpassword.Length > 0 Then
enteredpassword = enteredpassword.Substring(0, enteredpassword.Length - 1)
Console.Write(vbBack & " ")
Console.CursorLeft -= 1
Else
enteredpassword &= info.KeyChar
Console.Write("*"c)
End If
Loop
Dim foundItem() As String = Nothing
For Each line As String In File.ReadAllLines("f:\Computing\Spelling Bee\stdnt&staffdtls.csv")
Dim item() As String = line.Split(","c)
If (enteredusername = item(0)) And (enteredpassword = item(1)) Then
foundItem = item
Exit For
End If
Next
To allow ALL classes within your program access the variable, you need to make it class-level and define it with Public and Shared.
Demonstration:
Public Class MainClass
Public Shared enteredusername As String
Public Shared enteredpassword As String
Private Sub SomeSub()
' Some Code ...
' You can access it here:
enteredusername = "something"
enteredpassword = "something else"
' ... More Code ...
End Sub
End Class
Public Class AnotherClass
'Also, please note, that this class can also be in another file.
Private Sub AnotherSub()
' Some Code ...
' You can also access the variable here, but you need to specify what class it is from, like so:
Console.WriteLine(MainClass.enteredusername)
Console.WriteLine(MainClass.enteredpassword)
' ... More Code ...
End Sub
End Class
Also, on a separate note, the Public and Shared modifiers can also be used on methods. If you make a method Private or don't specify anything, the method will only be accessible from methods in the same class. If you use only Public, other classes can access the method, but they will need to create a instance of the class, like so:
Dim AC As New AnotherClass
AC.AnotherSub()
If you use both the Public and the Shared modifiers, other classes will be able to access the method directly, without creating a new instance. But, you must note, that Shared methods cannot access non-Shared methods or variables. Other classes can access Public Shared methods like so:
AnotherClass.AnotherSub()
It depends on the scope. If you want all of the subroutines in the current class to be able to access them then make them a field of the class
Class TheClassName
Dim enteredusername As String
Dim enteredpassword As String
...
End Class
If you want all subroutines in all classes and modules to be able to access them then make them a module level field
Module TheModuleName
Dim enteredusername As String
Dim enteredpassword As String
...
End Module
I recommend against this approach though. Sure it's easier in the short term because it requires less ceremony and thought on the uses of the values. But long term it serves to reduce the maintainability of your code base

Can't use Type in VB

Is there anything wrong with the following code ? It failed on Form_Load() line , and complains about it.
Private Sub Form_Load()
Type Human
Name As String
End Type
Dim stu As Student
With Human:
.Name = "Someone"
End With
Debug.Print ("Name: " & stu.Name)
End Sub
You have two options:
1
Create a new class
Private Class Human
Public Name As String
End Class
(Obviously it would be better to wrap the Name in a public property, but for simplicity, exposing it as a public variable is easier.)
2
Create a new struct:
Structure Human
Dim Name As String
End Structure
Note
It should be noted that both of these options must be done outside of the function, not within Form_Load function
The keyword is no longer Type; it is Structure now. Type was used in VB6 and earlier, but not in .NET.

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)