populating ADODB command Binary parameters in VB6 - vb6

I need to modify someone else's VB code, and I don't have much experience with VB6.
I need to call a SQL2000 stored procedure using ADODB. One of the parameters is of type Binary and it's giving me problems. Every time I create the parameter, I get an error "Application uses a value of the wrong type for the current operation". The error happens at the cmd.parameter.append line, it doesn't even give me a chance call the cmd.execute.
Dim HexPassword As String
Dim BinPassword As String
Dim AsciiCode As Integer
Dim unitDigit As String
Dim TensDigit As String
Set obj_hash = New EDCrypt
' Returns Hash of password hex encoded
HexPassword = obj_hash.GetTextHash(Trim(txtPassword.text), haSHA1)
' Converts Hex Encoded string to Binary encoded string
Dim i As Integer
For i = 1 To 40 Step 2
unitDigit = Mid(HexPassword, i + 1, 1)
TensDigit = Mid(HexPassword, i, 1)
AsciiCode = HexStrtoInt(TensDigit + unitDigit)
BinPassword = BinPassword + Chr(AsciiCode)
Next i
conn.Open ConnectionString
Dim cmd As ADODB.Command
Set cmd = New ADODB.Command
Set cmd.ActiveConnection = conn
cmd.CommandType = adCmdStoredProc
cmd.CommandText = "ValidatePasswordNew"
cmd.Parameters.Append cmd.CreateParameter("LoginID", adVarChar, adParamInput, 30, UserID)
cmd.Parameters.Append cmd.CreateParameter("ShaPassword", adBinary, adParamInput, 20, BinPassword)

Try this for your string concatenation:
BinPassword = BinPassword & ChrB(AsciiCode)
+ is not the right string concatenation operator, and using ChrB should convince VB and ADO that you're really passing it binary data, not character data.
There's also a chance DOK is right about the size. You can try setting the size to something you're certain is longer than you need, or you can probably get around setting the size at all by splitting the operation into two statements:
cmd.Parameters.Append cmd.CreateParameter("ShaPassword", adBinary, adParamInput)
cmd.Parameters("ShaPassword").Value = BinPassword
It's weird, but it's worked for me in the past.
Just for added emphasis in case someone sees this again: the size property on Parameter objects does not have to match the exact byte length of your argument; it merely has to be at least big enough to hold your argument.

I think the problem may be in setting size = 20. That may be an optional argument. Try leaving it out. There might be a different overload of CreateParameter that you need to use here.
Check out this MSDN page which gives this general pattern:
command.CreateParameter (Name, Type, Direction, Size, Value)
Also, you have declared BinPassword as a String. You can't pass a string into a parameter of adBinary. You need to pass a binary object into that, or change adBinary to adVarChar.

You probably want adVarBinary. Most likely you should be passing Byte arrays as values.

Related

keeping leading zeros and keeping variable as text value instead of numeric

I've got the following VBScript code:
WScript.Echo findUser(strUser)
It calls the function: findUser. It works fine however, when the value of strUser is a value like 0001 then for some reason the function strips the zeros and returns only 0 and hence the findUser function returns error saying user not found.
Any idea how I can make sure it does not strip any characters? For some reason it is being treated as a numeric value but seeing as user account may not always have numbers only, i would prefer it treats the value of strUser as a text string
* UDPATE *
The rest of the code of how I am getting the strUser variable is below. It's getting the strUser from a CSV file. However, even if I set the strUser like this:
strUser = 0001
it still returns as an interger and removes the leading zeros.
Set oConnection = CreateObject("adodb.connection")
Set oRecordSet = CreateObject("adodb.recordset")
oConnection.Open "Provider=Microsoft.Jet.OLEDB.4.0;Data Source= " & sCSVFileLocation & ";Extended Properties=""text;HDR=NO;FMT=Delimited"""
oRecordSet.Open "SELECT * FROM " & sCSVFile, oConnection
Do Until oRecordSet.EOF
strUser = oRecordSet.Fields.Item(0).value
WScript.Echo findUser(strUser)
On Error Resume Next
oRecordSet.MoveNext
Loop
If the numbers are listed with leading zeroes in your CSV, but end up as plain integers in your code they most likely are not in double quotes.
ID,...
0001,...
0002,...
As #Lankymart suspected the JET engine interprets numbers as numbers unless they're explicitly defined as strings by putting them in quotes:
ID,...
"0001",...
"0002",...
The preferred solution would be to fix the CSV or the process creating the CSV, so that string values are properly defined.
If that isn't possible/feasible for some reason the canonical way to turn an integer into a string with leading zeroes is to prepend the number with an all-zeroes string and cut the result string to the right length:
i = 1
s = Right("0000" & i, 4) 's = "0001"

Creating a copy of a recordset in a Session variable

This is a follow on from a question I asked here > Converting an HTML table to JSON to pass to an AJAX call for downloading as a CSV
I have a report page that outputs a number of recordsets as graphs and tables but also buttons to "download as a CSV file" I have a generic function that will take any number of recordsets (as the stored proc returns multiple recordsets) and outputs a CSV so thats fine.
The issue is I want to set the output of my stored proc into a Session("DATA") variable and then create a "copy" of the data in memory so that whenever the "download" button is pressed I can just look for a Session("DATA") variable and output it if it exists.
The problem is that when I set a recordset object to point at the Session it is referential so that once it has looped through all the recordsets outputting them on the page the Session is then empty (or at the end of all the recordsets - so it's an object with no data in it)
How can I Create a "copy" of the recordset instead of a pointer so that the Session always has the full recordset in it e.g
Set Session("DATA") = objCon.Execute(strSQL) '* returns multiple recordsets
Set objRS = Session("DATA")
Do While Not objRS.EOF......
'* BUT now when I want to access Session("DATA") it is at the start of all the recordsets and not a spent, EOF of the last recordset due to me looping through objRS
I could have a function that loops through the recordsets and makes a duplicate but then that seems like a lot of effort and performance and I thought there must be a way to copy the recordsets for the session somehow without looping through it multiple times.
If I have to create a "Copy" object function then I suppose I will have to but is there not an easier way in ASP CLASSIC to create a copy of an object and not a reference pointer?
You can read the entire recordset into an array, using GetRows:
'GetDataSet
' Returns a table of data based on the supplied SQL statement and connection string.
'Parameters:
' sqlString (string) - The SQL string to be sent. This can be either a valid SQL string or an Application setting
' specified using the '#' prefix (e.g. #GET_USERNAME)
' connString (string) - The database connection string. Either a valid connection string, an Application setting
' (using the '#' prefix, e.g. #CONN_STRING) or an AMC (AppModeConnection string).
'Usage:
' dataSet = GetDataSet(sqlString, connString)
'Description:
' This function generates a table of information in a 2 dimensional array. The first dimension represents the columns
' and the second the rows. If an error occurs while the routine is executing the array and the base index (0,0) is set
' to C_ERROR, (0,1) to the VBScript error index, and (0,2) to the VBScript error description.
'Notes:
' Updated this function to take advantage of the AppModeConnection feature.
'Revisions:
' 30/09/2015 1.1 Added facility to allow recovery of Application settings as Query and connection strings using
' '#', (e.g.: ds = GetDataSet("#GET_USER_DETAIL", "#CONN_DATABASE")
' 25/09/2015 1.0 Added AMC support for Classic ASP. The system will test to see if there is a valid connection
' string based on the current application mode and the connection string provided (e.g. if the
' connection string is 'CONN_DATABASE' and the application mode is 'DEV' then the final connection
' string will be 'CONN_DATABASE_DEV'. A connection string should be present to cover this.
' < 25/09/2015 0.1 Bug ironed out that prevented closing of the database.
' < 25/09/2015 0.0 Initial version.
function GetDataSet(ByVal sqlString, ByVal connString)
'Test to see if there's an application connection string first...
If Left(connString, 1) = "#" Then
connString = Application(Mid(connString, 2))
Else
Dim amc
amc = AppModeConnection(connString)
If amc <> "" then connString = amc
End If
'Test the SQL string to see if it's stored as an Application setting...
If Left(sqlString, 1) = "#" Then sqlString = Application(Mid(sqlString, 2))
'Define the initial output...
dim rV, rs
If (Application("APP_MODE") = Application("MODE_DEV") And Application("DEV_TRAP_ERRORS")) Or _
(Application("APP_MODE") <> Application("MODE_DEV")) Then On Error Resume Next
'Define and open the recordset object...
set rs = Server.CreateObject("ADODB.RecordSet")
rs.Open sqlString, connString, 0, 1, 1
'Initialise an empty value for the containing array...
redim rV(0,0)
rV(0,0) = C_NO_DATA
'Deal with any errors...
if not rs.EOF and not rs.BOF then
'Store the data...
rV = rs.GetRows()
'Tidy up...
rs.close
set rs = nothing
select case err.number
case 3021 'No data returned
'Do nothing as the initial value will still exist (C_NO_DATA)
case 0 'No error
'Do nothing as data has been returned
case else
redim rV(4,0)
rV(C_COL_IDENTIFIER,0) = C_ERROR
rV(C_COL_ERROR_ID,0) = err.number
rV(C_COL_ERROR_MESSAGE,0) = err.description
rV(C_COL_SQL,0) = sqlString
rV(C_COL_CONNECTION,0) = "Withheld"
end select
end if
on error goto 0
'Return the array...
GetDataSet = rV
end function
This is my own in depth version which does some funky stuff with connection strings etc, so feel free to use it, but note that you'll have to set-up the handling for the connection strings etc. Within the code, though, is the core element - the GetRows, that you require.
You shouldn't need to set any Session variables, simply process all in the same page, as per marekful's answer to your post. You can do this using a simple For...Next loop using an array.
To use the function above simply declare your SQL and call it like so...
Dim ds, sql
sql = "EXEC prc_get_report_data "
ds = GetDataSet(sql, "#my_conn")
(Note: read the code comments about the connection strings).
The array returned from this is obviously two dimensional zero based, where x = columns, y = rows:
ds(x, y)
What I tend to do is define constants to cover the column names, matching them to the equivalents in the database...
Const COL_ID = 0 'Column 0 is the ID field (note zero based)
Const COL_TITLE = 1 'Title field
Const COL_DESCRIPTION = 2 'Description field
...and so on.
Then you can reference them eaasily:
If ds(COL_ID, row) = 14 Then
Use the UBound function to get the extents of the array...
Dim row, rows
For rows = 0 To UBound(ds, 2) '2 is the second dimension of the array (note not zero based
If ds(COL_ID, row) = avalue Then
You get the idea.

VBS convert string to floating point

Dim strnumber
strnumber = "0.3"
Dim add
add = 0.1
Dim result
result = strnumber + add
MsgBox result
I want to get 0.4 as result, but get 3.1.
I tried clng(strnumber) and int(strnumber), nothing works. There is a simple solution for sure but I won't find it.
EDIT: Solution
result = CDbl(Replace(strnumber,".",",") + add
Has to do with your locale settings. Automatic conversion (as well as explicit one) observes it in the same manner as in CStr() function.
E.g. in my locale CStr( 0.3) results to 0,3 that is invert to CDbl("0,3") while CDbl("0.3") results to an error.
BTW: always use option explicit and, for debugging purposes, On Error Goto 0
Following below procedures can help:
Replacing the dot(".") with comma (",") in the string
change the string to double by Cdbl
example:
dim a,b,c
a="10.12"
b="5.05"
a=Replace(a,".",",")
b= Replace(b,".",",")
c=Cdbl(a)+Cdbl(b)
msgbox c
You want to add two numbers. So you should use numbers (and not a string (strnumber) and a number (add):
>> n1 = 0.3
>> n2 = 0.1
>> r = n1 + n2
>> WScript.Echo r
>>
0,4
As you can see from the output (0,4), I'm using a locale (German) that uses "," as decimal 'point'. But literals always use ".". So by using the proper data types you can write your scripts in a language/locale independent fashion as long as you don't need to process external string data (from a file or user input). Then you have to modify the input before you feed it to a conversion function like CDbl(). For simple cases that can be done with Replace(inp, badmarker, goodmarker).
P.S. You said you " tried clng(strnumber) and int(strnumber)". You should have tried CDbl(). CLng() tries to get a long integer (cf. CInt()), Int() removes/rounds the fraction from number.

Vb6 .text property for textbox required

I am trying to convert letters to numbers.
I have a sub which ensures only numbers are put into the textbox.
My questions is will the following code work. I have a textbox(for numbers) and combobbox(for letters)
Dim sha As String
Dim stringposition As Long
Dim EngNumber As Long
sha = "abcdefghifjklmnopqrstuvwxyz"
stringposition = InStr(1, sha, Mid(1, cmbEngletter.Text, 1))
MsgBox "stringposition"
EngNumber = (txtManuNo.Text * 10) + stringposition
My only question above would be will the multiplication work with a .text. I believe it won't because it is a string. Please advise then on how to deal with a situation.
You can use CLng() to convert a string to a Long variable
CLng() will throw an error though if it doesn't like the contents of the string (for example if it contains a non-numeric character), so only use it when you are certain your string will only contain numbers
More forgiving is it to use Val() to convert a string into a numeric variable (a Double by default)
I also suggest you look into the following functions:
Asc() : returns the ASCII value of a character
Chr$() : coverts an ASCII value into a character
Left$() : returns the first characters of a string
CStr() : convert a number into a string
I think in your code you mean to show the contents of your variable stringposition instead of the word "stringposition", so you should remove the ""
I do wonder though what you are trying to accomplish with your code, but applying the above to your code gives:
Dim sha As String
Dim stringposition As Long
Dim EngNumber As Long
sha = "abcdefghifjklmnopqrstuvwxyz"
stringposition = InStr(1, sha, Left$(cmbEngletter.Text, 1))
MsgBox CStr(stringposition)
EngNumber = (Val(txtManuNo.Text) * 10) + stringposition
I used Val() because I am not certain your txtManuNo will contain only numbers
To ensure an user can only enter numbers you can use the following code:
Private Sub txtManuNo_KeyPress(KeyAscii As Integer)
Select Case KeyAscii
Case vbKeyBack
'allowe backspace
Case vbKey0 To vbKey9
'allow numbers
Case Else
'refuse any other input
KeyAscii = 0
End Select
End Sub
An user can still input non-numeric charcters with other methods though, like copy-paste via mouse actions, but it is a quick and easy first filter

How to Find Record Set Value is Number or String?

Original:
Using VB6
If rsCardEvent(4).Value = Str Then
TimOut = rsCardEvent(4)
Else
TimeOut = Left(TimOut, 2) & ":" & Mid(TimOut, 3, 2) & ":" & Right(TimOut, 2)
End If
Getting Type MisMatch Error.
How To Find Record Set Value is String or Number
Exactly i need
If Number means print number like Time Format (HH:MM:SS)
else
print string value
Coding Help for the above condition
Edited Version:
I'm working with an ADO.Recordset object and am trying to determine the data type of a column at run-time. I need to handle the column value differently in my code depending on its underlying data type. If the column value is a string, I want to work with the value as-is. If it is a number, I want to treat the number as an packed time and convert it to HH:MM:SS format (i.e. the number 120537 would be converted to the string "12:05:37").
Below is some example code that demonstrates what I want to achieve. However, when I run this code I get a "Type Mismatch" error:
If rsCardEvent(4).Value = Str Then
TimOut = rsCardEvent(4)
Else
TimeOut = Left(TimOut, 2) & ":" & Mid(TimOut, 3, 2) & ":" & Right(TimOut, 2)
End If
Have a look at the Visual Basic 6 function library. There are functions that can help you determine the underlying type of a value.
There are quite a few but you might find these useful:
IsNumeric
IsDate
Based on this article, if rsCardEvent is an ADO recordset, you could check the Type property. Something like this:
Select Case rsCardEvent(4).Type
Case adBSTR, adChar, adVarChar, adWChar, _
adVarWChar, adLongVarChar, adLongVarWChar
' It is a string '
Case Else
' It is not a string '
End Select
You can use the IsNumeric function available in VB6.
How about:
If TypeName(rsCardEvent(4).Value) = "String" then
http://msdn.microsoft.com/en-us/library/5422sfdf.aspx

Resources