Results 1 to 10 of 10

Thread: VB paramArray equivalent in C++ export DLL

  1. #1

    Thread Starter
    Lively Member
    Join Date
    Apr 2015
    Posts
    72

    Smile 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.

  2. #2
    PowerPoster
    Join Date
    Jun 2015
    Posts
    2,224

    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

  3. #3
    Fanatic Member 2kaud's Avatar
    Join Date
    May 2014
    Location
    England
    Posts
    996

    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)

  4. #4

    Thread Starter
    Lively Member
    Join Date
    Apr 2015
    Posts
    72

    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!

  5. #5
    Fanatic Member 2kaud's Avatar
    Join Date
    May 2014
    Location
    England
    Posts
    996

    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)

  6. #6

    Thread Starter
    Lively Member
    Join Date
    Apr 2015
    Posts
    72

    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.

  7. #7
    PowerPoster
    Join Date
    Jun 2015
    Posts
    2,224

    Re: VB paramArray equivalent in C++ export DLL

    Quote Originally Posted by vietnamvodich View Post
    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.

  8. #8
    PowerPoster
    Join Date
    Jun 2015
    Posts
    2,224

    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)

  9. #9

    Thread Starter
    Lively Member
    Join Date
    Apr 2015
    Posts
    72

    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!

  10. #10
    PowerPoster
    Join Date
    Jun 2015
    Posts
    2,224

    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
  •  



Click Here to Expand Forum to Full Width