1.

Solve : Return array from ActiveX exe?

Answer»

I have an activex exe written in vb6 that I am accessing using vbscript. I have a sub in this activex exe with a parameter that needs to return an array. I tried using a function to return a variant type array, but it would not allow access to the array in vbscript. Basically, I just need to figure out a way to pass an array or a collection from my vb6 exe to my vbscript. I have seen examples, but none of them will work for me probably because most of them are talking about ASP.

After reading this code I know you are going to tell me there is already a way to enumerate registry keys in vbscript. I know that. I don't have WMI enabled on this system and I need another way to enumerate registry in vbscript, so I am writing my own code.


The error occurs on the line that reads "reg.EnumKeys "HKEY_LOCAL_MACHINE", "SYSTEM\CurrentControlSet\Services", keys"
Type mismatch EnumKeys

That does not make any sense because EnumKeys is a sub in my vb6 exe.

Here is part of the code in the activex exe:
Code: [Select]Public Sub EnumKeys(strSection As String, keyName As String, retKeyNames() As Variant)
Dim hKey As Long
Dim keyNames() As Variant
Dim section As Long
Dim i As Long

' Convert from string to the section code
section = GetSection(strSection)

' Open the key.
If RegOpenKeyEx(section, keyName, 0, KEY_ALL_ACCESS, hKey) <> ERROR_SUCCESS Then
MsgBox "Error opening key."
Exit Sub
End If

' Enumerate
keyName = Space(KEY_LENGTH)
While RegEnumKey(hKey, i, keyName, KEY_LENGTH) = ERROR_SUCCESS
ReDim Preserve keyNames(i)
keyNames(i) = keyName
keyName = Space(KEY_LENGTH)
i = i + 1
Wend

' Close the key.
If RegCloseKey(hKey) <> ERROR_SUCCESS Then
MsgBox "Error closing key."
End If

retKeyNames = keyNames
End Sub

Here is the vbscript to enumerate registry:
Code: [Select]Option Explicit
Dim reg
Dim keys
Dim i
Dim io
SET io = CreateObject("wshshell.io")
Set reg = CreateObject("wshshell.registry")

reg.EnumKeys "HKEY_LOCAL_MACHINE", "SYSTEM\CurrentControlSet\Services", keys

For i = 0 to ubound(keys)
msgbox keys(i)
Next

wscript.stdout.write "Press any key to continue. . ."

While io.GetKeysASCII = vbNull
wscript.sleep 50
Wend

Set io = Nothing
Set reg = Nothing
VB6 ActiveX EXE Project name "TestEXE", Class "TestClass":

Code: [Select]Option Explicit

Public Function ReturnArray(ByVal Argument As String, ByVal totalsize As Integer) As Variant

Dim returnme As Variant, I As Integer
ReDim returnme(0 To totalsize - 1)
For I = 0 To totalsize - 1
returnme(I) = Argument & Str(I)
Next


ReturnArray = returnme


End Function

After compiling I ran this VBScript:

Code: [Select]Dim objaccess,returnedvalue
Dim I
set objaccess = CreateObject("TestEXE.TestClass")
returnvalue = objaccess.ReturnArray("String",15)
for I = 0 to Ubound(returnvalue)
WScript.Echo(I)
Next


And this was the result, as expected:
Code: [Select]D:\>C:\windows\syswow64\cscript.exe testacx.vbs
Microsoft (R) Windows Script Host Version 5.8
Copyright (C) Microsoft Corporation. All rights reserved.

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14

So, the question is, what's different? Well, there are a few differences. For example your code is using a Sub() procedure, and returning the value via a ByRef (implied) Array argument; Mine is returning that value directly as a Variant array, which brings up the other difference- note that the function, as well as the local variable used to hold the array, are declared as Variant Array (Dim X as Variant) rather then an array of Variants (Dim X() as Variant).

Additionally, the Type Mismatch error MAKES perfect sense; in your VB6 Executable, the ByRef argument is of Type Variant() (an array of variants), whereas the variable that VBScript is passing it is Variant . I think it is impossible to even create a Array of variants in VBScript, all arrays in VBScript are Variant arrays. The terminology can get confusing. Here's a quick once-over:

As you know, a Variant Variable can store any number of different types; One of these types is in fact, an array. So, a normal Variant Variable can in fact hold an entire array of values. It's important to note that a Variant Variable HOLDING an Array is quite different from a Array of Variant Variables, I don't remember a lot of the differences ATM but I believe they are marshalled quite differently (being that one is a Variant variable, and the other is a first-class Array).

Anyway, long story short, the best option would probably be to either try declaring "keys" as an array:

Code: [Select]Dim keys() 'instead of Dim keys

Which might work (I'm not sure though, since I think VBScript might internally just create a Variant Array (and not the desired Array of Variants). The other option is to simply use a normal Variant Variable for the ByRef argument, and use Redim() to turn it into an array in the routine. (Wether you use a function instead is entirely optional, of course)

Quote

and use Redim() to turn it into an array in the routine.

That fixed it ReDim Preserve aparently doesn't work the first time. I had to start it out by using ReDim keyNames(0) to make the variant into an array. I also decided to make it into a function instead of a sub. I removed the msgboxes because they don't ever display, I even made a new sub with a msgbox in it and it didn't work. There's only one small problem left with the "keyName = Space(KEY_LENGTH)". In the console, it shows all the extra spaces after the key name. I figure that's because there is no null termination CHARACTER, but it's weird because it displays fine in a msgbox (no extra spaces).

So I have two final questions. Why won't msgboxes display from the activex exe? And how do I get rid of the trailing spaces in the console?

Thanks so much for your help I was at this for three hours.

Code: [Select]Public Function EnumKeys(strSection As String, keyName As String) As Variant
Dim hKey As Long
Dim keyNames As Variant
Dim section As Long
Dim i As Long

'FORCE IT TO TURN VARIANT INTO ARRAY
ReDim keyNames(0)

' Convert from string to the section code
section = GetSection(strSection)

' Open the key.
If RegOpenKeyEx(section, keyName, 0, KEY_ALL_ACCESS, hKey) <> ERROR_SUCCESS Then
Exit Function
End If

' Enumerate
keyName = Space(KEY_LENGTH)
While RegEnumKey(hKey, i, keyName, KEY_LENGTH) = ERROR_SUCCESS
ReDim Preserve keyNames(i) '<---- NOT GOOD ENOUGH. WON'T FORCE IT TO BE ARRAY FIRST TIME
keyNames(i) = keyName
keyName = Space(KEY_LENGTH)
i = i + 1
Wend

' Close the key.
RegCloseKey (hKey)

EnumKeys = keyNames
End Function

Here is the vbscript:
Code: [Select]Option Explicit
Dim reg
Dim keys
Dim values
Dim i
Dim io
Set io = CreateObject("wshshell.IO")
Set reg = CreateObject("wshshell.Registry")

keys = reg.EnumKeys("HKLM", "SYSTEM\CurrentControlSet\Services")
values = reg.EnumValues("HKLM", "SYSTEM\CurrentControlSet\Services\Tcpip")

For i = 0 to Ubound(keys)
wscript.stdout.writeline keys(i)
Next
AnyKey
For i = 0 to Ubound(values)
wscript.stdout.writeline values(i)
Next
AnyKey

Set io = Nothing
Set reg = Nothing

Sub AnyKey()
wscript.stdout.writeline "Press any key to continue. . ."
wscript.sleep 200
While io.GetKeysASCII = 0
wscript.sleep 50
Wend
End Sub
Quote from: Linux711 on November 04, 2010, 08:01:45 AM
There's only one small problem left with the "keyName = Space(KEY_LENGTH)". In the console, it shows all the extra spaces after the key name. I figure that's because there is no null termination character, but it's weird because it displays fine in a msgbox (no extra spaces).
The MsgBox() Function Calls MessageBoxA(), As I'm sure you are aware.

When VB passes the String to MessageBoxA, it (converts it to ASCII from the native VB Unicode) and then tacks a null character on the end. Since your string probably have a null character (as returned from the Registry function) Messagebox only displays up to that first null character.

The Console, However, does not work this way. The Console is written to using WriteFile(), which basically takes a data block, a size, and then writes it to the handle. In this case, WScript.Echo() passes in the string, and the string length; so ALL of the string gets written. (Note that null characters usually just appear as spaces in the console). The solution? Use something like:

Code: [Select]Struse = Left$(struse,instr(struse,vbnullchar)-1)
before returning it from the EXE.


Quote
Why won't msgboxes display from the activex exe?
Don't know. It works fine for me. Make sure "unattended Execution" isn't checked in the Project properties, Msgbox is ignored if that option is set on compilation.
[/quote]

Quote
Thanks so much for your help I was at this for three hours.
You're Welcome

EDIT:
Actually, now that I think about it, you may find this class useful. As you can see from the comments, I wasn't the original author; however, It is heavily modified from the original version, I fixed several bugs, made it independent of a Module that was originally required, and probably most interesting, I switched the APIs from using the ANSI version (RegReadKeyExA, for example) to using the Wide (Unicode) versions (RegReadKeyExW) when it detects it's running on a NT platform.

Essentially, it wraps almost all the Registry manipulation functions. I've found it quite useful. That being said, I'm not sure how well it would work with VBScript, since there is a lot of strong typing and ByRef Arguments as well in the various public methods. It might make creating your versions easier I guess.


Example-
Code: [Select]'Declarations:
Private mReg as cRegistry
'-------------------------------------------------------------------
'Class Initialize should have this:
Private Sub Class_Initialize()
Set mReg = new cRegistry
End Sub

'and then your EnumKeys Function could be changed as well:
Public Function EnumKeys(strSection As String, keyName As String) As Variant
Dim returnvar as Variant

Dim sCount as Long,sKeys() as String
if mReg.EnumerateSections(sKeys, sCount) Then
Redim returnvar(0 to sCount-1)
'only problem is the fact that it now has to be "CONVERTED" to the variant array...
For I = 1 to sCount
returnvar(I-1) = sKeys(I)

Next I

End If
EnumKeys = returnvar
End Function



It's far from perfect; the strong typing of byRef Arguments means it wouldn't work directly with VBScript, at least not without helper functions, and 1 based arrays? what was I thinking.... Actually, no, I'll just blame the original author for that . But it might be helpful.


Discussion

No Comment Found