summaryrefslogtreecommitdiff
path: root/src/mzcom
diff options
context:
space:
mode:
authorDavid Bremner <bremner@debian.org>2015-06-26 15:47:13 +0200
committerDavid Bremner <bremner@debian.org>2015-06-26 15:47:13 +0200
commit9e9ed7939a749d17e00c95ae01c2441855a0085f (patch)
tree7c401029193c7de91bcd922dcd49de3e4e9d9c6c /src/mzcom
parent682f326601a0f4a2c585bc8563950eaf40edfc06 (diff)
Importing racket_6.2.orig.tar.gz
Diffstat (limited to 'src/mzcom')
-rw-r--r--src/mzcom/.gitignore3
-rw-r--r--src/mzcom/README3
-rw-r--r--src/mzcom/com_glue.c704
-rw-r--r--src/mzcom/com_glue.h75
-rw-r--r--src/mzcom/mzcom.cxx314
-rw-r--r--src/mzcom/mzcom.idl53
-rw-r--r--src/mzcom/mzobj.cxx569
-rw-r--r--src/mzcom/mzobj.h70
-rw-r--r--src/mzcom/resource.h21
9 files changed, 1812 insertions, 0 deletions
diff --git a/src/mzcom/.gitignore b/src/mzcom/.gitignore
new file mode 100644
index 0000000000..0fc6c14d27
--- /dev/null
+++ b/src/mzcom/.gitignore
@@ -0,0 +1,3 @@
+# Generated for 3m build:
+mzobj3m.cxx
+mzobj3m.sdep
diff --git a/src/mzcom/README b/src/mzcom/README
new file mode 100644
index 0000000000..dc961bc291
--- /dev/null
+++ b/src/mzcom/README
@@ -0,0 +1,3 @@
+This directory contains the MzCOM source files.
+
+See ..\worksp\README for build instructions.
diff --git a/src/mzcom/com_glue.c b/src/mzcom/com_glue.c
new file mode 100644
index 0000000000..48a9f0e0ac
--- /dev/null
+++ b/src/mzcom/com_glue.c
@@ -0,0 +1,704 @@
+/* Much of this code is from "COM in Plain C" by Jeff Gatt on Code
+ Project. That code is licensed under the Code Project Open License
+ (CPOL). */
+
+#include <windows.h>
+#include <objbase.h>
+#include <activscp.h>
+#include <olectl.h>
+#include <stddef.h>
+#define FOR_GLUE
+#include "com_glue.h"
+
+// A count of how many objects our DLL has created (by some
+// app calling our IClassFactory object's CreateInstance())
+// which have not yet been Release()'d by the app
+static DWORD OutstandingObjects;
+
+// A count of how many apps have locked our DLL via calling our
+// IClassFactory object's LockServer()
+static DWORD LockCount;
+
+// Where I store a pointer to my type library's TYPEINFO
+static ITypeInfo *MyTypeInfo;
+
+// The MzObj object ////////////////////////////////////////////////////////////
+
+// In our .H file, we use a macro which defines our MzObj struct
+// as so:
+//
+// typedef struct {
+// IMzObjVtbl *lpVtbl;
+// } MzObj;
+//
+// In other words, the .H file defines our MzObj to have nothing
+// but a pointer to its VTable. And of course, every COM object must
+// start with a pointer to its VTable.
+//
+// But we actually want to add some more members to our MzObj.
+// We just don't want any app to be able to know about, and directly
+// access, those members. So here we'll define a MyRealMzObj that
+// contains those extra members. The app doesn't know that we're
+// really allocating and giving it a MyRealMzObj object. We'll
+// lie and tell it we're giving a plain old MzObj. That's ok
+// because a MyRealMzObj starts with the same VTable pointer.
+//
+// We add a DWORD reference count so that this MzObj
+// can be allocated (which we do in our IClassFactory object's
+// CreateInstance()) and later freed. And, we have an extra
+// BSTR (pointer) string, which is used by some of the functions we'll
+// add to MzObj
+typedef struct {
+ IMzObjVtbl *lpVtbl;
+ DWORD count;
+ void *obj;
+ IConnectionPointContainer container;
+ IConnectionPoint point;
+ IMzObjEvents *evts;
+} MyRealMzObj;
+
+// Here are MzObj's functions.
+//
+// Every COM object's interface must have the 3 functions QueryInterface(),
+// AddRef(), and Release().
+//
+// I also chose to add 2, extra functions to MzObj, which a program
+// will call with the names GetString and SetString.
+
+// MzObj's QueryInterface()
+static HRESULT STDMETHODCALLTYPE QueryInterface(IMzObj *com_obj, REFIID vTableGuid, void **ppv)
+{
+ // Because our IMzObj sources events, we must return an
+ // IConnectionPointContainer sub-object if the app asks for one. Because we've
+ // embedded our IConnectionPointContainer object inside of our MyRealIMzObj,
+ // we can get that sub-object very easily using pointer arithmetic
+ if (IsEqualIID(vTableGuid, &IID_IConnectionPointContainer))
+ *ppv = ((unsigned char *)com_obj + offsetof(MyRealMzObj, container));
+ else if (IsEqualIID(vTableGuid, &IID_IConnectionPoint))
+ *ppv = ((unsigned char *)com_obj + offsetof(MyRealMzObj, point));
+
+ // Check if the GUID matches MzObj VTable's GUID. We gave the C variable name
+ // IID_MzObj to our VTable GUID. We can use an OLE function called
+ // IsEqualIID to do the comparison for us. Also, if the caller passed a
+ // IUnknown GUID, then we'll likewise return the MzObj, since it can
+ // masquerade as an IUnknown object too. Finally, if the called passed a
+ // IDispatch GUID, then we'll return the MzObj, since it can masquerade
+ // as an IDispatch too
+ else if (!IsEqualIID(vTableGuid, &IID_IUnknown) && !IsEqualIID(vTableGuid, &IID_IMzObj) && !IsEqualIID(vTableGuid, &IID_IDispatch))
+ {
+ // We don't recognize the GUID passed to us. Let the caller know this,
+ // by clearing his handle, and returning E_NOINTERFACE.
+ *ppv = 0;
+ return(E_NOINTERFACE);
+ }
+ else
+ // Fill in the caller's handle
+ *ppv = com_obj;
+
+ // Increment the count of callers who have an outstanding pointer to this object
+ com_obj->lpVtbl->AddRef(com_obj);
+
+ return(NOERROR);
+}
+
+// MzObj's AddRef()
+static ULONG STDMETHODCALLTYPE AddRef(IMzObj *com_obj)
+{
+ // Increment MzObj's reference count, and return the updated value.
+ // NOTE: We have to typecast to gain access to any data members. These
+ // members are not defined in our .H file (so that an app can't directly
+ // access them). Rather they are defined only above in our MyRealMzObj
+ // struct. So typecast to that in order to access those data members
+ return(++((MyRealMzObj *)com_obj)->count);
+}
+
+// MzObj's Release()
+static ULONG STDMETHODCALLTYPE Release(IMzObj *com_obj)
+{
+ // Decrement MzObj's reference count. If 0, then we can safely free
+ // this MzObj now
+ if (--((MyRealMzObj *)com_obj)->count == 0)
+ {
+ delete_mzobj(((MyRealMzObj *)com_obj)->obj);
+ GlobalFree(com_obj);
+ InterlockedDecrement(&OutstandingObjects);
+
+ if (com_can_unregister()) {
+ /* Only allowed object is released... */
+ PostMessage(NULL, WM_QUIT, 0, 0);
+ }
+
+ return(0);
+ }
+ return(((MyRealMzObj *)com_obj)->count);
+}
+
+// ================== The standard IDispatch functions
+
+// This is just a helper function for the IDispatch functions below
+static HRESULT loadMyTypeInfo(void)
+{
+ register HRESULT hr;
+ LPTYPELIB pTypeLib;
+
+ // Load our type library and get a ptr to its TYPELIB. Note: This does an
+ // implicit pTypeLib->lpVtbl->AddRef(pTypeLib)
+ if (!(hr = LoadRegTypeLib(&CLSID_TypeLib, 1, 0, 0, &pTypeLib)))
+ {
+ // Get Microsoft's generic ITypeInfo, giving it our loaded type library. We only
+ // need one of these, and we'll store it in a global Tell Microsoft this is for
+ // our MzObj's VTable, by passing that VTable's GUID
+ if (!(hr = pTypeLib->lpVtbl->GetTypeInfoOfGuid(pTypeLib, &IID_IMzObj, &MyTypeInfo)))
+ {
+ // We no longer need the ptr to the TYPELIB now that we've given it
+ // to Microsoft's generic ITypeInfo. Note: The generic ITypeInfo has done
+ // a pTypeLib->lpVtbl->AddRef(pTypeLib), so this TYPELIB ain't going away
+ // until the generic ITypeInfo does a pTypeLib->lpVtbl->Release too
+ pTypeLib->lpVtbl->Release(pTypeLib);
+
+ // Since caller wants us to return our ITypeInfo pointer,
+ // we need to increment its reference count. Caller is
+ // expected to Release() it when done
+ MyTypeInfo->lpVtbl->AddRef(MyTypeInfo);
+ }
+ }
+
+ return(hr);
+}
+
+// MzObj's GetTypeInfoCount()
+static ULONG STDMETHODCALLTYPE GetTypeInfoCount(IMzObj *com_obj, UINT *pCount)
+{
+ *pCount = 1;
+ return(S_OK);
+}
+
+// MzObj's GetTypeInfo()
+static ULONG STDMETHODCALLTYPE GetTypeInfo(IMzObj *com_obj, UINT itinfo, LCID lcid, ITypeInfo **pTypeInfo)
+{
+ register HRESULT hr;
+
+ // Assume an error
+ *pTypeInfo = 0;
+
+ if (itinfo)
+ hr = ResultFromScode(DISP_E_BADINDEX);
+
+ // If our ITypeInfo is already created, just increment its ref count. NOTE: We really should
+ // store the LCID of the currently created TYPEINFO and compare it to what the caller wants.
+ // If no match, unloaded the currently created TYPEINFO, and create the correct one. But since
+ // we support only one language in our IDL file anyway, we'll ignore this
+ else if (MyTypeInfo)
+ {
+ MyTypeInfo->lpVtbl->AddRef(MyTypeInfo);
+ hr = 0;
+ }
+ else
+ {
+ // Load our type library and get Microsoft's generic ITypeInfo object. NOTE: We really
+ // should pass the LCID to match, but since we support only one language in our IDL
+ // file anyway, we'll ignore this
+ hr = loadMyTypeInfo();
+ }
+
+ if (!hr) *pTypeInfo = MyTypeInfo;
+
+ return(hr);
+}
+
+// MzObj's GetIDsOfNames()
+static ULONG STDMETHODCALLTYPE GetIDsOfNames(IMzObj *com_obj, REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgdispid)
+{
+ if (!MyTypeInfo)
+ {
+ register HRESULT hr;
+
+ if ((hr = loadMyTypeInfo())) return(hr);
+ }
+
+ // Let OLE32.DLL's DispGetIDsOfNames() do all the real work of using our type
+ // library to look up the DISPID of the requested function in our object
+ return(DispGetIDsOfNames(MyTypeInfo, rgszNames, cNames, rgdispid));
+}
+
+// MzObj's Invoke()
+static ULONG STDMETHODCALLTYPE Invoke(IMzObj *com_obj, DISPID dispid, REFIID riid, LCID lcid, WORD wFlags,
+ DISPPARAMS *params, VARIANT *result, EXCEPINFO *pexcepinfo,
+ UINT *puArgErr)
+{
+ // We implement only a "default" interface
+ if (!IsEqualIID(riid, &IID_NULL))
+ return(DISP_E_UNKNOWNINTERFACE);
+
+ // We need our type lib's TYPEINFO (to pass to DispInvoke)
+ if (!MyTypeInfo)
+ {
+ register HRESULT hr;
+
+ if ((hr = loadMyTypeInfo())) return(hr);
+ }
+
+ // Let OLE32.DLL's DispInvoke() do all the real work of calling the appropriate
+ // function in our object, and massaging the passed args into the correct format
+ return(DispInvoke(com_obj, MyTypeInfo, dispid, wFlags, params, result, pexcepinfo, puArgErr));
+}
+
+// ================== The following are my own extra functions added to MzObj
+
+static HRESULT STDMETHODCALLTYPE Eval(IMzObj *com_obj, BSTR str, BSTR *res)
+{
+ if (!str) return(E_POINTER);
+
+ return mzobj_eval(((MyRealMzObj*)com_obj)->obj, str, res);
+}
+
+static HRESULT STDMETHODCALLTYPE About(IMzObj *com_obj)
+{
+ return mzobj_about(((MyRealMzObj*)com_obj)->obj);
+}
+
+static HRESULT STDMETHODCALLTYPE Reset(IMzObj *com_obj)
+{
+ return mzobj_reset(((MyRealMzObj*)com_obj)->obj);
+}
+
+// Here's MzObj's VTable. It never changes so we can declare it
+// static
+static const IMzObjVtbl IMzObj_Vtbl = {QueryInterface,
+ AddRef,
+ Release,
+ GetTypeInfoCount,
+ GetTypeInfo,
+ GetIDsOfNames,
+ Invoke,
+ Eval,
+ About,
+ Reset};
+
+
+VOID Fire_SchemeError(IMzObj *com_obj, BSTR description)
+{
+ if (((MyRealMzObj*)com_obj)->evts) {
+ VARIANTARG pvars[1];
+ DISPPARAMS disp = { pvars, NULL, 1, 0 };
+ memset(pvars, 0, sizeof(pvars));
+ pvars[0].vt = VT_BSTR;
+ pvars[0].bstrVal = description;
+ ((MyRealMzObj*)com_obj)->evts->lpVtbl->Invoke(((MyRealMzObj*)com_obj)->evts, 0x1, &IID_NULL,
+ LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp,
+ NULL, NULL, NULL);
+ }
+}
+
+// Our IConnectionPointContainer sub-object (for IMzObj) ////////////////////////
+
+static STDMETHODIMP QueryInterface_Connect(IConnectionPointContainer *com_obj, REFIID vTableGuid, void **ppv)
+{
+ // Because this is a sub-object of our IMzObj (ie, MyRealMzObj) object,
+ // we delegate to IMzObj's QueryInterface. And because we embedded the
+ // IConnectionPointContainer directly inside of MyRealMzObj, all we need
+ // is a little pointer arithmetic to get our IMzObj
+ return(QueryInterface((IMzObj *)((char *)com_obj - offsetof(MyRealMzObj, container)), vTableGuid, ppv));
+}
+
+static STDMETHODIMP_(ULONG) AddRef_Connect(IConnectionPointContainer *com_obj)
+{
+ // Because we're a sub-object of IMzObj, delegate to its AddRef()
+ // in order to increment IMzObj's reference count
+ return(AddRef((IMzObj *)((char *)com_obj - offsetof(MyRealMzObj, container))));
+}
+
+static STDMETHODIMP_(ULONG) Release_Connect(IConnectionPointContainer *com_obj)
+{
+ // Because we're a sub-object of IMzObj, delegate to its Release()
+ // in order to decrement IMzObj's reference count
+ return(Release((IMzObj *)((char *)com_obj - offsetof(MyRealMzObj, container))));
+}
+
+static STDMETHODIMP EnumConnectionPoints(IConnectionPointContainer *com_obj, IEnumConnectionPoints **enumPoints)
+{
+ // The app had better know the GUIDs of whatever objects our
+ // IMzObj supports for callbacks (ie, an IMzObjEvents), because
+ // we're not going to bother providing him with an object to
+ // enumerate the VTable GUIDs of all those supported objects
+ *enumPoints = 0;
+ return(E_NOTIMPL);
+}
+
+static STDMETHODIMP FindConnectionPoint(IConnectionPointContainer *com_obj, REFIID vTableGuid, IConnectionPoint **ppv)
+{
+ // Is the app asking us to return an IConnectionPoint object it can use
+ // to give us its IMzObjEvents object? The app asks this by passing us
+ // IMzObjEvents VTable's GUID (which we defined in IMzObj.h)
+ if (IsEqualIID(vTableGuid, &DIID_IMzObjEvents))
+ {
+ MyRealMzObj *iExample;
+
+ // The app obviously wants to connect its IMzObjEvents object
+ // to IMzObj. In order to do that, we need to give the app a
+ // standard IConnectionPoint, so the app can call its Advise function
+ // to give us its IMzObjEvents. This is easy to do since we embedded both
+ // our IConnectionPointContainer and IConnectionPoint inside of our
+ // IMzObj. All we need is a little pointer arithmetic
+ iExample = (MyRealMzObj *)((char *)com_obj - offsetof(MyRealMzObj, container));
+ *ppv = &iExample->point;
+
+ // Because we're giving the app a pointer to our IConnectionPoint, and
+ // our IConnectionPoint is a sub-object of IMzObj, we need to
+ // increment IMzObj's reference count. The easiest way to do this is to call
+ // our IConnectionPointContainer's AddRef, because all we do there is delegate
+ // to our IMzObj's AddRef
+ AddRef_Connect(com_obj);
+
+ return(S_OK);
+ }
+
+ // We don't support any other app objects connecting to IMzObj
+ // events. All we've defined, and support, is an IMzObjEvents object. Tell
+ // the app we don't know anything about the GUID he passed to us, and
+ // do not give him any IConnectPoint object
+ *ppv = 0;
+ return(E_NOINTERFACE);
+}
+
+
+static const IConnectionPointContainerVtbl IConnectionPointContainer_Vtbl = {QueryInterface_Connect,
+ AddRef_Connect,
+ Release_Connect,
+ EnumConnectionPoints,
+ FindConnectionPoint};
+
+// Our IConnectionPoint sub-object (for IMzObj) ////////////////////////////
+
+static STDMETHODIMP QueryInterface_Point(IConnectionPoint *com_obj, REFIID vTableGuid, void **ppv)
+{
+ // Because this is a sub-object of our IMzObj (ie, MyRealMzObj) object,
+ // we delegate to IMzObj's QueryInterface. And because we embedded the
+ // IConnectionPoint directly inside of MyRealMzObj, all we need
+ // is a little pointer arithmetic to get our IMzObj
+ return(QueryInterface((IMzObj *)((char *)com_obj - offsetof(MyRealMzObj, point)), vTableGuid, ppv));
+}
+
+static STDMETHODIMP_(ULONG) AddRef_Point(IConnectionPoint *com_obj)
+{
+ // Because we're a sub-object of IMzObj, delegate to its AddRef()
+ // in order to increment IMzObj's reference count
+ return(AddRef((IMzObj *)((char *)com_obj - offsetof(MyRealMzObj, point))));
+}
+
+static STDMETHODIMP_(ULONG) Release_Point(IConnectionPoint *com_obj)
+{
+ // Because we're a sub-object of IMzObj, delegate to its Release()
+ // in order to decrement IMzObj's reference count
+ return(Release((IMzObj *)((char *)com_obj - offsetof(MyRealMzObj, point))));
+}
+
+// Called by the app to get our IMzObjEvents VTable's GUID (which we defined in IMzObj.h).
+// The app would call GetConnectionInterface() if it didn't link with IMzObj.h, and
+// therefore doesn't know our IMzObjEvents VTable's GUID. The app needs to know this GUID
+// because our Advise function below is going to pass this same GUID to some app object's
+// QueryInterface. The app's QueryInterface had better recognize this GUID if it intends
+// to honor our request to give us its IMzObjEvents object
+static STDMETHODIMP GetConnectionInterface(IConnectionPoint *com_obj, IID *vTableGuid)
+{
+ // Tell the app to recognize our IMzObjEvents VTable GUID (defined as
+ // DIID_IFeedback in IMzObj.h) when our Advise function calls
+ // some app QueryInterface function
+ CopyMemory(vTableGuid, &DIID_IMzObjEvents, sizeof(GUID));
+ return(S_OK);
+}
+
+// Called by the app to get the IConnectionPointContainer sub-object for our
+// IMzObj object.
+static STDMETHODIMP GetConnectionPointContainer(IConnectionPoint *com_obj, IConnectionPointContainer **ppv)
+{
+ MyRealMzObj *iExample;
+
+ // Get the MyRealMzObj that this IConnectionPoint sub-object belongs
+ // to. Because this IConnectPoint sub-object is embedded directly inside its
+ // MyRealMzObj, all we need is a little pointer arithmetic
+ iExample = (MyRealMzObj *)((char *)com_obj - offsetof(MyRealMzObj, point));
+
+ // Because the IConnectionPointContainer sub-object is also embedded right inside
+ // the same MyRealMzObj, we can get a pointer to it easily as so
+ *ppv = &iExample->container;
+
+ // Because we're giving the app a pointer to our IConnectionPointContainer, and
+ // our IConnectionPointContainer is a sub-object of IMzObj, we need to
+ // increment IMzObj's reference count. The easiest way to do this is to call
+ // our IConnectionPoint's AddRef, because all we do there is delegate
+ // to our IMzObj's AddRef
+ AddRef_Point(com_obj);
+
+ return(S_OK);
+}
+
+// Called by the app to give us its IMzObjEvents object. Actually, the app doesn't
+// just give us its IMzObjEvents. Rather, the app calls our Advise, passing us some
+// app object from which we can request the app to give us its IMzObjEvents. All of
+// this convoluted stuff is a combination of poor pre-planning by Microsoft
+// programmers when they designed this stuff, as well as the colossal blunder of
+// designing COM to accomodate the limitations of early, primitive editions of
+// Visual Basic.
+//
+// The second arg passed here is some app object whose QueryInterface function
+// we call to request the app's IMzObjEvents. We pass the GUID DIID_IMzObjEvents to
+// this QueryInterface in order to tell the app to give us its IMzObjEvents
+static STDMETHODIMP Advise(IConnectionPoint *com_obj, IUnknown *obj, DWORD *cookie)
+{
+ HRESULT hr;
+ MyRealMzObj *iExample;
+
+ // Get the MyRealMzObj that this IConnectionPoint sub-object belongs
+ // to. Because this IConnectPoint sub-object is embedded directly inside its
+ // MyRealMzObj, all we need is a little pointer arithmetic
+ iExample = (MyRealMzObj *)((char *)com_obj - offsetof(MyRealMzObj, point));
+
+ // We allow only one IMzObjEvents for our IMzObj, so see if the app already
+ // called our Advise(), and we got one. If so, let the app know that it is trying
+ // to give us more IFeedbacks2 than we allow
+ if (iExample->evts) return(CONNECT_E_ADVISELIMIT);
+
+ // Ok, we haven't yet gotten the one IMzObjEvents we allow from the app. Get the app's
+ // IMzObjEvents object. We do this by calling the QueryInterface function of the
+ // app object passed to us. We pass IMzObjEvents VTable's GUID (which we defined
+ // in IMzObj.h).
+ //
+ // Save the app's IMzObjEvents pointer in our IMzObj feedback member, so we
+ // can get it when we need it
+ hr = obj->lpVtbl->QueryInterface(obj, &DIID_IMzObjEvents, (void **)&iExample->evts);
+
+ // We need to return (to the app) some value that will clue our Unadvise() function
+ // below how to locate this app IMzObjEvents. The simpliest thing is to just use the
+ // app's IMzObjEvents pointer as that returned value
+ *cookie = (DWORD)iExample->evts;
+
+ return(hr);
+}
+
+// Called by the app to tell us to stop using, and Release(), its IMzObjEvents object.
+// The second arg passed here is the value our Advise() function above returned when
+// we got the IMzObjEvents from the app. This value should help us locate wherever we
+// stored that IMzObjEvents pointer we got in Advise()
+static STDMETHODIMP Unadvise(IConnectionPoint *com_obj, DWORD cookie)
+{
+ MyRealMzObj *iExample;
+
+ // Get the MyRealMzObj that this IConnectionPoint sub-object belongs
+ // to. Because this IConnectPoint sub-object is embedded directly inside its
+ // MyRealMzObj, all we need is a little pointer arithmetic
+ iExample = (MyRealMzObj *)((char *)com_obj - offsetof(MyRealMzObj, point));
+
+ // Use the passed value to find wherever we stored his IMzObjEvents pointer.
+ // Well, since we allow only one IMzObjEvents for our IMzObj, we already
+ // know we stored it in our IMzObj->feedback member. And Advise()
+ // returned that pointer as the "cookie" value. So we already got the
+ // IMzObjEvents right now.
+ //
+ // Let's just make sure the cookie he passed is really the pointer we expect
+ if (cookie && (IMzObjEvents *)cookie == iExample->evts)
+ {
+ // Release the app's IMzObjEvents
+ ((IMzObjEvents *)cookie)->lpVtbl->Release((IMzObjEvents *)cookie);
+
+ // We no longer have the app's IMzObjEvents, so clear the IMzObj
+ // feedback member
+ iExample->evts = 0;
+
+ return(S_OK);
+ }
+ return(CONNECT_E_NOCONNECTION);
+}
+
+static STDMETHODIMP EnumConnections(IConnectionPoint *com_obj, IEnumConnections **enumConnects)
+{
+ *enumConnects = 0;
+ return(E_NOTIMPL);
+}
+
+
+static const IConnectionPointVtbl IConnectionPoint_Vtbl = {
+ QueryInterface_Point,
+ AddRef_Point,
+ Release_Point,
+ GetConnectionInterface,
+ GetConnectionPointContainer,
+ Advise,
+ Unadvise,
+ EnumConnections};
+
+// The IClassFactory object ///////////////////////////////////////////////////////
+
+// Since we only ever need one IClassFactory object, we declare
+// it static. The only requirement is that we ensure any
+// access to its members is thread-safe
+static IClassFactory MyIClassFactoryObj;
+
+// IClassFactory's AddRef()
+static ULONG STDMETHODCALLTYPE classAddRef(IClassFactory *com_obj)
+{
+ // Someone is obtaining my IClassFactory, so inc the count of
+ // pointers that I've returned which some app needs to Release()
+ InterlockedIncrement(&OutstandingObjects);
+
+ // Since we never actually allocate/free an IClassFactory (ie, we
+ // use just 1 static one), we don't need to maintain a separate
+ // reference count for our IClassFactory. We'll just tell the caller
+ // that there's at least one of our IClassFactory objects in existance
+ return(1);
+}
+
+// IClassFactory's QueryInterface()
+static HRESULT STDMETHODCALLTYPE classQueryInterface(IClassFactory *com_obj, REFIID factoryGuid, void **ppv)
+{
+ // Make sure the caller wants either an IUnknown or an IClassFactory.
+ // In either case, we return the same IClassFactory pointer passed to
+ // us since it can also masquerade as an IUnknown
+ if (IsEqualIID(factoryGuid, &IID_IUnknown) || IsEqualIID(factoryGuid, &IID_IClassFactory))
+ {
+ // Call my IClassFactory's AddRef
+ com_obj->lpVtbl->AddRef(com_obj);
+
+ // Return (to the caller) a ptr to my IClassFactory
+ *ppv = com_obj;
+
+ return(NOERROR);
+ }
+
+ // We don't know about any other GUIDs
+ *ppv = 0;
+ return(E_NOINTERFACE);
+}
+
+// IClassFactory's Release()
+static ULONG STDMETHODCALLTYPE classRelease(IClassFactory *com_obj)
+{
+ // One less object that an app has not yet Release()'ed
+ return(InterlockedDecrement(&OutstandingObjects));
+}
+
+// IClassFactory's CreateInstance() function. It is called by
+// someone who has a pointer to our IClassFactory object and now
+// wants to create and retrieve a pointer to our MzObj
+static HRESULT STDMETHODCALLTYPE classCreateInstance(IClassFactory *com_obj, IUnknown *punkOuter, REFIID vTableGuid, void **objHandle)
+{
+ HRESULT hr;
+ IMzObj *thisobj;
+
+ // Assume an error by clearing caller's handle
+ *objHandle = 0;
+
+ // We don't support aggregation in this example
+ if (punkOuter)
+ hr = CLASS_E_NOAGGREGATION;
+ else
+ {
+ // Allocate our MzObj object (actually a MyRealMzObj)
+ if (!(thisobj = (IMzObj *)GlobalAlloc(GMEM_FIXED, sizeof(MyRealMzObj))))
+ hr = E_OUTOFMEMORY;
+ else
+ {
+ // Store MzObj's VTable in the object
+ thisobj->lpVtbl = (IMzObjVtbl *)&IMzObj_Vtbl;
+
+ // Our MyRealIMzObj is a multiple interface object. It has an
+ // IConnectionPointContainer sub-object embedded directly inside of
+ // it. And we just allocated it when we allocated the MyRealIMzObj
+ // above. Now we need to set its VTable into its lpVtbl member and
+ // we're done initializing this sub-object
+ ((MyRealMzObj *)thisobj)->container.lpVtbl = (IConnectionPointContainerVtbl *)&IConnectionPointContainer_Vtbl;
+
+ // Our MyRealIMzObj also has an IConnectionPoint sub-object
+ // embedded directly inside of it. And we just allocated it when we
+ // allocated the MyRealIMzObj above. Now we need to set its
+ // VTable into its lpVtbl member and we're done initializing this sub-object
+ ((MyRealMzObj *)thisobj)->point.lpVtbl = (IConnectionPointVtbl *)&IConnectionPoint_Vtbl;
+
+ // Increment the reference count so we can call Release() below and
+ // it will deallocate only if there is an error with QueryInterface()
+ ((MyRealMzObj *)thisobj)->count = 1;
+
+ // Initialize any other members we added to the MzObj. We added
+ // a string member
+ ((MyRealMzObj *)thisobj)->obj = new_mzobj(thisobj);
+
+ ((MyRealMzObj *)thisobj)->evts = NULL;
+
+ // Fill in the caller's handle with a pointer to the MzObj we just
+ // allocated above. We'll let MzObj's QueryInterface do that, because
+ // it also checks the GUID the caller passed, and also increments the
+ // reference count (to 2) if all goes well
+ hr = IMzObj_Vtbl.QueryInterface(thisobj, vTableGuid, objHandle);
+
+ // Decrement reference count. NOTE: If there was an error in QueryInterface()
+ // then Release() will be decrementing the count back to 0 and will free the
+ // MzObj for us. One error that may occur is that the caller is asking for
+ // some sort of object that we don't support (ie, it's a GUID we don't recognize)
+ IMzObj_Vtbl.Release(thisobj);
+
+ // If success, inc static object count to keep this DLL loaded
+ if (!hr) InterlockedIncrement(&OutstandingObjects);
+ }
+ }
+
+ return(hr);
+}
+
+// IClassFactory's LockServer(). It is called by someone
+// who wants to lock this DLL in memory
+static HRESULT STDMETHODCALLTYPE classLockServer(IClassFactory *com_obj, BOOL flock)
+{
+ if (flock) InterlockedIncrement(&LockCount);
+ else InterlockedDecrement(&LockCount);
+
+ return(NOERROR);
+}
+
+// IClassFactory's VTable
+static const IClassFactoryVtbl IClassFactory_Vtbl = {classQueryInterface,
+ classAddRef,
+ classRelease,
+ classCreateInstance,
+ classLockServer};
+
+
+
+// Miscellaneous functions ///////////////////////////////////////////////////////
+
+static DWORD reg_cookie;
+
+HRESULT com_register()
+{
+ // Initialize my IClassFactory with the pointer to its vtable
+ MyIClassFactoryObj.lpVtbl = (IClassFactoryVtbl *)&IClassFactory_Vtbl;
+
+ return CoRegisterClassObject(&CLSID_IMzObj, &MyIClassFactoryObj,
+ CLSCTX_LOCAL_SERVER, REGCLS_SINGLEUSE, &reg_cookie);
+}
+
+int com_can_unregister()
+/* called from multiple threads */
+{
+ /* Note that OutstandingObjects will stay at least 1 after the class is registered. */
+ return !((OutstandingObjects > 1) || (LockCount > 0));
+}
+
+int com_unregister()
+{
+ // If someone has retrieved pointers to any of our objects, and
+ // not yet Release()'ed them, then we return S_FALSE to indicate
+ // not to unload this DLL. Also, if someone has us locked, return
+ // S_FALSE
+ if (!com_can_unregister())
+ return 0;
+ else {
+ if (MyTypeInfo) MyTypeInfo->lpVtbl->Release(MyTypeInfo);
+ CoRevokeClassObject(reg_cookie);
+ return 1;
+ }
+}
+
+const GUID com_get_class_iid()
+{
+ return IID_IMzObj;
+}
diff --git a/src/mzcom/com_glue.h b/src/mzcom/com_glue.h
new file mode 100644
index 0000000000..bceb397723
--- /dev/null
+++ b/src/mzcom/com_glue.h
@@ -0,0 +1,75 @@
+#ifndef _COM_GLUE_H_
+#define _COM_GLUE_H_
+
+#ifdef FOR_GLUE
+
+#include <initguid.h>
+
+// {A604CB9C-2AB5-11D4-B6D3-0060089002FE}
+DEFINE_GUID(CLSID_TypeLib, 0xA604CB9C, 0x2ab5, 0x11d4, 0xb6, 0xd3, 0x00, 0x60, 0x08, 0x90, 0x02, 0xfe);
+
+// {A3B0AF9E-2AB0-11D4-B6D2-0060089002FE}
+DEFINE_GUID(CLSID_IMzObj, 0xA3B0AF9E, 0x2ab0, 0x11d4, 0xb6, 0xd2, 0x00, 0x60, 0x08, 0x90, 0x02, 0xfe);
+
+// {A604CBA8-2AB5-11D4-B6D3-0060089002FE}
+DEFINE_GUID(IID_IMzObj, 0xA604CBA8, 0x2ab5, 0x11d4, 0xb6, 0xd3, 0x00, 0x60, 0x08, 0x90, 0x02, 0xfe);
+
+#undef INTERFACE
+#define INTERFACE IMzObj
+DECLARE_INTERFACE_ (INTERFACE, IDispatch)
+{
+ // IUnknown functions
+ STDMETHOD (QueryInterface) (THIS_ REFIID, void **) PURE;
+ STDMETHOD_ (ULONG, AddRef) (THIS) PURE;
+ STDMETHOD_ (ULONG, Release) (THIS) PURE;
+ // IDispatch functions
+ STDMETHOD_ (ULONG, GetTypeInfoCount)(THIS_ UINT *) PURE;
+ STDMETHOD_ (ULONG, GetTypeInfo) (THIS_ UINT, LCID, ITypeInfo **) PURE;
+ STDMETHOD_ (ULONG, GetIDsOfNames) (THIS_ REFIID, LPOLESTR *, UINT, LCID, DISPID *) PURE;
+ STDMETHOD_ (ULONG, Invoke) (THIS_ DISPID, REFIID, LCID, WORD, DISPPARAMS *, VARIANT *, EXCEPINFO *, UINT *) PURE;
+ // Extra functions
+ STDMETHOD (Eval) (THIS_ BSTR, BSTR *) PURE;
+ STDMETHOD (About) (THIS) PURE;
+ STDMETHOD (Reset) (THIS) PURE;
+};
+
+// {A604CBA9-2AB5-11D4-B6D3-0060089002FE}
+DEFINE_GUID(DIID_IMzObjEvents, 0xA604CBA9, 0x2ab5, 0x11d4, 0xb6, 0xd3, 0x00, 0x60, 0x08, 0x90, 0x02, 0xfe);
+
+#undef INTERFACE
+#define INTERFACE IMzObjEvents
+DECLARE_INTERFACE_ (INTERFACE, IDispatch)
+{
+ // IUnknown functions
+ STDMETHOD (QueryInterface) (THIS_ REFIID, void **) PURE;
+ STDMETHOD_ (ULONG, AddRef) (THIS) PURE;
+ STDMETHOD_ (ULONG, Release) (THIS) PURE;
+ // IDispatch functions
+ STDMETHOD_ (ULONG, GetTypeInfoCount)(THIS_ UINT *) PURE;
+ STDMETHOD_ (ULONG, GetTypeInfo) (THIS_ UINT, LCID, ITypeInfo **) PURE;
+ STDMETHOD_ (ULONG, GetIDsOfNames) (THIS_ REFIID, LPOLESTR *, UINT, LCID, DISPID *) PURE;
+ STDMETHOD_ (ULONG, Invoke) (THIS_ DISPID, REFIID, LCID, WORD, DISPPARAMS *, VARIANT *, EXCEPINFO *, UINT *) PURE;
+ // Extra functions
+ STDMETHOD (SchemeError) (THIS_ BSTR *) PURE;
+};
+
+#else
+
+typedef struct IMzObj { int dummy; } IMzObj;
+
+#endif
+
+extern HRESULT com_register();
+extern int com_unregister();
+extern int com_can_unregister();
+extern const GUID com_get_class_iid();
+
+extern void *new_mzobj(IMzObj*);
+extern void delete_mzobj(void*);
+extern HRESULT mzobj_about(void*);
+extern HRESULT mzobj_reset(void*);
+extern HRESULT mzobj_eval(void*, BSTR, BSTR*);
+
+extern VOID Fire_SchemeError(IMzObj *com_obj, BSTR description);
+
+#endif // _COM_GLUE_H_
diff --git a/src/mzcom/mzcom.cxx b/src/mzcom/mzcom.cxx
new file mode 100644
index 0000000000..263dd64f18
--- /dev/null
+++ b/src/mzcom/mzcom.cxx
@@ -0,0 +1,314 @@
+// mzcom.cxx : Implementation of WinMain
+
+// This file is not xformed for 3m. There's just one
+// bit of conditional compilation on MZCOM_3M.
+
+#include "../racket/src/schvers.h"
+#include "resource.h"
+
+#include <objbase.h>
+extern "C" {
+#include "com_glue.h"
+};
+
+// time to wait for threads to finish up
+#define dwPause (1000)
+
+HINSTANCE globHinst;
+
+/* A monitor thread might be a good idea to make sure the process
+ terminates if it's somehow started and not used. It also creates
+ a race condition, though, so it's disabled for now. */
+
+#if 0
+
+// time for EXE to be idle before shutting down
+#define dwTimeOut (5000)
+
+static HANDLE hEventShutdown;
+static DWORD dwThreadID;
+
+// Polls for idle state
+static DWORD WINAPI MonitorProc(void* pv)
+{
+ while (1) {
+ DWORD dwWait=0;
+ do
+ {
+ dwWait = WaitForSingleObject(hEventShutdown, dwTimeOut);
+ } while (dwWait == WAIT_OBJECT_0);
+
+ if (com_can_unregister())
+ break;
+ }
+ CloseHandle(hEventShutdown);
+ PostThreadMessage(dwThreadID, WM_QUIT, 0, 0);
+
+ return 0;
+}
+
+static bool StartMonitor()
+{
+ dwThreadID = GetCurrentThreadId();
+ hEventShutdown = CreateEvent(NULL, false, false, NULL);
+ if (hEventShutdown == NULL)
+ return false;
+
+ DWORD subThreadID;
+ HANDLE h = CreateThread(NULL, 0, MonitorProc, NULL, 0, &subThreadID);
+ return (h != NULL);
+}
+
+#else
+
+static bool StartMonitor() { return TRUE; }
+
+#endif
+
+static int set_reg_string(HKEY sub, char *name, char *s)
+{
+ return RegSetValueExA(sub, name, 0, REG_SZ, (const BYTE *)s, strlen(s));
+}
+
+static int set_reg_sub_string(HKEY sub, char *name, char *s)
+{
+ HKEY sub2;
+ int nRet;
+
+ nRet = RegCreateKeyExA(sub, name, 0, NULL, 0, KEY_SET_VALUE, NULL, &sub2, NULL);
+ if (!nRet) {
+ nRet |= set_reg_string(sub2, NULL, s);
+ nRet |= RegCloseKey(sub2);
+ }
+
+ return nRet;
+}
+
+LPCTSTR FindOneOf(LPCTSTR p1, LPCTSTR p2)
+{
+ while (p1 != NULL && *p1 != NULL)
+ {
+ LPCTSTR p = p2;
+ while (p != NULL && *p != NULL)
+ {
+ if (*p1 == *p)
+ return CharNext(p1);
+ p = CharNext(p);
+ }
+ p1 = CharNext(p1);
+ }
+ return NULL;
+}
+
+int IsFlag(LPCTSTR cmd, LPCTSTR flag)
+{
+ if ((*cmd == '-') || (*cmd == '/'))
+ cmd++;
+ else
+ return 0;
+
+ while (*flag) {
+ if (toupper(*cmd) != toupper(*flag))
+ return 0;
+ cmd++;
+ flag++;
+ }
+ if (!*cmd || (*cmd == ' '))
+ return 1;
+ return 0;
+}
+
+#define DLL_RELATIVE_PATH L"."
+#include "../racket/delayed.inc"
+
+#define ASSUME_ASCII_COMMAND_LINE
+#define GC_CAN_IGNORE
+#include "../racket/parse_cmdl.inc"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+extern "C" int WINAPI WinMain(HINSTANCE hInstance,
+ HINSTANCE /*hPrevInstance*/, LPTSTR lpCmdLine, int /*nShowCmd*/) {
+
+ globHinst = hInstance;
+
+ lpCmdLine = GetCommandLine(); //this line necessary for _ATL_MIN_CRT
+
+#ifdef MZCOM_3M
+ load_delayed_dll(hInstance, "libracket3mxxxxxxx.dll");
+#else
+ load_delayed_dll(hInstance, "libmzgcxxxxxxx.dll");
+ load_delayed_dll(hInstance, "libracketxxxxxxx.dll");
+#endif
+
+ HRESULT nRet = CoInitialize(NULL);
+
+ int argc, i;
+ char **argv, *normalized_path;
+
+ argv = cmdline_to_argv(&argc, &normalized_path);
+
+ int verbose = 0;
+ BOOL bRun = TRUE;
+ LPCTSTR lpszToken;
+ for (i = 1; i < argc; i++)
+ {
+ lpszToken = argv[i];
+ if (IsFlag(lpszToken, "UnregServer"))
+ {
+ if (!nRet) {
+ HKEY sub;
+
+ nRet |= RegDeleteKeyA(HKEY_CLASSES_ROOT, "MzCOM.MzObj");
+ nRet |= RegDeleteKeyA(HKEY_CLASSES_ROOT, "MzCOM.MzObj." MZSCHEME_VERSION);
+
+ if (!nRet) {
+ nRet = RegCreateKeyExA(HKEY_CLASSES_ROOT, "CLSID", 0, NULL, 0, KEY_SET_VALUE, NULL, &sub, NULL);
+ if (!nRet) {
+ nRet |= RegDeleteKeyA(sub, "{A3B0AF9E-2AB0-11D4-B6D2-0060089002FE}");
+ nRet |= RegCloseKey(sub);
+ }
+ }
+
+ if (!nRet) {
+ nRet = RegCreateKeyExA(HKEY_CLASSES_ROOT, "AppID", 0, NULL, 0, KEY_SET_VALUE, NULL, &sub, NULL);
+ if (!nRet) {
+ nRet |= RegDeleteKeyA(sub, "{A604CB9D-2AB5-11D4-B6D3-0060089002FE}");
+ nRet |= RegCloseKey(sub);
+ }
+ }
+
+ bRun = FALSE;
+ }
+ }
+ else if (IsFlag(lpszToken, "RegServer"))
+ {
+ if (!nRet) {
+ HKEY sub, sub2;
+
+ nRet |= RegCreateKeyExA(HKEY_CLASSES_ROOT, "MzCOM.MzObj", 0, NULL, 0, KEY_SET_VALUE, NULL, &sub, NULL);
+
+ if (!nRet) {
+ nRet |= set_reg_string(sub, NULL, "MzObj Class");
+ nRet |= set_reg_sub_string(sub, "CLSID", "{A3B0AF9E-2AB0-11D4-B6D2-0060089002FE}");
+ nRet |= set_reg_sub_string(sub, "CurVer", "MzCOM.MzObj." MZSCHEME_VERSION);
+ nRet |= RegCloseKey(sub);
+ }
+
+ if (!nRet) {
+ nRet = RegCreateKeyExA(HKEY_CLASSES_ROOT, "MzCOM.MzObj." MZSCHEME_VERSION, 0, NULL, 0, KEY_SET_VALUE, NULL, &sub, NULL);
+ if (!nRet) {
+ nRet |= set_reg_string(sub, NULL, "MzObj Class");
+ nRet |= set_reg_sub_string(sub, "CLSID", "{A3B0AF9E-2AB0-11D4-B6D2-0060089002FE}");
+ nRet |= RegCloseKey(sub);
+ }
+ }
+
+ if (!nRet) {
+ nRet = RegCreateKeyExA(HKEY_CLASSES_ROOT, "CLSID", 0, NULL, 0, KEY_SET_VALUE, NULL, &sub, NULL);
+ if (!nRet) {
+ nRet = RegCreateKeyExA(sub, "{A3B0AF9E-2AB0-11D4-B6D2-0060089002FE}", 0, NULL, 0, KEY_SET_VALUE, NULL, &sub2, NULL);
+ if (!nRet) {
+ nRet |= set_reg_string(sub2, NULL, "MzObj Class");
+ nRet |= set_reg_string(sub2, "AppId", "{A604CB9D-2AB5-11D4-B6D3-0060089002FE}");
+ nRet |= set_reg_sub_string(sub2, "ProgID", "MzCOM.MzObj." MZSCHEME_VERSION);
+ nRet |= set_reg_sub_string(sub2, "VersionIndependentProgID", "MzCOM.MzObj");
+ nRet |= set_reg_sub_string(sub2, "Programmable", "");
+
+ char *path;
+ path = (char *)malloc(1024 * sizeof(wchar_t));
+ GetModuleFileNameA(NULL, path, 1024);
+ nRet |= set_reg_sub_string(sub2, "LocalServer32", path);
+ free(path);
+
+ nRet |= set_reg_sub_string(sub2, "TypeLib", "{A604CB9C-2AB5-11D4-B6D3-0060089002FE}");
+ nRet |= RegCloseKey(sub2);
+ }
+ nRet |= RegCloseKey(sub);
+ }
+ }
+
+ if (!nRet) {
+ nRet = RegCreateKeyExA(HKEY_CLASSES_ROOT, "AppID", 0, NULL, 0, KEY_SET_VALUE, NULL, &sub, NULL);
+ if (!nRet) {
+ nRet = RegCreateKeyExA(sub, "{A604CB9D-2AB5-11D4-B6D3-0060089002FE}", 0, NULL, 0, KEY_SET_VALUE, NULL, &sub2, NULL);
+ if (!nRet) {
+ nRet |= set_reg_string(sub2, NULL, "MzCOM");
+ nRet |= RegCloseKey(sub2);
+ }
+ }
+ if (!nRet) {
+ nRet = RegCreateKeyExA(sub, "MzCOM.EXE", 0, NULL, 0, KEY_SET_VALUE, NULL, &sub2, NULL);
+ if (!nRet) {
+ nRet |= set_reg_string(sub2, "AppID", "{A604CB9D-2AB5-11D4-B6D3-0060089002FE}");
+ nRet |= RegCloseKey(sub2);
+ }
+ }
+ nRet |= RegCloseKey(sub);
+ }
+
+ bRun = FALSE;
+ }
+ }
+ else if (IsFlag(lpszToken, "v"))
+ {
+ verbose = 1;
+ }
+ else if (IsFlag(lpszToken, "?"))
+ {
+ MessageBox(NULL,
+ "/RegServer - register\n"
+ "/UnregServer - unregister\n"
+ "/Embedding - ignored\n"
+ "/v - report failures\n"
+ "/? - show this help",
+ "Help",
+ MB_OK);
+ bRun = FALSE;
+ }
+ else if (IsFlag(lpszToken, "Embedding"))
+ {
+ /* ??? */
+ }
+ else
+ {
+ if (verbose)
+ MessageBox(NULL, lpszToken, "Unknown Flag", MB_OK);
+ bRun = FALSE;
+ break;
+ }
+ }
+
+ if (bRun) {
+ StartMonitor();
+
+ nRet = com_register();
+
+ if (!nRet) {
+ MSG msg;
+ while (GetMessage(&msg, 0, 0, 0))
+ DispatchMessage(&msg);
+
+ while (!com_unregister()) {
+ Sleep(dwPause); // wait for any objects to finish
+ }
+ }
+ }
+
+ if (verbose && (nRet != 0)) {
+ wchar_t *res;
+ FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER
+ | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ nRet,
+ 0,
+ (wchar_t *)&res,
+ 0,
+ 0);
+ MessageBoxW(NULL, res, L"Registration Failed", MB_OK);
+ }
+
+ CoUninitialize();
+
+ return nRet;
+}
diff --git a/src/mzcom/mzcom.idl b/src/mzcom/mzcom.idl
new file mode 100644
index 0000000000..f9bf98af95
--- /dev/null
+++ b/src/mzcom/mzcom.idl
@@ -0,0 +1,53 @@
+// MzCOM.idl : IDL source for MzCOM.dll
+//
+
+// This file will be processed by the MIDL tool to
+// produce the type library (MzCOM.tlb) and marshalling code.
+
+import "oaidl.idl";
+import "ocidl.idl";
+ [
+ object,
+ uuid(A604CBA8-2AB5-11D4-B6D3-0060089002FE),
+ dual,
+ helpstring("IMzObj Interface"),
+ pointer_default(unique)
+ ]
+ interface IMzObj : IDispatch
+ {
+ [id(1), helpstring("method Eval")] HRESULT Eval(BSTR input,[out,retval]BSTR *output);
+ [id(2), helpstring("method About")] HRESULT About();
+ [id(3), helpstring("method Reset")] HRESULT Reset();
+ };
+
+[
+ uuid(A604CB9C-2AB5-11D4-B6D3-0060089002FE),
+ version(1.0),
+ helpstring("MzCOM 1.0 Type Library")
+]
+library MZCOMLib
+{
+ importlib("stdole32.tlb");
+ importlib("stdole2.tlb");
+
+ [
+ uuid(A604CBA9-2AB5-11D4-B6D3-0060089002FE),
+ helpstring("_IMzObjEvents Interface")
+ ]
+ dispinterface _IMzObjEvents
+ {
+ properties:
+ methods:
+ [id(1), helpstring("method SchemeError")] HRESULT SchemeError(BSTR description);
+ };
+
+ [
+ uuid(A3B0AF9E-2AB0-11D4-B6D2-0060089002FE),
+ helpstring("MzObj Class")
+ ]
+ coclass MzObj
+ {
+ [default] interface IMzObj;
+ [default, source] dispinterface _IMzObjEvents;
+ };
+};
diff --git a/src/mzcom/mzobj.cxx b/src/mzcom/mzobj.cxx
new file mode 100644
index 0000000000..96d7bb45b8
--- /dev/null
+++ b/src/mzcom/mzobj.cxx
@@ -0,0 +1,569 @@
+// mzobj.cxx : Implementation of CMzObj
+
+#ifdef MZCOM_3M
+/* xform.rkt converts this file to mzobj3m.cxx: */
+# define i64 /* ??? why does expansion produce i64? */
+# include "mzobj3m.cxx"
+#else
+
+#include "scheme.h"
+
+#ifdef MZ_PRECISE_GC
+START_XFORM_SKIP;
+#endif
+
+#include "resource.h"
+
+#include <process.h>
+
+#include <objbase.h>
+extern "C" {
+#include "com_glue.h"
+};
+
+#ifdef MZ_PRECISE_GC
+END_XFORM_SKIP;
+#endif
+
+#include "mzobj.h"
+
+#ifndef MZ_PRECISE_GC
+# define GC_CAN_IGNORE /* empty */
+#endif
+
+#ifdef MZ_PRECISE_GC
+START_XFORM_SKIP;
+#endif
+
+static void ErrorBox(char *s) {
+ ::MessageBox(NULL,s,"MzCOM",MB_OK);
+}
+
+#ifdef MZ_PRECISE_GC
+END_XFORM_SKIP;
+#endif
+
+static THREAD_GLOBALS tg;
+
+static Scheme_Env *env;
+
+static BOOL *pErrorState;
+static OLECHAR *wideError;
+
+static HANDLE evalLoopSems[2];
+static HANDLE exitSem;
+
+static Scheme_Object *exn_catching_apply;
+static Scheme_Object *exn_p;
+static Scheme_Object *exn_message;
+
+static Scheme_At_Exit_Callback_Proc at_exit_callback;
+
+/* This indirection lets us delayload libmzsch.dll: */
+#define scheme_false (scheme_make_false())
+
+static Scheme_Object *_apply_thunk_catch_exceptions(Scheme_Object *f,
+ Scheme_Object **exn) {
+ Scheme_Object *v;
+
+ v = _scheme_apply(exn_catching_apply,1,&f);
+
+ /* v is a pair: (cons #t value) or (cons #f exn) */
+
+ if (SCHEME_TRUEP(SCHEME_CAR(v))) {
+ return SCHEME_CDR(v);
+ }
+ else {
+ *exn = SCHEME_CDR(v);
+ return NULL;
+ }
+}
+
+static Scheme_Object *extract_exn_message(Scheme_Object *v) {
+ if (SCHEME_TRUEP(_scheme_apply(exn_p,1,&v)))
+ return _scheme_apply(exn_message,1,&v);
+ else
+ return NULL; /* Not an exn structure */
+}
+
+static Scheme_Object *do_eval(void *s,int,Scheme_Object **) {
+ return scheme_eval_string_all((char *)s,env,TRUE);
+}
+
+static Scheme_Object *eval_string_or_get_exn_message(char *s) {
+ Scheme_Object *v;
+ Scheme_Object *exn;
+
+ v = _apply_thunk_catch_exceptions(scheme_make_closed_prim(do_eval,s),&exn);
+ /* value */
+ if (v) {
+ *pErrorState = FALSE;
+ return v;
+ }
+
+ v = extract_exn_message(exn);
+ /* exn */
+ if (v) {
+ *pErrorState = TRUE;
+ return v;
+ }
+
+ /* `raise' was called on some arbitrary value */
+ return exn;
+}
+
+OLECHAR *wideStringFromSchemeObj(Scheme_Object *obj,char *fmt,int fmtlen) {
+ char *s;
+ OLECHAR *wideString;
+ int len;
+
+ s = scheme_format_utf8(fmt,fmtlen,1,&obj,NULL);
+ len = strlen(s);
+ wideString = (OLECHAR *)scheme_malloc((len + 1) * sizeof(OLECHAR));
+ MultiByteToWideChar(CP_ACP,(DWORD)0,s,len,wideString,len + 1);
+ wideString[len] = 0;
+ return wideString;
+}
+
+void exitHandler(int) {
+ if (at_exit_callback) at_exit_callback();
+ ReleaseSemaphore(exitSem,1,NULL);
+ _endthreadex(0);
+}
+
+void setupSchemeEnv(Scheme_Env *in_env)
+{
+ char *wrapper;
+ char exeBuff[260];
+ HMODULE mod;
+ static BOOL registered;
+
+ if (!registered) {
+ scheme_register_static(&env,sizeof(env));
+ scheme_register_static(&exn_catching_apply,sizeof(exn_catching_apply));
+ scheme_register_static(&exn_p,sizeof(exn_p));
+ scheme_register_static(&exn_message,sizeof(exn_message));
+ registered = TRUE;
+ }
+
+ env = in_env;
+
+ if (env == NULL) {
+ ErrorBox("Can't create Racket environment");
+ _endthreadex(0);
+ }
+
+ // set up collection paths, based on Racket startup
+
+ mod = GetModuleHandle("mzcom.exe");
+ GetModuleFileName(mod,exeBuff,sizeof(exeBuff));
+
+ scheme_add_global("mzcom-exe",scheme_make_utf8_string(exeBuff),env);
+ scheme_set_exec_cmd(exeBuff);
+ scheme_set_collects_path(scheme_make_path("../collects"));
+ scheme_set_config_path(scheme_make_path("../etc"));
+ scheme_init_collection_paths(env, scheme_make_null());
+
+ // initialize namespace
+
+ scheme_namespace_require(scheme_intern_symbol("racket"));
+
+ // set up exception trapping
+
+ wrapper =
+ "(lambda (thunk) "
+ "(with-handlers ([void (lambda (exn) (cons #f exn))]) "
+ "(cons #t (thunk))))";
+
+ exn_catching_apply = scheme_eval_string(wrapper,env);
+ exn_p = scheme_builtin_value("exn?");
+ exn_message = scheme_builtin_value("exn-message");
+}
+
+static int do_evalLoop(Scheme_Env *env, int argc, char **_args)
+{
+ LPVOID args = (LPVOID)_args;
+ HRESULT *pHr;
+ BOOL doEval;
+ UINT len;
+ DWORD waitVal;
+ char *narrowInput;
+ Scheme_Object *outputObj;
+ Scheme_Object *sleepFun;
+ OLECHAR *outputBuffer;
+ THREAD_GLOBALS *pTg;
+ HANDLE readSem;
+ HANDLE writeSem;
+ HANDLE resetSem;
+ HANDLE resetDoneSem;
+ BSTR **ppInput;
+ BSTR *pOutput, po;
+ MSG msg;
+
+ // make sure all Racket calls are in this thread
+
+ setupSchemeEnv(env);
+
+ scheme_set_exit(exitHandler);
+ sleepFun = scheme_builtin_value("sleep");
+
+ pTg = (THREAD_GLOBALS *)args;
+
+ ppInput = pTg->ppInput;
+ pOutput = pTg->pOutput;
+ pHr = pTg->pHr;
+ readSem = pTg->readSem;
+ writeSem = pTg->writeSem;
+ resetSem = pTg->resetSem;
+ resetDoneSem = pTg->resetDoneSem;
+ pErrorState = pTg->pErrorState;
+
+ while (1) {
+
+ doEval = FALSE;
+
+ while (doEval == FALSE) {
+ waitVal = MsgWaitForMultipleObjects(2,evalLoopSems,FALSE,
+ 5,QS_ALLINPUT);
+
+ switch (waitVal) {
+
+ case WAIT_TIMEOUT :
+
+ scheme_apply(sleepFun,0,NULL);
+ break;
+
+ case WAIT_OBJECT_0 + 1:
+
+ // reset semaphore signaled
+
+ setupSchemeEnv(scheme_basic_env());
+ ReleaseSemaphore(resetDoneSem,1,NULL);
+
+ break;
+
+ case WAIT_OBJECT_0 + 2:
+
+ // Windows msg
+
+ while (PeekMessage(&msg,NULL,0x400,0x400,PM_REMOVE)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ scheme_apply(sleepFun,0,NULL);
+
+ break;
+
+ default :
+
+ // got string to eval
+
+ doEval = TRUE;
+
+ break;
+ }
+ }
+
+ len = SysStringLen(**ppInput);
+
+ narrowInput = (char *)scheme_malloc(len + 1);
+
+ scheme_dont_gc_ptr(narrowInput);
+
+ WideCharToMultiByte(CP_ACP,(DWORD)0,
+ **ppInput,len,
+ narrowInput,len + 1,
+ NULL,NULL);
+
+ narrowInput[len] = '\0';
+
+ outputObj = eval_string_or_get_exn_message(narrowInput);
+
+ scheme_gc_ptr_ok(narrowInput);
+
+ if (*pErrorState) {
+ wideError = wideStringFromSchemeObj(outputObj,"Racket error: ~a",18);
+ po = SysAllocString(L"");
+ *pOutput = po;
+ *pHr = E_FAIL;
+ }
+ else {
+ outputBuffer = wideStringFromSchemeObj(outputObj,"~s",2);
+ po = SysAllocString(outputBuffer);
+ *pOutput = po;
+ *pHr = S_OK;
+ }
+
+ ReleaseSemaphore(writeSem,1,NULL);
+
+ }
+
+ return 0;
+}
+
+static void record_at_exit(Scheme_At_Exit_Callback_Proc p) XFORM_SKIP_PROC
+{
+ at_exit_callback = p;
+}
+
+static __declspec(thread) void *tls_space;
+
+static unsigned WINAPI evalLoop(void *args) XFORM_SKIP_PROC {
+#ifndef _WIN64
+ scheme_register_tls_space(&tls_space, 0);
+#endif
+ scheme_set_atexit(record_at_exit);
+
+ return scheme_main_setup(1, do_evalLoop, 0, (char **)args);
+}
+
+#ifdef MZ_PRECISE_GC
+START_XFORM_SKIP;
+#endif
+
+void CMzObj::startMzThread(void) {
+ tg.pHr = &hr;
+ tg.ppInput = &globInput;
+ tg.pOutput = &globOutput;
+ tg.readSem = readSem;
+ tg.writeSem = writeSem;
+ tg.resetSem = resetSem;
+ tg.resetDoneSem = resetDoneSem;
+ tg.pErrorState = &errorState;
+
+ threadHandle = (HANDLE)_beginthreadex(NULL, 0, evalLoop, &tg, 0, NULL);
+}
+
+
+CMzObj::CMzObj(void *_com_obj) {
+
+ com_obj = _com_obj;
+
+ inputMutex = NULL;
+ readSem = NULL;
+ threadHandle = NULL;
+
+ inputMutex = CreateSemaphore(NULL,1,1,NULL);
+ if (inputMutex == NULL) {
+ ErrorBox("Can't create input mutex");
+ return;
+ }
+
+ readSem = CreateSemaphore(NULL,0,1,NULL);
+
+ if (readSem == NULL) {
+ ErrorBox("Can't create read semaphore");
+ return;
+ }
+
+ writeSem = CreateSemaphore(NULL,0,1,NULL);
+
+ if (writeSem == NULL) {
+ ErrorBox("Can't create write semaphore");
+ return;
+ }
+
+ exitSem = CreateSemaphore(NULL,0,1,NULL);
+
+ if (exitSem == NULL) {
+ ErrorBox("Can't create exit semaphore");
+ return;
+ }
+
+ resetSem = CreateSemaphore(NULL,0,1,NULL);
+
+ if (resetSem == NULL) {
+ ErrorBox("Can't create reset semaphore");
+ return;
+ }
+
+ resetDoneSem = CreateSemaphore(NULL,0,1,NULL);
+
+ if (resetSem == NULL) {
+ ErrorBox("Can't create reset-done semaphore");
+ return;
+ }
+
+ evalLoopSems[0] = readSem;
+ evalLoopSems[1] = resetSem;
+ evalDoneSems[0] = writeSem;
+ evalDoneSems[1] = exitSem;
+
+ startMzThread();
+
+}
+
+void CMzObj::killMzThread(void) {
+ if (threadHandle) {
+ DWORD threadStatus;
+
+ GetExitCodeThread(threadHandle,&threadStatus);
+
+ if (threadStatus == STILL_ACTIVE) {
+ TerminateThread(threadHandle,0);
+ }
+
+ CloseHandle(threadHandle);
+
+ threadHandle = NULL;
+ }
+}
+
+CMzObj::~CMzObj(void) {
+
+ killMzThread();
+
+ if (readSem) {
+ CloseHandle(readSem);
+ }
+
+ if (writeSem) {
+ CloseHandle(writeSem);
+ }
+
+ if (exitSem) {
+ CloseHandle(exitSem);
+ }
+
+ if (inputMutex) {
+ CloseHandle(inputMutex);
+ }
+}
+
+void CMzObj::RaiseError(const OLECHAR *msg) {
+ BSTR bstr;
+ ICreateErrorInfo *pICreateErrorInfo;
+ IErrorInfo *pIErrorInfo;
+
+ bstr = SysAllocString(msg);
+
+ if (CreateErrorInfo(&pICreateErrorInfo) == S_OK &&
+ pICreateErrorInfo != NULL) {
+ pICreateErrorInfo->SetGUID(com_get_class_iid());
+ pICreateErrorInfo->SetDescription((LPOLESTR)msg);
+ pICreateErrorInfo->SetSource((LPOLESTR)L"MzCOM.MzObj");
+ if (pICreateErrorInfo->QueryInterface(IID_IErrorInfo,
+ (void **)&pIErrorInfo) == S_OK &&
+ pIErrorInfo != NULL) {
+ SetErrorInfo(0,pIErrorInfo);
+ }
+ }
+
+ Fire_SchemeError((IMzObj *)com_obj, bstr);
+ SysFreeString(bstr);
+}
+
+BOOL CMzObj::testThread(void) {
+ DWORD threadStatus;
+
+ if (threadHandle == NULL) {
+ RaiseError(L"No evaluator");
+ return FALSE;
+ }
+
+ if (GetExitCodeThread(threadHandle,&threadStatus) == 0) {
+ RaiseError(L"Evaluator may be terminated");
+ }
+
+ if (threadStatus != STILL_ACTIVE) {
+ RaiseError(L"Evaluator terminated");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CMzObj
+
+HRESULT CMzObj::Eval(BSTR input, BSTR *output) {
+ if (!testThread()) {
+ return E_ABORT;
+ }
+
+ WaitForSingleObject(inputMutex,INFINITE);
+ globInput = &input;
+ // allow evaluator to read
+ ReleaseSemaphore(readSem,1,NULL);
+
+ // wait until evaluator done or eval thread terminated
+ if (WaitForMultipleObjects(2,evalDoneSems,FALSE,INFINITE) ==
+ WAIT_OBJECT_0 + 1) {
+ RaiseError(L"Racket terminated evaluator");
+ return E_FAIL;
+ }
+
+ *output = globOutput;
+ ReleaseSemaphore(inputMutex,1,NULL);
+
+ if (errorState) {
+ RaiseError(wideError);
+ }
+
+ return hr;
+}
+
+INT_PTR WINAPI dlgProc(HWND hDlg,UINT msg,WPARAM wParam,LPARAM) {
+ switch(msg) {
+ case WM_INITDIALOG :
+ SetDlgItemText(hDlg,MZCOM_URL,
+ "http://www.cs.rice.edu/CS/PLT/packages/mzcom/");
+ return TRUE;
+ case WM_COMMAND :
+ switch (LOWORD(wParam)) {
+ case IDOK :
+ case IDCANCEL :
+ EndDialog(hDlg,0);
+ return FALSE;
+ }
+ default :
+ return FALSE;
+ }
+}
+
+HRESULT CMzObj::About() {
+ DialogBox(globHinst,MAKEINTRESOURCE(ABOUTBOX),NULL,dlgProc);
+ return S_OK;
+}
+
+HRESULT CMzObj::Reset() {
+ if (!testThread()) {
+ return E_ABORT;
+ }
+
+ ReleaseSemaphore(resetSem,1,NULL);
+ WaitForSingleObject(resetDoneSem,INFINITE);
+ return S_OK;
+}
+
+void *new_mzobj(IMzObj *com_obj)
+{
+ return new CMzObj(com_obj);
+}
+
+void delete_mzobj(void *o)
+{
+ delete (CMzObj *)o;
+}
+
+HRESULT mzobj_about(void *o)
+{
+ return ((CMzObj *)o)->About();
+}
+
+HRESULT mzobj_reset(void *o)
+{
+ return ((CMzObj *)o)->Reset();
+}
+
+HRESULT mzobj_eval(void *o, BSTR s, BSTR *r)
+{
+ return ((CMzObj *)o)->Eval(s, r);
+}
+
+#ifdef MZ_PRECISE_GC
+END_XFORM_SKIP;
+#endif
+
+#endif // MZCOM_3M
diff --git a/src/mzcom/mzobj.h b/src/mzcom/mzobj.h
new file mode 100644
index 0000000000..b0794f60b7
--- /dev/null
+++ b/src/mzcom/mzobj.h
@@ -0,0 +1,70 @@
+// MzObj.h : Declaration of the CMzObj
+
+#ifndef __MZOBJ_H_
+#define __MZOBJ_H_
+
+#include "resource.h" // main symbols
+
+typedef struct {
+ BSTR **ppInput;
+ BSTR *pOutput;
+ HRESULT *pHr;
+ HANDLE readSem;
+ HANDLE writeSem;
+ HANDLE resetSem;
+ HANDLE resetDoneSem;
+ BOOL *pErrorState;
+ BOOL *pResetFlag;
+} THREAD_GLOBALS;
+
+extern HINSTANCE globHinst;
+
+/////////////////////////////////////////////////////////////////////////////
+// CMzObj
+
+#ifdef MZ_XFORM
+START_XFORM_SKIP;
+#endif
+
+class CMzObj
+{
+
+ private:
+
+ void *com_obj;
+
+ HRESULT hr;
+ HANDLE inputMutex;
+ HANDLE readSem;
+ HANDLE writeSem;
+ HANDLE resetSem;
+ HANDLE resetDoneSem;
+ HANDLE evalDoneSems[2];
+ BSTR *globInput;
+ BSTR globOutput;
+ HANDLE threadHandle;
+ BOOL errorState;
+
+ void RaiseError(const OLECHAR *);
+ BOOL testThread(void);
+ void startMzThread(void);
+ void killMzThread(void);
+
+ public:
+
+ CMzObj(void* com_obj);
+ ~CMzObj(void);
+
+// IMzObj
+public:
+ HRESULT Reset(void);
+ HRESULT About(void);
+ HRESULT Eval(BSTR input,/*[out,retval]*/BSTR *output);
+};
+
+#ifdef MZ_XFORM
+END_XFORM_SKIP;
+#endif
+
+#endif //__MZOBJ_H_
+
diff --git a/src/mzcom/resource.h b/src/mzcom/resource.h
new file mode 100644
index 0000000000..8090465b8d
--- /dev/null
+++ b/src/mzcom/resource.h
@@ -0,0 +1,21 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by MzCOM.rc
+//
+#define IDS_PROJNAME 100
+#define IDR_MZCOM 100
+#define IDR_MZOBJ 101
+#define ABOUTBOX 201
+#define MZCOM_URL 201
+#define MZICON 207
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 208
+#define _APS_NEXT_COMMAND_VALUE 32768
+#define _APS_NEXT_CONTROL_VALUE 202
+#define _APS_NEXT_SYMED_VALUE 102
+#endif
+#endif