I'm relatively new to functions and classes so not too sure if this is a beginners mistake. I'm getting:
Microsoft VBScript runtime error '800a01a8'
Object required: 'EngineerNote(...)'
/backup-check/backup_frontendlist_import_NEW.asp, line 76
Line 76 is:
Set NoteArray=EngineerNote(company, servername, backupsolution)
The three variables I'm passing are all strings. All the function and class is set in:
Class EngineerNoteClass
public note
public notesubmitdate
End Class
Function EngineerNote(Company, ServerName, Solution)
Set RecordSet = Server.CreateObject("ADODB.Recordset")
RecordSetSQLString = "SELECT note, submitdate FROM tbl_BackupChecks_AuditInformation WHERE Company='" & company & "' AND ServerName='" & servername & "' AND Solution='" & solution & "' ORDER BY submitdate desc;"
RecordSet.Open RecordSetSQLString, DatabaseConnection
If Recordset.EOF Then
'Do Nothing
Else
Dim NoteResults
Set NoteResults = new EngineerNoteClass
noteresults.note = RecordSet("note")
noteresults.notesubmitdate = RecordSet("submitdate")
Set Engineernote = NoteResults
End If
Recordset.Close
End Function
Most likely your database query didn't return any results. You only set the return value of your function when the recordset isn't at EOF:
If Recordset.EOF Then
'Do Nothing
Else
...
Set Engineernote = NoteResults
End If
Setting the return value to Nothing (or to an empty EngineerNoteClass object) in the Then branch should make the error go away:
If Recordset.EOF Then
Set EngineerNote = Nothing
Else
...
Set Engineernote = NoteResults
End If
Make sure you handle the returned value/object appropriately in the rest of your script.
Related
On a few of the Classic ASP websites I manage for the last few days I have been getting some error notifications (with no error number) that always show an error on a line number where a cookie value is being requested.
Looking at the request for each of these errors, they all have unusual cookies, and look like some sort of hack attempt.
The lines that are indicated as causing the error are all like this:
strCookieCart = Request.Cookies("cart")
Here's a couple of samples of the cookies being sent (truncated)... Note the =true (no name, just a value).
HTTP_COOKIE:=true; yuv=u97Yoe-o0UWp7ho_vaB2csT-xxaQ37gMWzhB1MARTSNk1QKpjJTXmZYMRQ095rM96MaNbhx1tEdJ
HTTP_COOKIE:pll_language=en; =true; yandexuid=6536735381437958890; st=6c9838994ffb
Is Classic ASP incapable of handling these? Is there any way to avoid these errors and ignore the bad values? Are these always likely to be hack attempts or could there be legitimate requests without cookie names?
I suppose I could check for these looking at Request.ServerVariables("HTTP_COOKIE") by manually parsing or using a regular expression check of some sort. Does anyone else do this? Any code to share?
A second answer to my own question and the solution I have now implemented is to add the following code to my common include file.
It tests whether Classic ASP can read the cookies and, using error trapping, ends the response if an error is detected.
On Error Resume Next
Request.Cookies("test")
If Err.Number <> 0 Then Response.End
On Error Goto 0
This is a better solution to my other answer as there is no point in generating a page for what is obviously an attack of some sort so ending the script as soon as possible is a better choice.
My proposed answer to my own question is to create a class that extracts all the valid keys and values for the cookies on initialisation, and has a function to return a value for a specified key.
Unfortunately it doesn't work for cookies that contain a collection of multiple values, but I don't generally use these anyway.
Here is the class:
<%
Class MyRequest
Private m_objCookies
Private Sub Class_Initialize()
Dim strCookies, i, strChar, strName, strValue, blnInValue
strCookies = Request.ServerVariables("HTTP_COOKIE")
Set m_objCookies = Server.CreateObject("Scripting.Dictionary")
i = 1
strName = ""
strValue = ""
blnInValue = False
Do
strChar = Mid(strCookies, i, 1)
If strChar = ";" Or i = Len(strCookies) Then
strValue = Trim(strValue)
If strName <> "" And strValue <> "" Then
If m_objCookies.Exists(strName) Then
m_objCookies.Item(strName) = strValue
Else
m_objCookies.Add strName, strValue
End If
End If
If i = Len(strCookies) Then Exit Do
strName = ""
strValue = ""
blnInValue = False
ElseIf strChar = "=" Then
strName = Trim(strName)
blnInValue = True
ElseIf blnInValue Then
strValue = strValue & strChar
Else
strName = strName & strChar
End If
i = i + 1
Loop
End Sub
Public Function Cookies(strKey)
Cookies = m_objCookies.Item(strKey)
End Function
End Class
%>
The changes to my code to use this class are minimal. Where I currently have...
strCookieCart = Request.Cookies("cart")
I will need to change to...
Dim objMyRequest : Set objMyRequest = New MyRequest
strCookieCart = objMyRequest.Cookies("cart")
I have tested the above with many of the bad requests I have logged and it works fine.
Add three line codes for #johna is answer, after this line:
If strChar = ";" Or i = Len(strCookies) Then
add these lines:
If i = Len(strCookies) And strChar <> ";" Then
strValue = strValue & strChar
End If
I have 9 pages with 10 fields in each page. Can i use a single session variable to store all the field(textbox,drop downlist,radiobuttons) values of 9 pages? If so could you give me small example inorder to proceed. Im kind of stuck.
Could you? Yes. Should you? Most likely not - though I can't say for sure without understanding what problem you are intending to solve.
Update with one sample solution
OK, I'm going to assume you want to store the values from the controls and not the controls themselves. If so, the easiest solution is stuff them in using some meaningful token to separate them. Like:
Session("MyControlValueList") = "name='txt1',value='hello'|name='txt2', value'world'"
To retrieve you would split them into a string array:
myArray = Session("MyControlValueList").Split("|")
And then iterate through to find the control/value you want.
So strictly speaking that's an answer. I still question whether it is the best answer for your particular scenario. Unfortunately I can't judge that until you provide more information.
Create a custom class with all the fields you want to save, then populate an instance of that and save that instance as a session variable.
I have something similar, but not identical - I'm saving various shipping address fields for an order, and I'm allowing the admins to update the order, either the shipping information or the order line items. Since that information is kept on separate tables, I store the shipping information in a session variable, and then compare it to what's on the form when they hit the "Update" button. If nothing has changed, I skip the update routine on the SQL Server database.
The easiest way for me to do this was to create a "OrderInfo" class. I saved the shipping information to this class, then saved that class to a session variable. Here's the code showing the class -
Public Class OrderInfo
Private v_shipname As String
Private v_add1 As String
Private v_add2 As String
Private v_city As String
Private v_state As String
Private v_zipcd As String
Private v_dateneeded As Date
Private v_billingmeth As Integer
Public Property ShipName() As String
Get
Return v_shipname
End Get
Set(value As String)
v_shipname = value
End Set
End Property
Public Property Add1() As String
Get
Return v_add1
End Get
Set(value As String)
v_add1 = value
End Set
End Property
Public Property Add2() As String
Get
Return v_add2
End Get
Set(value As String)
v_add2 = value
End Set
End Property
Public Property City() As String
Get
Return v_city
End Get
Set(value As String)
v_city = value
End Set
End Property
Public Property State() As String
Get
Return v_state
End Get
Set(value As String)
v_state = value
End Set
End Property
Public Property ZipCd() As String
Get
Return v_zipcd
End Get
Set(value As String)
v_zipcd = value
End Set
End Property
Public Property DateNeeded() As Date
Get
Return v_dateneeded
End Get
Set(value As Date)
v_dateneeded = value
End Set
End Property
Public Property BillingMeth() As Integer
Get
Return v_billingmeth
End Get
Set(value As Integer)
v_billingmeth = value
End Set
End Property
End Class
Here's the code for when I tested the concept to see if I could store a custom class in a session variable. This routine gets the order record, populates the fields in an instance of the custom class, and on the web form, as well. I save that instance to a session variable, then I initialize another new instance of that custom class, load the session variable to it. I then display the field values from the "retrieved" custom class, and what showed on the label matched what it should be -
Protected Sub LoadOrderInfo(ByVal ordID As Integer)
Dim connSQL As New SqlConnection
connSQL.ConnectionString = ConfigurationManager.ConnectionStrings("sqlConnectionString").ToString
Dim strProcName As String = "uspGetOrderInfoGeneral"
Dim cmd As New SqlCommand(strProcName, connSQL)
cmd.CommandType = CommandType.StoredProcedure
cmd.Parameters.AddWithValue("OrderID", ordID)
If connSQL.State <> ConnectionState.Open Then
cmd.Connection.Open()
End If
Dim drOrderInfo As SqlDataReader
drOrderInfo = cmd.ExecuteReader
If drOrderInfo.Read Then
Dim orgOrder As New OrderInfo
orgOrder.ShipName = drOrderInfo("shipName")
orgOrder.Add1 = drOrderInfo("ShipAdd1")
orgOrder.Add2 = drOrderInfo("ShipAdd2")
orgOrder.City = drOrderInfo("ShipCity")
orgOrder.State = drOrderInfo("ShipState")
orgOrder.ZipCd = drOrderInfo("ShipZip")
orgOrder.DateNeeded = drOrderInfo("DateNeeded")
orgOrder.BillingMeth = drOrderInfo("BillingMethodID")
If Session.Item("orgOrder") Is Nothing Then
Session.Add("orgOrder", orgOrder)
Else
Session.Item("orgOrder") = orgOrder
End If
' I could just as easily populate the form from the class instance here
txtShipName.Text = drOrderInfo("shipName")
txtAdd1.Text = drOrderInfo("ShipAdd1")
txtAdd2.Text = drOrderInfo("ShipAdd2")
txtCity.Text = drOrderInfo("ShipCity")
txtState.Text = drOrderInfo("ShipState")
txtZipCd.Text = drOrderInfo("ShipZip")
selDate.Value = drOrderInfo("DateNeeded")
ddlBillMeth.SelectedValue = drOrderInfo("BillingMethodID")
End If
cmd.Connection.Close()
Dim retOrder As New OrderInfo
retOrder = Session.Item("orgOrder")
lblWelcomeMssg.Text = retOrder.ShipName & ", " & retOrder.Add1 & ", " & retOrder.City & ", " & retOrder.DateNeeded.ToShortDateString & ", " & retOrder.BillingMeth.ToString
End Sub
This might not be practical or desirable, given the number of fields you are trying to hold onto that way, but I'm not here to judge, so this is one possibility. I've worked with other projects where you create a table, and save that table as a session variable, so whatever structure you put into an object is retained if you save that object as a session variable.
I have the follwoing code that performs a query and returns a result. However, I looked around and found some examples to take care of null values but I get an error: "Invalid attempt to read when no data is present." I also got the error: "Conversion from type 'DBNull' to type 'Decimal' is not valid."
Can someone help me out with this code to prevent null values from crashing my program?
Private Sub EFFICIENCY_STACKRANK_YTD(ByVal EMPLOYEE As String)
Dim queryString As String = "SELECT " & _
" (SELECT CAST(SUM(TARGET_SECONDS) AS DECIMAL)/ CAST(SUM(ROUTE_SECONDS) AS DECIMAL) FROM dbo.APE_BUSDRIVER_MAIN WITH(NOLOCK) WHERE APE_AREA_OBJID = " & lblAreaOBJID.Text & " AND EMPLOYEE_NAME = '" & EMPLOYEE & "' AND YEAR_TIME = '" & cbYear.Text & "' AND ACTIVE = 1) AS RESULT1" & _
" FROM dbo.APE_BUSDRIVER_MAIN "
Using connection As New SqlConnection(SQLConnectionStr)
Dim command As New SqlCommand(queryString, connection)
connection.Open()
Dim reader As SqlDataReader = command.ExecuteReader()
If reader.Read Then
RESULT1 = reader("RESULT1")
Else
RESULT1 = 0
End If
End Using
End Sub
You have opened the reader, but have not asked it to actually read anything.
After this line:
Dim reader As SqlDataReader = command.ExecuteReader()
add
If reader.Read() Then
and wrap the result reading into this if statement, i.e.
If reader.Read() Then
Dim index As Integer = reader.GetOrdinal("RESULT1")
If reader.IsDBNull(index) Then
RESULT1 = String.Empty
Else
RESULT1 = reader(index)
End If
End If
Note that this works because your SQL should only return a single record. In the event that you were reading multiple records, you would need to call the Read statement in a loop until there were no more records, i.e.
Do While reader.Read()
Loop
I wanted to provide another, more-advanced, answer as an option. Many classes can be extended in .NET like this.
If you are regularly performing "Is NULL" checks like this in your applications, you can choose to extend the DataReader class once to have additional functions available everywhere in your application. Here is an example that creates an extension called "ReadNullAsString()" onto the data reader class. This makes a function that always returns String.Empty when a DbNull is encountered.
Part 1, place this module code in a new class file in App_Code if application is a website, otherwise place where ever you prefer. There are two overloads, one for the field's ordinal position (aka index), and one for the field's ColumnName.
Public Module DataReaderExtensions
''' <summary>
''' Reads fieldName from Data Reader. If fieldName is DbNull, returns String.Empty.
''' </summary>
''' <returns>Safely returns a string. No need to check for DbNull.</returns>
<System.Runtime.CompilerServices.Extension()> _
Public Function ReadNullAsEmptyString(ByVal reader As IDataReader, ByVal fieldName As String) As String
If IsDBNull(reader(fieldName)) Then
Return String.Empty
Else
Return reader(fieldName)
End If
Return False
End Function
''' <summary>
''' Reads fieldOrdinal from Data Reader. If fieldOrdinal is DbNull, returns String.Empty.
''' </summary>
''' <returns>Safely returns a string. No need to check for DbNull.</returns>
<System.Runtime.CompilerServices.Extension()> _
Public Function ReadString(ByVal reader As IDataReader, ByVal fieldOrdinal As Integer) As String
If IsDBNull(reader(fieldOrdinal)) Then
Return ""
Else
Return reader(fieldOrdinal)
End If
Return False
End Function
End Module
Step 2, call the new extension like so:
' no need to check for DbNull now, this functionality is encapsulated in the extension module.
RESULT1 = reader.ReadNullAsEmptyString(index)
'or
RESULT1 = reader.ReadNullAsEmptyString("RESULT1")
I created this function but it seems to give me a problem. I want to store a dictionary into an Session variable so I can access the dictionary throughout the website. I keep getting the error Object required: DictionaryObject or it will say This key already exist in the dictionary. Can someone please tell me what I am doing wrong?
I did look storing dictionary in session at this posting but didn't really fit what I am trying to do!
Function LoadPermissions()
Dim SQLString
SQLString ="SELECT datafields here... FROM " & TBL_employees_permissions & " AS p WHERE p.eid = '" & Clng(12) & "';"
If IsObject(Session("dicPermissions")) = True Then
Set dicPermissions = Session("dicPermissions")
Else
Set dicPermissions = Server.CreateObject("Scripting.Dictionary")
End If
db_conn conn, rs '
Set myRS = conn.Execute (SQLString)
For each item in myRS.Fields
If IsObject(Session("dicPermissions")) = True AND DictionaryObject.Exists(Trim(item.Name)) = False Then
dicPermissions.Add Trim(item.Name), Trim(myRS(item.Name))
End If
Next
db_disconn conn, rs
Set Session("dicPermissions") = dicPermissions 'Store Dictionary to session array.
End Function
I was able to get it working and here is what I did? If anyone see anything wrong or if I need to add in any error trapping. This is load once when the user logs in.
Dim SQLString
SQLString ="SELECT Datefields here... & " AS p WHERE p.eid = '" & Clng(12) & "';"
'Create the dictionary object.
Set Session("dicPermissions") = Server.CreateObject("Scripting.Dictionary") 'Create the Dictionary object.
'sets up a connection to the database
db_conn conn, rs 'Open account table.
Set myRS = conn.Execute (SQLString) ' Uses any ADODB connection
For each item in myRS.Fields 'Create the dictionary with the field names and cell data.
'dicPermissions.Add fieldname, feild value
Session("dicPermissions").Add Trim(item.Name), Trim(myRS(item.Name))
Next
db_disconn conn, rs 'Close the database
You can access it like so:
Response.write Session("dicPermissions").Item("itemnamehere...")
In .Net you can use reflection to get access to an enumeration of all properties of a class. Can this be done too with a VB6 class module?
Found it!
You need to set a reference to the TypeLib library (tlbinf32.dll) and then you can use code like (this is class module):
EDIT: Unfortunately, while the code below works as expected when run in debug mode inside the VB6 IDE, it fails when compiled. After compiling any attempt to read the .Members propery causes an 'Object doesn't support this action' error (445). I have given up on it, unless someone can make the code below work both in and outside of the IDE.
Option Explicit
Private TLI As TLIApplication
Private m_clsInterface As InterfaceInfo
Private m_clsClassUnderInvestigation As Object
Private Sub Class_Terminate()
Set m_clsClassUnderInvestigation = Nothing
Set m_clsInterface = Nothing
Set TLI = Nothing
End Sub
Public Sub FillListBoxWithMembers(pList As ListBox, Optional pObject As Object)
Dim lMember As MemberInfo
If pObject = Empty Then
Set pObject = ClassUnderInvestigation
End If
Set m_clsInterface = TLI.InterfaceInfoFromObject(pObject)
For Each lMember In m_clsInterface.Members
pList.AddItem lMember.Name & " - " & WhatIsIt(lMember)
Next
Set pObject = Nothing
End Sub
Public Function GetPropertyLetNames() As Collection
Dim filters(1 To 1) As InvokeKinds
filters(1) = INVOKE_PROPERTYPUT
Set GetPropertyLetNames = Filter(filters)
End Function
Public Function GetPropertySetNames() As Collection
Dim filters(1 To 1) As InvokeKinds
filters(1) = INVOKE_PROPERTYPUTREF
Set GetPropertySetNames = Filter(filters)
End Function
Public Function GetPropertyLetAndSetNames() As Collection
Dim filters(1 To 2) As InvokeKinds
filters(1) = INVOKE_PROPERTYPUT
filters(2) = INVOKE_PROPERTYPUTREF
Set GetPropertyLetAndSetNames = Filter(filters)
End Function
Public Function GetPropertyGetNames() As Collection
Dim filters(1 To 1) As InvokeKinds
filters(1) = INVOKE_PROPERTYGET
Set GetPropertyGetNames = Filter(filters)
End Function
Private Function Filter(filters() As InvokeKinds) As Collection
Dim Result As New Collection
Dim clsMember As MemberInfo
Dim i As Integer
For Each clsMember In m_clsInterface.Members
For i = LBound(filters) To UBound(filters)
If clsMember.InvokeKind = filters(i) Then
Result.Add clsMember.Name
End If
Next i
Next
Set Filter = Result
End Function
Private Function WhatIsIt(lMember As Object) As String
Select Case lMember.InvokeKind
Case INVOKE_FUNC
If lMember.ReturnType.VarType <> VT_VOID Then
WhatIsIt = "Function"
Else
WhatIsIt = "Method"
End If
Case INVOKE_PROPERTYGET
WhatIsIt = "Property Get"
Case INVOKE_PROPERTYPUT
WhatIsIt = "Property Let"
Case INVOKE_PROPERTYPUTREF
WhatIsIt = "Property Set"
Case INVOKE_CONST
WhatIsIt = "Const"
Case INVOKE_EVENTFUNC
WhatIsIt = "Event"
Case Else
WhatIsIt = lMember.InvokeKind & " (Unknown)"
End Select
End Function
Private Sub Class_Initialize()
Set TLI = New TLIApplication
End Sub
Public Property Get ClassUnderInvestigation() As Object
Set ClassUnderInvestigation = m_clsClassUnderInvestigation
End Property
Public Property Set ClassUnderInvestigation(clsClassUnderInvestigation As Object)
Set m_clsClassUnderInvestigation = clsClassUnderInvestigation
Set m_clsInterface = TLI.InterfaceInfoFromObject(m_clsClassUnderInvestigation)
End Property
I am heavily endebted to this post.