Accessing Variant array of VT_I4 values

Accessing Variant array of VT_I4 values

John B. Walter's picture

Am I missing something?
I'm trying to get an integer array from an ActiveX device interface.
Being COM it passes the data as a SafeArray.
I thought when I found the post on passing an array of strings from VB that it would have what I need
but I find no documentation on routines like SafeArrayGetLBound, SafeArrayGetUBound and possible other routines I need.

Are those routines documented somewhere, or do I need to resort to writing my own routines in C++ to access this safe array of Ints? Clearly I don't want to use SafeArrayGetElement to access a large array of values.

What SafeArray routines are available for Intel Visual Fortran? Currently I have version 11.1, but I should be receiving version 12 this week

15 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.
Wendy Doerner (Intel)'s picture

Others will probably chime in with more experience with SafeArrays, but I wanted to point out from our documentation on QuickWin:

An array is always passed as a SafeArray ByRef. Therefore it must be defined with Intent set to InOut

This from the section in the documentation, titled:

Fortran COM Server Interface Design Considerations

The documenation should be installed on your system (for 11.1):

Start -> All Programs => Intel Software Development Tools => Intel Fortran Compiler X.X.XXX => Intel Visual Fortran Documentation

Hope this helps,

------

Wendy

Attaching or including files in a post

John B. Walter's picture

So, there is documentation for the SafeArray routines in the C++ help of Visual Studio,
and they seem to carry over to IVF with the IFWINTY module.

The AUTOINVOKE routine, returns the VARIANT in $RETURN, and %RETURN%VU%PTR_VAL does indeed point to the desired SafeArray

And, STATUS = SafeArrayAccessData( $RETURN%VU%PTR_VAL, pInt4)
where I have>> integer (kind=4), pointer ::pInt4
does indeed point to the desired data

but when I attempt to copy the data to my array from there, so that I can free the $RETURN buffer,
the compiler doesn't like
>> do i=1, Length
>> mydata(i) = pInt4(i)

>>end do

It complains that pInt4 wasn't declared with a dimension.

But, SafeArrayAccessData doesn't like it when it is declared with a dimension.

Any ideas what I need to do to actually get at my data?

any help appreciated, thank.

Paul Curtis's picture

The error msg is correct, your POINTER does not have an associated pointee.

Try this instead:

STATUS = SafeArrayAccessData($Return%vu%ptr_val, LOC(mydata))

where mydata(:) has been declared as an integer array.

John B. Walter's picture

Thanks for the suggestion Paul.
The problem is that the second argument to SafeArrayAccessData has either INTENT(OUT) or INTENT(INOUT), and so LOC() is not allowed as it is a constant and so can't be changed in the routine.

I did try preceeding the call to AUTOINVOKE (for the COM getdata routine) with
$RETURN%VU%PTR_VAL=LOC(mydata), but it simply gets overwritten by the AUTOINVOKE call

Three other items:
1) integer (kind=4), pointer :: pInt4(:) SafeArrayAccessData complains about a shape mismatch

2) If I leave out the call to anySafeArray routines, my program executes cleanly (although it accomplishes little) but it I include the call to SafeArrayAccessData or include it and also the UnaccessData routine, and also if I include those two and also SafeArrayDestroyData in an attempt to clean things up,
THEN (in any of those cases) I get stack problems -- so I'm clearly not cleaning up correctly. The safe array is being created in the COM call, so I wouldn't think I should be explicitly destroying it in my routine.

3) in the VisualStudio C++ example for variants and SafeArrays, it uses SafeArrayCreate to create and SafeArrayDestroy to destroy the safe array, and SafeArrayAccessData and SafeArrayUnaccessData to access and release a pointer to the data. The 2nd argument to the Access routine is declared as
int* pData; and the 2nd argument is (void**) &pData so it's passing the pointer by reference, passing the address of the pointer.

I'm still using version 11.1 -- is it not compatible with receiving a SafeArray from a COM object?

Wendy Doerner (Intel)'s picture

John,

You might want to reference the sample we include in our product about Safe Arrays and Visual Basic in the Mixed Language samples. Samples are installed by default at:

C:\Program Files\Intel\Compiler\11.1\xxx\Samples

the sample is in: MixedLanguage\VB.NET-SafeArrays\

------

Wendy

Attaching or including files in a post

John B. Walter's picture

Got it

I was making this too hard

declare integer (kind=4) Int4_vals(256)
pointer (pInt4, Int4_vals)
type (variant), volatile :: $RETURN

integer(int_ptr_kind()) invokeargs
.

.

.

invokeargs = autoallocateinvokeargs()

call autoaddarg(invokeargs, '$RETURN', $RETURN, AUTO_ARG_OUT)

.

status = autoinvoke(pOBJECT, mID, invokeargs)

! Return%VU%PTR_VAL contains a pointer to a pointer to the safe array
pInt4 = $RETURN%VU%PTR_VAL ! now Int4_vals(1) has the pointer to the data

pIint4 = Int4_vals(1) ! now Int4_vals(1:256) is the data in the safe array
do i=1,256
mydata(I) = Int4_vals(I)
end do
call autodeallocateinvokeargs(invokeargs) ! now done with the safearray and $RETURN
.
.

The C++ examples says not to access the data directly -- Does anyone know anything about that? Is something going to bite me if I do it this way?

Thanks

Steve Lionel (Intel)'s picture

I'd be more comfortable if you used the provided SafeArray routines in module IFCOM, such as SafeArrayGetElement.

Steve
John B. Walter's picture

Thanks Steve. Me too.

After think back over my experience I realized some of my trouble with those routines was operator error, apparently connected to my naming of the routine I was calling.

Now I'm using the function produced by the Fortran Module Wizard, which returns a VARIANT
pInt4 is defined like previous post with pointer (pInt4,vec) with INTEGER (kind=4) vec(512)

HRESULT = AUTOGetProperty (OBJ_Comm, 30, nActive, vt_I4) ! get active size of data
pyvals = $DUMCBICONN2_GetData1(OBJ_Comm, 0, nActive-1, HRESULT) ! get all the data

! pyvals points to VARIANT, pyvals%VU%PTR_VAL points to SafeArray
HRESULT = SafeArrayAccessData(pyvals%VU%PTR_VAL, pInt4)

do i = 1, nActive
ycounts(i) = vec(i)
end do
HRESULT = SafeArrayUnaccessData(pyvals%VU%PTR_VAL)

and this works just fine. Of course both vec and ycounts must be large enough or there is a subscript complaint when"i" gets too large.

It would be nice if vec could be allocatable, so that I could ensure it was large enough without having to just use the largest possible value.

Steve Lionel (Intel)'s picture

I don't see declarations for vec and ycounts here, so I don't know what to advise you. If vec is a pointee in a POINTER declaration, you can dimension it (*).

Steve
John B. Walter's picture

Thanks Steve.
I guess I get a little confused with some of the changes from 66 to 77 to 90 to 95 to ...

integer (kind=4) vec(*)
pointer (pInt4, vec)

compiles just fine.

I was thinking that it wouldn't like declaration of vec(*) because I was used to only doing that for arguments passed into the routine. Three and more decades back. Good to know.

Steve Lionel (Intel)'s picture

Well, this entire usage (including this form of POINTER) is an extension and not in any Fortran standard.

Steve
John B. Walter's picture

Obviously it would be more portable without using this POINTER extension of the old Digital Equipment Corporation FORTRAN that has carried through all of the transitions to now. I'm supposing it has made it intoIntel Visual Fortran Composer XE 2011.

Is therea way to accomplish this within one of the Fortran standards?

Steve Lionel (Intel)'s picture

I'd think that being standards-conforming for such an application would be the furthest from your mind, given how nonportable the application is otherwise.

Steve

John

I'm having a similar problem passing an array to a vb.net COM. I've seen several examples of people having similar problems but I haven't found a complete solution that shows the .net code as well as the Fortran code. It sounds like you may have found a solution to this but I can't quite piece together your code segments to figure out what I need to do. Would it be possible for you to provide your complete code as an example? The module wizard creates the following for one of my vb.net test functions. I've tried Marshaling the array in vb as a safearray but am not sure what to do on both the vb and Fortran sides. Any advice or examples from anyone would be appreciated.

I can't figure out what to pass to A1. I've tried passing a safearray wrapped in a variant based on the following code example but there is a type mismatch.
http://origin-software.intel.com/en-us/forums/showthread.php?t=64908

REAL(4) FUNCTION $Graphics_SumArrayA($OBJECT, A1, $STATUS)

IMPLICIT NONE

INTEGER(INT_PTR_KIND()), INTENT(IN) :: $OBJECT ! Object Pointer

!DEC$ ATTRIBUTES VALUE :: $OBJECT

REAL(4), DIMENSION(:), INTENT(INOUT), VOLATILE :: A1 ! (SafeArray)

!DEC$ ATTRIBUTES REFERENCE :: A1

INTEGER(4), INTENT(OUT), OPTIONAL :: $STATUS ! Method status

!DEC$ ATTRIBUTES REFERENCE :: $STATUS

REAL(4), VOLATILE :: $RETURN

INTEGER(4) $$STATUS

INTEGER(INT_PTR_KIND()) invokeargs

invokeargs = AUTOALLOCATEINVOKEARGS()

CALL AUTOADDARG(invokeargs, '$RETURN', $RETURN)

CALL AUTOADDARG(invokeargs, '$ARG1', A1, AUTO_ARG_INOUT)

$$STATUS = AUTOINVOKE($OBJECT, 1610743812, invokeargs)

IF (PRESENT($STATUS)) $STATUS = $$STATUS

$Graphics_SumArrayA = $RETURN

CALL AUTODEALLOCATEINVOKEARGS (invokeargs)

END FUNCTION $Graphics_SumArrayA

Login to leave a comment.