none
Passing Multidimensional String Array to ASP (COM) RRS feed

  • Question

  • I have been reading 10 years of posts on this topic, or something close to it, without being any closer to an answer. I need to be able access a multidimensional (2-dimensions, in fact) array of strings which is populated by a c# library from an ASP (classic - using VBScript) page. ASP is an untyped language, so all the posts about Variant arrays and SafeArrays seem irrelevant. In my class, I have an object which has a 2-dimensional array as a property. I have an Add method that accepts 2 parameters and adds a row to the array after getting a result code from a webservice. I know the array is being populated; I used a VBA test program and could see the values in the Locals window - but I can' t seem to access the array. In pseudocode:

    My object: myObj=createObject("MyClass.MyObject")

    myObj.AddRow (param1, param3)

    msgbox (or Debug.print): myObj.myList(0,0) - or in a nested loop - myObj.myList(i,j).

    But any effort to access myList in code seems to throw an error - "

    DataVal.vbs(9, 3) Microsoft VBScript runtime error: Wrong number of arguments or invalid property assignment: 'EmailList'"

    Can anyone help with this issue, or point me towards some relevant discussion?

    Monday, July 9, 2012 9:21 PM

Answers

  • I suggest you to open the COM's server's type library (typically embedded in the OCX/DLL that exposes the COM object but can also shipped separately) in OLEView. Then paste the IDL here. You can find the explanation of coclass and idl at http://www.microsoft.com/msj/0898/idl/idl.aspx.

    I believe the default marshaler can do a safe array translation if you declare the property's return value as [MarshalAs(UnmanagedType.SafeArray)] ref string[,] and do some low level modification. Alternatively you can pinvoke safe array APIs like SafeArrayCreate and SafeArrayPutElement to populate a safe array held in an IntPtr, then assign the array to the parray member of a VARIANT structure initialized using VariantInit. The vt member of the structure needs to be a bitwise OR of VT_SAFEARRAY and VT_BSTR. 



    The following is signature, not part of post
    Please mark the post answered your question as the answer, and mark other helpful posts as helpful, so they will appear differently to other users who are visiting your thread for the same problem.
    Visual C++ MVP

    • Proposed as answer by Mike FengModerator Thursday, July 19, 2012 3:30 AM
    • Marked as answer by Eric F Thursday, July 19, 2012 4:38 PM
    Tuesday, July 10, 2012 3:05 AM

All replies

  • What is the EmailList property/method in the coclass idl for the object? For myObj.myList(0,0) you need a myList property of type VARIANT and pack a multiple dimensional safe array into the variant at runtime.



    The following is signature, not part of post
    Please mark the post answered your question as the answer, and mark other helpful posts as helpful, so they will appear differently to other users who are visiting your thread for the same problem.
    Visual C++ MVP

    Monday, July 9, 2012 10:05 PM
  • Sheng,

    Thank you for responding so promptly. Unfortunately, your answer is similar to many posts which I found on this topic. Although I am not new to programming, I do not have a great deal of experience at the interop layer. Could you please tell me what the following terms mean:

    1) coclass

    2) idl

    3) "pack a multi-dimensional safe array" - I have seen references to a SafeArray type(?), but VBScript is untyped, so I don't know how to write code to create such an object at runtime.

    .Net does not have a Variant type, so I can not create a variant property in my .Net class. I understand I can make an Object array property in .Net, and that somehow this can be materialized as a Variant array in VBScript, but I would really like to see a code example in VBScript or VBA that would let me see all the pieces.

    Thanks,

    Eric

    Tuesday, July 10, 2012 2:02 AM
  • I suggest you to open the COM's server's type library (typically embedded in the OCX/DLL that exposes the COM object but can also shipped separately) in OLEView. Then paste the IDL here. You can find the explanation of coclass and idl at http://www.microsoft.com/msj/0898/idl/idl.aspx.

    I believe the default marshaler can do a safe array translation if you declare the property's return value as [MarshalAs(UnmanagedType.SafeArray)] ref string[,] and do some low level modification. Alternatively you can pinvoke safe array APIs like SafeArrayCreate and SafeArrayPutElement to populate a safe array held in an IntPtr, then assign the array to the parray member of a VARIANT structure initialized using VariantInit. The vt member of the structure needs to be a bitwise OR of VT_SAFEARRAY and VT_BSTR. 



    The following is signature, not part of post
    Please mark the post answered your question as the answer, and mark other helpful posts as helpful, so they will appear differently to other users who are visiting your thread for the same problem.
    Visual C++ MVP

    • Proposed as answer by Mike FengModerator Thursday, July 19, 2012 3:30 AM
    • Marked as answer by Eric F Thursday, July 19, 2012 4:38 PM
    Tuesday, July 10, 2012 3:05 AM
  • // Generated .IDL file (by the OLE/COM Object Viewer)
    // 
    // typelib filename: DataVal.tlb

    [
      uuid(33ECED4B-EB27-4BE1-8F91-693D570BAB08),
      version(1.0),
      custom(90883F05-3D28-11D2-8F17-00A0C9A6186D, "DataVal, Version=1.0.0.0, Culture=neutral, PublicKeyToken=52b5e59204ae5c39")

    ]
    library DataVal
    {
        // TLib :     // TLib :  : {935E3B59-8908-4E06-86EA-D4CC1153AA6A}
        importlib("IEmailList.tlb");
        // TLib : mscorlib.dll : {BED7F4EA-1A96-11D2-8F08-00A0C9A6186D}
        importlib("mscorlib.tlb");

        // Forward declare all types defined in this typelib

        [
          uuid(79FF6864-DA23-3B78-ABA2-BD58B92C7F55),
          version(1.0),
          custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "DataVal.DVList2")
        ]
        coclass DVList2 {
            interface _Object;
            [default] interface IEMailList;
        };
    };

    Here is the Interface definition which I think documents the Properties and Methods of the class:

    [
      uuid(88F7C23A-E5AF-3983-9194-62D842AD33C3),
      version(1.0),
      dual,
      custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "IEmailList.IEMailList")

    ]
    dispinterface IEMailList {
        properties:
        methods:
            [id(0x60020000), propget]
            SAFEARRAY(BSTR) EMailList();
            [id(0x60020000), propput]
            void EMailList([in] SAFEARRAY(BSTR) rhs);
            [id(0x60020002), propget]
            BSTR ClientAPIKey();
            [id(0x60020002), propput]
            void ClientAPIKey([in] BSTR rhs);
            [id(0x60020004)]
            void Validate();
            [id(0x60020005)]
            void AddEMail(
                            [in] BSTR emailaddress, 
                            [in] long ProspectID);
    };

    I have added a workaround that enables retrieving the elements of any row of the EMailList as a comma-separated string, which can then be parsed pretty easily in VBScript. I think I understand your recommendation as involving poking around in the Windows API - I'm not sure that I can do that in VBScript.

    Tuesday, July 10, 2012 3:30 AM
  • I am under the impression that you want to populate a safe array in managed code and pass to an array in VB script. Are you suggesting that you have VBS both at client and at server? Where is .Net come into play then?


    The following is signature, not part of post
    Please mark the post answered your question as the answer, and mark other helpful posts as helpful, so they will appear differently to other users who are visiting your thread for the same problem.
    Visual C++ MVP

    Tuesday, July 10, 2012 3:42 AM
  • No - you are correct - the DataVal.dll library is written in managed code (C#). The EMailList property of the DVList2 class (which inherits from the IEMailList interface) is defined as a string[,] Array (although I originally had a Generic List<string, int, string> - but that generated error messages during compile). I don't understand what a "Safe Array" is - how is it different from a regular Array type which already exists in both managed and unmanaged environments?

    Thanks for all your help - I really do want to do this the most effective way.

    Eric

    Tuesday, July 10, 2012 11:16 AM
  • A safe array is a data type that can hold an array of any number of dimensions (the dimensions and bound for each dimension are determined at runtime).

    Your IDL looks correct. Can you post your VBS code regarding EmailList? You can also check the first  2 bytes of the safe array in a C++ client of your COM server to make sure the array is marshalled as a 2D array.



    The following is signature, not part of post
    Please mark the post answered your question as the answer, and mark other helpful posts as helpful, so they will appear differently to other users who are visiting your thread for the same problem.
    Visual C++ MVP

    Wednesday, July 11, 2012 8:42 PM
  • Sheng,

    Thanks for your help with this problem. My client has abandoned the .net approach to this problem for the time being and is developing the library in VB, so I can not continue this dialog at this time. I may be back with some more questions about COM/.Net interfaces in the future!

    Eric

    Thursday, July 19, 2012 4:40 PM