Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

Passing Arrays to a .NET COM object from Visual FoxPro


:P
On this page:

A number of people have asked me about passing arrays to .NET functions from Visual FoxPro applications. The problem here is that Visual FoxPro arrays are not formatted the right way to just be called to a .NET COM object by default. VFP arrays are 1 based while in general COM objects expect arrays to be 0 based so you need to fix up arrays to make this happen. .NET arrays are always 0 based unless explicitly declared otherwise.

 

Assume you have a method in a .NET COM object that looks like this:

 

[ClassInterface(ClassInterfaceType.AutoDual)]

[ProgId("DotNetCom.DotNetComPublisher")] 

public class DotNetComPublisher

 

public string PassArray(object[] Objects )

{

      string Output = "";

 

      foreach(object Customer in Objects)

      {

            // *** Must access array by using Reflection

            Output += ComUtils.GetProperty(Customer,"Name") + " " +    

                      ComUtils.GetProperty(Customer,"Name") + "\r\n";

 

      }

 

      return Output;

}

}

 

And you want to pass an object like this:

 

loAddr = CREATEOBJECT("EMPTY")

ADDPROPERTY(loAddr,"Name","Rick Strahl")

ADDPROPERTY(loAddr,"Company","West Wind")

ADDPROPERTY(loAddr,"Date",DATETIME())

 

loAddr2 = CREATEOBJECT("EMPTY")

ADDPROPERTY(loAddr2,"Name","Rick Strahl")

ADDPROPERTY(loAddr2,"Company","West Wind")

ADDPROPERTY(loAddr2,"Date",DATETIME())

 

DIMENSION loAddrArray[2]

loAddrArray[1] = loAddr

loAddrArray[2] = loAddr2

 

To .NET. Your first try likely looks like this:

 

LOCAL loNet as DotNetCom.DotNetComPublisher

loNet = CREATEOBJECT('DotNetCom.DotNetComPublisher')

? loNet.PassArray(@loAddrArray)

 

which will fail with Invalid Parameter type.

 

The key to make this work is to use the COMARRAY() function in VFP to mark the array as 0 based and passed by reference:

 

LOCAL loNet as DotNetCom.DotNetComPublisher

loNet = CREATEOBJECT('DotNetCom.DotNetComPublisher')

 

COMARRAY(loNet,10)

 

? loNet.PassArray(@loAddrArray)

 

And this works correctly passing the objects to .NET.

 

To return arrays works as well. Take the following .NET method:

 

public Customer[] ReturnArray(int Count)

{

      Customer[] Customers = new Customer[Count];

      for (int x=0; x < Count; x++)

      {

            Customers[x] = new Customer();

            Customers[x].Company = "West Wind " + DateTime.Now.ToString();

            Customers[x].oAddress.StreetAddress = DateTime.Now.ToString();

      }

 

      return Customers;

}

 

Which you can retrieve like this in FoxPro:

 

LOCAL loNet as DotNetCom.DotNetComPublisher

loNet = CREATEOBJECT('DotNetCom.DotNetComPublisher')

loArray =loNet.ReturnArray(3)

 

lnCount = ALEN(loArray,1)

 

FOR x=1 TO lnCount

   loCust = loArray[x]

   ? loCust.Company

   ? loCust.oAddress.StreetAddress

ENDFOR

 

RETURN

 

Note that you are not getting the Array object that .NET is returning. There’s no Length property for example, but rather VFP marshals this .NET array to a standard array. Actually .NET returns this array to COM as a COM compatible SafeArray and VFP picks that up and exposes the array as a VFP array object. You won't need to use COMARRAY() on the returned array.

 

A final option that I like to use in many situations is to not pass arrays as parameter or return types directly, but rather pass objects that contain arrays. The advantage is that you get more control over the data and you have a chance to potentially call COMARRAY() with other marshalling options.


The Voices of Reason


 

Edgar
December 15, 2005

# re: Passing Arrays to a .NET COM object from Visual FoxPro

Is there also some information on pasing arrays by reference in the opposite direction (from .net application to foxpro comserver ?)

Rick Strahl
August 02, 2006

# re: Passing Arrays to a .NET COM object from Visual FoxPro

See this entry for more info on how to deal with arrays passed from .NET to FoxPro:

http://west-wind.com/weblog/posts/6728.aspx

Arrays are always passed by reference from .NET, but VFP converts the arrays to VFP arrays and it's very difficult to update these arrays and send them back especially if you change the item count of the array.

Rick Strahl's Web Log
October 15, 2006

# Advanced FoxPro .NET COM Interop Article Series - Rick Strahl's Web Log

I've put out a three part article series on FoxPro to .NET COM Interop that addresses several common advanced Interop scenerios. This entry links these articles and provides a feedback link to the articles.

Rick Strahl's Web Log
October 29, 2006

# Passing Arrays between FoxPro and .NET with COM Interop - Rick Strahl's Web Log

FoxPro doesn't do very well when dealing with arrays that originate in environments other than FoxPro. VFP's COM handling of arrays provides access to arrays, but makes it very difficult to update the array count by adding or removing items. Here's one way to work around this problem with a generic routine that adds and remove items from a .NET array via COM Interop.

# DotNetSlackers: Passing Arrays to a .NET COM object from Visual FoxPro


Igor Nikiforov
August 21, 2009

# re: Passing Arrays to a .NET COM object from Visual FoxPro

Hi Rick,

Thank you very much for your article.
I wont to make more precise one point:
we cannot return an array to Foxpro if even an element of the array has a Long(Int64) type of data.

<Microsoft.VisualBasic.ComClass(), ProgId("UDFDotNET.FoxPro")> _
Public Class FoxPro
    ' it returns the array to FoxPro
    Public Function ReturnArrayObject(ByVal Count As Integer) As Object(,)
        Dim ReturnedArray(,) As Object
        ReDim ReturnedArray(Count - 1, 1)
        For x As Integer = 0 To Count - 1
            ReturnedArray(x, 0) = (x + 1)
            ReturnedArray(x, 1) = CStr(-(x + 1))
        Next x
        Return ReturnedArray
    End Function

    ' it returns the array to FoxPro
    Public Function ReturnArrayString(ByVal Count As Integer) As String(,)
        Dim ReturnedArray(,) As String
        ReDim ReturnedArray(Count - 1, 1)
        For x As Integer = 0 To Count - 1
            ReturnedArray(x, 0) = (x + 1).ToString
            ReturnedArray(x, 1) = -(x + 1).ToString
        Next x
        Return ReturnedArray
    End Function

    ' it returns the array to FoxPro
    Public Function ReturnArrayInteger(ByVal Count As Integer) As Integer(,)
        Dim ReturnedArray(,) As Integer
        ReDim ReturnedArray(Count - 1, 1)
        For x As Integer = 0 To Count - 1
            ReturnedArray(x, 0) = x + 1
            ReturnedArray(x, 1) = -(x + 1)
        Next x
        Return ReturnedArray
    End Function

    ' The call to the function in Foxro results in the error message, "Function argument value, type, or count is invalid (Error 11)". 
    Public Function ReturnArrayLong(ByVal Count As Integer) As Long()
        Dim ReturnedArray() As Long
        ReDim ReturnedArray(Count - 1)
        For x As Integer = 0 To Count - 1
            ReturnedArray(x) = (x + 1)
        Next x
        Return ReturnedArray
    End Function
End Class 'FoxPro


loNet = Createobject("UDFDotNET.FoxPro")
loArray = loNet.ReturnArrayInteger(3)
display memory like loarray
loArray = loNet.ReturnArrayString(3)
display memory like loarray
loArray = loNet.ReturnArrayObject(3)
display memory like loarray
loArray = loNet.ReturnArrayLong(3) && error !
.

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024