#lang scribble/doc
@(require scribble/manual
          scribble/bnf
          (for-label scheme/base
                     mysterx))

@(define (com-index name what . proto)
   (index* (list (format "~a COM ~a" name what))
           (list @elem{@(tt name) COM @|what|})
           (apply tt proto)))

@title{@bold{MzCOM}: Racket as a Windows COM Object}

@author["Paul Steckler"]

@exec{MzCOM.exe} is a Windows COM (i.e., Component Object Model) class
wrapper for Racket.

During normal installation of MzCOM, the executable is registered as a
COM object automatically.  If you move the Racket installation
folder, re-register @exec{MzCOM.exe} with

@commandline{mzcom.exe /RegServer}

The @exec{MzCOM.exe} executable find DLLs and Racket library
collections relative to its own path.

@; ----------------------------------------------------------------------

@section{Loading MzCOM}

To load a COM object, COM hosts require a COM class name or a ProgID.
MzCOM has the class name @tt{"MzObj Class"} and the ProgID
@tt{"MzCOM.MzObj.@nonterm{version}"}, where @nonterm{version} is
@(version).

In the Visual BASIC 6 environment, from the @menuitem["Project"
"References (VB6)"], check @onscreen{MzCOM 1.0 Type Library}.  In
Visual BASIC .NET, choose @menuitem["Project" "Add Reference"], and
from the @onscreen{COM} tab, select @onscreen{MzCOM 1.0 Type Library}.
In your code, declare a variable, then assign to it:

@verbatim[#:indent 2]{
  DIM schemeObject AS MzObj
  SET schemeObject = NEW MzObj
}

From Visual C++:

@verbatim[#:indent 2]{
 #include "mzcom.h" 
     
 CLSID clsid;
 IMzObj *pIMzObj;

 CoInitialize(NULL);
 CLSIDFromProgID(L"MzCOM.MzObj.<version>",&clsid);
 CoCreateInstance(clsid,NULL,CLSCTX_SERVER,IID_IMzObj, (void **)&pIMzObj);
}

where @tt{<version>} is the version number.  You'll need the
definition of @tt{IID_IMzObj} (see @secref["guids"]).  The header file
@filepath{mzcom.h} is generated as @filepath{src\worksp\mzcom\} when
building from the Racket source distribution. The above C/C++ code
is for illustration; your actual code should check return values, of
course.

Using @schememodname[mysterx] to manipulate COM objects within Racket,
you can load MzCOM with either

@schemeblock[
  (cci/coclass "MzObj Class")
]

or

@schemeblock[
  (cci/progid "MzCOM.MzObj.<version>")
]

Consult your documentation for loading MzCOM into other COM
environments.  MzCOM is compiled as a ``dual-mode'' class, meaning its
methods may be called directly or by using OLE Automation.

@section[#:tag "guids"]{GUIDs}

When compiled from the Racket source distibrution, the directory
@filepath{src\worksp\mzcom\} contains the file @filepath{MzCOM_i.c}
that contains GUIDs for MzCOM.  Those GUIDs are as follows:

@verbatim[#:indent 2]{
  const IID IID_IMzObj = 
    {0xA604CBA8,0x2AB5,0x11D4,{0xB6,0xD3,0x00,0x60,0x08,0x90,0x02,0xFE}};

  const IID LIBID_MZCOMLib = 
    {0xA604CB9C,0x2AB5,0x11D4,{0xB6,0xD3,0x00,0x60,0x08,0x90,0x02,0xFE}};

  const IID DIID__IMzObjEvents = 
    {0xA604CBA9,0x2AB5,0x11D4,{0xB6,0xD3,0x00,0x60,0x08,0x90,0x02,0xFE}};

  const CLSID CLSID_MzObj = 
    {0xA3B0AF9E,0x2AB0,0x11D4,{0xB6,0xD2,0x00,0x60,0x08,0x90,0x02,0xFE}};
}

which represent the @tt{IMzObj} interface, the MzCOM type library, the
@tt{IMzObjEvents} interface, and the @tt{MzObj} class, respectively.


@section{Methods}

MzCOM support three COM methods:

@itemize[

 @item{@com-index["About" "method"]{void About(void)}

      Takes no arguments and displays an informational 
      dialog.}

 @item{@com-index["Eval" "method"]{BSTR Eval(BSTR input)}

      Takes and returns @tt{BSTR}s (BASIC strings).  The returned
       value is the result of evaluating the input expression,
       formatted as a string.  The input string may contain several
       S-expressions.  The embedded Racket updates its environment
       with each evaluation.  Therefore, it is possible to define
       procedures in a call to @tt{Eval}, and use the procedures in
       subsequent calls.}

 @item{@com-index["Reset" "method"]{Reset :: void Reset(void)}

       Resets the Racket environment to the initial environment.
       Also, the custodian for the primary Racket thread is invoked,
       shutting all its managed values.}
]

@section{Events}

MzCOM supports a single event.

@itemize[

 @item{@com-index["SchemeError" "event"]{SchemeError()}

        Passed a BSTR (BASIC string) that explains the error.}

]

@section{Errors}

When an error occurs in MzCOM, it creates a COM error object.  C and
C++ clients can use @tt{GetErrorInfo} to retrieve error information.
Clients implemented in other languages typically have some equivalent
means to obtain COM error information.

@section{Evaluation thread}

The Racket evaluator runs in a Win32 thread created when MzCOM is
loaded.  If an expression kills the primary Racket thread, as in

@schemeblock[
  (kill-thread (current-thread))
]

then the evaluator Win32 thread is also killed.  
When that happens, subsequent calls to Eval() will fail.

@section{Acknowledgments}

MzCOM was developed in response to a query by 
Andre Van Meulebrouck.  Andre also did extensive 
testing with Visual BASIC.
