COM server returning a safearray in a variant

COM server returning a safearray in a variant

Portrait de skyhigh

I have turned an old FORTRAN code into a COM server and written a GUI C#. The COM server runs a simulation and saves the results in an allocatable real(4) 2D array (referenced ObjectData%export). So far I am picking off each element using a for loop in C#. This is a painful process because 'export' is typically aound the size (20,6000), which means 120,000 calls to the COM method. Its taking longer than the actual simulation itself.

I want to pass the entire 'export' array (which is 2D but of changing length) in onego toC#.From what I have read, I need toout the data ina SafeArray andwrap it in a Variant, before passing to C#. Can anyone help me with this?

Sorry if this is a really newbie question. I just started working with FORTRAN last week.

7 posts / 0 nouveau(x)
Dernière contribution
Reportez-vous à notre Notice d'optimisation pour plus d'informations sur les choix et l'optimisation des performances dans les produits logiciels Intel.
Portrait de Jugoslav Dujic
Quoting - skyhigh

I have turned an old FORTRAN code into a COM server and written a GUI C#. The COM server runs a simulation and saves the results in an allocatable real(4) 2D array (referenced ObjectData%export). So far I am picking off each element using a for loop in C#. This is a painful process because 'export' is typically aound the size (20,6000), which means 120,000 calls to the COM method. Its taking longer than the actual simulation itself.

I want to pass the entire 'export' array (which is 2D but of changing length) in onego toC#.From what I have read, I need toout the data ina SafeArray andwrap it in a Variant, before passing to C#. Can anyone help me with this?

Not that I'm an expert on the field, but you probably need to start with SafeArrayCreate. See this oldish comp.lang.fortran thread. There, Steve Lionel referred back to (an older version of) this forum, but I can't find that message anymore, and even if I could the attachment would be lost (and Steve is currently on vacation). See also this old thread.

Jugoslav www.xeffort.com
Portrait de skyhigh

Thanks Jugoslav. I worked it out. I am posting the generalized code if anyone ever needs this.

1. After creating a COM server name ProtoServer using the wizard, add a method...

function IProtoServer_GetVariantSafeArray(ObjectData, VariantSafeArray) result (hresult)
use ProtoServer_Types
implicit none
type(ProtoServer_InstanceData) ObjectData
!dec$ attributes reference :: ObjectData
TYPE(VARIANT), intent(out) :: VariantSafeArray
integer(long) hresult
! DO NOT REMOVE THIS LINE
! TODO: Add implementation
real(8), allocatable, dimension(:,:) :: rubbish
allocate(rubbish(1:3,1:3))
rubbish(1,1) = 11.0; rubbish(1,2) = 12.0; rubbish(1,3) = 13.0;
rubbish(2,1) = 21.0; rubbish(2,2) = 22.0; rubbish(2,3) = 23.0;
rubbish(3,1) = 31.0; rubbish(3,2) = 32.0; rubbish(3,3) = 33.0;
call MakeVariantSafeArray(VariantSafeArray, rubbish, 3, 3)
hresult = S_OK
! DO NOT REMOVE THIS LINE
end function

2. In a separate f90 file, writing the subroutine MakeVariantSafeArray...

subroutine MakeVariantSafeArray(VSA, FArr, ROWS, COLS)
use IFCOMTY
use IFCOM
implicit none
type(VARIANT), intent(out) :: VSA !Variant SafeArray
integer, intent(in) :: ROWS, COLS
real(8), dimension(ROWS,COLS), intent(in) :: FArr !Fortran Array
type(VARIANT) :: TV !Temporary Variant
integer(4) :: ret, indices(2), lbounds(2), ubounds(2), i, j
type(sa_bounds) :: bounds(2)
lbounds(1) = lbound(FArr,1)
ubounds(1) = ubound(FArr,1)
lbounds(2) = lbound(FArr,2)
ubounds(2) = ubound(FArr,2)
bounds(1)%lbound = lbounds(1)
bounds(1)%extent = ubounds(1)
bounds(2)%lbound = lbounds(2)
bounds(2)%extent = ubounds(2)
VSA%VT = ior(VT_VARIANT, VT_ARRAY)
VSA%VU%PTR_VAL = SafeArrayCreate(VT_VARIANT, 2, bounds(1))
call VariantInit(TV)
TV%VT = VT_R8
do j = lbounds(2), ubounds(2)
do i = lbounds(1), ubounds(1)
indices(1) = i
indices(2) = j
TV%VU%DOUBLE_VAL = FArr(i,j)
ret = SafeArrayPutElement(VSA%VU%PTR_VAL, indices(1), LOC(TV))
end do
end do
end subroutine

3. On the C# end, after adding a reference to the DLL...

ProtoServer PS = new ProtoServer();

System.Object y;
PS.GetVariantSafeArray(out y);
System.Array z = (System.Array)y;
{
int i, j;
for (i = z.GetLowerBound(0); i <= z.GetUpperBound(0); i++)
{
for (j = z.GetLowerBound(1); j <= z.GetUpperBound(1); j++)
{
Console.Write(z.GetValue(i,j) + " ");
}
Console.Write("n");
}
}

This code can beextended to return large multi-dimensional arrays from FORTRAN.

P.S. Up until a week ago I thought FORTRAN was a dead language...

Portrait de anthonyrichards
That looks really useful stuff. Thanks for posting it.

Portrait de craig.h

This post was helpful but I'm trying to do something similar and still have some questions. I'm going the other way where I have a COM developed in vb.net and am trying to call it from Fortran. Currently I just have a test program.

Your code for creating a safearray wrapped in a variant works, however, I'm not following what data type needs to be declared in c# (or vb.net in my case). The "Intel Visual Fortran Module Wizard" produces the following code for my test program. It is asking for a real for the array A1. I can't pass the variant to the safearray because the types are different.

If anyone can fill me in on what I am missing it would be greatly appreciated.

Thanks
Craig

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

Portrait de anthonyrichards

I believe that in VB.Net you will have to use MarshalAs to tell VB.Net or C# how you want to it to treat a variable, in this case an array, when passing it to an external program. You should be able to find a recent example here by doing a search of the forum.

I recommend doing a search of the forum for 'C#' in the title.

Perhaps the person who posted this can help you:

http://software.intel.com/en-us/forums/showthread.php?t=79374&o=a&s=lr

Portrait de craig.h

I've tried several versions of MarshalAs and looked at all the relavent posts I could find. I've also tryed marshaling as a c style array rather than safearray. It seems likeI need a very explicit declaration in both VB.net and Fortran and just haven't found those declarations.

Thanks for the help.
Craig

Connectez-vous pour laisser un commentaire.