Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

VBScript and ActiveX

Status
Not open for further replies.

jfbeck

Programmer
Feb 6, 2002
7
US

I have an ActiveX DLL to read database connection settings from the Windows registry. It works great in VB 6, but am trying to use it in VBScript. Whenever I call a method of the DLL class, I get a Type Mismatch error.

My code looks like this:
Dim sServer, sDB, sLogin, sPW
Dim oConn
Dim oReg 'registry object
Dim cRegistryKey
Dim cEncryptKey
Dim vDBSettings()
Dim iCounter
Dim lRC

cRegistryKey="Software\CompName\DBConnections"

Set oReg = CreateObject("WinRegistry.clsRegistry")
Set oConn = CreateObject("ADODB.Connection")
lRC = oReg.CheckKey(cRegistryKey)
If lRC = 0 Then 'key exists
'get the registry settings and put into an array variable
vDBSettings = oReg.GetRegistrySettings(cRegistryKey)

'if settings found
If IsArray(vDBSettings) Then
'check to see that all values other than password not blank

etc ...

The first call returns a long, second returns an array. I get the type mismatch error on both.

 
First, why not just use WshShell.RegRead ?

As far as your real question, I've had trouble scripting my own objects unless I use variants for all properties and method parameters.

I suspect the reality is that:

* You SHOULD use strong typing, but

* This requires a type library for your component, to permit the scripting engine to properly coerce types to variants.

Anyone have a clearer picture to share? I admit mine is murky.
 
Thanks for your response. Considered using RegRead, but wanted to store some of the data in the registry in encrypted form, and the DLL does that. Plus the VBScript is only part of a larger project that involves other languages, so wanted a consistent way to retrieve this data for all programs.

I suspected that the variable typing in the DLL may be the problem. Guess I will try recompiling the DLL to return only variants and see if that works.
 
jfbeck - can you post the function declarations and return value assignments of your CheckKey & GetRegistrySetting methods.

I can understand why returning the array might be a problem, but not simply returning a long. Jon Hawkins
 
Thanks for all comments. I made it work by changing all arguments and return values in DLL public functions to variants.

jonscott8:
Here are the function declarations in the DLL, both the original (which didn't work) and the new (which does):

Original:
Public Function GetRegistrySettings(keyname As String, encryptkey As String) As Variant
'this function returns an array
Dim c_sValue As String
Dim c_sArray(3, 1) As String

c_sArray(0, 0) = "Server"
c_sArray(0, 1) = QueryValue(&H80000002, keyname, "Server")
c_sArray(1, 0) = "Database"
c_sArray(1, 1) = QueryValue(&H80000002, keyname, "Database")
c_sArray(2, 0) = "Login"
c_sArray(2, 1) = QueryValue(&H80000002, keyname, "Login")
c_sArray(2, 1) = Encrypt(c_sArray(2, 1), encryptkey)
c_sArray(3, 0) = "Password"
c_sArray(3, 1) = QueryValue(&H80000002, keyname, "Password")
c_sArray(3, 1) = Encrypt(c_sArray(3, 1), encryptkey)

GetRegistrySettings = c_sArray

End Function

Public Function CheckKey(keyname As String) As Long

'determine if specified key exists
Dim lResult As Long

CheckKey = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyname, 0, &H8, lResult)

End Function


Public Function GetRegistrySettings(keyname As Variant, encryptkey As Variant) As Variant

Dim c_sValue As Variant
Dim c_sArray(3, 1) As Variant
Dim sKeyname As String
Dim sEncKey As String

'convert variant arguments to strings for internal use
sKeyname = CStr(keyname)
sEncKey = CStr(encryptkey)

c_sArray(0, 0) = "Server"
c_sArray(0, 1) = QueryValue(&H80000002, sKeyname, "Server")
c_sArray(1, 0) = "Database"
c_sArray(1, 1) = QueryValue(&H80000002, sKeyname, "Database")
c_sArray(2, 0) = "Login"
c_sArray(2, 1) = QueryValue(&H80000002, sKeyname, "Login")
c_sArray(2, 1) = Encrypt(c_sArray(2, 1), sEncKey)
c_sArray(3, 0) = "Password"
c_sArray(3, 1) = QueryValue(&H80000002, sKeyname, "Password")
c_sArray(3, 1) = Encrypt(c_sArray(3, 1), sEncKey)

GetRegistrySettings = c_sArray

End Function

Public Function CheckKey(keyname As Variant) As Variant

'determine if specified key exists
Dim lResult As Long
Dim sKeyname As String
sKeyname = CStr(keyname)
CheckKey = RegOpenKeyEx(HKEY_LOCAL_MACHINE, sKeyname, 0, &H8, lResult)

End Function
 
I still find this issue a bit murkey.

I have had success with my own VB components when I consume/accept only Variants.

It seems that many other components are able to use strong typing but still interoperate with script though too.

JohnYingling - have you found anything definitive on this? I've always believed Variants were required myself, but recently I have become a lot less sure of myself on this point.
 
I'm not an expert on COM, but I think the issue may be that you can use strong typing in your component and use is successfully in VBScript *IF* the component includes a Type Library. However, I don't know how to create a type library with a VB component. Perhaps this is only a function of C?

Can anyone confirm/correct my theory?
 
FWIW, I typically use VFP to build our COM components; however, prior to version7, VFP did not support strong typing. So, I created a VB6 COM DLL with the below definition:

Public Function GetArray() As Variant
Dim aArray(3) As Variant
Dim nCnt As Integer

For nCnt = 0 To 3
aArray(nCnt) = Str(nCnt)
Next
GetArray = aArray
End Function
Public Function GetBoolean() As Boolean
Dim bBoolean As Boolean
lBoolean = True
GetBoolean = lBoolean
End Function
Public Function GetLong() As Long
Dim lLong As Long
lLong = 200000000
GetLong = lLong
End Function
Public Function GetDate() As Date
GetDate = Now
End Function

The following is the VBS, thru WSH, with which I interfaced the class:

Dim bArray()

Set oMine = WScript.CreateObject("Project1.MyClass")
WScript.Echo oMine.GetLong()
WScript.Echo oMine.GetBoolean()
WScript.Echo oMine.GetDate()
baArray = oMine.GetArray()
WScript.Echo bArray(1)

ALL method calls to the class return the appropriate values, with the exception of the method that returns the array, which leads me to my question for jfbeck. Did your GetRegistrySettings method return the array properly? Mine produced a type mismatch error.
Jon Hawkins
 
jonscott8.
It is all murky with variants.
As to the array in VBScript, Dim bArray(), try taking off the parens. Dim bArray(), is an array of variants rather than a variant that contains an array. There is a difference as far as VBScript/VB6 interface is concerned, at least in ASP 1 when I spent a day or two figuring it out.
I believe the DLL should be changed also to
Public Function GetArray() As Variant
Dim aArray As Variant ' Variant first
Redim aArray(3) ' Put Array in the Variant
Dim nCnt As Integer

For nCnt = 0 To 3
aArray(nCnt) = Str(nCnt)
Next
GetArray = aArray
End Function
Techically, you created an array of variants, returned the array of variants INSIDE another Variant and VBscript could not store a Variant into an array of Variants that had not been allocated (which if it was you probably would have had to store it in ONE of elements). If it makes cpmplete sense then I question your sanity. NET is "blowing them away" becuase of all the cross language compatibilty problems and the "need" for strong typing.

Way back, I'm sure I tried
Dim bArray() ' inVBScript and
Public Function GetArray() As Variant() ' in VB5
an ran into trouble.

I've "heard" also that you can set VB6 to accept ByVal and pass Clng(varLng), Cstr(varStr) etc but I have never tried it.

A = "ABCD" ' in VBScript
CALL VB6SUB(Cstr(A))
....
VB6SUB SUB (ByVal strA As String)

I gave m'self a headache. Methinks I need a beer and a couple of aspirin...or vice versa.




Compare Code (Text)
Generate Sort in VB or VBScript
 
jonscott,
Yes the array was returned properly once I changed the argument type to variant.

jfbeck
 
FWIW, I had to make the following changes to the VBS:

Dim bArray 'remove parens per JY's comment
bArray = oMine.GetArray() 'typo baArray corrected

NO changes were necessary in the COM DLL.

FYI - I'm using WSH 5.1, VBS 5.5.6330, VB6 SP4, Win98SE. Jon Hawkins
 
jonscott:
Okay, did some more testing after your last post. It now appears that return values can be other data types, but function arguments must be variants. My DLL works in VBS with return values other than variant (long, etc.). However, if the public methods of the DLL have any input arguments and I set those to anything other than variant, I get a type mismatch error. To test this, try modifying your COM object so that one function has an argument that gets passed in (even if you don't use it in the function). Set the argument type as string or long and see if you get an error calling the function from VBS. If you do, then change the data type of the argument to variant and see if the error goes away.

I would appreciate knowing how this test turns out.
Thanks,
jfbeck
 
jfbeck - per your request I added the following methods to the above class:

Public Function SendBoolean(bBoolean As Boolean) As Boolean
SendBoolean = bBoolean
End Function
Public Function SendLong(lLong As Long) As Long
SendLong = lLong
End Function
Public Function SendDate(dDate As Date) As Date
SendDate = dDate
End Function
Public Function SendString(sString As String) As String
SendString = sString
End Function

Here's the VBS:

Set oMine = WScript.CreateObject("Project1.MyClass")
WScript.Echo oMine.SendBoolean(False)
WScript.Echo oMine.SendDate(Now)
WScript.Echo oMine.SendLong(100000000)
WScript.Echo oMine.SendString("Hello World")

ALL methods returned whatever was passed in appropriately. Let me know if you need anything further. Jon Hawkins
 
I wonder what happens when you pass varables instead of literals.
Dim strW: strW = "Hello World"
Dim lngW: lngW = 100000000
Dim dteW: dteW = Now
Dim blnW: blnW = False
Set oMine = WScript.CreateObject("Project1.MyClass")
WScript.Echo oMine.SendBoolean(blnW)
WScript.Echo oMine.SendDate(dteW)
WScript.Echo oMine.SendLong(lngW)
WScript.Echo oMine.SendString(strW)


Click on Passing Parameters.

This one says
"VBScript will pass parameters to a method by value if the argument's data type is NOT declared as a variant and the parameter is passed by reference if the argument's data type is declared as variant by the method."

"Parameters to be passed by reference to a method should always be declared as a variant data type by the method, while parameters to be passed by value can be declared as any type by the method."

The next question is "What happens if you try to change a ByRef VBScript argument in the VB program where the parameter was defined as other than variant." It seems that Com Automation performs certain conversions for IN parameters behind the scenes but may refuse to convert Out parameters. Compare Code (Text)
Generate Sort in VB or VBScript
 
JohnYingling:
Thanks, I think you've solved the issue: you can pass non-variant data types to the COM object if the arguments are specified in the COM methods as ByVal. Since VB defaults to ByRef and I did not specify ByVal, I was getting a type mismatch error on string and long arguments (argument was string but reference was to variant). When I change the COM function declaration to receive arguments ByVal, I can use any data type.

jonscott8: is this consistent with your experience?

jfbeck
 
I mentioned in my second post that
"I've heard also that you can set VB6 to accept ByVal and pass Clng(varLng), Cstr(varStr) etc but I have never tried it."

The part about having to use Clng and Cstr is probably incorrect. Compare Code (Text)
Generate Sort in VB or VBScript
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top