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