Author Topic: Passing reactors/callbacks to COM object and calling them from the COM object  (Read 4727 times)

0 Members and 1 Guest are viewing this topic.

SIDESHOWBOB

  • Guest
I tried passing a function to my COM object as an argument, with the hope that the COM object might be handed an IDispatch* which I could call ...
Code - Auto/Visual Lisp: [Select]
  1. (defun hi () (princ "hi\n"))
  2. (vlax-invoke-method calculation 'callbacktest 'hi)
  3. ; error: lisp value has no coercion to VARIANT with this type:  HI
but vlax-invoke-method seems to expect a VARIANT rather than a function for its argument. 

Is there a way to do this does anyone know?

Daniel Eiszele

  • Newt
  • Posts: 85
Maybe pass your autocad application object as well as a string representing the lisp function you wish to create a callback to and then use late binding (or early binding if you have the autocad type library linked), to call application.document.utility.sendcommand("function name"). You could also pass the utility object directly.  This is untested but I am sure it will pass an Idispatch interface you can cast to the relevant interface.

BlackBox

  • King Gator
  • Posts: 3770
AFAIK- it's not vlax-invoke-method that is expecting a variant, but the Calculation Method in your example above.

Perhaps this is only loosly related to the topic, but at least when calling a Method, one may get different returns depending on the syntax of the calling statement, I.e., vlax-invoke, etc. Not sure if this has any role in calling any given Method, per-se.

HTH
"How we think determines what we do, and what we do determines what we get."

SIDESHOWBOB

  • Guest
I don't think my callbacktest is expecting a variant, here's the declaration:

Code - C++: [Select]
  1. interface ICalculation : IDispatch{
  2.         [id(7), helpstring("method callbacktest")] HRESULT callbacktest([in] IDispatch* callback);
  3. };
  4.  

SIDESHOWBOB

  • Guest
Calling vlax-invoke instead is a nice idea though I get
Code - Auto/Visual Lisp: [Select]
  1. ; error: argument type mismatch: HI
... will have a go with Daniel's suggestion

SIDESHOWBOB

  • Guest
Daniel - I have nearly got your suggestion working, thank you!!  (The only difference being that SendCommand is a member of Document, not Utility). 

Is there a way to prevent the command being echoed in autocad though?  One use for a callback I have is setting a progress bar, so it won't look great to see
(acet-ui-progress-safe n) appear 100 times in the command window.

SIDESHOWBOB

  • Guest
Another issue I have just found is that the commands sent this way seem to be queued until my call to (vlax-invoke-method) is over.  Is there any way to have them executed immediately?

Daniel Eiszele

  • Newt
  • Posts: 85
Daniel - I have nearly got your suggestion working, thank you!!  (The only difference being that SendCommand is a member of Document, not Utility). 

Is there a way to prevent the command being echoed in autocad though?  One use for a callback I have is setting a progress bar, so it won't look great to see
(acet-ui-progress-safe n) appear 100 times in the command window.

Yes, I remembered that after I replied.  You can use Document.EvaluateLisp() to send your commands without echoing to the command line. (It may also remedy your second problem also?)
« Last Edit: March 19, 2012, 05:46:59 PM by Daniel Eiszele »

SIDESHOWBOB

  • Guest
There doesn't seem to be a method on Application or Document called EvaluateLisp.

Where are these all documented?  I found this link http://www.kxcad.net/autodesk/autocad/AutoCAD_ActiveX_and_VBA_Reference/idh_application_object.htm which mentioned a method Eval() but that's for VBA not Lisp.

Daniel Eiszele

  • Newt
  • Posts: 85
Ahh... I do most of my hacking for Bricscad and found the function by accident by interrogating my IDE's intellisense for the document object.  It seems that the function isn't even documented in the Bricscad Help Files.  I have however found reference to something called the "Visual Lisp ActiveX Module" which looks promising for Autocad use.  The only thing is it seems to be undocumented also, which means a bit of googling and probably a lot of trial and error!  There are probably arx functions that you could reference if you are confident doing this but nothing that is pure COM so far as I understand.

BlackBox

  • King Gator
  • Posts: 3770
For Visual LISP documentation, consult the VLIDE's Apropos, and VBAIDE's developer documentation. Both Visual LISP and VBA can access the ActiveX API.
"How we think determines what we do, and what we do determines what we get."

SIDESHOWBOB

  • Guest
Thanks RenderMan but I'm looking for the reverse really - wanting to call some visual lisp functions from ActiveX/COM, not vice versa.

SIDESHOWBOB

  • Guest
Looks like if I can't callback synchronously over COM I'll have to pop up my own progress bar window.

Is there a call that tells autocad to at least handle window events so it doesn't appear to freeze up while my dll is calculating?

Daniel Eiszele

  • Newt
  • Posts: 85
Looks like if I can't callback synchronously over COM I'll have to pop up my own progress bar window.

Is there a call that tells autocad to at least handle window events so it doesn't appear to freeze up while my dll is calculating?

The synchronous call may be possible using the visual lisp activex module but if that's too much effort (and it probably is), have you considered converting your activex dll to a Com addin and do away with the lisp completely?

As to the second issue, surely if you are displaying a progress bar it shouldn't matter if autocad appears to "freeze" as the bar informs the user it is still working?

SIDESHOWBOB

  • Guest
Daniel thanks again for the pointers - but I have to ask

The Visual lisp activex module - what is that and where to find docs?

What is a COM addin (from an autocad perspective) and what extra functionality would that buy me given I can already make COM calls to autocad from my c++?

Daniel Eiszele

  • Newt
  • Posts: 85
I don't have autocad so the visual lisp activex module is only something I found online. It appears to be a dll you can reference in your  com project which gives direct access to the available lisp functions bypassing the command line. You will have to google it if you want to find out more as I don't have access to acad and can't test anything I find anyway.

As far as a Com addin is concerned; it is basically the same activex object but you implement the Iextensibility2 interface.  Then you add entries in the registry so that it loads automatically when autocad starts.  From there on in any functions that you register with autocad are automatically available from the command line. You can also add these functions to toolbars and menu items and react to application and document events.  And because it is in process you should see a big speed difference in computation intensive operations.

Something to think about maybe?

SIDESHOWBOB

  • Guest
Cheers will have to investigate further.  Don't think I need to make a COM addin - my vlisp handles the interface quite nicely now and the dll when called is fast enough.

SIDESHOWBOB

  • Guest
For future reference - over COM, I just found the Update method on the application object which causes it to, er, update.

This can be combined with ActiveDocument.Utility.Prompt to display messages synchronously in the Autocad window over COM.

If you begin each message with "\r" then it will overwrite the "Command:" prompt, though not the previous message sent to Prompt (any ideas on how to do this welcome!)

So, it's not quite a progress bar, but it'll do the job.

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
If it's a progress bar you want ... and you're using the command-line to show a text representation of it, you don't need C++/ActiveX/COM for that. Here's a pure lisp function of mine which does much the same - but in the status bar:
Code - Auto/Visual Lisp: [Select]
  1. ;;; -------------------------------------------------------------------------------------
  2. ;;; Show a progress bar message in the status bar
  3. ;;; -------------------------------------------------------------------------------------
  4. (defun TextProgressBar (msg n / l i s)
  5.   (if n
  6.     (progn
  7.       ;; Get the mode tile's length
  8.       (setq l (- (atoi (menucmd "M=$(linelen)")) (strlen msg)))
  9.       ;; Get the numnber of characters simulating the number n (0.0= none, 1.0=all)
  10.       (setq i (fix (* l n)))
  11.       (repeat l
  12.         (if (> (setq i (1- i)) 0)
  13.           (setq s (cons 33 s))
  14.           (setq s (cons 46 s))
  15.         )
  16.       )
  17.       (grtext -1 (strcat msg (vl-list->string (reverse s))))
  18.     )
  19.     (grtext -1 msg)
  20.   )
  21. )
  22.  
If you want a true progress bar, then you could import a function call from the acad.exe file (as if it's a dll): http://through-the-interface.typepad.com/through_the_interface/2007/05/displaying_a_pr.html

BTW, while you're in C++, why're you making an ActiveX connection? I'd have thought you'd be better off going with C# & DotNet ... see some of the comments on the bottom of that page.

Edit: And if you want to call a lisp function directly, the the P/Invoke method (as explained in Kean's blog) can also work: http://www.theswamp.org/index.php?topic=35714.0
« Last Edit: April 01, 2012, 04:28:52 AM by irneb »
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

BlackBox

  • King Gator
  • Posts: 3770
If you don't mind the .ARX requirement, there's always the Dos_GetProgress function too.
"How we think determines what we do, and what we do determines what we get."

SIDESHOWBOB

  • Guest
@irneb - cheers - displaying a progress bar from lisp was never the issue.  My app architecture is autocad -> vlisp -> COM -> C++ because I was hoping to gain wide compatibility over different autocad versions, rather than taking the .net approach where I have to recompile the plugin for each autocad version I would like to support.  The progress bar however has to call functions in the reverse direction (C++ to autocad) so whether I'll have actually achieved version independence (given I'm now using undocumented COM calls) is another matter..!

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Yes, if you're using P/Invoke you might find there's some issues between versions - seeing as the function entry point may be different in 2 acad.exe files. I think the major one would be at the 2006 split when ADesk changed to using UniCode.

The DotNet stuff doesn't need recompiling all the time. It depends on the versions, some versions work perfectly fine with the same DLL file (e.g. I've got some which work on both 2008 and 2012). The issue is that you need to compile using the lowest common DotNet version - e.g. instead of 3.5 use 2.0. Also try to steer clear of API functions newly introduced into newer versions. Apparently there's some new requirement with 2013, though I've not tested it yet.

The ARX's need recompiling between major versions. Every time the "true" version number's integer portion changes. Thus 2007/8/9 may work fine with the same ARX, but a new one would be required for 2010/11/12. Thus far ADesk's grouped their major versions into 3 consecutive releases.

All that said, I'm unsure why you're going with COM outside of lisp. Is there some reason? AFAIK all acad's com interfaces are also available in VLisp. And the stuff you can't get to in ALisp/VLisp is definitely also impossible to get to through COM.
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

SIDESHOWBOB

  • Guest
All that said, I'm unsure why you're going with COM outside of lisp. Is there some reason?

Yes.  The app backend is CPU heavy, and is already written in native c++ (for another frontend, not autocad).  I wrapped it in COM so I could call it from lisp.  This is all fine until I need a progress bar - the backend has to make 'update progress' calls, but as vlisp doesn't support callbacks over COM, another method was needed so the C++ could communicate to the autocad user how much progress had been made (at intervals chosen by the algorithm running in C++).  This eventually led me to autocad's COM interface which allows me to put messages in the display directly over COM, bypassing the lisp environment (which I can't access from there in any case).

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Have you thought of popping up a modal dialog box with a message & windows progress bar? That way you don't need to fiddle with strange ways of getting acad's progress bar to display - just use a normal windows form which you can create direct in C++.
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

SIDESHOWBOB

  • Guest
Yes, though I'm happy with the Prompt and Update COM calls for now.

SIDESHOWBOB

  • Guest
Have you thought of popping up a modal dialog box with a message & windows progress bar? That way you don't need to fiddle with strange ways of getting acad's progress bar to display - just use a normal windows form which you can create direct in C++.

Irneb,

I'm coming back around to this idea.  If I want to do this, don't I need to get the HWND of autocad from somewhere?  Do you know how to do that?


Lee Mac

  • Seagull
  • Posts: 12915
  • London, England
If I want to do this, don't I need to get the HWND of autocad from somewhere?  Do you know how to do that?

In LISP:

Code - Auto/Visual Lisp: [Select]

Or, for the Active Document window:

Code - Auto/Visual Lisp: [Select]

BlackBox

  • King Gator
  • Posts: 3770

I also use this (vla-get-hwnd) with the Shell.Application Object's BrowseForFolder Method.  :wink:
"How we think determines what we do, and what we do determines what we get."