| allegro-cl archives 2003-8-22 | home index prev H thread prev K thread-next J next L | 
| 
           From: Thomas R. Hinrichs Subject: embedding ActiveX controls in Common Graphics windows Date: 2003-8-22 17:06 
I'm getting stuck trying to embed a web browser control in a Lisp window  (in Franz Allegro 6.2, Common Graphics).  Has anybody successfully embedded an ActiveX control in a Common Graphics window?
Franz has a nice example of using OLE to control Internet Explorer, but I would really like it embedded in the window so I can control its size and location and eliminate the navbar and so forth.  (My main goal is to display equations in MathML, so I don't want user controls & etc)
I've gotten reasonably far, but the documentation is a bit vague about a few things that should be simple.
The first thing I realized is that I don't want to embed I.E. itself, since that's an application.  I want to embed the reusable Web Browser Control, which is contained in shodocvw.dll.  Spelunking the registry, I discovered the  VersionIndependentProgID is "Shell.Explorer" and its classid is "{8856F961-340A-11D0-A96B-00C04FD705A2}".
Now it's going to need to be an inproc server and the autotool should be a subclass of control, so we want to require :ocx-container and define the client interface :IViewObject2, since this is a visible control.
Here I ran into some problems with ACL: When you call (require :ocx-container) it barfs because it tries to require :olewin, a module that doesn't exist. I can only assume that this is a typo and work around it by explicitly providing :olewin.
Next, it complains because it tries to load autotool.fasl, but somebody left off the ".fasl" so it can't find it.  I worked around that by manually loading it beforehand.
Based on these workarounds, I'm starting to think nobody's done this before...
I managed to make an autotool that seems like it should be the right thing (I can query it & etc), but I'm stuck trying to figure out what should be the site object, what should be the container of the site object & etc.  
My test code below dies when it gets to ole:install-control because it calls ole:get-Container with a clos instance and a number.  Something isn't right there and I have no clue if it's how I set it up or if it's a bug in the ole code.  Does anybody have any insight into this?  It would be *really* nice to be able to embed ActiveX controls in Lisp windows.
Thanks,
Tom Hinrichs
Computer Science Department
Northwestern University
(in-package :cg-user)
(eval-when (compile eval)
  (require :ole-dev))
(eval-when (load eval)
  (require :ole)
  (load "sys:;ole;client;autotool.fasl")      ;; workaround bug in Franz' ole
  (provide :olewin)                           ;; workaround another bug in Franz' ole
  (ole:require-modules :ocx-container)        ;; now this should work
  (ole:def-client-interface :IViewObject2)    ;; we need this interface for visible activeX controls
)
(defparameter ie-classid
  (ole:unique-guid "{0002DF01-0000-0000-C000-000000000046}"))
(defun get-autotool ()
  (setf (sys:gsgc-switch :print) nil)
  ;; Check the registry to be sure the explorer is out there
  (ole:with-open-registry-key (ie-reg ole:rkey-classes-root
				      `("CLSID" ,(ole:guid-name ie-classid))
				      :error-return nil)
     (when (null ie-reg)
       (format t "Sorry, no explorer on this system~2%")
       (return-from get-autotool nil)))
  (ole:start-ole)  ; in case ole not yet started
  (ole:ask-for-autotool
   "Shell.Explorer"
   ole:CLSCTX_INPROC_SERVER
   'ole:control            ; make the autotool a subclass of control
   :ole-classid "{8856F961-340A-11D0-A96B-00C04FD705A2}"))
(defun close-autotool (autotool)
  (ole:auto-method autotool :quit)
  (ole:release autotool))
(defmacro net-wait (tool)
  `(do () ((not (ole::auto-getf ,tool :Busy)))))
(defun do-query (tool)
  (ole::query-interface tool :IViewObject2))
(defun go-to-url (tool where)
  (cond ((symbolp where)
         (setq where (symbol-name where)))
        ((not (stringp where))
         (format t "destination must be entered as a symbol or string~%")
         (return-from go-to-url)))
  (ole:auto-method tool :navigate2
                   where   ; url
                   0       ; flags
                   "IE Window"      ; target frame name
                   0       ; postdata
                   0)      ; extra headers
  (net-wait tool))
;;;
;;; Define the container & control classes
;;; (Is windows-widget a reasonable thing to subclass?)
(defclass activex-pane (cg::widget-window ole:container-mixin) ())
(defclass activex-control (cg::windows-widget ole:site-mixin) ())
(defmethod widget-device ((item activex-control) dialog)
  (declare (ignore dialog))
  'activex-pane)
(defmethod device-open ((widget-window activex-pane) options)
  (apply #'cg::open-widget-window widget-window options))
(defmethod cg::widget-set-value ((widget-window activex-pane)
                             item new-value old-value recursive-p)
  (declare (ignore item new-value old-value recursive-p))
  (and (component widget-window)
       (cg::InvalidateRect (cg::handle! widget-window) ct::hnull cg::FALSE))
  t)
(defun make-ie-control ()
  (make-instance 'activex-control 
    :name :my-activeX-control
    :left 50
    :top 100
    :height 300 
    :width 500))
(defun make-parent-window ()
  "Make a plain window to hold the ActiveX control"
  (make-window :test-window 
    :owner (screen *system*)
    :class 'dialog
    :exterior (make-box 107 139 932 666)
    :close-button t
    :state :normal))
;;;
;;; Testing
;;;
(defparameter *autotool*  nil "The ole autotool")
(defparameter *activex-site* nil "The site object")
(defparameter *browserwin*   nil "The toplevel window")
(defun test-embedded-browser ()
  (setf *autotool* (get-autotool)) 
  (setf (ole::auto-getf *autotool* :visible) t)
  (setf *activex-site* (make-ie-control))
  (setf *browserwin* (make-parent-window))
  (add-component *activex-site* *browserwin*)
  (ole:install-control *autotool* *activex-site*)
  (go-to-url *autotool* "http://www.google.com"))
;;; End
           |