diff options
author | David Bremner <bremner@debian.org> | 2015-06-26 15:47:13 +0200 |
---|---|---|
committer | David Bremner <bremner@debian.org> | 2015-06-26 15:47:13 +0200 |
commit | 9e9ed7939a749d17e00c95ae01c2441855a0085f (patch) | |
tree | 7c401029193c7de91bcd922dcd49de3e4e9d9c6c /src/mzcom | |
parent | 682f326601a0f4a2c585bc8563950eaf40edfc06 (diff) |
Importing racket_6.2.orig.tar.gz
Diffstat (limited to 'src/mzcom')
-rw-r--r-- | src/mzcom/.gitignore | 3 | ||||
-rw-r--r-- | src/mzcom/README | 3 | ||||
-rw-r--r-- | src/mzcom/com_glue.c | 704 | ||||
-rw-r--r-- | src/mzcom/com_glue.h | 75 | ||||
-rw-r--r-- | src/mzcom/mzcom.cxx | 314 | ||||
-rw-r--r-- | src/mzcom/mzcom.idl | 53 | ||||
-rw-r--r-- | src/mzcom/mzobj.cxx | 569 | ||||
-rw-r--r-- | src/mzcom/mzobj.h | 70 | ||||
-rw-r--r-- | src/mzcom/resource.h | 21 |
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, ®_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 |