-
Aug 7th, 2017, 08:10 PM
#1
Thread Starter
Lively Member
VB paramArray equivalent in C++ export DLL
In VB.NET or VB6, I can do something like this:
Code:
'VB.NET:
Public Function calcSum(ByVal ParamArray args() As Double) As Double
calcSum = 0
If args.Length <= 0 Then Exit Function
For i As Integer = 0 To UBound(args, 1)
calcSum = calcSum + args(i)
Next i
End Function
'VB6:
Private Function calcSum(ParamArrayargs() As Variant) As Single
Dim total As Single
Dim i As Integer
For i = 0 To UBound(args)
total = total + args(i)
Next i
calcSum = total
End Function
And call the Function with one, two, three or more params as I want:
Code:
returnedValue = calcSum(4, 3, 2, 1)
Is there any equivalent version in c++ export DLL so I can call it from vb6/vb.net? Thank you all very much!
Last edited by vietnamvodich; Aug 7th, 2017 at 08:24 PM.
-
Aug 8th, 2017, 08:24 AM
#2
Re: VB paramArray equivalent in C++ export DLL
A VB6 ParamArray is just a VARIANT SAFEARRAY as the last parameter C/C++.
It just has to be marked as such in the typelib, so that VB knows to use it as a Param Array.
[vararg]
https://msdn.microsoft.com/en-us/lib...(v=vs.85).aspx
-
Aug 8th, 2017, 11:14 AM
#3
Re: VB paramArray equivalent in C++ export DLL
This is different in c and c++ (although the c solution can be used with c++ it's not recommended).
For c, use variable arguments list. See https://msdn.microsoft.com/en-us/library/kb57fad8.aspx and the given example of how to use.
In c++, use an initialiser list. See http://www.cplusplus.com/reference/i...tializer_list/
Consider for c++
Code:
#include <initializer_list>
#include <iostream>
using namespace std;
template<typename T>
auto sum(initializer_list<T> il)
{
T s { 0 };
for (const auto& l : il)
s += l;
return s;
}
int main()
{
cout << "Total 1 is " << sum({ 1, 2, 3, 4, 5 }) << endl;
cout << "Total 2 is " << sum({ 11, 21, 31 }) << endl;
cout << "Total 3 is " << sum({ 1.1, 3.4, 4.6, 7.8 }) << endl;
}
This could also be done using a variadic template. Consider
Code:
template<typename T>
constexpr auto sum() {
return T{};
}
template<typename I, typename... Is>
constexpr auto sum(I first, Is... rest) {
return first + sum<I>(rest...);
}
....
cout << "Total 1 is " << sum( 1, 2, 3, 4, 5 ) << endl;
cout << "Total 2 is " << sum( 11, 21, 31 ) << endl;
cout << "Total 3 is " << sum( 1.1, 3.4, 4.6, 7.8 ) << endl;
This method doesn't require the {} in the calling function, but the implementation is slightly more complicated.
But where does export DLL come into this?
Last edited by 2kaud; Aug 9th, 2017 at 03:42 AM.
Reason: Added variadic template
All advice is offered in good faith only. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/
C++23 Compiler: Microsoft VS2022 (17.6.5)
-
Aug 9th, 2017, 04:43 AM
#4
Thread Starter
Lively Member
Re: VB paramArray equivalent in C++ export DLL
Hi, thank you all for your reply!
I'm not sure if initializer_list<T> is compatible to vb6/vb.net as 2kaud's answer because I need to write an export DLL for calling convenient from vb.net and vb6. But it's good choice if I need to write a native c++ app without using vb6/vb.net.
As DEXWERX's recommended about SAFEARRAY. I just found this code from rxbagain (codeguru : http://forums.codeguru.com/showthrea...-VC-DLL-to-VB6 ) for passing byte array to c++ dll.
Code:
Private Declare Sub ProcByteArrayVC Lib "VCDLL.dll" (ByRef bArr() As Byte)
Private Sub Form_Load()
Dim bArr() As Byte, ctr As Long
ReDim bArr(0 To 20) As Byte
For ctr = LBound(bArr) To UBound(bArr)
bArr(ctr) = ctr
Next ctr
Call ProcByteArrayVC(bArr)
End Sub
CPP CODE (VCDLL.DLL)
void WINAPI ProcByteArrayVC(SAFEARRAY **p_ppArr)
{
BYTE *pByteArr = NULL;
SAFEARRAY *pArr = *p_ppArr;
HRESULT hr = SafeArrayAccessData(pArr, (void HUGEP**)&pByteArr);
if (SUCCEEDED(hr)) {
for (ULONG ctr = 0; ctr < pArr->rgsabound[0].cElements; ctr++) {
printf("index = %d value = %d", ctr, pByteArr[ctr]);
}
}
SafeArrayUnaccessData(pArr);
}
And the call from the C++ dll to the VB (callback) function
Code:
Sub ProcByteArrayVB(ByRef bArr() As Byte)
Dim strText As String, ctr As Long
For ctr = LBound(bArr) To UBound(bArr)
strText = strText & " " & bArr(ctr)
Next ctr
MsgBox strText
End Sub
CPP CODE
void CallVBWithArray()
{
SAFEARRAY *pArr;
... ... // deal with the pArr first (eg. filling the values, etc.)
ProcByteArrayVB(&pArr);
}
As theoretically, I can convert it to array of long or array of integer. But what about array of Variant? I need to pass an array of variant to c++ dll. Then in c++ I need to iterate the array to display the value and the type name of each element. Anyone know how to do it? Thank you all!
-
Aug 9th, 2017, 05:58 AM
#5
Re: VB paramArray equivalent in C++ export DLL
If you want to have the c/c++ code in a dll, that eliminates any code that uses a template. The template code and the calling function needs to be in the same compilation unit. So this leaves the c way using variable argument lists. The problem with using variable argument lists is that you don't know how many arguments there are (if you don't know or can't determine, you either need to specify a special terminating value or specify the number as the first parameter) and you can't tell the type of the argument. Variable argument lists were introduced into c to support the printf() type functions where the type/number of arguments is explicitly specified.
In c/c++ there is no concept of 'Variant' type. Boost has a 'pseudo' variant type, but this won't help you within a dll and with VB interfacing.
SAFEARRAY would seem to be the best fit and does have the concept of 'Variant' - type, VT_VARIANT, but I've not used this and can't comment upon how to deal with this type in c++.
All advice is offered in good faith only. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/
C++23 Compiler: Microsoft VS2022 (17.6.5)
-
Aug 9th, 2017, 10:04 AM
#6
Thread Starter
Lively Member
Re: VB paramArray equivalent in C++ export DLL
Thank you 2kaud for your reply and your effort to help me. May be I will try a different method if this technique earn too much time.
-
Aug 10th, 2017, 08:32 AM
#7
Re: VB paramArray equivalent in C++ export DLL
Originally Posted by vietnamvodich
Thank you 2kaud for your reply and your effort to help me. May be I will try a different method if this technique earn too much time.
the Variant type in VB is just the COM type VARIANT struct in C++. --> https://msdn.microsoft.com/en-us/lib...(v=vs.85).aspx
I'll write up a demo if I have a chance later.
but 1) you can't just use a SAFEARRAY(VARIANT)* without understanding basic interop with VB.
Especially how to handle the VARIANT struct subtype of ByRef (VT_BYREF) in C/C++. This is especially the case with ParamArray/vararg parameters.
2) without a typelib VB6 won't know to treat your SAFEARRAY(VARIANT) as a ParamArray.
NOTE:
the way C/C++ handles variable arguments is another issue altogether that I'm sure you probably don't care about, as it's generally not compatible with VB's Standard Calling convention. It is however possible to call a C Calling Convention function using the DispCallFunc API.
I think i have an VB6 to C/C++ interop tutorial link somewhere...
some more info here --> http://computer-programming-forum.co...72acaeb9d8.htm
and here. although McKinney has a habit of over complicating things. --> https://ecs.syr.edu/faculty/fawcett/...SafeArrays.htm
Last edited by DEXWERX; Aug 10th, 2017 at 09:00 AM.
-
Aug 10th, 2017, 03:09 PM
#8
Re: VB paramArray equivalent in C++ export DLL
Here's the DLL function.
Code:
HRESULT __stdcall MyParamTest(SAFEARRAY** ppsa, double* RetVal)
{
if (ppsa == NULL || RetVal == NULL) return E_POINTER;
SAFEARRAY *psa = *ppsa;
if (psa == NULL) return E_POINTER;
if (SafeArrayGetDim(psa) != 1) return E_INVALIDARG;
HRESULT hr;
VARTYPE vt;
if(!SUCCEEDED(hr = SafeArrayGetVartype(psa, &vt))) return hr;
// VB6 likes to pass ParamArrays with VT = 0
if (vt != 0 && vt != VT_VARIANT) return E_UNEXPECTED;
LONG lbound, ubound;
if(!SUCCEEDED(hr = SafeArrayGetLBound(psa, 1, &lbound))) return hr;
if(!SUCCEEDED(hr = SafeArrayGetUBound(psa, 1, &ubound))) return hr;
VARIANT *v;
if(!SUCCEEDED(hr = SafeArrayAccessData(psa, (void **)&v))) return hr;
double r = 0;
for (LONG i = lbound; i <= ubound; i++, v++)
{
switch (V_VT(v))
{
case VT_I4: r += V_I4(v); break;
case VT_I2: r += V_I2(v); break;
case VT_R4: r += V_R4(v); break;
case VT_R8: r += V_R8(v); break;
case VT_I4 | VT_BYREF: r += *V_I4REF(v); break;
case VT_I2 | VT_BYREF: r += *V_I2REF(v); break;
case VT_R4 | VT_BYREF: r += *V_R4REF(v); break;
case VT_R8 | VT_BYREF: r += *V_R8REF(v); break;
case VT_CY:
case VT_CY | VT_BYREF:
CY c;
DOUBLE d;
if (V_VT(v) & VT_BYREF)
c = *V_CYREF(v);
else
c = V_CY(v);
if (!SUCCEEDED(hr = VarR8FromCy(c, &d)))
{
SafeArrayUnaccessData(psa);
return hr;
}
r += d;
break;
default:
SafeArrayUnaccessData(psa);
return E_INVALIDARG;
}
}
if(!SUCCEEDED(hr = SafeArrayUnaccessData(psa))) return hr;
*RetVal = r;
return S_OK;
}
Here is the IDL for the typelib.
Code:
[
helpstring("Test DLL Typelib"),
uuid(C06EFBA2-8462-41FE-B592-E06AEC77110E),
lcid(0x00000000),
version(1.0)
]
library TestDLL {
[dllname("DLL Test.DLL"),helpstring("DLL Test")]
module RuntimeExports {
[entry("MyParamTest"),helpstring("Sum a list of Numeric Values"),vararg]
HRESULT MyParamTest([in] SAFEARRAY(VARIANT)* List, [out,retval] DOUBLE* RetVal);
}
}
Here's what it looks like in VB.
Code:
MsgBox MyParamTest(3@, 6.1, 7&, 123)
-
Aug 10th, 2017, 09:32 PM
#9
Thread Starter
Lively Member
Re: VB paramArray equivalent in C++ export DLL
Oh noo, really amazing DEXWERX!!
How can you do that -____- . Nothing to say but thank you very much for your help. I'm reading all your words and codes again and again now to understand it. Thank you thousand times!
-
Aug 11th, 2017, 06:52 AM
#10
Re: VB paramArray equivalent in C++ export DLL
I used the classic C / Win32 API style Macros that should get you up and running. That way you can corroborate it with all the examples on the net.
There some great Helper template classes in Microsofts ATL, for dealing with VARIANT and SAFEARRAYs and Com Objects in a much more reliable / C++ Object Oriented way. But that code should at least get you running.
https://msdn.microsoft.com/en-us/library/ac97df2h.aspx CComVariant
https://msdn.microsoft.com/en-us/library/3xzbsee8.aspx CComSafeArray
edit:
One gotcha I posted in the above comments is that the VB6 Runtime creates ParamArrays manually, and doesn't go through the SafeArrayCreate COM/Ole framework. That means it doesn't have an explicit VARTYPE. and SafeArrayGetVartype returns 0/VT_EMPTY.
This will throw an exception if you try and use a CComSafeArray::Attach() so modify atlsafe.h accordingly if you want to use it with a VB ParamArray.
This is what I changed in atlsafe.h / CComSafeArray::Attach()
Code:
//ATLENSURE_THROW(vt == GetType(), E_INVALIDARG);
ATLENSURE_THROW(vt == GetType() || (vt == VT_EMPTY && GetType() == VT_VARIANT), E_INVALIDARG);
and the ATL Version of the above function.
Code:
STDMETHODIMP MyParamTest(SAFEARRAY** ppsa, DOUBLE* RetVal)
{
_ATLTRY
{
ATLENSURE_THROW(ppsa && RetVal, E_POINTER);
CComSafeArray<VARIANT, VT_VARIANT> sa;
sa.Attach(*ppsa); // VB6 likes to pass ParamArrays with VT = 0!
ATLENSURE_THROW(sa.GetDimensions() == 1, E_INVALIDARG);
CComVariant cv;
DOUBLE r = 0;
HRESULT hr;
for (LONG i = sa.GetLowerBound(); i <= sa.GetUpperBound(); i++)
{
cv = sa[i];
hr = cv.ChangeType(VT_R8);
ATLENSURE_SUCCEEDED(hr);
r += cv.dblVal;
}
*RetVal = r;
sa.Detach();
}
_ATLCATCH(e)
{
return e;
}
return S_OK;
}
Last edited by DEXWERX; Aug 14th, 2017 at 02:30 PM.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|