Level Extreme platform
Subscription
Corporate profile
Products & Services
Support
Legal
Français
Problem passing array to .NET COM
Message
General information
Forum:
Visual FoxPro
Category:
COM/DCOM and OLE Automation
Environment versions
Visual FoxPro:
VFP 9 SP2
OS:
Windows 7
Miscellaneous
Thread ID:
01523745
Message ID:
01524000
Views:
38
Hi,

>It's 1am here :-)
So you're on the west coast not Hawaii ?

>
>Not sure about perf but Xml serialization/deserialization is pretty damn slow. Even adding arrays one at a time is probably not that resource intensive with InProcess COM/Interop.

I've found Serialization/Deserialization fast enough (if you ignore the first-time assembly generation hit) but I guess I'd forgotten about the extra hoops on the VFP end - or just assumed most VFPer's would by now have acquired for tools to simplify the process.

>THe problem is that there are issues with serialization in FoxPro that is problematic. Some types (notably dates) need some fixup (like you can't pass empty dates) and any small error in formatting/encoding can break the whole thing down. If you're doing your own XML encoding it's even worse because the myriad of rules required for proper encoding. These things are difficult to catch sometimes because they usually don't occur until you're past the basic implementation phase.
>
>Been there done that and if I can avoid XML as way to pass data between platforms I would do it. It's OK if you have the same engines handling both ends then it's easier and cleaner.

But XML was invented to make it easier to pass entites between different engines :-}

>I've had good luck with the ComArray proxy approach (which is easy to implement on your own for that matter) that can be created generically.
>
>
>+++ Rick ---
>
>>>John,
>>>
>>>Since you are in control of the .NET code I would highly recommend that you simplify this and add methods that create these objects and add them to an internally managed array. This is way easier than trying to hand code up some XML and then serializing/deserializing.
>>>
>>>Create a private property private Contacts[] contacts in your .NET class then create AddContact() and GetContact() (possibly RemoveContact()). You can pass values via parameters or pass in Contact objects which you can easily create via COM.
>>
>>That's basically what I suggested to Jon in my first reply before he switched to an XML based approach.
>>I'm not sure I'd dismiss the XML approach without knowing more about the expected usage. If, for example, a couple of hundred contacts were being added then pushing them over the wire one at a time rather than as an array may not be so attractive? Ditto in the opposite direction if a complete list is required on the client side......
>>
>>What time is it there ?
>>
>>>Or as I mentioned before with wwDotNetBridge which lets you use arrays (and collections, and generics and a bunch of other stuff that doesn't work with plain COM Interop plus your .NET object doesn't need to be COM registered).
>>>
>>>Here's an example of simulated code for your scenario:
>>>
>>>
>>>do wwDotNetBridge
>>>LOCAL loBridge as wwDotNetBridge
>>>loBridge = CreateObject("wwDotNetBridge")
>>>
>>>*** Load your .NET assembly
>>>loBridge.LoadAssembly(FULLPATH("ContactManager.dll"))
>>>
>>>*** Create the top level .NET object
>>>loContactManager = loBridge.CreateInstance("MyApp.ContactManager")
>>>
>>>*** Create an empty array of MyApp.Contact items 
>>>loContacts = loBridge.CreateComArray("MyApp.Contact")
>>>
>>>*** Create a Contact instance 
>>>* loContact = loBridge.CreateInstance("MyApp.Contact")
>>>
>>>*** Easier way to create a contact object from the array 
>>>*** since it knows what our base type is
>>>loContact = loContacts.CreateItem() 
>>>
>>>loContact.Name = "Rick"
>>>loContact.Entered = DATETIME()
>>>
>>>*** Add first contact
>>>loContacts.Add(loContact)
>>>
>>>loContact = loContacts.CreateItem() && get a Contact object of type MyApp.Contact
>>>loContact.Name = "Jim"
>>>loContact.Entered = DATETIME()
>>>
>>>*** Add Second contact
>>>loContacts.Add(loContact)
>>>
>>>*** Call AddContacts(Contacts[] contacts,DateTime enteredOn)
>>>*** InvokeMethod is required to properly pass the loContact array as a .NET array
>>>loBridge.InvokeMethod(loContactManager,"AddContacts",loContacts,DATETIME())
>>>
>>>*** You can still call most methods direct just like with plain COM Interop
>>>loContactManager.Close()
>>>
>>>
>>>(note this is off the cuff so there might be minor errors here but it gives you the idea)
>>>
>>>Hope this helps,
>>>
>>>+++ Rick ---
>>>
>>>>Viv,
>>>>
>>>>Thank you for the informative response. I tried implementing it but failed when attempting to deserialize. Does the XML output from CURSORTOXML() need to look a certain way for the deserialization to work?
>>>>
>>>>Another thing to point out is that I'm only passing over three properties to populate for the Contact object (i.e. first/last name, email), whereas the Contact class has a lot more properties defined. Would the XML need to contain empty elements for the properties I'm not using?
>>>>
>>>>Thanks.
>>>>
>>>>Jon
>>>>
>>>>>Hi,
>>>>>Sounds OK - but if you don't really need a datatable then if's very simple to use a generic list instead. A couple of generic methods will convert any list of objects to XML and back (assuming the objects themselves are serializable). e.g:
public static class Helper
>>>>>    {
>>>>>        public static List<T> RetrieveList<T>(string s)
>>>>>        {
>>>>>            XmlSerializer xs = new XmlSerializer(typeof(List<T>));
>>>>>            System.Xml.XmlReader v = System.Xml.XmlReader.Create(new StringReader(s));
>>>>>            return (List<T>)xs.Deserialize(v);
>>>>>        }
>>>>>
>>>>>        public static string ConvertToXML<T>(List<T> list)
>>>>>        {
>>>>>            XmlSerializer xs = new XmlSerializer(typeof(List<T>));
>>>>>            StringWriter sw = new StringWriter();
>>>>>            xs.Serialize(sw, list);
>>>>>            return sw.ToString();
>>>>>        }
>>>>>    }
Example use:
       List<Contact> list = new List<Contact>();
>>>>>            list.Add(new Contact { Name = "Fred", Age = 33 });
>>>>>            list.Add(new Contact { Name = "Joe", Age = 66 });
>>>>>
>>>>>            string xmlString = Helper.ConvertToXML<Contact>(list);
>>>>>            //(Examine this string to determine what VFP should create)
>>>>>
>>>>>            List<Contact> retrievedList = Helper.RetrieveList<Contact>(xmlString);
>>>>>>Thanks for the tip. In the meantime, I've decided to switch to sending XML and convert that to a DataTable that I can iterate through. That seems to do the trick for now.
>>>>>>
>>>>>>Jon
>>>>>>
>>>>>>>>My C# COM DLL has a public method with the following signature:
>>>>>>>>
>>>>>>>>public void AddClientsToList(Contact[] contacts, string listName)
>>>>>>>>
>>>>>>>>
>>>>>>>>In VFP, I do the following:
  • Call a DLL method to get a new contact object
  • Populate the properties
  • Create an array and place the object in the first element
  • Call COMARRAY(loDLL,10)

>>>>>>>>When I attempt loDLL.AddClientsToList(@MyArray, "test"), I get a type mismatch COM error.
>>>>>>>>
>>>>>>>>Any ideas on what I'm doing wrong?
>>>>>>>
>>>>>>>No. But passing arrays of anything is always a pain. Since you have control of both sides maybe it would be simpler to just pass the Contacts in one-by-one? e.g:
public class MyDll
>>>>>>>    {
>>>>>>>        Dictionary<string, List<Contact>> lists = new Dictionary<string, List<Contact>>();
>>>>>>>        public Contact GetNewContact() { return new Contact();}
>>>>>>>       
>>>>>>>        public void AddClientToList(Contact contact,string listName)
>>>>>>>        {
>>>>>>>             if (!lists.Keys.Contains(listName))
>>>>>>>                lists.Add(listName, new List<Contact>());
>>>>>>>            lists[listName].Add(contact);
>>>>>>>        }
>>>>>>>    }
Previous
Reply
Map
View

Click here to load this message in the networking platform