Declaring VB6 DLL Functions with two Dimensional Array - vb6

I have these arrays:
Dim sInp(10) As String
Dim dInp(90) As Double
Dim sOut(500, 10) As String
Dim dOut(500, 100) As Double
Now I have to declare the following function:
Integer MyFunction(sInp, dInp, dOut, sOut)
How do I do that?
So far I have this Code:
Declare Function MyFunction Lib "C:\Path\To\My\dll.dll" (ByRef sInp() As String, ByRef dInp() As Double, ByRef dOut() As Double, ByRef sOut() As String): Integer
How do I make the last two params to multidimensional arrays?
I already did register the library and the IDE can find the function but I can't use it so I thought I have to declare it like this.
This is all of the documentation I got from the company:
Integer MyFunction(sInp, dInp, dOut, sOut )
sInp[10] (string type)
dInp[90] (double type)
sOut[500, 10] (string type) (The String are initialized as “”)
dOut[500, 100] (double type) (The value are initialized as -9999.99 )

If you say you registered the DLL with "regsvr32" and got a success message then it is an ActiveX DLL (a COM object) and as such you need to instantiate it as you do with any object. First you need to click Project -> References, look for the name of your DLL in the list and click the checkmark beside it. Then you can declare instances from it:
Dim ObjDLL as New WhateverTheDLLClassIsCalled
Then it should expose the "MyFunction" method and you should see its parameters:
Call ObjDll.MyFunction()
The "Declare Function MyFunction Lib" syntax is for regular DLLs that do not need to be registered.

Related

Using Nim to Creating a vb6 dll that Returns String

Nim Compiler Version 1.6.6 [Windows: i386]
Compiled at 2022-05-05
Copyright (c) 2006-2021 by Andreas Rumpf
active boot switches: -d:release
Cmd Compile
nim c --cpu:i386 -d:release --app:lib --nomain mydll.nim
Hi there, I was able to return a Long value, now I'm trying to get string values.
I googled to find some exemples and find out here:
https://my.oschina.net/yuekcc/blog/775990
I'm getting this error:
VB6:
Private Declare Function MyStr Lib "mydll.dll" (ByVal s As String) As String
Private Declare Function return_multiply Lib "mydll.dll" Alias "return_multiply#8" (ByVal a As Long, ByVal b As Long) As Long
Private Sub Form_Click()
MsgBox MyStr("?") 'error
MsgBox return_multiply(5, 4) 'ok
End Sub
Another question, why the Alias has #8 at the end? return_multiply#8
Nim:
import encodings
const
vbCodePage = "GB2312"
vbTrue* = 1
vbFalse* = 0
type
VBString* = cstring
VBBoolean* = int32
proc MyStr*(): cstring {.stdcall, exportc, dynlib.} =
result = $"teste"
proc fromVBString*(a: VBString): string =
return encodings.convert($a, "UTF-8", vbCodePage)
proc toVBString*(a: string): VBString =
return VBString(encodings.convert(a, vbCodePage, "UTF-8"))
proc return_multiply*(a, b: int): int {.stdcall, exportc, dynlib.} =
a * b
Since you want to export toVBString in the dynamic library, you have to add the exportc, dynlib pragmas to it as to others:
proc toVBString*(a: string): VBString {.exportc, dynlib, stdcall.} =
return VBString(encodings.convert(a, vbCodePage, "UTF-8"))
But the definition is wrong anyway - I don't know what type VB's String is, but it certainly is different from the Nim string, and I'm not sure why you are importing it in your VB program.
Also, I don't think it's correct to just convert the Nim string to cstring to pass it to VB - Nim's cstring doesn't actually "own" the string data, so when the Nim runtime frees the Nim string, the cstring of it will point to invalid data. I don't know if VB has specific APIs for that or not though.
I know nothing about Nim, but the way to create a VB string is to make an OLE BSTR. SysAllocStringLen() would probably be your best bet. Others in that family might be better depending on what your string data looks like and where it comes from. Check out the MS docs.

Why is the result of VarPtr(ByVal str) the same as StrPtr(str) ? (VB6)

In VB6 VarPtr should return the address of the variable, in this case the address of the variable str which is allocated on stack, and holds a pointer to a string in memory. StrPtr (or StrPtr) should return the address of the allocated string in the memory. ByVal should just create a copy, but in this case, it works strangely:
Dim str As String
str = "asd"
Debug.Print VarPtr(str)
Debug.Print VarPtr(ByVal str)
Debug.Print StrPtr(str)
The result is:
1636452
110882980
110882980
Why is the result of VarPtr(ByVal str) the same as StrPtr(str) ?
Strings passed ByVal pass the address of the first character of the containing C string in the BStr. StrPtr does the same.
There are two reasons that spring to mind for doing this. Passing Unicode to API calls and string building.
Passing Unicode to API calls
You could use StrPtr on a string rather than a byte array when sending Unicode strings to API functions.
Dim ByteArr() as Byte
Var1="My Text"
ByteArr = Var1
APICall(ByteArr(0))
APICall(StrPtr(Var1))
Should both pass a Unicode string to an API functions. Unicode strings are converted to ANSI strings when using the declare statement as Win 95 didn't do unicode.
String Building
On the other hand if you are string building, then that is built in to VBA using the Left, Right, and Mid statements, not functions (they are overloaded).
Sub Main()
Dim Var As String
Var = "gggggggggggg"
MsgBox StrPtr(Var)
Mid(Var, 1, 2) = "xx"
MsgBox StrPtr(Var) & " - " & Var
End Sub
ByVal Versus ByRef
Some authors like to say that the ByVal keyword is overloaded for
strings, meaning that it takes on a different meaning when applied to
strings than when applied to other variables. Frankly, I don't see it.
Writing:
ByVal str As String
tells VB to pass the contents of the BSTR (actually the ABSTR), which is the pointer to the character array. Thus, ByVal is acting
normally--it just happens that the content of the BSTR is a pointer to
another object, so this simulates a pass by reference. Similarly:
ByRef str As String
passes the address of the BSTR, as expected.
Win32 API Programming with Visual Basic, Chapter 6 Strings, O'Reilly, from MSDN Library October 2001
StrPtr
Strings in Visual Basic are stored as BSTR's. If you use the VarPtr on
a variable of type String, you will get the address of the BSTR, which
is a pointer to a pointer of the string. To get the address of the
string buffer itself, you need to use the StrPtr function. This
function returns the address of the first character of the string.
Take into account that Strings are stored as UNICODE in Visual Basic.
To get the address of the first character of a String, pass the String
variable to the StrPtr function.
Example:
Dim lngCharAddress as Long
Dim strMyVariable as String
strMyVariable = "Some String"
lngCharAddress = StrPtr(strMyVariable)
You can use this function when you need to pass a pointer to a
UNIOCODE string to an API call.
HOWTO: Get the Address of Variables in Visual Basic Q199824 Microsoft Knowledge Base, MSDN October 2001.
VarPtr is not part of the VBA/VB6 language, therefore companies that implement VBA (like Corel) may not implement it in their VBA. The VBA spec is here https://msdn.microsoft.com/en-us/library/dd361851.aspx

Is Nothing type mismatch VB6

I tried everything, but everything gives me type mismatch:
Type UserType
...
End Type
Dim SomeArray() As UserType
...
If SomeArray() Is Nothing Then <do smth>
If SomeArray() Is Empty Then <do smth>
If SomeArray Is Nothing Then <do smth>
If SomeArray Is Empty Then <do smth>
I do want to know when in my array of user-defined type is no elements! Because I don't want to use additional variables if I can use VB6 possibilities.
I'll use
Erase SomeArray
when its size = 1 (UBound(SomeArray) = 1) and i want to remove last element.
WHAT I DO WRONG? XD
VB6 "Is Nothing" applies to objects, not VB6 arrays.
"Ubound(myarray)", or "Ubound - LBound" is the way to determine an array's current length in VB6.
FYI, using a VB6 Collection might be a much better for you.
Heh, I found an arse-way to solve this problem from VBForums "VB6 - Returning/Detecting Empty Arrays". B)
(L/UBound doesn't work with empty arrays - it returns out of range subscript. ;) )
So...
Private Declare Function ArrPtr Lib "msvbvm60" Alias "VarPtr" (Ptr() As Any) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (lpvDest As Any, lpvSource As Any, ByVal cbCopy As Long)
Public Function Peek(ByVal lPtr As Long) As Long
Call CopyMemory(Peek, ByVal lPtr, 4)
End Function
Remember declare your own variables BEFORE definition of this subroutines! Or it will cause some strange mistakes (VB suddenly replaced my Exit Sub statement with Exit Function!).
Then I used
If Peek(ArrPtr(SomeArray)) = 0 Then
MsgBox "Looks like empty array SomeArray() before ReDim ^_^"
End If
and
Erase SomeArray
If Peek(ArrPtr(SomeArray)) = 0 Then
MsgBox "Looks like empty array SomeArray() after Erase ^_^"
End If
and everything works fine!
Not very simple, but fine.
Thx everybody, I shall learn this chained list named Collection.
Especially thanks for VBForums, they are really geeked ones.

Calling CryptCATCatalogInfoFromContext Throw errors

I want to call the function CryptCATCatalogInfoFromContext available in wintrust.dll. But when I do that I receive an error saying Specified array was not of the expected type.
I am using the following code to invoke method. It seems some data type I'm using is mismatching with the required data type.
'import wintrust.dll
<DllImport("Wintrust.dll", PreserveSig:=True, SetLastError:=False)> _
Private Shared Function CryptCATCatalogInfoFromContext(ByVal catalogContext As IntPtr, ByVal hash As CATALOG_INFO_, ByVal dwFlags As Integer) As Boolean
End Function
'create structure CATALOG_INFO
<StructLayout(LayoutKind.Sequential)> _
Public Structure CATALOG_INFO_
Public cbStruct As UInteger
<MarshalAs(UnmanagedType.SafeArray)> _
Public wszCatalogFile() As Char
End Structure
I have already obtained CatalogContext.
Dim infoStruct As New CATALOG_INFO_()
infoStruct.cbStruct = 256
Dim c(255) As Char
infoStruct.wszCatalogFile = c
CryptCATCatalogInfoFromContext(CatalogContext, infoStruct, 0)
the last line throws the error Specified array was not of the expected type.
Have I used a wrong data type for the array?
Yes, wrong declaration. It is not a SafeArray, it is a Unicode string. The proper declaration is:
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)> _
Public Structure CATALOG_INFO
Public cbStruct As UInteger
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=260)> _
Public wszCatalogFile As String
End Structure

Passing a string in VBScript to a COM Function which expects a BSTR*

I calling a third-party COM function in my VBScript. The method signature is as follows:
HRESULT ParseXML ([in] BSTR *textIn,[in] VARIANT_BOOL *aValidateIn,[out, retval] MSXML2.IXMLDOMDocument2 **aXMLDocOut)
In my VBScript the following call gives back a type mismatch:
Dim someText
someText = "Hello"
Dim response
response = ParseXml(someText, False)
But passing in the string literal works fine:
Dim response
response = ParseXml("Hello", False)
Any ideas what I need to do on the VBScript side?
BSTR is already a pointer.
BSTR* is therefore a pointer to pointer.
That is, you are passing a string by reference (ByRef textIn As String).
When you pass a variable by reference, the types must match. someText is VARIANT.
If you were to pass just BSTR (ByVal textIn As String), VB would handle the conversion for you.
Any ideas what I need to do on the VBScript side?
If you are sure it's the script you want to fix, not the library, then trick VB into using a temp variable which will be passed by ref:
response = ParseXml((someText), False)
Did you really write ParseXml(somText, False) in your script? Then it is a typo; it should be someText.

Resources