Author Topic: Getting arrays to/from a custom activex function  (Read 7141 times)

0 Members and 1 Guest are viewing this topic.

SIDESHOWBOB

  • Guest
Getting arrays to/from a custom activex function
« on: February 09, 2012, 07:13:36 AM »
Hi

this is a followup to my original topic on getting numbers back and forth (which I can do now, thank you!) 

How can I get arrays to/from activex?  I am trying this:

Code - Auto/Visual Lisp: [Select]
  1. (setq n (vlax-create-object "mycomdll.MyComClass"))
  2. (setq sa (vlax-make-safearray vlax-vbDouble '(0 . 1)))
  3. (vlax-safearray->list sa)
  4. (1.0 2.0)
  5. (vlax-invoke-method n 'arraytimestwo sa 'newsa)
  6. ; error: lisp value has no coercion to VARIANT with this type:  #<safearray...>
  7.  
Meanwhile in visual c++
Code - C++: [Select]
  1. STDMETHODIMP MyComClass::arraytimestwo(DOUBLE* in, DOUBLE** out)
  2. {
  3.         *out[0] = in[0]*2;
  4.         *out[1] = in[1]*2;
  5.         return S_OK;
  6. }
  7.  

As always any help would be much appreciated!  Thank you.

Lee Mac

  • Seagull
  • Posts: 12926
  • London, England
Re: Getting arrays to/from a custom activex function
« Reply #1 on: February 09, 2012, 08:10:44 AM »
A guess:

Code - Auto/Visual Lisp: [Select]
  1. (setq n (vlax-create-object "mycomdll.MyComClass"))
  2. (setq v (vlax-make-variant (vlax-safearray-fill (vlax-make-safearray vlax-vbdouble '(0 . 1)) '(1 2))))
  3. (vlax-invoke-method n 'arraytimestwo v 'newsa)

SIDESHOWBOB

  • Guest
Re: Getting arrays to/from a custom activex function
« Reply #2 on: February 09, 2012, 08:46:00 AM »
Even more mysterious, that gives the error
Code: [Select]
; error: lisp value has no coercion to VARIANT with this type:  #<variant 8197 ...>

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: Getting arrays to/from a custom activex function
« Reply #3 on: February 09, 2012, 10:04:34 AM »
Isn't the issue in your C++ code? I mean it's expecting Arrays of Doubles DOUBLE - should your function not rather expect Variant types and then convert from that? I.e. similar to what happens in C# with lisp - using ResultBuffers as input, output and return.

BTW, I'm sure your example is simply as a test case ... it would be an extremely trivial thing to write the times2 function directly in lisp. Even a lot simpler than in C++:
Code: [Select]
(defun ListTimesTwo (lst / ) (mapcar '(lambda (n) (* n 2)) lst))
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

JohnK

  • Administrator
  • Seagull
  • Posts: 10661
Re: Getting arrays to/from a custom activex function
« Reply #4 on: February 09, 2012, 01:01:02 PM »
I'm not sure if the 'AutoLisp ListTimesTwo' is "a lot simpler than in C++". -i.e. It's still just a loop (FOR instead of MAPCAR-LAMBDA)...and not to mention the choice (in c++) of either changing the orig array or not (vs creating another in alisp) so there is that extra cool-ness factor.

Code - C++: [Select]
  1. void TimesTwo(std::vector<int> &array) {
  2.     for(unsigned i = 0; i < array.size(); ++i)
  3.         array[i] = array[i] * 2;
  4. }


SIDESHOWBOB, looks like a rabbit hole. What kind of custom solution is this (is it a big thing/lib/whatever, is speed an issue, etc., etc.)? Would it be easier to just port this "thing" to AutoLisp or C#?
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

Lee Mac

  • Seagull
  • Posts: 12926
  • London, England
Re: Getting arrays to/from a custom activex function
« Reply #5 on: February 09, 2012, 01:59:12 PM »
Another long shot:

Code - Auto/Visual Lisp: [Select]
  1. (setq n (vlax-create-object "mycomdll.MyComClass"))
  2. (vlax-invoke n 'arraytimestwo '(1.0 2.0) 'newsa)

SIDESHOWBOB

  • Guest
Re: Getting arrays to/from a custom activex function
« Reply #6 on: February 10, 2012, 05:05:04 AM »
SIDESHOWBOB, looks like a rabbit hole. What kind of custom solution is this (is it a big thing/lib/whatever, is speed an issue, etc., etc.)? Would it be easier to just port this "thing" to AutoLisp or C#?

It's a big analysis tool that already plugs into other programs, definitely can't be changed.  I need a wrapper for it in autolisp - well I could try c# but as far as I have gathered autolisp has a greater chance of working across many different autocad versions.

SIDESHOWBOB

  • Guest
Re: Getting arrays to/from a custom activex function
« Reply #7 on: February 10, 2012, 09:41:39 AM »
Ok, down the rabbit hole we go!

Tried writing two c++ functions.  One accepts a variant containing a safearray of doubles.  The other accepts a variant containing a safearray of variants:
Code - C++: [Select]
  1. STDMETHODIMP MyComClass::safearraytimestwo(VARIANT in, VARIANT* out)
  2. {
  3.         CComSafeArray<double> sa_in;
  4.         sa_in.Attach(*in.pparray);
  5.         ULONG size = sa_in.GetCount();
  6.  
  7.         CComSafeArray<double> out_sa;
  8.         out_sa.Create(size);
  9.  
  10.         for (long i=0;i<size;i++)
  11.                 out_sa.SetAt(i,sa_in[i]*2);
  12.        
  13.         sa_in.Detach();
  14.         *out = CComVariant(out_sa);
  15.         return S_OK;
  16. }
  17.  
  18. STDMETHODIMP MyComClass::array3(VARIANT in, VARIANT* out)
  19. {
  20.         CComSafeArray<VARIANT> sa_in;
  21.         sa_in.Attach(*in.pparray);
  22.         ULONG size = sa_in.GetCount();
  23.  
  24.         CComSafeArray<double> out_sa;
  25.         out_sa.Create(size);
  26.  
  27.         for (long i=0;i<size;i++)
  28.                 out_sa.SetAt(i,sa_in[i].dblVal*2);
  29.        
  30.         sa_in.Detach();
  31.         *out = CComVariant(out_sa);
  32.         return S_OK;
  33. }
  34.  
 

Then I tried calling each using the suggestions above

Code - Auto/Visual Lisp: [Select]
  1. _$ (setq v (vlax-make-variant (vlax-safearray-fill (vlax-make-safearray vlax-vbdouble '(0 . 1)) '(1 2))))
  2. _$ (vlax-invoke n 'safearraytimestwo '(1.0 2.0) 'newsa)
  3. ; error: Unknown exception occurred
  4. ; warning: unwind skipped on unknown exception
  5. _$ (vlax-invoke-method n 'safearraytimestwo v 'newsa)
  6. ; error: Unknown exception occurred
  7. ; warning: unwind skipped on unknown exception
  8. _$
  9. _$ (vlax-invoke n 'array3 '(1.0 2.0) 'newsa)
  10. ; error: Unknown exception occurred
  11. ; warning: unwind skipped on unknown exception
  12. _$ (vlax-invoke-method n 'array3 v 'newsa)
  13. ; error: Unknown exception occurred
  14. ; warning: unwind skipped on unknown exception
  15. _$
  16.  

In all cases the exception was an assert thrown by CComSafeArray::Attach() that seemed to be saying the data type in the array wasn't as expected.  Is vbDouble different to double I wonder?

JohnK

  • Administrator
  • Seagull
  • Posts: 10661
Re: Getting arrays to/from a custom activex function
« Reply #8 on: February 10, 2012, 09:49:39 AM »
Sorry, pressed for time so your gonna get a typical kind of response from me right now: small vague/obscure sentences packed with thoughts and assumptions.

***
It's a big analysis tool that already plugs into other programs, definitely can't be changed.  I need a wrapper for it in autolisp - well I could try c# but as far as I have gathered autolisp has a greater chance of working across many different autocad versions.

Oh yes, that is true; it works with more versions (no "recompile" situation).

(brief)
The link below doesn't really fit in the spirit of the above--and I apologize for just tossin' a link at cha and not explaining my thoughts--but you'll get my idea once you see it.

http://www.theswamp.org/index.php?topic=33994.msg393291#msg393291

(assuming, vague and obscure)
My thoughts were: If you have to distribute this package (with an installer) of yours, the installer can check which version of the above link to install.


**EDIT: I just noticed you posted
>  vbDouble different to double
"vb" stands for Visual Basic. Does that mean anything? Is a double in vb different (I'm pretty sure it is but I don't know for sure)?
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

JohnK

  • Administrator
  • Seagull
  • Posts: 10661
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

SIDESHOWBOB

  • Guest
Re: Getting arrays to/from a custom activex function
« Reply #10 on: February 10, 2012, 09:54:04 AM »
Se7en - thanks - I guess calling the windows shell is an option.

Another thought.  Is there a well documented way of getting arrays back and forth between autolisp and C# or VB (over COM)?  If that works well I can learn one of the latter languages and presumably use them to call my backend code (unmanaged C++).

JohnK

  • Administrator
  • Seagull
  • Posts: 10661
Re: Getting arrays to/from a custom activex function
« Reply #11 on: February 10, 2012, 10:11:16 AM »
Another thought.  Is there a well documented way of getting arrays back and forth between autolisp and C# or VB (over COM)?
...

Not sure about the doc's. ...From the last two links, I was wondering if you could not use COM (a straight dll instead) with alisp. Of course I'm just thinking here and I don't have the first clue; I'm more of a *nix/gcc C++ kinda person so when we start talking COM my head gets all mushy.
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: Getting arrays to/from a custom activex function
« Reply #12 on: February 13, 2012, 07:02:19 AM »
It seems there's something strange going on. At first I thought it might be that you need to use the ObjectARX defined types instead of C++'s own types. But then why would VL work fine with ActiveX from (say) Excel? Doing some research, it seems you need to use a SAFEARRAY type for input/output/returns when working with arrays in ActiveX - the standard C++ array is actually a "dynamic" array in that it basically just passes the RAM pointer to the 1st item in the array. Perhaps that's some "requirement" in ActiveX???

See some of these: http://www.canbal.com/index.php?/Using-VisualBasic/vb-and-safearray.html

I don't know if you'd rather just go through to DotNet. It's reasonably simple - though not as "off-the-hip" as Lisp would be. There are quite a few samples of linking to ActiveX and/or non-managed C or even porting your non-managed into C#. And since DotNet compiles to a bytecode DLL it works much like a Java class in that it gets "interpreted" by the DotNet runtime environment. So in most cases all you need to worry about is that the client's PC has the same version of DotNet installed as you've compiled to. It's just in situations where you're referring to objects in Acad which has changed / added / removed definitions in ACad's DotNet SDK where it starts to matter about which version of ACad you're compiling for as well.

E.g. I've written some VB.Dot and C# DLL's using various DotNet versions (2, 3 & 3.5) and in most cases the same DLL would run perfectly in both ACad 2008 and 2011. It's when I'm starting to go with unmanaged code (e.g. the invoke function from inside the acad.exe) where things go a bit wonky. In those cases it seems I need to compile a DLL for each major release of ACad.
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

SIDESHOWBOB

  • Guest
Re: Getting arrays to/from a custom activex function
« Reply #13 on: February 14, 2012, 10:57:21 AM »
Woohoo - got it working at last!!  Thanks Se7en and irneb, some of your leads proved useful.

Here's the code, if anyone needs it in future:

Code - C++: [Select]
  1.         STDMETHODIMP MyComClass::arraytimestwo(VARIANT in, VARIANT* out)
  2.         {
  3.                 CComSafeArray<double> sa_in;
  4.                 sa_in.Attach(in.parray);
  5.                 ULONG size = sa_in.GetCount();
  6.                 CComSafeArray<double> out_sa;
  7.                 out_sa.Create(size);
  8.                 for (long i=0;i<size;i++)
  9.                         out_sa.SetAt(i,sa_in.GetAt(i)*2);
  10.                
  11.                 CComVariant(out_sa).Detach(out);
  12.                 return S_OK;
  13.         }

Code - Auto/Visual Lisp: [Select]
  1.     (vl-load-com)
  2.     (setq n (vlax-create-object "mycomdll.MyComClass"))
  3.     (setq sa (vlax-make-safearray vlax-vbDouble '(0 . 1)))
  4.     (vlax-safearray-fill sa '(1 2))
  5.     (vlax-safearray->list sa)
  6.     (vlax-invoke-method n 'arraytimestwo sa 'newvar)
  7.     (vlax-safearray->list newvar)

Mistakes I was making: not realising that CComVariant::Detach returns a pointer to the detached variant (I was trying to return variant by value).  Also I was trying to initialize CComSafeArray with in.pparray not in.parray, which threw an assert as the type was wrong.
« Last Edit: February 14, 2012, 11:03:25 AM by SIDESHOWBOB »

JohnK

  • Administrator
  • Seagull
  • Posts: 10661
Re: Getting arrays to/from a custom activex function
« Reply #14 on: February 14, 2012, 11:12:23 AM »
w00t! Congratulations!
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org