I've inherited a large VB6 app at my current workplace. I'm kinda learning VB6 on the job and there are a number of problems I'm having. The major issue at the moment is I can't figure out how to check if a key exists in a Collection object. Can anyone help?
My standard function is very simple. This will work regardless of the element type, since it doesn't bother doing any assignment, it merely executes the collection property get.
Public Function Exists(ByVal oCol As Collection, ByVal vKey As Variant) As Boolean
On Error Resume Next
oCol.Item vKey
Exists = (Err.Number = 0)
Err.Clear
End Function
#Mark Biek Your keyExists closely matches my standard Exists() function. To make the class more useful for COM-exposed collections and checking for numeric indexes, I'd recommend changing sKey and myCollection to not be typed. If the function is going to be used with a collection of objects, 'set' is required (on the line where val is set).
EDIT: It was bugging me that I've never noticed different requirements for an object-based and value-based Exists() function. I very rarely use collections for non-objects, but this seemed such a perfect bottleneck for a bug that would be so hard to track down when I needed to check for existence. Because error handling will fail if an error handler is already active, two functions are required to get a new error scope. Only the Exists() function need ever be called:
Public Function Exists(col, index) As Boolean
On Error GoTo ExistsTryNonObject
Dim o As Object
Set o = col(index)
Exists = True
Exit Function
ExistsTryNonObject:
Exists = ExistsNonObject(col, index)
End Function
Private Function ExistsNonObject(col, index) As Boolean
On Error GoTo ExistsNonObjectErrorHandler
Dim v As Variant
v = col(index)
ExistsNonObject = True
Exit Function
ExistsNonObjectErrorHandler:
ExistsNonObject = False
End Function
And to verify the functionality:
Public Sub TestExists()
Dim c As New Collection
Dim b As New Class1
c.Add "a string", "a"
c.Add b, "b"
Debug.Print "a", Exists(c, "a") ' True '
Debug.Print "b", Exists(c, "b") ' True '
Debug.Print "c", Exists(c, "c") ' False '
Debug.Print 1, Exists(c, 1) ' True '
Debug.Print 2, Exists(c, 2) ' True '
Debug.Print 3, Exists(c, 3) ' False '
End Sub
I've always done it with a function like this:
public function keyExists(myCollection as collection, sKey as string) as Boolean
on error goto handleerror:
dim val as variant
val = myCollection(sKey)
keyExists = true
exit sub
handleerror:
keyExists = false
end function
As pointed out by Thomas, you need to Set an object instead of Let. Here's a general function from my library that works for value and object types:
Public Function Exists(ByVal key As Variant, ByRef col As Collection) As Boolean
'Returns True if item with key exists in collection
On Error Resume Next
Const ERR_OBJECT_TYPE As Long = 438
Dim item As Variant
'Try reach item by key
item = col.item(key)
'If no error occurred, key exists
If Err.Number = 0 Then
Exists = True
'In cases where error 438 is thrown, it is likely that
'the item does exist, but is an object that cannot be Let
ElseIf Err.Number = ERR_OBJECT_TYPE Then
'Try reach object by key
Set item = col.item(key)
'If an object was found, the key exists
If Not item Is Nothing Then
Exists = True
End If
End If
Err.Clear
End Function
As also advised by Thomas, you can change the Collection type to Object to generalize this. The .Item(key) syntax is shared by most collection classes, so that might actually be useful.
EDIT Seems like I was beaten to the punch somewhat by Thomas himself. However for easier reuse I personally prefer a single function with no private dependencies.
Using the error handler to catch cases when the key does not exists in the Collection can make debugging with "break on all errors" option quite annoying. To avoid unwanted errors I quite often create a class which has the stored objects in a Collection and all keys in a Dictionary. Dictionary has exists(key) -function so I can call that before trying to get an object from the collection. You can only store strings in a Dictionary, so a Collection is still needed if you need to store objects.
The statement "error handling will fail if an error handler is already active" is only partly right.
You can have multiple error handlers within your routine.
So, one could accommodate the same functionality in only one function.
Just rewrite your code like this:
Public Function Exists(col, index) As Boolean
Dim v As Variant
TryObject:
On Error GoTo ExistsTryObject
Set v = col(index)
Exists = True
Exit Function
TryNonObject:
On Error GoTo ExistsTryNonObject
v = col(index)
Exists = True
Exit Function
ExistsTryObject:
' This will reset your Err Handler
Resume TryNonObject
ExistsTryNonObject:
Exists = False
End Function
However, if you were to only incorporate the code in the TryNonObject section of the routine, this would yield the same information.
It will succeed for both Objects, and non-objects.
It will speed up your code for non-objects, however, since you would only have to perform one single statement to assert that the item exists within the collection.
Better solution would be to write a TryGet function. A lot of the time you are going to be checking exists, and then getting the item. Save time by doing it at the same time.
public Function TryGet(key as string, col as collection) as Variant
on error goto errhandler
Set TryGet= col(key)
exit function
errhandler:
Set TryGet = nothing
end function
see
http://www.visualbasic.happycodings.com/Other/code10.html
the implementation here has the advantage of also optionally returning the found element, and works with object/native types (according to the comments).
reproduced here since the link is no longer available:
Determine if an item exists in a collection
The following code shows you how to determine if an item exists within a collection.
Option Explicit
'Purpose : Determines if an item already exists in a collection
'Inputs : oCollection The collection to test for the existance of the item
' vIndex The index of the item.
' [vItem] See Outputs
'Outputs : Returns True if the item already exists in the collection.
' [vItem] The value of the item, if it exists, else returns "empty".
'Notes :
'Example :
Function CollectionItemExists(vIndex As Variant, oCollection As Collection, Optional vItem As Variant) As Boolean
On Error GoTo ErrNotExist
'Clear output result
If IsObject(vItem) Then
Set vItem = Nothing
Else
vItem = Empty
End If
If VarType(vIndex) = vbString Then
'Test if item exists
If VarType(oCollection.Item(CStr(vIndex))) = vbObject Then
'Return an object
Set vItem = oCollection.Item(CStr(vIndex))
Else
'Return an standard variable
vItem = oCollection.Item(CStr(vIndex))
End If
Else
'Test if item exists
If VarType(oCollection.Item(Int(vIndex))) = vbObject Then
'Return an object
Set vItem = oCollection.Item(Int(vIndex))
Else
'Return an standard variable
vItem = oCollection.Item(Int(vIndex))
End If
End If
'Return success
CollectionItemExists = True
Exit Function
ErrNotExist:
CollectionItemExists = False
On Error GoTo 0
End Function
'Demonstration routine
Sub Test()
Dim oColl As New Collection, oValue As Variant
oColl.Add "red1", "KEYA"
oColl.Add "red2", "KEYB"
'Return the two items in the collection
Debug.Print CollectionItemExists("KEYA", oColl, oValue)
Debug.Print "Returned: " & oValue
Debug.Print "-----------"
Debug.Print CollectionItemExists(2, oColl, oValue)
Debug.Print "Returned: " & oValue
'Should fail
Debug.Print CollectionItemExists("KEYC", oColl, oValue)
Debug.Print "Returned: " & oValue
Set oColl = Nothing
End Sub
See more at: https://web.archive.org/web/20140723190623/http://visualbasic.happycodings.com/other/code10.html#sthash.MlGE42VM.dpuf
While looking for a function like this i designed it as following.
This should work with objects and non-objects without assigning new variables.
Public Function Exists(ByRef Col As Collection, ByVal Key) As Boolean
On Error GoTo KeyError
If Not Col(Key) Is Nothing Then
Exists = True
Else
Exists = False
End If
Exit Function
KeyError:
Err.Clear
Exists = False
End Function
Related
I am new to vb6 so might be obvious for some of you.I have a collection problem, trying to put items in a collection to then evaluate if the item exists and setting a button to be enabled or not.
The Code:
For Each vBookmark In lstAssign.SelBookmarks
'---------------------------------------
'filtering with agency code and crew code.
sAssignmentValue = lstAssign.columns("AgencyCode").Value & lstAssign.columns("CrewCode").Value
'Show/hide value depending on crew existance.
If Not ExistsStartLocation(colParameters, sValue) Then
bEnableMyButton = True
colParameters.Add (sValue)
Else
bEnableMyButton = False
End If
'----------------------------------------
Next
sAssignmentValue = ""
tbrMain.TbrButtonEnabled "XXX", bEnableMyButton
tbrMain.TbrButtonEnabled "YYY", bEnable
Set colStartLocationParameters = Nothing
Exit Sub
Private Function ExistsStartLocation(col As collection, index As Variant) As Boolean
On Error GoTo ErrHandler
Dim v As Variant
v = col(index)
ExistsStartLocation = True
Exit Function
ErrHandler:
ExistsStartLocation = False
End Function
The problem is at this moment is that I only have colParameters(index) accessible, so I can't access my collection with a value "123-ABC" directly. I do not want to add an integer index, I want to keep simply accessing by item value, but my exists method will always return false. therefore always disabling my button.
How does this works?
At first glance, you should have to do something like this:
Private Function ExistsStartLocation(col As collection, val As String) As Boolean
Dim blnFoundItem As Boolean = False
For index As Integer = 1 To col.Count
If col(index) = val Then
blnFoundItem = True
End If
Next
ExistsStartLocation = blnFoundItem
End Function
Looping the collection works but is not efficient. If you assign the optional Key value in the Add method you can also use that as the Index to the Item method. In your example it appears you are assigning a string to the collection so the Add method would look something like ...
colParameters.Add sValue, sValue
Be aware though that if you are adding duplicate values this won't work. The keys need to be unique.
With the the collection item's key populated you can use a function that leverages the err object. If you try to get a collection item by the key and it exists no error is thrown. If it does not exists err.number 5 is thrown. The new function would be something like this.
Public Function ItemExists(ByVal vCollection As Collection, ByVal vKey As String) As Boolean
Dim varItem As Variant
On Error Resume Next
varItem = vCollection.Item(vKey)
ItemExists = (Err.Number = 0)
End Function
I have a VB6 application where I am trying to avoid inserting a duplicate entry of a PIN number. But my code is always saving the duplicate entry.
Here is my current code:
Public Function IsPIN_NOExists(ByVal TableName As String, _
ByRef EmployeeCode As String, ByVal FieldName As String, ByVal DataToCheck As String, _
Optional ByVal CodeFieldName As String, Optional ByVal CodeFieldValue As String) As Boolean
TableName = UCase$(Trim$(TableName))
EmployeeCode = Trim$(EmployeeCode)
On Error GoTo ErrorHandle
Dim lstrSQL1 As String
Dim lrsTemp1 As ADODB.Recordset
lstrSQL1 = " Select " & FieldName & " from " & TableName & " Where PIN_NO =" & DataToCheck & ""
If Len(Trim$(CodeFieldName)) <> 0 And Len(Trim$(CodeFieldValue)) <> 0 Then
lstrSQL1 = lstrSQL1 & " AND " & CodeFieldName & " <> '" & CodeFieldValue & "'"
End If
Set lrsTemp1 = cObjDBConn.ExecuteSQL(lstrSQL1)
If lrsTemp1 Is Nothing Then
IsPIN_NOExists = False
ElseIf Not (lrsTemp1.BOF And lrsTemp1.EOF) Then
IsPIN_NOExists = True
lrsTemp1.MoveFirst
EmployeeCode = lrsTemp1.Fields("EMPLOYEE_CODE")
MsgBox (EmployeeCode)
ElseIf lrsTemp1.RecordCount = 0 Then
IsPIN_NOExists = False
Else
IsPIN_NOExists = False
End If
If lrsTemp1.State = adStateOpen Then lrsTemp1.Close
Set lrsTemp1 = Nothing
Exit Function
ErrorHandle:
IsPIN_NOExists = False
End Function
And here is my calling code for this function:
If Trim$(TxtPINno.text) <> "" And Trim$(TxtPINno.text) <> "-" Then
'If gObjValidation.IsCodeExists(fstrTableName, gEmployerCode, "PIN_NO", Trim$(TxtPINno.text)) = True Then
If gobjValidation.IsDescriptionExists(fstrTableName, gEmployerCode, "PIN_NO", Trim$(TxtPINno.text), "EMPLOYEE_ID", Val(txtEmpCode.Tag)) = True Then
If gobjValidation.IsPIN_NOExists(fstrTableName, gEmployeeCode, "EMPLOYEE_CODE", _
Trim$(TxtPINno.text)) = True Then
MsgBox (gEmployeeCode)
Call MessageBox("This PIN Number is already existing for another employee. Cannot enter duplicate number!", OKOnly, Information, DefaultButton1, Me.Caption)
sstInformationTab.Tab = 0
If TxtPINno.Enabled = True Then TxtPINno.SetFocus
CheckAllValidations = False
Exit Function
End If
End If
End If
How can I fix this code to avoid entering a duplicate entry?
Edit: Adding in ExecuteSQL function code
Public Function ExecuteSQL(ByVal SQLQueryStatement As String) As ADODB.Recordset
On Error GoTo ErrorHandler
Dim lrs As ADODB.Recordset
cintDBHitCtr = cintDBHitCtr + 1
Set lrs = DBConnection.Execute(SQLQueryStatement, , adCmdText)
Set lrs.ActiveConnection = Nothing
Set ExecuteSQL = lrs
Set lrs = Nothing
Exit Function
ErrorHandler:
Set ExecuteSQL = Nothing
Call TrapDatabaseError(SQLQueryStatement, DBConnection.Errors(0), cDBType)
End Function
It looks like you've thrown extra logic into your function to try to fix your problem. This:
If lrsTemp1 Is Nothing Then
IsPIN_NOExists = False
ElseIf Not (lrsTemp1.BOF And lrsTemp1.EOF) Then
IsPIN_NOExists = True
lrsTemp1.MoveFirst
EmployeeCode = lrsTemp1.Fields("EMPLOYEE_CODE")
MsgBox (EmployeeCode)
ElseIf lrsTemp1.RecordCount = 0 Then
IsPIN_NOExists = False
Else
IsPIN_NOExists = False
End If
Stripped of all extraneous logic, can be replaced with this:
With lrsTemp1
IsPIN_NOExists = Not (.BOF And .EOF)
If IsPIN_NOExists Then
EmployeeCode = lrsTemp1.Fields("EMPLOYEE_CODE")
End If
End With
(Presumably, all your MsgBoxes are to try to troubleshoot what's wrong, so they can be left out.)
Now, the logic in your function, in plain English, is "If the PIN exists in the database, then return true." Looking at your calling code, your logic in plain English is "If the function returns true then tell the user that the PIN already exists." Since this logic is correct, it follows that your error is somewhere else in your code.
Your error is very probably somewhere in the code itself. Disable your error handler (just comment out the "on error" statement) and you will probably find that you get an error on another line. As a general rule, don't put error handlers in your code until you are sure that it works as intended. Error handlers are for users, not developers.
The way that you have your error handler set up, if you get an error your code will keep adding the duplicate error, which is the problem that you describe. So it seems likely that that is your problem. Now, without intending to be offensive, I must also tell you that this is a very bad (terrible, even) error handling design. Your logic in plain English is "if there is an error, tell the calling code that you didn't find a duplicate key." That's not so good, is it? :) For example, if you have an error in your SQL statement, then your code will insert the duplicate PIN.
Also, your function sounds like "PIN doesn't exist", when you are returning true if it does exist. Consider renaming it to something like "PIN_IsDuplicate" or better (that is, more consistent with standard naming conventions) "IsDuplicatePIN".
***** EDIT *****
Ok, I believe I have your solution. If you are too conscientious about "cleaning up" your open object references, you run the risk of dropping them before you are finished using them. In this case, you are attempting to break the active connection to your recordset, because you are under the incorrect impression that the connection is only necessary when you first pull the data from the recordset. While this is true in the .Net world, it is only true in the VB6 world if you specifically set up a "disconnected recordset."
Your problem is with this line of code:
Set lrs.ActiveConnection = Nothing
When you do this, you will get the error (if you disable your error handler) "Operation is not allowed when object is open." This means that you have to maintain your connection to the database as long as the recordset is open, which it is until you close it or until the function ends.
Tracing through your code, if you get an error on this line by trying to destroy the connection, then you call your error handler. The error handler tells the function to return a null object pointer to the calling function. So, in the calling function, this line of code:
Set lrsTemp1 = cObjDBConn.ExecuteSQL(lstrSQL1)
actually sets lrsTemp1 to Nothing. Therefore, the first condition of your If block is satisfied, and your function returns False as instructed. This gives the obscure behavior that you understandably found confusing.
So, here's what to take away from this. First, error handlers are for users, not for developers. They should be the last step in your coding process, because they turn runtime errors into logical errors which are harder to find. If you find that you have errors in code that has error handlers in place, the first step is to disable all error handlers. To do this, go to Tools/Options/General and select Break on All Errors. That will cause the runtime to ignore the error handlers.
Next, don't keep setting things to Nothing. A lot of misguided expertise in earlier versions of VB suggests that you need to do this. In truth, the VB runtime is much better at releasing object references that go out of scope than you are. It does so automatically in all but a few very well defined situations which you are unlikely to encounter. So, get rid of all of your "Set Object = Nothing" statements. You don't need them, and as you are finding out, they can cause errors.
One thing that it is helpful to do, however, is to explicitly close your connection (call the connection object's Close method) when you are done using it. Dropping the Recordset's ActiveConnection property doesn't close the connection, it just says that the recordset doesn't want to use it anymore. Also, if you quit using it by setting the connection variable to nothing, the server won't automatically be notified that the connection is closed. It will get around to figuring it out eventually, but you will save resources by doing it in your code.
Finally, get clearer on the different types of cursors. The default CursorType is adOpenForwardOnly, a cursor which only supports MoveFirst and MoveNext. This is the simplest one, which is appropriate for either selecting a single value (as you are) or iterating through a recordset from top to bottom (for example, when populating a listbox with the contents of the recordset). This cursor must remain connected to the database until you close the recordset, which is why you get an error when you try to drop the active connection.
If you want a recordset that you can still use after disconnecting from the database, you need a client-side recordset, and will need to set the CursorLocation property to adUseClient. This can only have a CursorType of adOpenStatic, which is a fully-traversable (supports moving backwards, forwards, by multiple records, etc.) non-updateable cursor--a "static" or "snapshot" cursor. (If you set the CursorLocation property to adUseClient, the CursorType will be automatically forced to adOpenStatic.)
If you want to do all this, you can't use the Execute method of the Connection object. Instead, you have to use the Recordset's Open method. Here's a bit of code that pulls the authors table from the SQL Server pubs sample database into a disconnected recordset and prints all the last names to the debug window:
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Set cn = New ADODB.Connection
cn.Open "Provider=SQLOLEDB.1;Persist Security Info=False;Data Source=.\SQLEXPRESS;Initial Catalog=pubs;Integrated Security=SSPI;"
Set rs = New ADODB.Recordset
With rs
.CursorLocation = adUseClient
.Open "select * from authors", cn, adOpenStatic
Set .ActiveConnection = Nothing
cn.Close
Do Until .EOF
Debug.Print .Fields("au_lname")
.MoveNext
Loop
.MoveFirst
End With
I have wrote this below function. However, i keep getting invalid function argument in QTP.
The object is a link in a webtable and i keep getting this error message of "The statement contains one or more invalid function agrument."
Can someone help me please?
Function CheckData(Value_To_Match, Rpttext, ByRef Row_Num)
Dim tab_val, arr_val(2), rowNum
tab_val = Split(Value_To_Match,";")
arr_val(0) = Browser("xyz").Page("abc").WebTable("xsw").getColumnIndex(tab_val(0))
If UBound(tab_val ) > 0 Then
arr_val(1) = tab_val(1)
arr_val(2) = Browser("xyz").Page("abc").WebTable("xsw").getMatchingRow(arr_val)
Row_Num = arr_val(2)
Browser("xyz").Page("abc").WebTable("xsw").MatchVal arr_val, Rpttext, "Verify"
Else
arr_val(1) = tab_val(0)
arr_val(2) = Browser("xyz").Page("abc").WebTable("xsw").getMatchingRow(arr_val)
Browser("xyz").Page("abc").WebTable("xsw").MatchVal arr_val, Rpttext, "Verify"
arr_val(2) = Row_Num
Browser("xyz").Page("abc").WebTable("xsw").MatchVal arr_val, Rpttext, "Check"
End If
End Function
If Trim(Parameter("DraftsViewDraftIcon")) <> "" And _
LCase(Trim(Parameter("DraftsViewDraftIcon"))) <> "[null]" And _
LCase(Trim(Parameter("Wrapper_Mode"))) ="set" Then
Set ViewDraftLink = Browser("xyz").Page("abc").WebTable("xsw").ChildItem(Row_Num, 4, "Link", 0)
ViewDraftLink.click
End If
This function below for getting matching Row
Public Function getMatchingRow(byRef object, byVal arraydata)
Dim introw
Dim varComp
On Error Resume Next
If UBound(arraydata) = 0 Then
Reporter.ReportEvent micFail, "Array Data", "No Data found in input parameter Array"
getMatchingRow = 0
Exit Function
End If
For introw = 1 to object.RowCount
varComp = StrComp(Trim(object.GetCellData(introw, arrayData(0))), Trim(arrayData(1)), 0)
If varComp = 0 And Not(isNull(varComp)) Then
getMatchingRow = introw
Exit Function
End If
Next
getMatchingRow = 0
On Error Goto 0
If you want to register the getMatchingRow function as a method, the first formal argument must be a byVal parameter:
Public Function getMatchingRow(byVal object, byVal arraydata)
You are using a byRef argument:
Public Function getMatchingRow(byRef object, byVal arraydata)
This might cause the trouble you´re experiencing.
I don't have a chance to verify this right now, but please give it a try anyways.
(I have some doubt about this answer of mine, especially because you say the error line is the Set ViewDraftLink assignment, while it should happen upon the .getMatchingRow call. But you also mention the ViewDraftLink.Click and End If lines in that comment, so you are being unprecise in that regard anyway.
It really would help if you merge all this info into the question, further clean up the code in the question, and delete the then-obsolete comments.)
I'm trying to pre-view if a field of the recordset is empty/null or not.
If IsNull(rs.Fields("fieldname")) = True Then ...
If IsNull(rs.Fields("fieldname")).Value = True Then ...
if IsNull(rs.Fields("fieldName").Value) Then...
All of these methods fires up an error... Why? How may I check if the recordset is null before I assign it's value to a variable.
If I understand correctly, you want to ensure that a field exists in the recordset. If that is correct, you need to either iterate the fields looking for the field you are searching for, or try to directly access the field and trap any errors. Here is a method that iterates the field collection and returns True if the field exists.
Public Function FieldExists(ByVal rsRecSet As ADODB.Recordset, ByVal FieldName As String) As Boolean
Dim fld As ADODB.Field
Dim Rtn As Boolean
If Not rsRecSet Is Nothing Then
For Each fld In rsRecSet.Fields
If StrComp(fld.Name, FieldName, vbTextCompare) = 0 Then
Rtn = True
Exit For
End If
Next fld
End If
FieldExists = Rtn
End Function
Here is a way to print out the columns of a table.
Dim cat
Set cat = CreateObject("ADOX.Catalog")
Set cat.ActiveConnection = db 'db is the adodb.connection object
Dim tbl
Dim clm
For Each tbl In cat.Tables
For Each clm In tbl.Columns
Debug.Print (clm) ' Prints the column name from the table
Next
Next
Try using IsDbNull() instead. DbNull is different than Null.
Edit, just loop through the field names and have a boolean if it found it, otherwise use a try catch structure.
For Each field in rs.Fields
if field.Name = "someFieldName" then
foundField = true
exit for
else
foundField = false
end if
next
I'm using AtValue and AtField helpers like this
Option Explicit
Private Sub Form_Load()
Dim rs As Recordset
If IsEmpty(AtValue(rs, "Test")) Then
Debug.Print "Field is Empty or non-existant"
End If
If LenB(C2Str(AtValue(rs, "Test"))) = 0 Then
Debug.Print "Field is Null, Empty, empty string or non-existant"
End If
'-- this will never fail, even if field does not exist
AtField(rs, "Test").Value = 42
End Sub
Public Function AtValue(rs As Recordset, Field As String) As Variant
On Error GoTo QH
AtValue = rs.Fields(Field).Value
Exit Function
QH:
' Debug.Print "Field not found: " & Field
End Function
Public Function AtField(rs As Recordset, Field As String) As ADODB.Field
Static rsDummy As Recordset
On Error GoTo QH
Set AtField = rs.Fields(Field)
Exit Function
QH:
' Debug.Print "Field not found: " & Field
Set rsDummy = New Recordset
rsDummy.Fields.Append Field, adVariant
rsDummy.Open
rsDummy.AddNew
Set AtField = rsDummy.Fields(Field)
End Function
Public Function C2Str(Value As Variant) As String
On Error GoTo QH
C2Str = CStr(Value)
QH:
End Function
My type-casting helpers are actually using VariatChangeType API (so to work with Break on all errors setting) like this
Public Function C_Str(Value As Variant) As String
Dim vDest As Variant
If VarType(Value) = vbString Then
C_Str = Value
ElseIf VariantChangeType(vDest, Value, VARIANT_ALPHABOOL, VT_BSTR) = 0 Then
C_Str = vDest
End If
End Function
rs.EOF flag will tell whether RecordSet is Empty or not
If Not rs.EOF Then
..Your desired logic..
End If
Is there a way to ask for a control property in a loop??
I need somethig like this:
For each p in control.properties
if p = "Value" then
msgbox "I Have Value Property"
elseif p = "Caption" then
msgbox "I Have Caption Property"
end if
next
It could be done somehow?
Found this code on Experts Exchange. Add a reference to TypeLib Information.
Public Enum EPType
ReadableProperties = 2
WriteableProperties = 4
End Enum
Public Function EnumerateProperties(pObject As Object, pType As EPType) As Variant
Dim rArray() As String
Dim iVal As Long
Dim TypeLib As TLI.InterfaceInfo
Dim Prop As TLI.MemberInfo
On Error Resume Next
ReDim rArray(0) As String
Set TypeLib = TLI.InterfaceInfoFromObject(pObject)
For Each Prop In TypeLib.Members
If Prop.InvokeKind = pType Then
iVal = UBound(rArray)
rArray(iVal) = UCase$(Prop.Name)
ReDim Preserve rArray(iVal + 1) As String
End If
Next
ReDim Preserve rArray(UBound(rArray) - 1) As String
EnumerateProperties = rArray
End Function
You can ask for a list of the readable, or writeable properties.
Bonus, ask if a specific property exists.
Public Function DoesPropertyExist(pObject As Object, ByVal _
PropertyName As String, pType As EPType) As Boolean
Dim Item As Variant
PropertyName = UCase$(PropertyName)
For Each Item In EnumerateProperties(pObject, pType)
If Item = PropertyName Then
DoesPropertyExist = True
Exit For
End If
Next
End Function
Beaner has given an excellent direct answer to the question you have asked.
I'm guessing what you might be trying to do. Perhaps you're trying to get the "text" from a control but you don't know the type of the control at runtime. You could consider something like this, which tries a number of hard-coded property names in turn until something works.
Function sGetSomeText(ctl As Object) As String
On Error Resume Next
sGetSomeText = ctl.Text
If Err = 0 Then Exit Function
sGetSomeText = ctl.Caption
If Err = 0 Then Exit Function
sGetSomeText = ctl.Value
If Err = 0 Then Exit Function
sGetSomeText = "" 'Nothing worked '
End Function
Another approach would be to check the type of the control at runtime. You can use
If TypeName(ctl) = "whatever" or
If TypeOf ctl Is whatever.
Then you could switch to code for specific control types that definitely have the Text property, etc.
I'm not sure what you're hoping to accomplish, but I'm pretty sure VB6 does not support what you're talking about. You could try something like this:
If control.Value Is Not Nothing Then
msgbox "I Have Value Property"
Else If control.Caption Is Not Nothing Then
msgbox "I Have Caption Property"
See if that accomplishes what you're looking to do.