VB6: How to pass temporary to a function in Visual Basic 6 - vb6

In C++ it is possible to pass a temporary object argument to a function:
struct Foo
{
Foo(int arg);
// ...
}
void PrintFoo(const Foo& f);
PrintFoo(Foo(10))
I am trying to implement something similar in Visual Basic 6:
'# Scroll bar params
Public Type ScrollParams
sbPos As Long
sbMin As Long
sbMax As Long
End Type
Public Function MakeScrollParams(pos As Long, min As Long, max As Long)
Dim params As ScrollParams
With params
.sbPos = pos
.sbMin = min
.sbMax = max
End With
Set MakeScrollParams = params
End Function
'# Set Scroll bar parameters
Public Sub SetScrollParams(sbType As Long, sbParams As ScrollParams)
Dim hWnd As Long
' ...
End Sub
However, Call SetScrollParams(sbHorizontal, MakeScrollParams(3, 0, 10)) raises an error: ByRef argument type mismatch. Why?

A couple of things need to change from your existing code:
You need to strongly-type the declaration of the MakeScrollParams function.
It returns an instance of the ScrollParams type, so you should specify that explicitly in the declaration. Like so:
Public Function MakeScrollParams(pos As Long, min As Long, max As Long) As ScrollParams
You need to remove the Set keyword from the last line in that function in order to avoid an "Object Required" compilation error. You can only use Set with objects, such as instances of classes. For regular value types, you omit it altogether:
MakeScrollParams = params
So the full function declaration would look like this:
Public Function MakeScrollParams(pos As Long, min As Long, max As Long) As ScrollParams
Dim params As ScrollParams
With params
.sbPos = pos
.sbMin = min
.sbMax = max
End With
MakeScrollParams = params
End Function
And calling it like this:
Call SetScrollParams(sbHorizontal, MakeScrollParams(3, 0, 10))
now works perfectly.

Maybe?
Public Function MakeScrollParams(pos As Long, min As Long, max As Long) As ScrollParams

Related

How to get the Browser UserAgent String in Visual Basic 6?

I am trying to get the UserAgent of the default browser using the ObtainUserAgentString API in Visual Basic 6. I found the documentation on the MSDN and tried to convert it to Visual Basic 6 but it did not work.
C++ (MSDN)
HRESULT ObtainUserAgentString(
_In_ DWORD dwOption = 0,
_Out_ LPCSTR *pcszUAOut,
_Out_ DWORD *cbSize
);
Visual Basic 6 API
Private Declare Function ObtainUserAgentString Lib "Urlmon.dll" (ByVal dwOption As Long, ByRef pcszUAOut As String, ByRef cbSize As Long) As Long
Private Function BrowserUserAgent() As String
Dim httpUseragent As String
Dim szhttpUserAgent As Long
httpUseragent = Space(512)
szhttpUserAgent = Len(httpUseragent)
Call ObtainUserAgentString(0, httpUseragent, szhttpUserAgent)
BrowserUserAgent = httpUseragent
End Function
Private Sub Command1_Click()
MsgBox BrowserUserAgent
End Sub
Aside from the fact this is a cruddy old ANSI entrypoint, everything you need appears to be documented.
Option Explicit
Private Const NOERROR As Long = 0
Private Const E_OUTOFMEMORY As Long = &H8007000E
Private Enum UAS_OPTIONSENUM
[_UAS_EXACTLEGACY] = &H1000&
UAS_DEFAULT = 0
UAS_7 = 7 'Compatible mode.
UAS_7_LEGACY = 7 Or [_UAS_EXACTLEGACY]
UAS_8 = 8
UAS_9 = 9
UAS_10 = 10
UAS_11 = 11
End Enum
Private Declare Function ObtainUserAgentString Lib "urlmon" ( _
ByVal dwOption As Long, _
ByVal pcszUAOut As Long, _
ByRef cbSize As Long) As Long
Private Function BrowserUserAgent( _
Optional ByVal Options As UAS_OPTIONSENUM = UAS_DEFAULT) As String
Const MAX_BUFFER As Long = 2048
Dim Size As Long
Dim Buffer() As Byte
Dim HRESULT As Long
Do
Size = Size + 128
ReDim Buffer(Size - 1)
HRESULT = ObtainUserAgentString(Options, VarPtr(Buffer(0)), Size)
Loop While HRESULT = E_OUTOFMEMORY And Size < MAX_BUFFER
If HRESULT = NOERROR Then
BrowserUserAgent = StrConv(LeftB$(Buffer, Size - 1), vbUnicode)
Else
Err.Raise &H8004D000, _
, _
"ObtainUserAgentString error &H" & Hex$(HRESULT)
End If
End Function
Private Sub Form_Load()
AutoRedraw = True
Print BrowserUserAgent()
Print BrowserUserAgent(UAS_7)
Print BrowserUserAgent(UAS_7_LEGACY)
Print BrowserUserAgent(UAS_8)
Print BrowserUserAgent(UAS_11)
End Sub
HRESULT ObtainUserAgentString(
_In_ DWORD dwOption = 0,
_Out_ LPCSTR *pcszUAOut,
_Out_ DWORD *cbSize
);
Param 2 is LongPointerCString. You always pass C strings ByVal which in reality passes the C string part of the B String ByRef. If it was a IN param you would have to end the string with a Chr(0) which is what real C strings have.
String arguments are a special case. Passing a string by value means you are passing the address of the first data byte in the string; passing a string by reference means you are passing the memory address where another address is stored; the second address actually refers to the first data byte of the string. How you determine which approach to use is explained in the topic "Passing Strings to a DLL Procedure" later in this chapter.
From Visual Basic Concepts in Help.

Change datatype from char to int in VBSCRIPT

Hi I need your help guys!
I am new to the system I am using and I am working on customizing a report that uses crystal report,
I need to get the value of the last page and compare this to the current page
(CurrentPage <> LastPage) , yet the data type of the Last Page is set to string/char..
I guess this is the reason why I can't get the result on the condition above. is there any way to change
its data type to Integer? or is there other way to get the LastPage integer value from a crystal reports without using the set variables for last page?
Thank You.
Remember that ALL textbox values is always a string value no matter of the content.
'Private Sub TextBox1_Change()
Dim IntValue As Integer
If TextBox1.TextLength > 0 Then
IntValue = TextBox1 * 1 ' method 1
'IntValue = TextBox1 + 1 - 1 ' method 2
'IntValue = TextBox1 + 0 ' method 3
MsgBox "IntValue = " & IntValue
End If
End Sub'
CInt specifies the integer datatype. VBScript normally autoconverts.
I have a different user id so ihave to reply here. This is a variant,
struct tagVARIANT {
VARTYPE vt;
WORD wReserved1;
WORD wReserved2;
WORD wReserved3;
union {
// C++ Type Union Name Type Tag Basic Type
// -------- ---------- -------- ----------
long lVal; // VT_I4 ByVal Long
unsigned char bVal; // VT_UI1 ByVal Byte
short iVal; // VT_I2 ByVal Integer
float fltVal; // VT_R4 ByVal Single
double dblVal; // VT_R8 ByVal Double
VARIANT_BOOL boolVal; // VT_BOOL ByVal Boolean
SCODE scode; // VT_ERROR
CY cyVal; // VT_CY ByVal Currency
DATE date; // VT_DATE ByVal Date
BSTR bstrVal; // VT_BSTR ByVal String
IUnknown *punkVal; // VT_UNKNOWN
IDispatch *pdispVal; // VT_DISPATCH ByVal Object
SAFEARRAY *parray; // VT_ARRAY|* ByVal array
// A bunch of other types that don't matter here...
VARIANT *pvarVal; // VT_BYREF|VT_VARIANT ByRef Variant
void * byref; // Generic ByRef
};
};
Variants normally autoconvert, need a string it changes to a string.
This is one OLE function (and probably what VB uses)
HRESULT VariantChangeType(VARIANT * pvDst, VARIANT * pvSrc, WORD wFlags, VARTYPE vt);
This function changes the type of a VARIANT without changing its value (if possible). To change a variable in place, make the destination the same as the source.
CInt forces it to be an integer, even if vbscript think it should be something else.
So a string containing 52 will be an integer if you try and add another number to it.
Also in basic a int is 16 bit and a long is 32 bit for compatability with 16 bit VB.

How to return a byte array as a variant in vb6

I've been trying to figure out why the function below throws an error saying "Type Mismatch" when it returns. From what I know about VB6, this should work without any issue, yet it obviously does not. Can anyone see what I am doing wrong here and let me know how I can fix it? Thanks in advance.
Private Function GetByteArray(source As Variant, index As Integer, length As Integer) As Variant
Dim buff() As Byte
ReDim buff(0 To length - 1)
Dim i As Integer
For i = 0 To length - 1
buff(i) = CByte(source(index + i))
Next i
GetByteArray = buff
End Function
It turns out that the problem did not have anything to do with the Function I posted, but rather with what I was doing with the result. I was using the method to get the bytes of a double, and then using CDbl to get the double value. This is where the error was really happening.
The way I should have been doing this is to use the following code:
CopyMemory rfcTest.rfcFloat, GetByteArray(buff, 0, 8), Len(rfcTest.rfcFloat)
Note that in order to use this, you must also declare the CopyMemoryMethod like this:
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSrc As Any, ByVal ByteLen As Long)

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

BestPractices: Out parameters vs complex return types in methods

Using complex return type:
Public Type TimeType
hours As Integer
minutes As Integer
End Type
Public Function ParseTimeField(time As String) As TimeType
Dim timeObject As TimeType
Dim amountOfDigitHours As Integer
If time = "" Then time = "0"
If HasHoursAndMinutesParts(time) Then
amountOfDigitHours = GetAmountOfDigitHours(time)
timeObject.hours = CInt(Left(time, amountOfDigitHours))
timeObject.minutes = CInt(Right(time, 2))
Else
timeObject.hours = 0
timeObject.minutes = CInt(time)
End If
ParseTimeField = timeObject
End Function
Private Function HasHoursAndMinutesParts(time As String) As Boolean
HasHoursAndMinutesParts = Len(time) > 2
End Function
Private Function GetAmountOfDigitHours(time As String) As Integer
GetAmountOfDigitHours = Len(time) - 2
End Function
Call:
Dim timeObj As TimeType
timeObj = ParseTimeField(strTime)
OR using out parameters:
Public Function ParseTimeField(time As String, ByRef hours As Integer, ByRef minutes As Integer)
Dim timeObject As TimeType
Dim amountOfDigitHours As Integer
If time = "" Then time = "0"
If HasHoursAndMinutesParts(time) Then
amountOfDigitHours = GetAmountOfDigitHours(time)
hours = CInt(Left(time, amountOfDigitHours))
minutes = CInt(Right(time, 2))
Else
hours = 0
minutes = CInt(time)
End If
ParseTimeField = timeObject
End Function
Private Function HasHoursAndMinutesParts(time As String) As Boolean
HasHoursAndMinutesParts = Len(time) > 2
End Function
Private Function GetAmountOfDigitHours(time As String) As Integer
GetAmountOfDigitHours = Len(time) - 2
End Function
Call:
Dim hours As Integer
Dim minutes As Integer
Call ParseTimeField(strTime, hours, minutes)
BTW this is VB6 code =)
If you have a single return type, do not use an out parameter to return it.
In general, I find multiple ref/out parameters to be a code smell. If you need to return data from your method, it is better if it is in one coherent object.
I have a feeling we're going to see different opinions on this matter. Not sure there exists a best practice.
I usually prefer complex datatypes because I feel that is more in line with the original structure of functions where output parameter preceed the input parameters in the signature. Basically I don't like out parameters - they're superfluous. Whenever there's two ways of doing a thing in a programming language, you complicate unneccessary (guess I'm gonna get killed by Perl-fanatics stating this). You return data with the return statement. Period.
This said, I still find myself using out parameters often when I need to return two parameteres that have no natural grouping - i.e. would end up in a class which would be used solely in the return value of this specific function.
Whenever there's three or more parameters in the return data, I never use out simply because I find the calling code to be excessive verbose - needing to Dim the variables (or var'em in C#)
I tend to use out params as the universal style. Rarely need to implement helper functions that return UDT but these are usually private to the module so I can keep the scope of the UDT private to the module too.
In the latter case usually consume the retval like this
With ParseTimeField(strTime)
Debug.Print .hours, .minutes
End With
... and most probably would keep TimeType with private scope.
Definately a matter of opinion. I do use ByRef parameters from time to time, especially for utility functions which require a success/fail type scenario. For example, the TryParse functions in the .net framework do exactly this. Your parametrised function could look like:
Public Function ParseTimeField(time As String, ByRef hours As Integer, ByRef minutes As Integer) As Boolean
'Do stuff'
ParseTimeField = True 'or false depending on output success'
End Sub
Meaning you can call it like:
Dim hours As Integer
Dim mins as Integer
If ParseTimeField(time, hours, mins) = True Then
'It worked'
End If
However as things start to get more complicated, and you're actually returning business items as opposed to doing logical commands, then a separate class and a return type is more desirable. It also makes calling AND returning easier to maintain.

Resources