COM server returning a safearray in a variant

COM server returning a safearray in a variant

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 / novo 0
Último post
Para obter mais informações sobre otimizações de compiladores, consulte Aviso sobre otimizações.

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

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

That looks really useful stuff. Thanks for posting it.

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

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

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

Deixar um comentário

Faça login para adicionar um comentário. Não é membro? Inscreva-se hoje mesmo!