]> granicus.if.org Git - php/commitdiff
Major re-jig.
authorWez Furlong <wez@php.net>
Tue, 27 Jul 2004 03:57:31 +0000 (03:57 +0000)
committerWez Furlong <wez@php.net>
Tue, 27 Jul 2004 03:57:31 +0000 (03:57 +0000)
With thanks to Rob Richards for tracking down a couple of big bugs caused by
teeny bits of code.

sapi/activescript/README
sapi/activescript/classfactory.cpp
sapi/activescript/config.w32
sapi/activescript/marshal.cpp [new file with mode: 0755]
sapi/activescript/php5activescript.c
sapi/activescript/php5as_classfactory.h
sapi/activescript/php5as_scriptengine.h
sapi/activescript/scriptengine.cpp

index 39066f578362b9de05b78898eb846a4f9984eb83..fc889d9e1b61b6b7476313b908b0e361ee0470cb 100644 (file)
@@ -4,7 +4,6 @@ This is the ActiveScript SAPI for PHP.
 Once registered on your system (using regsvr32), you will be able to use
 PHP script in any ActiveScript compliant host.  The list includes:
 
-o. Client-side script in Internet Explorer
 o. Windows Script Host
 o. ASP and ASP.NET
 o. Windows Script Components / Behaviours
@@ -24,12 +23,6 @@ Build and install it somewhere; then register the engine like this:
 Usage.
 ======
 
-o. Client-side script in Internet Explorer
-
-  <script language="ActivePHP5">
-       $window->alert("Hello");
-  </script>
-
 o. Windows Script Host
 
   Create a .wsf file like this:
@@ -38,7 +31,7 @@ o. Windows Script Host
     <script language="ActivePHP5">
          $WScript->Echo("Hello");
        </script>
-  </script>
+  </job>
 
 o. ASP and ASP.NET
 
index bd0416d7ed4232c152e26349f39ef1fdec6577c1..5fa37c9f039491bb525cb61f97f0ff640a9273e4 100644 (file)
@@ -98,11 +98,15 @@ STDMETHODIMP TPHPClassFactory::LockServer(BOOL fLock)
 
 STDMETHODIMP TPHPClassFactory::CreateInstance(IUnknown *pUnkOuter, REFIID iid, void **ppvObject)
 {
-       TPHPScriptingEngine *engine = new TPHPScriptingEngine;
+       IUnknown *punk = create_scripting_engine(NULL);
+       HRESULT ret;
 
-       HRESULT ret = engine->QueryInterface(iid, ppvObject);
-
-       engine->Release();
+       if (punk) {
+               ret = punk->QueryInterface(iid, ppvObject);
+               punk->Release();
+       } else {
+               ret = E_UNEXPECTED;
+       }
        
        return ret;
 }
@@ -161,7 +165,13 @@ static const GUID *script_engine_categories[] = {
 };
 
 static const struct reg_class classes_to_register[] = {
-       { &CLSID_PHPActiveScriptEngine, "PHP Active Script Engine", "Both", engine_entries, script_engine_categories },
+       { &CLSID_PHPActiveScriptEngine, "PHP Active Script Engine",
+#if ACTIVEPHP_THREADING_MODE == COINIT_MULTITHREADED
+               "Both",
+#else
+               "Apartment",
+#endif
+               engine_entries, script_engine_categories },
        { NULL, NULL, NULL, 0, NULL }
 };
 /* }}} */
index 401f8663a66493471996b9b12fb9c5aecd6fcfb7..0dbfc5851c03ade410617c0a5d6810d6bfd864b9 100644 (file)
@@ -8,6 +8,6 @@ if (PHP_ACTIVESCRIPT == "yes") {
                ERROR("ActiveScript module requires an --enable-zts build of PHP");
        }
 
-       SAPI('activescript', 'classfactory.cpp php5activescript.c scriptengine.cpp', 'php' + PHP_VERSION + 'activescript.dll', '/D PHP5ISAPI_EXPORTS /D ACTIVEPHP_OBJECT_SAFETY=1');
+       SAPI('activescript', 'classfactory.cpp php5activescript.c scriptengine.cpp marshal.cpp', 'php' + PHP_VERSION + 'activescript.dll', '/D PHP5ISAPI_EXPORTS /D ACTIVEPHP_OBJECT_SAFETY=1');
        ADD_FLAG('LDFLAGS_ACTIVESCRIPT', 'oleaut32.lib ole32.lib user32.lib advapi32.lib /DEF:' + configure_module_dirname + '\\php5activescript.def');
 }
diff --git a/sapi/activescript/marshal.cpp b/sapi/activescript/marshal.cpp
new file mode 100755 (executable)
index 0000000..2d4c61c
--- /dev/null
@@ -0,0 +1,406 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 5                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 2004 The PHP Group                                     |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 3.0 of the PHP license,       |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available through the world-wide-web at the following url:           |
+   | http://www.php.net/license/3_0.txt.                                  |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Authors: Wez Furlong <wez@thebrainroom.com>                          |
+   +----------------------------------------------------------------------+
+ */
+/* $Id$ */
+
+/* Fun with threads */
+
+#define _WIN32_DCOM
+#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS
+#include <winsock2.h>
+#include "php5as_scriptengine.h"
+#include "php5as_classfactory.h"
+#include <objbase.h>
+#undef php_win_err
+
+extern "C" char *php_win_err(HRESULT ret);
+
+#define APHPM_IN       1
+#define APHPM_OUT      2
+
+#define APHPT_TERM     0
+#define APHPT_UNK              1       /* IUnknown *           */
+#define APHPT_DISP     2       /* IDispatch *          */
+#define APHPT_VAR              3       /* PVARIANT             */
+
+static inline void trace(char *fmt, ...)
+{
+       va_list ap;
+       char buf[4096];
+
+       sprintf(buf, "T=%08x [MARSHAL] ", tsrm_thread_id());
+       OutputDebugString(buf);
+       
+       va_start(ap, fmt);
+       vsnprintf(buf, sizeof(buf), fmt, ap);
+
+       OutputDebugString(buf);
+       
+       va_end(ap);
+}
+struct marshal_arg {
+       int type;
+       int argno;
+       int direction;
+};
+
+static int parse_script_text_mdef[] = {
+       APHPT_UNK, 2, APHPM_IN,
+       APHPT_VAR, 7, APHPM_OUT,
+       APHPT_TERM
+};
+
+static int get_script_dispatch_mdef[] = {
+       APHPT_DISP, 1, APHPM_OUT,
+       APHPT_TERM
+};
+
+static int *mdef_by_func[APHP__Max] = {
+       parse_script_text_mdef,
+       NULL, /* InitNew */
+       NULL,   /* AddNamedItem */
+       NULL,   /* SetScriptState */
+       get_script_dispatch_mdef,
+       NULL,   /* Close */
+       NULL,   /* AddTypeLib */
+       NULL,   /* AddScriptlet */
+};
+
+static HRESULT do_marshal_in(int stub, void *args[16], int *mdef, LPSTREAM *ppstm)
+{
+       int i = 0;
+       int want;
+       HRESULT ret = S_OK;
+       LPSTREAM stm = NULL;
+
+       if (!mdef)
+               return S_OK;
+
+       trace("marshalling ... \n");
+       
+       ret = CreateStreamOnHGlobal(NULL, TRUE, &stm);
+       if (FAILED(ret)) {
+               trace(" failed to create stm %s", php_win_err(ret));
+               return ret;
+       }
+               
+       *ppstm = stm;
+
+       /* if stub is true, we are the stub and are marshaling OUT params,
+        * otherwise, we are the proxy and are marshalling IN params */
+
+       if (stub) {
+               want = APHPM_OUT;
+       } else {
+               want = APHPM_IN;
+       }
+       
+       while (mdef[i] != APHPT_TERM) {
+               if ((mdef[i+2] & want) == want) {
+                       int argno = mdef[i+1];
+                       int isout = (mdef[i+2] & APHPM_OUT) == APHPM_OUT;
+
+#undef OUT_IFACE
+#define OUT_IFACE              (isout ? *(IUnknown**)args[argno] : (IUnknown*)args[argno])
+#define IFACE_PRESENT  args[argno] && (!isout || *(IUnknown**)args[argno])
+                       switch (mdef[i]) {
+                               case APHPT_UNK:
+                                       if (IFACE_PRESENT) {
+                                               ret = CoMarshalInterface(stm, IID_IUnknown, OUT_IFACE, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL);
+                                               trace("   arg=%d IUnknown --> %s", argno, php_win_err(ret));
+                                       } else {
+                                               trace("   arg=%d IUnknown(NULL) - skip\n", argno);
+                                       }
+                                       break;
+
+                               case APHPT_DISP:
+                                       if (IFACE_PRESENT) {
+                                               ret = CoMarshalInterface(stm, IID_IDispatch, OUT_IFACE, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL);
+                                               trace("   arg=%d IDispatch --> %s", argno, php_win_err(ret));
+                                       } else {
+                                               trace("   arg=%d IDispatch(NULL) - skip\n", argno);
+                                       }
+                                       break;
+
+                               case APHPT_VAR:
+                                       if (args[argno])
+                                               ret = E_NOTIMPL;
+                                       break;
+
+                               default:
+                                       ret = E_NOTIMPL;
+                       }
+
+                       if (FAILED(ret))
+                               break;
+               } else {
+                       trace(" -- skipping (this param is not needed in this direction)\n");
+               }
+               i += 3;
+       }
+
+       if (FAILED(ret)) {
+               /* TODO: rollback (refcounts are held during marshalling) */
+               trace(" rolling back\n");
+               stm->Release();
+               *ppstm = NULL;
+       } else {
+               LARGE_INTEGER pos = {0};
+               stm->Seek(pos, STREAM_SEEK_SET, NULL);
+       }
+       
+       return ret;
+}
+
+static HRESULT do_marshal_out(int stub, void *args[16], int *mdef, LPSTREAM stm)
+{
+       int i = 0;
+       int want;
+       HRESULT ret = S_OK;
+
+       if (!mdef)
+               return S_OK;
+
+       trace(" unmarshalling...\n");
+       
+       /* if stub is true, we are the stub and are unmarshaling IN params,
+        * otherwise, we are the proxy and are unmarshalling OUT params */
+
+       if (!stub) {
+               want = APHPM_OUT;
+       } else {
+               want = APHPM_IN;
+       }
+       
+       while (mdef[i] != APHPT_TERM) {
+               if ((mdef[i+2] & want) == want) {
+                       int argno = mdef[i+1];
+                       int isout = (mdef[i+2] & APHPM_OUT) == APHPM_OUT;
+#undef OUT_IFACE
+#define OUT_IFACE              (isout ? (void**)args[argno] : &args[argno])
+
+                       switch (mdef[i]) {
+                               case APHPT_UNK:
+                                       if (IFACE_PRESENT) {
+                                               ret = CoUnmarshalInterface(stm, IID_IUnknown, OUT_IFACE);
+                                               trace("   unmarshal arg=%d IUnknown --> %s", argno, php_win_err(ret));
+                                       } else {
+                                               trace("   unmarshal arg=%d IUnknown(NULL) - skip\n", argno);
+                                       }
+                                       break;
+
+                               case APHPT_DISP:
+                                       if (IFACE_PRESENT) {
+                                               trace("   unmarshal dispatch: args[%d]=%p   *args[%d]=%p\n",
+                                                               argno, args[argno], argno, args[argno] ? *(void**)args[argno] : NULL);
+                                               ret = CoUnmarshalInterface(stm, IID_IDispatch, OUT_IFACE);
+                                               trace("   unmarshal arg=%d IDispatch --> %s:  args[%d]=%p *args[%d]=%p\n", argno, php_win_err(ret),
+                                                               argno, args[argno], argno, args[argno] ? *(void**)args[argno] : NULL);
+                                       } else {
+                                               trace("   unmarshal arg=%d IDispatch(NULL) - skip\n", argno);
+                                       }
+                                       break;
+
+                               case APHPT_VAR:
+                                       if (args[argno])
+                                               ret = E_NOTIMPL;
+                                       break;
+
+                               default:
+                                       ret = E_NOTIMPL;
+                       }
+                       if (FAILED(ret))
+                               break;
+               }
+               i += 3;
+       }
+
+       return ret;
+}
+
+
+struct activephp_serialize_msg {
+       class TPHPScriptingEngine *engine;
+       void *args[16];
+       int nargs;
+       enum activephp_engine_func func;
+       int *marshal_defs;
+       LPSTREAM instm, outstm;
+       
+       HANDLE evt;
+       HRESULT ret;
+};
+
+static const char *func_names[APHP__Max] = {
+       "ParseScriptText",
+       "InitNew",
+       "AddnamedItem",
+       "SetScriptState",
+       "GetScriptDispatch",
+       "Close",
+       "AddTypeLib",
+       "AddScriptlet",
+};
+
+HRESULT marshal_call(class TPHPScriptingEngine *engine, enum activephp_engine_func func, int nargs, ...)
+{
+       va_list ap;
+       struct activephp_serialize_msg msg ;
+       HRESULT ret;
+
+       memset(&msg, 0, sizeof(msg));
+       
+       msg.engine = engine;
+       msg.func = func;
+       msg.marshal_defs = mdef_by_func[func];
+
+       trace(" prepping for function code %d %s, %d args, marshal defs at %p\n", func, func_names[func], nargs, msg.marshal_defs);
+
+       va_start(ap, nargs);
+       for (msg.nargs = 0; msg.nargs < nargs; msg.nargs++) {
+               msg.args[msg.nargs] = va_arg(ap, void*);
+       }
+       va_end(ap);
+
+       ret = do_marshal_in(0, msg.args, msg.marshal_defs, &msg.instm);
+
+       if (FAILED(ret)) {
+               return ret;
+       }
+       
+#if 1
+       msg.evt = CreateEvent(NULL, TRUE, FALSE, NULL);
+       PostMessage(engine->m_queue, WM_ACTIVEPHP_SERIALIZE, 0, (LPARAM)&msg);
+
+       while (WAIT_OBJECT_0 != WaitForSingleObject(msg.evt, 0)) {
+               DWORD status = MsgWaitForMultipleObjects(1, &msg.evt, FALSE, INFINITE, QS_ALLEVENTS|QS_ALLINPUT|QS_ALLPOSTMESSAGE|QS_SENDMESSAGE|QS_POSTMESSAGE);
+
+               if (status == WAIT_OBJECT_0)
+                       break;
+
+               MSG msg;
+
+               while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+                       TranslateMessage(&msg);
+                       DispatchMessage(&msg);
+               }
+       }
+       CloseHandle(msg.evt);
+#else
+       ret = SendMessage(engine->m_queue, WM_ACTIVEPHP_SERIALIZE, 0, (LPARAM)&msg);
+#endif
+       
+       if (msg.outstm) {
+               ret = do_marshal_out(0, msg.args, msg.marshal_defs, msg.outstm);
+               msg.outstm->Release();
+       }
+
+       if (msg.instm)
+               msg.instm->Release();
+       
+       trace("marshall call to %s completed %s", func_names[func], php_win_err(ret));
+       
+       return ret;
+}
+
+HRESULT marshal_stub(LPARAM lparam)
+{
+       struct activephp_serialize_msg *msg = (struct activephp_serialize_msg*)lparam;
+
+       if (msg->instm) {
+               msg->ret = do_marshal_out(1, msg->args, msg->marshal_defs, msg->instm);
+
+               if (FAILED(msg->ret)) {
+                       SetEvent(msg->evt);
+                       return msg->ret;
+               }
+       }
+       
+       switch (msg->func) {
+               case APHP_ParseScriptText:
+                       msg->ret = msg->engine->ParseScriptText(
+                                       (LPCOLESTR)msg->args[0],
+                                       (LPCOLESTR)msg->args[1],
+                                       (IUnknown*)msg->args[2],
+                                       (LPCOLESTR)msg->args[3],
+                                       (DWORD)msg->args[4],
+                                       (ULONG)msg->args[5],
+                                       (DWORD)msg->args[6],
+                                       (VARIANT*)msg->args[7],
+                                       (EXCEPINFO*)msg->args[8]);
+                       break;
+
+               case APHP_InitNew:
+                       msg->ret = msg->engine->InitNew();
+                       break;
+
+               case APHP_AddNamedItem:
+                       msg->ret = msg->engine->AddNamedItem(
+                                       (LPCOLESTR)msg->args[0],
+                                       (DWORD)msg->args[1]);
+                       break;
+
+               case APHP_SetScriptState:
+                       msg->ret = msg->engine->SetScriptState((SCRIPTSTATE)(LONG)msg->args[0]);
+                       break;
+
+               case APHP_GetScriptDispatch:
+                       msg->ret = msg->engine->GetScriptDispatch(
+                                       (LPCOLESTR)msg->args[0],
+                                       (IDispatch**)msg->args[1]);
+                       break;
+
+               case APHP_Close:
+                       msg->ret = msg->engine->Close();
+                       break;
+
+               case APHP_AddTypeLib:
+                       msg->ret = msg->engine->AddTypeLib(
+                                       (REFGUID)msg->args[0],
+                                       (DWORD)msg->args[1],
+                                       (DWORD)msg->args[2],
+                                       (DWORD)msg->args[3]);
+                       break;
+                               
+               case APHP_AddScriptlet:
+                       msg->ret = msg->engine->AddScriptlet(
+                                       (LPCOLESTR)msg->args[0],
+                                       (LPCOLESTR)msg->args[1],
+                                       (LPCOLESTR)msg->args[2],
+                                       (LPCOLESTR)msg->args[3],
+                                       (LPCOLESTR)msg->args[4],
+                                       (LPCOLESTR)msg->args[5],
+                                       (DWORD)msg->args[6],
+                                       (ULONG)msg->args[7],
+                                       (DWORD)msg->args[8],
+                                       (BSTR*)msg->args[9],
+                                       (EXCEPINFO*)msg->args[10]);
+                       break;
+
+               default:
+                       msg->ret = E_NOTIMPL;
+       }
+
+       if (SUCCEEDED(msg->ret)) {
+               msg->ret = do_marshal_in(1, msg->args, msg->marshal_defs, &msg->outstm);
+       }
+
+       SetEvent(msg->evt);
+       
+       return msg->ret;
+}
+
index 93222fdbf86ddd7fb135723b815d7e739b81c3a2..6e83b99d5c1e4a30a43d8bf02b2299625c6e72a2 100644 (file)
@@ -145,6 +145,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
                                activescript_sapi_module.shutdown(&sapi_module);
                        }
                        //OutputDebugString("PROCESS_DETACH\n");
+                       sapi_shutdown();
                        tsrm_shutdown();
                        break;
        }
index 61823fb689d5246752627b3b063c5ee696bf024b..048d38b7e5e824aa0d70d8fd405c4b1f1a3a9d46 100644 (file)
 DEFINE_GUID(CLSID_PHPActiveScriptEngine,
 0xcf108a38, 0x59a9, 0x468a, 0xaf, 0x45, 0x13, 0x68, 0xd7, 0x85, 0x5d, 0xae);
 
+// {AD504760-D6B9-4537-AEAC-512FFB359009}
+DEFINE_GUID(CLSID_PHPActiveScriptEngineMarshal,
+0xad504760, 0xd6b9, 0x4537, 0xae, 0xac, 0x51, 0x2f, 0xfb, 0x35, 0x90, 0x9);
+
 #if 0
 /* this was for PHP 4 */
 // {A0AD8E7A-95EC-4819-986F-78D93895F2AE}
index bd720a584c858c6f61e1c5b3820f72fea593ce31..83ab582cc2eaf1802bd3e7483932b3acb2fdce5b 100644 (file)
 /* $Id$ */
 
 #include <activscp.h>
-#if ACTIVEPHP_OBJECT_SAFETY
-# include <objsafe.h>
-#endif
+#include <objsafe.h>
 #include "zend.h"
-#include <setjmp.h>
-
-/* Definitions for thread messages */
-enum {
-       PHPSE_STATE_CHANGE = WM_USER + 20,
-       PHPSE_INIT_NEW,
-       PHPSE_PARSE_SCRIPT,
-       PHPSE_ADD_SCRIPTLET,
-       PHPSE_CLOSE,
-       PHPSE_CLONE,
-       PHPSE_ENTER,
-       PHPSE_LEAVE,
-       PHPSE_TERMINATE,
-       PHPSE_PARSE_PROC,
-       PHPSE_EXEC_PROC,
-       PHPSE_ADD_NAMED_ITEM,
-       PHPSE_SET_SITE,
-       PHPSE_ADD_TYPELIB,
-       PHPSE_TRIGGER_ERROR,
-       PHPSE_GET_DISPATCH,
-       PHPSE_DUMMY_TICK,
-};
 
-struct php_active_script_get_dispatch_info {
-       LPCOLESTR pstrItemName;
-       DWORD dispatch;
-};
-
-struct php_active_script_add_named_item_info {
-       LPCOLESTR pstrName;
-       DWORD dwFlags;
-       IUnknown *punk;
-       ITypeInfo *ptyp;
-       IDispatch *pdisp;
-       DWORD marshal;
-};
+#if 0
+#define ACTIVEPHP_THREADING_MODE       COINIT_MULTITHREADED
+#else
+#define ACTIVEPHP_THREADING_MODE       COINIT_APARTMENTTHREADED
+#endif
 
-struct php_active_script_add_scriptlet_info {
-       /* [in] */ LPCOLESTR pstrDefaultName;
-       /* [in] */ LPCOLESTR pstrCode;
-       /* [in] */ LPCOLESTR pstrItemName;
-       /* [in] */ LPCOLESTR pstrSubItemName;
-       /* [in] */ LPCOLESTR pstrEventName;
-       /* [in] */ LPCOLESTR pstrDelimiter;
-       /* [in] */ DWORD dwSourceContextCookie;
-       /* [in] */ ULONG ulStartingLineNumber;
-       /* [in] */ DWORD dwFlags;
-       /* [out] */ BSTR *pbstrName;
-       /* [out] */ EXCEPINFO *pexcepinfo;
-};
+#define ACTIVEPHP_HAS_OWN_THREAD       1
 
-struct php_active_script_parse_info {
-       /* [in] */ LPCOLESTR pstrCode;
-       /* [in] */ LPCOLESTR pstrItemName;
-       /* [in] */ IUnknown *punkContext;
-       /* [in] */ LPCOLESTR pstrDelimiter;
-       /* [in] */ DWORD dwSourceContextCookie;
-       /* [in] */ ULONG ulStartingLineNumber;
-       /* [in] */ DWORD dwFlags;
-       /* [out] */ VARIANT *pvarResult;
-       /* [out] */ EXCEPINFO *pexcepinfo;
-};
+#define WM_ACTIVEPHP_SERIALIZE WM_USER + 200
 
-struct php_active_script_parse_proc_info {
-       /* [in] */ LPCOLESTR pstrCode;
-       /* [in] */ LPCOLESTR pstrFormalParams;
-       /* [in] */ LPCOLESTR pstrProcedureName;
-       /* [in] */ LPCOLESTR pstrItemName;
-       /* [in] */ IUnknown *punkContext;
-       /* [in] */ LPCOLESTR pstrDelimiter;
-       /* [in] */ DWORD dwSourceContextCookie;
-       /* [in] */ ULONG ulStartingLineNumber;
-       /* [in] */ DWORD dwFlags;
-       DWORD dispcookie;
+enum activephp_engine_func { /* if you change the order, change marshal.cpp too */
+       APHP_ParseScriptText,
+       APHP_InitNew,
+       APHP_AddNamedItem,
+       APHP_SetScriptState,
+       APHP_GetScriptDispatch,
+       APHP_Close,
+       APHP_AddTypeLib,
+       APHP_AddScriptlet,
+       APHP__Max
 };
 
-struct php_active_script_add_tlb_info {
-       /* [in] */ const GUID * rguidTypeLib;
-       /* [in] */ DWORD dwMajor;
-       /* [in] */ DWORD dwMinor;
-       /* [in] */ DWORD dwFlags;
-};
+HRESULT marshal_call(class TPHPScriptingEngine *engine, enum activephp_engine_func func, int nargs, ...);
+HRESULT marshal_stub(LPARAM lparam);
 
 class TPHPScriptingEngine:
        public IActiveScript,
        public IActiveScriptParse,
-       public IActiveScriptParseProcedure
-#if ACTIVEPHP_OBJECT_SAFETY
-       , public IObjectSafety
+       public IActiveScriptParseProcedure,
+       public IObjectSafety,
+       public IDispatch
+#if 0
+       , public IMarshal
 #endif
 {
 public:
        volatile LONG m_refcount;
        IActiveScriptSite *m_pass;
        SCRIPTSTATE m_scriptstate;
-       MUTEX_T         m_mutex;        
-       HashTable       m_script_dispatchers;
-       HANDLE          m_engine_thread_handle;
 
-       HANDLE          m_sync_thread_msg;
-       HRESULT         m_sync_thread_ret;
-
-       /* This is hacky, but only used when the host queries us for a script dispatch */
-       void *** m_tsrm_hack;
-       
        void add_to_global_namespace(IDispatch *disp, DWORD flags, char *name TSRMLS_DC);
        
        THREAD_T        m_enginethread, m_basethread;
        HashTable   m_frags;
        ULONG           m_lambda_count;
-       IActiveScriptSite *m_pass_eng;
+       DWORD           m_gitcookie, m_asscookie;
+       HWND            m_queue;
+
+       int m_done_init;
 
-       jmp_buf *m_err_trap;
        int m_in_main, m_stop_main;
-               
-       HRESULT SendThreadMessage(LONG msg, WPARAM wparam, LPARAM lparam);
        
-       void engine_thread_func(void);
-       HRESULT engine_thread_handler(LONG msg, WPARAM wParam, LPARAM lParam, int *handled TSRMLS_DC);
+       void do_clone(TPHPScriptingEngine *src);
+void setup_engine_state(void);
+       int create_id(OLECHAR *name, DISPID *dispid TSRMLS_DC);
+
+       char *m_names[1024];
+       int m_lens[1024];
+       int m_ids;
        
 public: /* IUnknown */
        STDMETHODIMP QueryInterface(REFIID iid, void **ppvObject);
@@ -234,7 +173,6 @@ public: /* IActiveScriptParseProcedure */
                /* [in] */ DWORD dwFlags,
                /* [out] */ IDispatch **ppdisp);
 
-#if ACTIVEPHP_OBJECT_SAFETY
 public: /* IObjectSafety */
        STDMETHODIMP GetInterfaceSafetyOptions(
                /* [in]  */ REFIID      riid,                                           // Interface that we want options for
@@ -245,11 +183,33 @@ public: /* IObjectSafety */
                /* [in]  */ REFIID              riid,                                   // Interface to set options for
                /* [in]  */ DWORD               dwOptionSetMask,                // Options to change
                /* [in]  */ DWORD               dwEnabledOptions);              // New option values
+#if 0
+public: /* IMarshal */
+       STDMETHODIMP GetUnmarshalClass(
+                       REFIID riid, void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, CLSID *pCid);
+       STDMETHODIMP GetMarshalSizeMax(
+                       REFIID riid, void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, ULONG *pSize);
+       STDMETHODIMP MarshalInterface(
+                       IStream *pStm, REFIID riid, void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshflags);
+       STDMETHODIMP UnmarshalInterface(
+                       IStream *pStm, REFIID riid, void **ppv);
+       STDMETHODIMP ReleaseMarshalData(IStream *pStm);
+       STDMETHODIMP DisconnectObject(DWORD dwReserved);
 #endif
+
+public:        /* IDispatch */
+       STDMETHODIMP GetIDsOfNames( REFIID  riid, OLECHAR **rgszNames, unsigned int cNames, LCID lcid, DISPID *rgDispId);
+       STDMETHODIMP Invoke( DISPID  dispIdMember, REFIID  riid, LCID  lcid, WORD  wFlags,
+               DISPPARAMS FAR*  pdp, VARIANT FAR*  pvarRes, EXCEPINFO FAR*  pei,
+               unsigned int FAR*  puArgErr);
+       STDMETHODIMP GetTypeInfoCount(unsigned int *  pctinfo);
+       STDMETHODIMP GetTypeInfo( unsigned int iTInfo, LCID lcid, ITypeInfo **ppTInfo);
        
 public:
        TPHPScriptingEngine();
        ~TPHPScriptingEngine();
-
+       
 };
 
+IUnknown *create_scripting_engine(TPHPScriptingEngine *tobecloned);
+
index 9906ca14901c1e69e14878fa671ebbc43ccb9951..56e5c797c57d682c8647a4eef141b33e23f64138 100644 (file)
@@ -45,6 +45,7 @@ extern "C" {
 #include "php5activescript.h"
 #include "ext/com_dotnet/php_com_dotnet.h"
 #include "ext/com_dotnet/php_com_dotnet_internal.h"
+#include "zend_exceptions.h"
 }
 #include "php_ticks.h"
 #include "php5as_scriptengine.h"
@@ -52,7 +53,30 @@ extern "C" {
 #include <objbase.h>
 #undef php_win_err
 
-#define ACTIVEPHP_THREADING_MODE       COINIT_MULTITHREADED
+static int clone_frags(void *pDest, void *arg TSRMLS_DC);
+
+#define ENGINE_THREAD_ONLY(type, method)       \
+       if (tsrm_thread_id() != m_enginethread) { \
+               trace("WRONG THREAD !! " #type "::" #method "\n"); \
+               return RPC_E_WRONG_THREAD; \
+       } \
+               trace("[direct] " #type "::" #method "\n"); 
+
+
+#define ASS_CALL(ret, method, args)    \
+       if (tsrm_thread_id() == m_basethread) { \
+               trace("Calling [direct] m_pass->" #method "\n"); \
+               ret = m_pass->method args; \
+       } else { \
+               IActiveScriptSite *ass; \
+               trace("Calling [marshall] m_pass->" #method "\n"); \
+               ret = GIT_get(m_asscookie, IID_IActiveScriptSite, (void**)&ass); \
+               if (SUCCEEDED(ret)) { \
+                       ret = ass->method args; \
+                       ass->Release(); \
+               } \
+       } \
+       trace("--- done calling m_pass->" #method "\n");
 
 /* {{{ trace */
 static inline void trace(char *fmt, ...)
@@ -99,10 +123,12 @@ class TWideString {
                TWideString(LPOLESTR olestr) {
                        m_ole = olestr;
                        m_ansi = NULL;
+                       m_ansi_strlen = 0;
                }
                TWideString(LPCOLESTR olestr) {
                        m_ole = (LPOLESTR)olestr;
                        m_ansi = NULL;
+                       m_ansi_strlen = 0;
                }
 
                ~TWideString() {
@@ -110,6 +136,7 @@ class TWideString {
                                CoTaskMemFree(m_ansi);
                        }
                        m_ansi = NULL;
+                       m_ansi_strlen = 0;
                }
 
                char *safe_ansi_string() {
@@ -162,12 +189,13 @@ class TWideString {
 
                                        if (m_ansi_strlen) {
                                                m_ansi_strlen--;
-                                               m_ansi[m_ansi_strlen] = 0;
-
                                        } else {
                                                trace("conversion failed with return code %08x\n", GetLastError());     
                                        }
                                }
+                               if (m_ansi) {
+                                       m_ansi[m_ansi_strlen] = 0;
+                               }
                        }
                        return m_ansi;
                }
@@ -213,16 +241,6 @@ static code_frag *clone_code_fragment(code_frag *frag, TPHPScriptingEngine *engi
 
 /* }}} */
 
-/* Magic for handling threading correctly */
-static inline HRESULT SEND_THREAD_MESSAGE(TPHPScriptingEngine *engine, LONG msg, WPARAM wparam, LPARAM lparam TSRMLS_DC)
-{
-       if (engine->m_enginethread == 0)
-               return E_UNEXPECTED;
-       if (tsrm_thread_id() == (engine)->m_enginethread) 
-               return (engine)->engine_thread_handler((msg), (wparam), (lparam), NULL TSRMLS_CC);
-       return (engine)->SendThreadMessage((msg), (wparam), (lparam));
-}
-
 /* These functions do some magic so that interfaces can be
  * used across threads without worrying about marshalling
  * or not marshalling, as appropriate.
@@ -353,19 +371,183 @@ public:
        }
 };
 /* }}} */
+       STDMETHODIMP TPHPScriptingEngine::GetTypeInfoCount(unsigned int *  pctinfo) {
+               *pctinfo = 0;
+               trace("%08x: ScriptDispatch: GetTypeInfoCount\n", this);
+               return S_OK;
+       }
+       STDMETHODIMP TPHPScriptingEngine::GetTypeInfo( unsigned int iTInfo, LCID lcid, ITypeInfo **ppTInfo) {
+               trace("%08x: ScriptDispatch: GetTypeInfo\n", this);
+               return DISP_E_BADINDEX;
+       }
 
-/* {{{ This object represents the PHP engine to the scripting host.
- * Although the docs say it's implementation is optional, I found that
- * the Windows Script host would crash if we did not provide it. */
-class ScriptDispatch:
-       public IDispatchImpl
+int TPHPScriptingEngine::create_id(OLECHAR *name, DISPID *dispid TSRMLS_DC) 
 {
-public:
-       ScriptDispatch() {
-               m_refcount = 1;
+       int ex = 0;
+       char *lcname;
+       int l;
+
+       if (m_ids >= 1023) {
+               trace("too many ids\n");
+               return 0;
        }
-};
-/* }}} */
+
+       TWideString aname(name);
+
+       l = aname.ansi_len();
+       lcname = zend_str_tolower_dup(aname.ansi_string(), l);
+       ex = zend_hash_exists(EG(function_table), lcname, l+1);
+       efree(lcname);
+
+       if (!ex) {
+               trace("no such id %s\n", aname.ansi_string());
+               return 0;
+       }
+
+       /* do we already have an id for this name? */
+       int i;
+       for (i = 0; i < m_ids; i++) {
+               if (!strcasecmp(m_names[i], aname.ansi_string())) {
+                       trace("already had this ID\n");
+                       return i;
+               }
+       }
+
+       m_lens[m_ids] = aname.ansi_len();
+       m_names[m_ids] = aname.ansi_string();
+       trace("created ID %d for name %s\n", m_ids, m_names[m_ids]);
+       aname.m_ansi = NULL;
+       *dispid = m_ids;
+       m_ids++;
+       return 1;
+}
+
+STDMETHODIMP TPHPScriptingEngine::GetIDsOfNames( REFIID  riid, OLECHAR **rgszNames, unsigned int cNames, LCID lcid, DISPID *rgDispId)
+{
+       unsigned int i;
+       HRESULT ret = S_OK;
+       TSRMLS_FETCH();
+
+       if (tsrm_thread_id() != m_enginethread) {
+               trace("GetIDsOfNames called from wrong thread\n");
+               return RPC_E_WRONG_THREAD;
+       }
+       
+       trace("%08x: ScriptDispatch: GetIDsOfNames %d names: \n", this, cNames);
+       for (i = 0; i < cNames; i++) {
+               if (!create_id(rgszNames[i], &rgDispId[i] TSRMLS_CC)) {
+                       ret = DISP_E_UNKNOWNNAME;
+                       break;
+               }
+       }
+       return ret;
+}
+
+STDMETHODIMP TPHPScriptingEngine::Invoke( DISPID  dispIdMember, REFIID  riid, LCID  lcid, WORD  wFlags,
+               DISPPARAMS FAR*  pdp, VARIANT FAR*  pvarRes, EXCEPINFO FAR*  pei,
+               unsigned int FAR*  puArgErr)
+{
+       UINT i;
+       zval *retval = NULL;
+       zval ***params = NULL;
+       HRESULT ret = DISP_E_MEMBERNOTFOUND;
+       char *name;
+       int namelen;
+       TSRMLS_FETCH();
+
+       if (tsrm_thread_id() != m_enginethread) {
+               trace("Invoke called from wrong thread\n");
+               return RPC_E_WRONG_THREAD;
+       }
+       
+       name = m_names[dispIdMember];
+       namelen = m_lens[dispIdMember];
+
+       trace("%08x: ScriptDispatch: Invoke dispid %08x [%s]\n", this, dispIdMember, name);
+       /* this code is similar to that of our com_wrapper InvokeEx implementation */
+
+       /* convert args into zvals.
+        * Args are in reverse order */
+       if (pdp->cArgs) {
+               params = (zval ***)safe_emalloc(sizeof(zval **), pdp->cArgs, 0);
+               for (i = 0; i < pdp->cArgs; i++) {
+                       VARIANT *arg;
+                       zval *zarg;
+
+                       arg = &pdp->rgvarg[ pdp->cArgs - 1 - i];
+
+                       trace("alloc zval for arg %d VT=%08x\n", i, V_VT(arg));
+
+                       ALLOC_INIT_ZVAL(zarg);
+                       php_com_wrap_variant(zarg, arg, CP_ACP TSRMLS_CC);
+                       params[i] = &zarg;
+               }
+       }
+
+       trace("arguments processed, prepare to do some work\n");        
+
+       /* TODO: if PHP raises an exception here, we should catch it
+        * and expose it as a COM exception */
+
+#if 0
+       if (wFlags & DISPATCH_PROPERTYGET) {
+               retval = zend_read_property(Z_OBJCE_P(disp->object), disp->object, m_names[dispIdMember], m_lens[dispIdMember]+1, 1 TSRMLS_CC);
+       } else if (wFlags & DISPATCH_PROPERTYPUT) {
+               zend_update_property(Z_OBJCE_P(disp->object), disp->object, m_names[dispIdMember], m_lens[dispIdMember]+1, *params[0] TSRMLS_CC);
+       } else
+#endif
+               if (wFlags & DISPATCH_METHOD) {
+                       zval *zname;
+                       MAKE_STD_ZVAL(zname);
+                       ZVAL_STRINGL(zname, (char*)name, namelen, 1);
+                       trace("invoke function %s\n", Z_STRVAL_P(zname));
+
+                       zend_try {
+
+                               if (SUCCESS == call_user_function_ex(CG(function_table), NULL, zname,
+                                                       &retval, pdp->cArgs, params, 1, NULL TSRMLS_CC)) {
+                                       ret = S_OK;
+                                       trace("we ran it\n");
+                               } else {
+                                       ret = DISP_E_EXCEPTION;
+                                       trace("no such function\n");
+                               }
+
+                       } zend_catch {
+                               ret = DISP_E_EXCEPTION;
+                               /* need to populate the exception here */
+                               trace("bork\n");
+                       } zend_end_try();
+                       
+                       zval_ptr_dtor(&zname);
+               } else {
+                       trace("Don't know how to handle this invocation %08x\n", wFlags);
+                       ret = E_UNEXPECTED;
+               }
+
+       /* release arguments */
+       for (i = 0; i < pdp->cArgs; i++)
+               zval_ptr_dtor(params[i]);
+
+       if (params)
+               efree(params);
+
+       /* return value */
+       if (retval) {
+               if (pvarRes) {
+                       VariantInit(pvarRes);
+                       trace("setting up return value\n");
+                       php_com_variant_from_zval(pvarRes, retval, CP_ACP TSRMLS_CC);
+               }
+               zval_ptr_dtor(&retval);
+       } else if (pvarRes) {
+               VariantInit(pvarRes);
+       }
+
+       trace("Invocation complete\n");
+
+       return ret;
+}
 
 /* {{{ This object is used in conjunction with IActiveScriptParseProcedure to
  * allow scriptlets to be bound to events.  IE uses this for declaring
@@ -390,7 +572,7 @@ public:
 
                if (m_frag) {
                        trace("%08x: Procedure Dispatch: Invoke dispid %08x\n", this, dispIdMember);
-                       SEND_THREAD_MESSAGE(m_engine, PHPSE_EXEC_PROC, 0, (LPARAM)this TSRMLS_CC);
+                       execute_code_fragment(m_frag, NULL, NULL TSRMLS_CC);
                }
                return S_OK;
        }
@@ -496,10 +678,16 @@ static void free_code_fragment(code_frag *frag TSRMLS_DC)
                        break;
        }
        
-       if (frag->opcodes)
+       if (frag->opcodes) {
                destroy_op_array(frag->opcodes TSRMLS_CC);
-       if (frag->functionname)
+               frag->opcodes = NULL;
+       }
+
+       if (frag->functionname) {
                CoTaskMemFree(frag->functionname);
+               frag->functionname = NULL;
+       }
+
        CoTaskMemFree(frag->code);
        CoTaskMemFree(frag);
 }
@@ -533,23 +721,12 @@ trace("%08x: CLONED FRAG\n", newfrag);
        pv.value.str.val = newfrag->code;
        pv.value.str.len = newfrag->codelen;
 
-       newfrag->opcodes = compile_string(&pv, "fragment" TSRMLS_CC);
+       /* we defer compilation until we are ready to execute,
+        * as we need the host to AddNamedItem certain autoglobals
+        * BEFORE we compile */
+       newfrag->opcodes = NULL;
 
-       if (newfrag->opcodes == NULL) {
-               free_code_fragment(newfrag TSRMLS_CC);
-/*
-               if (excepinfo) {
-                       memset(excepinfo, 0, sizeof(EXCEPINFO));
-                       excepinfo->wCode = 1000;
-                       excepinfo->bstrSource = TWideString::bstr_from_ansi("fragment");
-                       excepinfo->bstrDescription = TWideString::bstr_from_ansi("Problem while parsing/compiling");
-               }
-*/             
-               return NULL;
-       }
-       
        return newfrag;
-
 }
 
 static int execute_code_fragment(code_frag *frag,
@@ -558,16 +735,26 @@ static int execute_code_fragment(code_frag *frag,
                TSRMLS_DC)
 {
        zval *retval_ptr = NULL;
-       jmp_buf *orig_jmpbuf;
-       jmp_buf err_trap;
 
        if (frag->fragtype == FRAG_MAIN && frag->executed)
                return 1;
-       
-       orig_jmpbuf = frag->engine->m_err_trap;
-       frag->engine->m_err_trap = &err_trap;
 
-       if (setjmp(err_trap) == 0) {
+       /* compiled cloned fragment, JIT */
+       if (frag->persistent && frag->opcodes == NULL) {
+               zval pv;
+               pv.type = IS_STRING;
+               pv.value.str.val = frag->code;
+               pv.value.str.len = frag->codelen;
+
+               frag->opcodes = compile_string(&pv, "fragment (JIT)" TSRMLS_CC);
+
+               if (!frag->opcodes) {
+                       trace("*** JIT compilation of cloned opcodes failed??");
+                       return 0;
+               }
+       }
+       
+       zend_try {
                trace("*** Executing code in thread %08x\n", tsrm_thread_id());
 
                if (frag->functionname) {
@@ -581,30 +768,29 @@ static int execute_code_fragment(code_frag *frag,
                        call_user_function_ex(CG(function_table), NULL, &fname, &retval_ptr, 0, NULL, 1, NULL TSRMLS_CC);
 
                } else {
-                       zend_op_array           *active_op_array                = EG(active_op_array);
-                       zend_function_state     *function_state_ptr             = EG(function_state_ptr);
-                       zval                            **return_value_ptr_ptr  = EG(return_value_ptr_ptr);
-                       zend_op                         **opline_ptr                    = EG(opline_ptr);
-
-                       EG(return_value_ptr_ptr)        = &retval_ptr;
-                       EG(active_op_array)                     = frag->opcodes;
-                       EG(no_extensions)                       = 1;
-
-                       zend_execute(frag->opcodes TSRMLS_CC);
-
-                       EG(no_extensions)                       = 0;
-                       EG(opline_ptr)                          = opline_ptr;
-                       EG(active_op_array)                     = active_op_array;
-                       EG(function_state_ptr)          = function_state_ptr;
-                       EG(return_value_ptr_ptr)        = return_value_ptr_ptr;
+                       zend_fcall_info_cache fci_cache;
+                       zend_fcall_info fci;
+
+                       memset(&fci, 0, sizeof(fci));
+                       memset(&fci_cache, 0, sizeof(fci_cache));
+               
+                       fci.size = sizeof(fci);
+                       fci.function_table = CG(function_table);
+                       fci.retval_ptr_ptr = &retval_ptr;
+                       fci.no_separation = 1;
+
+                       fci_cache.initialized = 1;
+                       fci_cache.function_handler = (zend_function*)frag->opcodes;
+                       frag->opcodes->type = ZEND_USER_FUNCTION;       // mini hack
+
+                       zend_call_function(&fci, &fci_cache TSRMLS_CC);
+                       
                }
-       } else {
+       } zend_catch {
                trace("*** --> caught error while executing\n");
                if (frag->engine->m_in_main)
                        frag->engine->m_stop_main = 1;
-       }
-
-       frag->engine->m_err_trap = orig_jmpbuf;
+       } zend_end_try();
 
        if (frag->fragtype == FRAG_MAIN)
                frag->executed = 1;
@@ -628,694 +814,231 @@ static void frag_dtor(void *pDest)
        free_code_fragment(frag TSRMLS_CC);
 }
 /* }}} */
-
-/* glue for getting back into the OO land */
-static DWORD WINAPI begin_engine_thread(LPVOID param)
-{
-       TPHPScriptingEngine *engine = (TPHPScriptingEngine*)param;
-       engine->engine_thread_func();
-       trace("engine thread has really gone away!\n");
-       return 0;
-}
-
-TPHPScriptingEngine::TPHPScriptingEngine()
-{
-       m_scriptstate = SCRIPTSTATE_UNINITIALIZED;
-       m_pass = NULL;
-       m_in_main = 0;
-       m_stop_main = 0;
-       m_err_trap = NULL;
-       m_lambda_count = 0;
-       m_pass_eng = NULL;
-       m_refcount = 1;
-       m_basethread = tsrm_thread_id();
-       m_mutex = tsrm_mutex_alloc();
-       m_sync_thread_msg = CreateEvent(NULL, TRUE, FALSE, NULL);
-       TPHPClassFactory::AddToObjectCount();
        
-       m_engine_thread_handle = CreateThread(NULL, 0, begin_engine_thread, this, 0, &m_enginethread);
-}
-
-void activescript_run_ticks(int count)
+void TPHPScriptingEngine::do_clone(TPHPScriptingEngine *src)
 {
-       MSG msg;
        TSRMLS_FETCH();
-       TPHPScriptingEngine *engine;
-       
-       trace("ticking %d\n", count);
-       
-       engine = (TPHPScriptingEngine*)SG(server_context);
-
-/*     PostThreadMessage(engine->m_enginethread, PHPSE_DUMMY_TICK, 0, 0); */
-       
-       while(PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
-               if (msg.hwnd) {
-                       PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
-                       TranslateMessage(&msg);
-                       DispatchMessage(&msg);
-               } else {
-                       break;
-               }
-       }
+       zend_hash_apply_with_argument(&src->m_frags, clone_frags, this TSRMLS_CC); 
 }
 
-/* Synchronize with the engine thread */
-HRESULT TPHPScriptingEngine::SendThreadMessage(LONG msg, WPARAM wparam, LPARAM lparam)
-{
+#if ACTIVEPHP_HAS_OWN_THREAD
+struct engine_startup {
+       HANDLE evt;
        HRESULT ret;
+       TPHPScriptingEngine *toclone;
+       TPHPScriptingEngine *localref;
+};
 
-       if (m_enginethread == 0)
-               return E_UNEXPECTED;
-
-       trace("I'm waiting for a mutex in SendThreadMessage\n   this=%08x ethread=%08x msg=%08x\n",
-                       this, m_enginethread, msg);
-       
-       tsrm_mutex_lock(m_mutex);
-       ResetEvent(m_sync_thread_msg);
-
-       /* If we call PostThreadMessage before the thread has created the queue, the message
-        * posting fails.  MSDN docs recommend the following course of action */
-       while (!PostThreadMessage(m_enginethread, msg, wparam, lparam)) {
-               Sleep(50);
-               if (m_enginethread == 0) {
-                       tsrm_mutex_unlock(m_mutex);
-                       trace("breaking out of dodgy busy wait\n");
-                       return E_UNEXPECTED;
-               }
-       }
-
-       /* Wait for the event object to be signalled.
-        * This is a nice "blocking without blocking" wait; window messages are dispatched
-        * and everything works out quite nicely */
-       while(1) {
-               DWORD result = MsgWaitForMultipleObjects(1, &m_sync_thread_msg, FALSE, 4000, QS_ALLINPUT);
-
-               if (result == WAIT_OBJECT_0 + 1) {
-                       /* Dispatch some messages */
-                       MSG msg;
-                       while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
-                               //trace("dispatching message while waiting\n"); 
-                               TranslateMessage(&msg);
-                               DispatchMessage(&msg);
-                       }
-               } else if (result == WAIT_TIMEOUT) {
-                       trace("timeout while waiting for thread reply\n");
-
-               } else {
-                       /* the event was signalled */
-                       break;
-               }
-       }
-       ret = m_sync_thread_ret;
-       ResetEvent(m_sync_thread_msg);
-       tsrm_mutex_unlock(m_mutex);
-       return ret;
-}
+static WNDCLASS wc = {0};
 
-TPHPScriptingEngine::~TPHPScriptingEngine()
+static LRESULT CALLBACK script_thread_msg_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
 {
-       trace("\n\n *** Engine Destructor Called\n\n");
+       switch (message) {
+               case WM_ACTIVEPHP_SERIALIZE:
+                       return marshal_stub(lparam);
 
-       if (m_scriptstate != SCRIPTSTATE_UNINITIALIZED && m_scriptstate != SCRIPTSTATE_CLOSED && m_enginethread) {
-               Close();
+               case WM_DESTROY:
+                       PostQuitMessage(0);
+                       return 0;
+                       
+               default:
+                       return DefWindowProc(hwnd, message, wparam, lparam);
        }
-               
-       PostThreadMessage(m_enginethread, WM_QUIT, 0, 0);
-       WaitForSingleObject(m_engine_thread_handle, INFINITE);
-       CloseHandle(m_engine_thread_handle);
-
-       TPHPClassFactory::RemoveFromObjectCount();
-       tsrm_mutex_free(m_mutex);
-}
-
-/* Set some executor globals and execute a zend_op_array.
- * The declaration looks wierd because this can be invoked from
- * zend_hash_apply_with_argument */
-static int execute_main(void *pDest, void *arg TSRMLS_DC)
-{
-       code_frag *frag = *(code_frag**)pDest;
-
-       if (frag->fragtype == FRAG_MAIN && !(frag->engine->m_in_main && frag->engine->m_stop_main))
-               execute_code_fragment(frag, NULL, NULL TSRMLS_CC);
-
-       return ZEND_HASH_APPLY_KEEP;
 }
 
-static int clone_frags(void *pDest, void *arg TSRMLS_DC)
+static DWORD WINAPI script_thread(LPVOID param)
 {
-       code_frag *frag, *src = *(code_frag**)pDest;
-       TPHPScriptingEngine *engine = (TPHPScriptingEngine*)arg;
-
-       if (src->persistent) {
-               frag = clone_code_fragment(src, engine TSRMLS_CC);
-               if (frag)
-                       zend_hash_next_index_insert(&engine->m_frags, &frag, sizeof(code_frag*), NULL);
-               else
-                       trace("WARNING: clone failed!\n");
-       }
+       struct engine_startup *su = (struct engine_startup*)param;
+       TPHPScriptingEngine *engine;
+       IUnknown *punk = NULL;
+       MSG msg;
 
-       return ZEND_HASH_APPLY_KEEP;
-}
+       trace("firing up engine thread/apartment\n");
        
-HRESULT TPHPScriptingEngine::engine_thread_handler(LONG msg, WPARAM wparam, LPARAM lParam, int *handled TSRMLS_DC)
-{
-       HRESULT ret = S_OK;
+       /* set up COM in this apartment */
+       CoInitializeEx(0, COINIT_APARTMENTTHREADED);
 
-       trace("engine_thread_handler: running in thread %08x, should be %08x msg=%08x this=%08x\n",
-                       tsrm_thread_id(), m_enginethread, msg, this);
-
-       if (handled)
-               *handled = 1;
+       /* create a window for message queueing */
+       wc.lpfnWndProc = script_thread_msg_proc;
+       wc.lpszClassName = "ActivePHP Message Window";
+       RegisterClass(&wc);
        
-       if (m_enginethread == 0)
-               return E_UNEXPECTED;
+       /* create the engine state */
+       engine = new TPHPScriptingEngine;
+
+       engine->m_enginethread = tsrm_thread_id();
+       engine->m_basethread = 0;
+       engine->m_queue = CreateWindow(wc.lpszClassName, wc.lpszClassName,
+                       0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+                       NULL, NULL, NULL, NULL);
+
+       /* marshall it for another apartment */
+       engine->QueryInterface(IID_IUnknown, (void**)&punk);
+       su->ret = GIT_put(punk, IID_IUnknown, &engine->m_gitcookie);
+       punk->Release();
+       su->localref = engine;
+
+       /* do we need to clone ? */
+       if (su->toclone) {
+               engine->do_clone(su->toclone);
+       }
        
-       switch(msg) {
-               case PHPSE_ADD_TYPELIB:
-                       {
-                               struct php_active_script_add_tlb_info *info = (struct php_active_script_add_tlb_info*)lParam;
-                               ITypeLib *TypeLib;
+       /* tell whoever spawned us that we're ready and waiting */
+       SetEvent(su->evt);
 
-                               if (SUCCEEDED(LoadRegTypeLib(*info->rguidTypeLib, (USHORT)info->dwMajor,
-                                                               (USHORT)info->dwMinor, LANG_NEUTRAL, &TypeLib))) {
-                                       php_com_import_typelib(TypeLib, CONST_CS, CP_ACP TSRMLS_CC);
-                                       TypeLib->Release();
-                               }
-                       }
-                       break;
-               case PHPSE_STATE_CHANGE:
-                       {
-                               /* handle the state change here */
-                               SCRIPTSTATE ss = (SCRIPTSTATE)lParam;
-                               int start_running = 0;
-                               trace("%08x: DoSetScriptState(current=%s, new=%s)\n",
-                                               this,
-                                               scriptstate_to_string(m_scriptstate),
-                                               scriptstate_to_string(ss));
-
-                               if (m_scriptstate == SCRIPTSTATE_INITIALIZED && (ss == SCRIPTSTATE_STARTED || ss == SCRIPTSTATE_CONNECTED))
-                                       start_running = 1;
-
-                               m_scriptstate = ss;
-
-                               /* inform host/site of the change */
-                               if (m_pass_eng)
-                                       m_pass_eng->OnStateChange(m_scriptstate);
-
-                               if (start_running) {
-                                       /* run "main()", as described in the docs */    
-                                       if (m_pass_eng)
-                                               m_pass_eng->OnEnterScript();
-                                       trace("%08x: apply execute main to m_frags\n", this);
-                                       m_in_main = 1;
-                                       m_stop_main = 0;
-                                       zend_hash_apply_with_argument(&m_frags, execute_main, this TSRMLS_CC);
-                                       m_in_main = 0;
-                                       trace("%08x: --- done execute main\n", this);
-                                       if (m_pass_eng)
-                                               m_pass_eng->OnLeaveScript();
-
-                                       /* docs are a bit ambiguous here, but it appears that we should
-                                        * inform the host that the main script execution has completed,
-                                        * and also what the return value is */
-                                       VARIANT varRes;
-
-                                       VariantInit(&varRes);
-                                       if (m_pass_eng)
-                                               m_pass_eng->OnScriptTerminate(&varRes, NULL);
-
-                                       /*
-                                          m_scriptstate = SCRIPTSTATE_INITIALIZED;
-                                          if (m_pass_eng)
-                                          m_pass_eng->OnStateChange(m_scriptstate);
-                                          */
-                               }
-                       }
-                       break;
-               case PHPSE_INIT_NEW:
-                       {
-                               /* Prepare PHP/ZE for use */
-
-                               trace("%08x: m_frags : INIT NEW\n", this);
-
-#if 0
-                               zend_hash_init(&m_frags, 0, NULL, frag_dtor, TRUE);
-
-                               SG(options) |= SAPI_OPTION_NO_CHDIR;
-                               SG(server_context) = this;
-                               /* override the default PHP error callback */
-                               zend_error_cb = activescript_error_handler;
-
-                               zend_alter_ini_entry("register_argc_argv", 19, "1", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
-                               zend_alter_ini_entry("html_errors", 12, "0", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
-                               zend_alter_ini_entry("implicit_flush", 15, "1", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
-                               zend_alter_ini_entry("max_execution_time", 19, "0", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
-
-                               php_request_startup(TSRMLS_C);
-                               PG(during_request_startup) = 0;
-//                             trace("\n\n     *** ticks func at %08x %08x ***\n\n\n", activescript_run_ticks, &activescript_run_ticks);
-//                             php_add_tick_function(activescript_run_ticks);
-#endif
-
-                       }
-                       break;
-               case PHPSE_CLOSE:
-                       {
-                               /* Close things down */
-                               trace("%08x: m_frags : CLOSE/DESTROY\n", this);
-                               m_scriptstate = SCRIPTSTATE_CLOSED;
-                               if (m_pass_eng) {
-                                       m_pass_eng->OnStateChange(m_scriptstate);
-                                       trace("%08x: release site from this side\n", this);
-                                       m_pass_eng->Release();
-                                       m_pass_eng = NULL;
-                               }
-#if 0
-                               zend_hash_destroy(&m_frags);
-                               php_request_shutdown(NULL);
-#endif
-
-                               break;
-                       }
-                       break;
-               case PHPSE_CLONE:
-                       {
-                               /* Clone the engine state.  This is semantically equal to serializing all
-                                * the parsed code from the source and unserializing it in the dest (this).
-                                * IE doesn't appear to use it, but Windows Script Host does. I'd expect
-                                * ASP/ASP.NET to do so also.
-                                *
-                                * FIXME: Probably won't work with IActiveScriptParseProcedure scriplets
-                                * */
-
-                               TPHPScriptingEngine *src = (TPHPScriptingEngine*)lParam;
-
-                               trace("%08x: m_frags : CLONE\n", this);
-                               zend_hash_apply_with_argument(&src->m_frags, clone_frags, this TSRMLS_CC); 
-
-                       }
-                       break;
-               case PHPSE_ADD_SCRIPTLET:
-                       {
-                               /* Parse/compile a chunk of script that will act as an event handler.
-                                * If the host supports IActiveScriptParseProcedure, this code will
-                                * not be called.
-                                * The docs are (typically) vague: AFAICT, once the code has been
-                                * compiled, we are supposed to arrange for an IConnectionPoint
-                                * advisory connection to the item/subitem, once the script
-                                * moves into SCRIPTSTATE_CONNECTED.
-                                * That's a lot of work!
-                                *
-                                * FIXME: this is currently almost useless
-                                * */
-
-                               struct php_active_script_add_scriptlet_info *info = (struct php_active_script_add_scriptlet_info*)lParam;
-
-                               TWideString
-                                       default_name(info->pstrDefaultName),
-                               code(info->pstrCode),
-                               item_name(info->pstrItemName),
-                               sub_item_name(info->pstrSubItemName),
-                               event_name(info->pstrEventName),
-                               delimiter(info->pstrDelimiter);
-
-                               /* lets invent a function name for the scriptlet */
-                               char sname[256];
-
-                               /* should check if the name is already used! */
-                               if (info->pstrDefaultName) 
-                                       strcpy(sname, default_name.ansi_string());
-                               else {
-                                       sname[0] = 0;
-                                       strcat(sname, "__");
-                                       if (info->pstrItemName) {
-                                               strcat(sname, item_name.ansi_string());
-                                               strcat(sname, "_");
-                                       }
-                                       if (info->pstrSubItemName) {
-                                               strcat(sname, sub_item_name.ansi_string());
-                                               strcat(sname, "_");
-                                       }
-                                       if (info->pstrEventName)
-                                               strcat(sname, event_name.ansi_string());
-                               }
-
-
-                               trace("%08x: AddScriptlet:\n state=%s\n name=%s\n code=%s\n item=%s\n subitem=%s\n event=%s\n delim=%s\n line=%d\n",
-                                               this, scriptstate_to_string(m_scriptstate),
-                                               default_name.safe_ansi_string(), code.safe_ansi_string(), item_name.safe_ansi_string(),
-                                               sub_item_name.safe_ansi_string(), event_name.safe_ansi_string(), delimiter.safe_ansi_string(),
-                                               info->ulStartingLineNumber);
-
-
-                               code_frag *frag = compile_code_fragment(
-                                               FRAG_SCRIPTLET,
-                                               sname,
-                                               info->pstrCode,
-                                               info->ulStartingLineNumber,
-                                               info->pexcepinfo,
-                                               this
-                                               TSRMLS_CC);
-
-                               if (frag) {
-
-                                       frag->persistent = (info->dwFlags & SCRIPTTEXT_ISPERSISTENT);
-
-                                       zend_hash_next_index_insert(&m_frags, &frag, sizeof(code_frag*), NULL);
-
-                                       /*
-                                          ScriptProcedureDispatch *disp = new ScriptProcedureDispatch;
-
-                                          disp->AddRef();
-                                          disp->m_frag = frag;
-                                          disp->m_procflags = info->dwFlags;
-                                          disp->m_engine = this;
-                                          frag->ptr = disp;
-
-                                        *info->ppdisp = disp;
-                                        */
-                                       ret = S_OK;
-                               } else {
-                                       ret = DISP_E_EXCEPTION;
-                               }
-
-                               *info->pbstrName = TWideString::bstr_from_ansi(sname);
-
-                               trace("%08x: done with scriptlet %s\n", this, sname);
-
-                       }
-                       break;
-               case PHPSE_GET_DISPATCH:
-                       {
-                               struct php_active_script_get_dispatch_info *info = (struct php_active_script_get_dispatch_info *)lParam;
-                               IDispatch *disp = NULL;
-                               
-                               if (info->pstrItemName != NULL) {
-                                       zval **tmp;
-                                       /* use this rather than php_OLECHAR_to_char because we want to avoid emalloc here */
-                                       TWideString itemname(info->pstrItemName);
-                                       
-                                       /* Get that item from the global namespace.
-                                        * If it is an object, export it as a dispatchable object.
-                                        * */
-
-                                       if (zend_hash_find(&EG(symbol_table), itemname.ansi_string(),
-                                                               itemname.ansi_len() + 1, (void**)&tmp) == SUCCESS) {
-                                               if (Z_TYPE_PP(tmp) == IS_OBJECT) {
-                                                       /* FIXME: if this causes an allocation (emalloc) and we are
-                                                        * not in the engine thread, things could get ugly!!! */
-                                                       disp = php_com_wrapper_export(*tmp TSRMLS_CC);
-                                               }
-                                       }
-
-                               } else {
-                                       /* This object represents PHP global namespace */
-                                       disp = (IDispatch*) new ScriptDispatch;
-                               }
+       /* pump COM messages */
+       while (GetMessage(&msg, NULL, 0, 0)) {
+               TranslateMessage(&msg);
+               DispatchMessage(&msg);
+       }
 
-                               if (disp) {
-                                       ret = GIT_put(disp, IID_IDispatch, &info->dispatch);
-                                       disp->Release();
-                                       trace("GET_DISPATCH: we put it in the GIT\n");
-                               } else {
-                                       ret = S_FALSE;
-                                       trace("GET_DISPATCH: FAILED to put it in the GIT\n");
-                               }
-                       }
-                       break;
-               case PHPSE_ADD_NAMED_ITEM:
-                       {
-                               /* The Host uses this to add objects to the global namespace.
-                                * Some objects are intended to have their child properties
-                                * globally visible, so we add those to the global namespace too.
-                                * */
-                               struct php_active_script_add_named_item_info *info = (struct php_active_script_add_named_item_info *)lParam;
-
-                               TWideString name(info->pstrName);
-                               IDispatch *disp = NULL;
-                               HRESULT res;
-                       
-                               trace("ADD_NAMED_ITEM\n");
+       trace("terminating engine thread\n");
+       
+       CoUninitialize();
 
-#if ACTIVEPHP_THREADING_MODE == COINIT_MULTITHREADED
-                               res = info->punk->QueryInterface(IID_IDispatch, (void**)&disp);
-#else
-                               res = GIT_get(info->marshal, IID_IDispatch, (void**)&disp);
+       return 0;
+}
 #endif
-                               if (SUCCEEDED(res) && disp) {
-                                       add_to_global_namespace(disp, info->dwFlags, name.ansi_string() TSRMLS_CC);
-                                       disp->Release();
-                               } else {
-                                       trace("Ouch: failed to get IDispatch for %s from GIT '%s'\n", name.ansi_string(), php_win_err(res));
-                               }
 
-                       }
-                       break;
-               case PHPSE_SET_SITE:
-                       {
-                               if (m_pass_eng) {
-                                       m_pass_eng->Release();
-                                       m_pass_eng = NULL;
-                               }
+IUnknown *create_scripting_engine(TPHPScriptingEngine *tobecloned)
+{
+       IUnknown *punk = NULL;
+#if ACTIVEPHP_HAS_OWN_THREAD
+       struct engine_startup su;
+       HANDLE hthr;
+       DWORD thid;
 
-                               if (lParam)
-                                       GIT_get(lParam, IID_IActiveScriptSite, (void**)&m_pass_eng);
+       su.evt = CreateEvent(NULL, TRUE, FALSE, NULL);
+       su.toclone = tobecloned;
 
-                               trace("%08x: site (engine-side) is now %08x (base=%08x)\n", this, m_pass_eng, m_pass);
+       hthr = CreateThread(NULL, 0, script_thread, &su, 0, &thid);
+       if (hthr)
+               CloseHandle(hthr);
 
-                       }
-                       break;
-               case PHPSE_EXEC_PROC:
-                       {
-                               ScriptProcedureDispatch *disp = (ScriptProcedureDispatch *)lParam;
-                               execute_code_fragment(disp->m_frag, NULL, NULL TSRMLS_CC);
-                       }
-                       break;
-               case PHPSE_PARSE_PROC:
-                       {
-                               /* This is the IActiveScriptParseProcedure implementation.
-                                * IE uses this to request for an IDispatch that it will invoke in
-                                * response to some event, and tells us the code that it wants to
-                                * run.
-                                * We compile the code and pass it back a dispatch object.
-                                * The object will then serialize access to the engine thread and
-                                * execute the opcodes */
-                               struct php_active_script_parse_proc_info *info = (struct php_active_script_parse_proc_info*)lParam;
-                               TWideString
-                                       formal_params(info->pstrFormalParams),
-                                       procedure_name(info->pstrProcedureName),
-                                       item_name(info->pstrItemName),
-                                       delimiter(info->pstrDelimiter);
-
-                               trace("%08x: ParseProc:\n state=%s\nparams=%s\nproc=%s\nitem=%s\n delim=%s\n line=%d\n",
-                                               this, scriptstate_to_string(m_scriptstate),
-                                               formal_params.ansi_string(), procedure_name.ansi_string(),
-                                               item_name.safe_ansi_string(), delimiter.safe_ansi_string(),
-                                               info->ulStartingLineNumber);
-
-
-                               code_frag *frag = compile_code_fragment(
-                                               FRAG_PROCEDURE,
-                                               NULL,
-                                               info->pstrCode,
-                                               info->ulStartingLineNumber,
-                                               NULL,
-                                               this
-                                               TSRMLS_CC);
-
-                               if (frag) {
-
-                                       frag->persistent = (info->dwFlags & SCRIPTTEXT_ISPERSISTENT);
-                                       zend_hash_next_index_insert(&m_frags, &frag, sizeof(code_frag*), NULL);
-
-                                       ScriptProcedureDispatch *disp = new ScriptProcedureDispatch;
-
-                                       disp->m_frag = frag;
-                                       disp->m_procflags = info->dwFlags;
-                                       disp->m_engine = this;
-                                       frag->ptr = disp;
-                                       info->dispcookie = disp->m_gitcookie;
-
-                               } else {
-                                       ret = DISP_E_EXCEPTION;
-                               }
+       WaitForSingleObject(su.evt, INFINITE);
 
-                       }
-                       break;
+       if (SUCCEEDED(su.ret)) {
+               punk = (IUnknown*)(void*)su.localref;
+       }
 
-               case PHPSE_PARSE_SCRIPT:
-                       {
-                               struct php_active_script_parse_info *info = (struct php_active_script_parse_info*)lParam;
-                               int doexec;
-
-                               TWideString
-                                       code(info->pstrCode),
-                               item_name(info->pstrItemName),
-                               delimiter(info->pstrDelimiter);
-
-                               trace("%08x: ParseScriptText:\n state=%s\ncode=%s\n item=%s\n delim=%s\n line=%d\n",
-                                               this, scriptstate_to_string(m_scriptstate),
-                                               code.safe_ansi_string(), item_name.safe_ansi_string(), delimiter.safe_ansi_string(),
-                                               info->ulStartingLineNumber);
-
-                               code_frag *frag = compile_code_fragment(
-                                               FRAG_MAIN,
-                                               info->dwFlags & SCRIPTTEXT_ISEXPRESSION ? FRAG_CREATE_FUNC : NULL,
-                                               info->pstrCode,
-                                               info->ulStartingLineNumber,
-                                               info->pexcepinfo,
-                                               this
-                                               TSRMLS_CC);
-
-                               doexec = (info->dwFlags & SCRIPTTEXT_ISEXPRESSION) ||
-                                       m_scriptstate == SCRIPTSTATE_STARTED ||
-                                       m_scriptstate == SCRIPTSTATE_CONNECTED ||
-                                       m_scriptstate == SCRIPTSTATE_DISCONNECTED;
-
-                               if (frag) {
-                                       frag->persistent = (info->dwFlags & SCRIPTTEXT_ISPERSISTENT);
-                                       ret = S_OK;
+       CloseHandle(su.evt);
+#else
+       punk = (IActiveScript*)new TPHPScriptingEngine;
+#endif
+       return punk;
+}
 
-                                       if (info->dwFlags & SCRIPTTEXT_ISEXPRESSION) {
-                                               if (m_scriptstate == SCRIPTSTATE_INITIALIZED) {
-                                                       /* not allowed to execute code in this state */
-                                                       ret = E_UNEXPECTED;
-                                                       doexec = 0;
-                                               }
-                                       }
+void TPHPScriptingEngine::setup_engine_state(void)
+{
+       TSRMLS_FETCH();
 
-                                       if (doexec) {
-                                               /* execute the code as an expression */
-                                               if (!execute_code_fragment(frag, info->pvarResult, info->pexcepinfo TSRMLS_CC))
-                                                       ret = DISP_E_EXCEPTION;
-                                       }
+       m_enginethread = tsrm_thread_id();
+       trace("initializing zend engine on this thread\n");
+       
+       SG(options) |= SAPI_OPTION_NO_CHDIR;
+       SG(headers_sent) = 1;
+       SG(request_info).no_headers = 1;
 
-                                       zend_hash_next_index_insert(&m_frags, &frag, sizeof(code_frag*), NULL);
-                               } else {
-                                       ret = DISP_E_EXCEPTION;
-                               }
+       SG(server_context) = this;
 
-                               if (info->pvarResult) {
-                                       VariantInit(info->pvarResult);
-                               }
+       /* override the default PHP error callback */
+       zend_error_cb = activescript_error_handler;
 
+       zend_alter_ini_entry("register_argc_argv", 19, "1", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
+       zend_alter_ini_entry("html_errors", 12, "0", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
+       zend_alter_ini_entry("implicit_flush", 15, "1", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
+       zend_alter_ini_entry("max_execution_time", 19, "0", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
 
-                       }
-                       break;
-               default:
-                       trace("XXXXX: unhandled message type %08x\n", msg);
-                       if (handled)
-                               *handled = 0;
-       }
-       return ret;
+       php_request_startup(TSRMLS_C);
+       PG(during_request_startup) = 0;
+       zend_hash_init(&m_frags, 0, NULL, frag_dtor, 0);
+
+       m_done_init = 1;
+       
+       trace("---- init done\n");
 }
 
-/* The PHP/Zend state actually lives in this thread */
-void TPHPScriptingEngine::engine_thread_func(void)
+TPHPScriptingEngine::TPHPScriptingEngine()
 {
        TSRMLS_FETCH();
-       int handled;
-       int terminated = 0;
-       MSG msg;
+       
+       /* CTOR */
 
-       trace("%08x: engine thread started up!\n", this);
+       trace("*** NEW this=%08x\n", this);
 
-       CoInitializeEx(0, ACTIVEPHP_THREADING_MODE);
-       
-       zend_first_try {
-               m_tsrm_hack = tsrm_ls;
-
-               SG(options) |= SAPI_OPTION_NO_CHDIR;
-               SG(headers_sent) = 1;
-               SG(request_info).no_headers = 1;
-
-               SG(server_context) = this;
-               /* override the default PHP error callback */
-               zend_error_cb = activescript_error_handler;
-
-               zend_alter_ini_entry("register_argc_argv", 19, "1", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
-               zend_alter_ini_entry("html_errors", 12, "0", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
-               zend_alter_ini_entry("implicit_flush", 15, "1", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
-               zend_alter_ini_entry("max_execution_time", 19, "0", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
-
-               if (SUCCESS == php_request_startup(TSRMLS_C)) {
-                       PG(during_request_startup) = 0;
-                       if (FAILURE == zend_hash_init(&m_frags, 0, NULL, frag_dtor, 1)) {
-                               trace("failed to init frags hash\n");
-                       }
+       m_scriptstate = SCRIPTSTATE_UNINITIALIZED;
+       m_pass = NULL;
+       m_in_main = 0;
+       m_done_init = 0;
+       m_stop_main = 0;
+       m_lambda_count = 0;
+       m_refcount = 1;
+       m_ids = 0;
+       TPHPClassFactory::AddToObjectCount();
 
-                       while(!terminated) {
-                               DWORD result = MsgWaitForMultipleObjects(0, NULL, FALSE, 4000, QS_ALLINPUT);
+#if ACTIVEPHP_HAS_OWN_THREAD
+       setup_engine_state();
+#endif
 
-                               switch(result) {
-                                       case WAIT_OBJECT_0:
-                                               while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+}
 
-                                                       if (msg.message == WM_QUIT) {
-                                                               terminated = 1;
-                                                       } else if (msg.hwnd) {
-                                                               TranslateMessage(&msg);
-                                                               DispatchMessage(&msg);
+TPHPScriptingEngine::~TPHPScriptingEngine()
+{
+       /* DTOR */
 
-                                                       } else {
-                                                               handled = 1;
-                                                               m_sync_thread_ret = engine_thread_handler(msg.message, msg.wParam, msg.lParam, &handled TSRMLS_CC);
-                                                               if (handled)
-                                                                       SetEvent(m_sync_thread_msg);
-                                                       }
+       trace("\n\n *** Engine Destructor Called\n\n");
 
-                                               }
-                                               break;
-                                       case WAIT_TIMEOUT:
-                                               trace("thread wait timed out\n");
-                                               break;
-                                       default:
-                                               trace("some strange value\n");
-                               }
-                       }
+       if (m_done_init && m_scriptstate != SCRIPTSTATE_CLOSED) {
+               Close();
+       }
 
-#if 0
-                       while(GetMessage(&msg, NULL, 0, 0)) {
+       while (m_ids--) {
+               CoTaskMemFree(m_names[m_ids]);
+       }
 
-                               if (msg.message == WM_QUIT)
-                                       break;
+       TPHPClassFactory::RemoveFromObjectCount();
+}
 
-                               handled = 1;
-                               m_sync_thread_ret = engine_thread_handler(msg.message, msg.wParam, msg.lParam, &handled TSRMLS_CC);
-                               if (handled)
-                                       SetEvent(m_sync_thread_msg);
-                       }
-#endif
+/* Set some executor globals and execute a zend_op_array.
+ * The declaration looks wierd because this can be invoked from
+ * zend_hash_apply_with_argument */
+static int execute_main(void *pDest, void *arg TSRMLS_DC)
+{
+       code_frag *frag = *(code_frag**)pDest;
 
-                       trace("%08x: engine thread exiting!!!!!\n", this);
-                       zend_hash_destroy(&m_frags);
-                       trace("calling request shutdown\n");
-                       php_request_shutdown(NULL);
-               } else {
-                       trace("request startup failed\n");
-               }
+       if (frag->fragtype == FRAG_MAIN && !(frag->engine->m_in_main && frag->engine->m_stop_main))
+               execute_code_fragment(frag, NULL, NULL TSRMLS_CC);
 
-       } zend_catch {
-               trace("ouch: bailed out\n");
-       } zend_end_try();
-       
-       m_enginethread = 0;
-       CoUninitialize();
+       return ZEND_HASH_APPLY_KEEP;
 }
 
+static int clone_frags(void *pDest, void *arg TSRMLS_DC)
+{
+       code_frag *frag, *src = *(code_frag**)pDest;
+       TPHPScriptingEngine *engine = (TPHPScriptingEngine*)arg;
+
+       if (src->persistent) {
+               frag = clone_code_fragment(src, engine TSRMLS_CC);
+               if (frag)
+                       zend_hash_next_index_insert(&engine->m_frags, &frag, sizeof(code_frag*), NULL);
+               else
+                       trace("WARNING: clone failed!\n");
+       }
+
+       return ZEND_HASH_APPLY_KEEP;
+}
+       
 /* Only call this in the context of the engine thread, or you'll be sorry.
  *
  * When SCRIPTITEM_GLOBALMEMBERS is set, we're only adding COM objects to the namespace.
  * We could add *all* properties, but I don't like this idea; what if the value changes
  * while the page is running?  We'd be left with stale data.
- *
- * TODO: evaluate if it is appropriate to register as an auto_global
  * */
 
 static inline void make_auto_global(char *name, zval *val TSRMLS_DC)
 {
        int namelen = strlen(name);
-
+trace("make_auto_global %s\n", name);
        zend_register_auto_global(name, namelen, NULL TSRMLS_CC);
        zend_auto_global_disable_jit(name, namelen TSRMLS_CC);
        ZEND_SET_SYMBOL(&EG(symbol_table), name, val);
@@ -1423,7 +1146,10 @@ trace("Add %s to global namespace\n", name);
 
 STDMETHODIMP_(DWORD) TPHPScriptingEngine::AddRef(void)
 {
-       return InterlockedIncrement(const_cast<long*> (&m_refcount));
+       DWORD ret;
+       ret = InterlockedIncrement(const_cast<long*> (&m_refcount));
+       trace("AddRef --> %d\n", ret);
+       return ret;
 }
 
 STDMETHODIMP_(DWORD) TPHPScriptingEngine::Release(void)
@@ -1433,25 +1159,26 @@ STDMETHODIMP_(DWORD) TPHPScriptingEngine::Release(void)
                trace("%08x: Release: zero refcount, destroy the engine!\n", this);
                delete this;
        }
+       trace("Release --> %d\n", ret);
        return ret;
 }
 
 STDMETHODIMP TPHPScriptingEngine::QueryInterface(REFIID iid, void **ppvObject)
 {
        *ppvObject = NULL;
-       
+
        if (IsEqualGUID(IID_IActiveScript, iid)) {
                *ppvObject = (IActiveScript*)this;
        } else if (IsEqualGUID(IID_IActiveScriptParse, iid)) {
                *ppvObject = (IActiveScriptParse*)this;
        } else if (IsEqualGUID(IID_IActiveScriptParseProcedure, iid)) {
                *ppvObject = (IActiveScriptParseProcedure*)this;
-#if ACTIVEPHP_OBJECT_SAFETY
        } else if (IsEqualGUID(IID_IObjectSafety, iid)) {
                *ppvObject = (IObjectSafety*)this;
-#endif
        } else if (IsEqualGUID(IID_IUnknown, iid)) {
                *ppvObject = this;      
+       } else if (IsEqualGUID(IID_IDispatch, iid)) {
+               *ppvObject = (IDispatch*)this;
        } else {
                LPOLESTR guidw;
                StringFromCLSID(iid, &guidw);
@@ -1465,7 +1192,7 @@ STDMETHODIMP TPHPScriptingEngine::QueryInterface(REFIID iid, void **ppvObject)
                AddRef();
                return S_OK;
        }
-       
+
        return E_NOINTERFACE;
 }
 
@@ -1473,38 +1200,53 @@ STDMETHODIMP TPHPScriptingEngine::QueryInterface(REFIID iid, void **ppvObject)
  * It also defines the base thread. */
 STDMETHODIMP TPHPScriptingEngine::SetScriptSite(IActiveScriptSite *pass)
 {
+       HRESULT ret = S_OK;
        TSRMLS_FETCH();
 
-       tsrm_mutex_lock(m_mutex);
+       if (m_pass && pass) {
+               trace("SetScriptSite: we're already set\n");
+               return E_FAIL;
+       }
+
+       trace("%08x: SetScriptSite(%08x) -----> Base thread is %08x\n", this, pass, tsrm_thread_id());
 
-       trace("%08x: -----> Base thread is %08x\n", this, tsrm_thread_id());
+       if (pass) {
+               m_basethread = tsrm_thread_id();
+#if ACTIVEPHP_HAS_OWN_THREAD
+               HRESULT ret = GIT_put(pass, IID_IActiveScriptSite, &m_asscookie);
+#endif
+       }
        
        if (m_pass) {
+#if ACTIVEPHP_HAS_OWN_THREAD
+               trace("killing off ass cookie\n");
+               GIT_revoke(m_asscookie, m_pass);
+#endif
                m_pass->Release();
                m_pass = NULL;
-               SEND_THREAD_MESSAGE(this, PHPSE_SET_SITE, 0, 0 TSRMLS_CC);
        }
 
        if (pass == NULL) {
                trace("Closing down site; we should have no references to objects from the host\n"
-                               "  m_pass=%08x\n  m_pass_eng=%08x\n   What about named items??\n",
-                               m_pass, m_pass_eng);
+                               "  m_pass=%08x\n  What about named items??\n",
+                               m_pass);
        }
        
-       m_pass = pass;
-       if (m_pass) {
-               m_pass->AddRef();
-
-               DWORD cookie;
-               if (SUCCEEDED(GIT_put(m_pass, IID_IActiveScriptSite, &cookie)))
-                       SEND_THREAD_MESSAGE(this, PHPSE_SET_SITE, 0, cookie TSRMLS_CC);
+       if (pass) {
+               trace("taking a ref on pass\n");
+               ret = pass->QueryInterface(IID_IActiveScriptSite, (void**)&m_pass);
+               trace("----> %s", php_win_err(ret));
                
-               if (m_scriptstate == SCRIPTSTATE_UNINITIALIZED)
-                       SEND_THREAD_MESSAGE(this, PHPSE_STATE_CHANGE, 0, SCRIPTSTATE_INITIALIZED TSRMLS_CC);
+               if (SUCCEEDED(ret)) {
+
+#if !ACTIVEPHP_HAS_OWN_THREAD
+                       setup_engine_state();
+#endif
+                       SetScriptState(SCRIPTSTATE_INITIALIZED);
+               }
        }
 
-       tsrm_mutex_unlock(m_mutex);
-       return S_OK;
+       return ret;
 }
 
 STDMETHODIMP TPHPScriptingEngine::GetScriptSite(REFIID riid, void **ppvObject)
@@ -1512,87 +1254,148 @@ STDMETHODIMP TPHPScriptingEngine::GetScriptSite(REFIID riid, void **ppvObject)
        HRESULT ret = S_FALSE;
        
        trace("%08x: GetScriptSite()\n", this);
-       tsrm_mutex_lock(m_mutex);
 
+       if (m_pass) {
+               ASS_CALL(ret, QueryInterface, (riid, ppvObject))
+       }
 
-       if (m_pass)
-               ret = m_pass->QueryInterface(riid, ppvObject);
-
-       tsrm_mutex_unlock(m_mutex);
        return ret;
 }
 
 STDMETHODIMP TPHPScriptingEngine::SetScriptState(SCRIPTSTATE ss)
 {
+       HRESULT dummy = E_UNEXPECTED;
+       int start_running = 0;
        TSRMLS_FETCH();
-       trace("%08x: SetScriptState(%s)\n", this, scriptstate_to_string(ss));
-       return SEND_THREAD_MESSAGE(this, PHPSE_STATE_CHANGE, 0, ss TSRMLS_CC);
+
+       if (tsrm_thread_id() != m_enginethread)
+               return marshal_call(this, APHP_SetScriptState, 1, ss);
+       
+       trace("%08x: SetScriptState(current=%s, new=%s)\n",
+                       this,
+                       scriptstate_to_string(m_scriptstate),
+                       scriptstate_to_string(ss));
+
+       if (m_scriptstate == SCRIPTSTATE_INITIALIZED && (ss == SCRIPTSTATE_STARTED || ss == SCRIPTSTATE_CONNECTED))
+               start_running = 1;
+
+       m_scriptstate = ss;
+
+       if (start_running) {
+               /* run "main()", as described in the docs */    
+               if (m_pass) {
+                       ASS_CALL(dummy, OnEnterScript, ());
+               }
+               trace("%08x: apply execute main to m_frags\n", this);
+               m_in_main = 1;
+               m_stop_main = 0;
+               zend_hash_apply_with_argument(&m_frags, execute_main, this TSRMLS_CC);
+               m_in_main = 0;
+               trace("%08x: --- done execute main\n", this);
+
+               if (m_pass) {
+                       ASS_CALL(dummy, OnLeaveScript, ());
+               }
+       }
+
+       /* inform host/site of the change */
+       if (m_pass) {
+#if 0
+               if (ss == SCRIPTSTATE_INITIALIZED) {
+                       VARIANT varRes;
+                       VariantInit(&varRes);
+
+//                     ASS_CALL(dummy, OnScriptTerminate, (&varRes, NULL));
+               }
+#endif
+
+               ASS_CALL(dummy, OnStateChange, (m_scriptstate));
+       }
+
+       
+       return S_OK;
 }
 
 STDMETHODIMP TPHPScriptingEngine::GetScriptState(SCRIPTSTATE *pssState)
 {
        trace("%08x: GetScriptState(current=%s)\n", this, scriptstate_to_string(m_scriptstate));
-       tsrm_mutex_lock(m_mutex);
        *pssState = m_scriptstate;
-       tsrm_mutex_unlock(m_mutex);
        return S_OK;
 }
 
 STDMETHODIMP TPHPScriptingEngine::Close(void)
 {
        TSRMLS_FETCH();
+       
+       trace("Close() refcount = %d\n", m_refcount);
 
-       if (m_pass) {
+       if (m_scriptstate == SCRIPTSTATE_CLOSED)
+               return E_UNEXPECTED;
+
+       if (m_done_init) {
+
+               if (tsrm_thread_id() != m_enginethread)
+                       return marshal_call(this, APHP_Close, 0);
+               
+               m_done_init = 0;
+               m_scriptstate = SCRIPTSTATE_CLOSED;
+               zend_hash_destroy(&m_frags);
+               php_request_shutdown(NULL);
+               m_enginethread = 0;
+       }
+
+
+       if (m_pass && tsrm_thread_id() == m_basethread) {
+               m_pass->OnStateChange(m_scriptstate);
+       }
+       
+       //GIT_revoke(m_asscookie, m_pass);
+       trace("%08x: release site \n", this);
+
+       if (m_pass && tsrm_thread_id() == m_basethread) {
                m_pass->Release();
                m_pass = NULL;
        }
-       SEND_THREAD_MESSAGE(this, PHPSE_CLOSE, 0, 0 TSRMLS_CC);
+
        return S_OK;
 }
 
-/* Add an item to global namespace.
- * This is called in the context of the base thread (or perhaps some other thread).
- * We want to be able to work with the object in the engine thread, so we marshal
- * it into a stream and let the engine thread deal with it.
- * This works quite nicely when PHP scripts call into the object; threading is
- * handled correctly. */
+/* The Host uses this to add objects to the global namespace.  Some objects are
+ * intended to have their child properties globally visible, so we add those to
+ * the global namespace too.  */
+
 STDMETHODIMP TPHPScriptingEngine::AddNamedItem(LPCOLESTR pstrName, DWORD dwFlags)
 {
-       struct php_active_script_add_named_item_info info;
        HRESULT res;
+       IUnknown *punk = NULL;
+       ITypeInfo *ti = NULL;
        TSRMLS_FETCH();
 
-       info.pstrName = pstrName;
-       info.dwFlags = dwFlags;
-
-       res = m_pass->GetItemInfo(pstrName, SCRIPTINFO_IUNKNOWN, &info.punk, NULL);
+       if (tsrm_thread_id() != m_enginethread)
+               return marshal_call(this, APHP_AddNamedItem, 2, pstrName, dwFlags);
+       
+       TWideString name(pstrName);
+       trace("AddNamedItem: %s (%08x) m_pass=%08x\n", name.ansi_string(), dwFlags, m_pass);
+       
+       res = m_pass->GetItemInfo(pstrName, SCRIPTINFO_IUNKNOWN, &punk, &ti);
 
        if (SUCCEEDED(res)) {
-#if ACTIVEPHP_THREADING_MODE == COINIT_MULTITHREADED
-               /* strangely, the GIT doesn't want to give the engine thread the interface
-                * in this threading mode */
-               SEND_THREAD_MESSAGE(this, PHPSE_ADD_NAMED_ITEM, 0, (LPARAM)&info TSRMLS_CC);
-#else
-               IDispatch *disp;
-               res = info.punk->QueryInterface(IID_IDispatch, (void**)&disp);
-               
+               IDispatch *disp = NULL;
+
+               trace("ADD_NAMED_ITEM\n");
+
+               res = punk->QueryInterface(IID_IDispatch, (void**)&disp);
                if (SUCCEEDED(res) && disp) {
-                       if (SUCCEEDED(GIT_put(disp, IID_IDispatch, &info.marshal))) {
-                               trace("put disp=%p into git with marshal ID of %x\n", disp, info.marshal);
-                               SEND_THREAD_MESSAGE(this, PHPSE_ADD_NAMED_ITEM, 0, (LPARAM)&info TSRMLS_CC);
-                               GIT_revoke(info.marshal, disp);
-                       }
+                       add_to_global_namespace(disp, dwFlags, name.ansi_string() TSRMLS_CC);
                        disp->Release();
                } else {
-                       trace("failed to get IDispatch from punk %s", php_win_err(res));
+                       trace("Ouch: failed to get IDispatch for %s from GIT '%s'\n", name.ansi_string(), php_win_err(res));
                }
-               info.punk->Release();
-#endif
+
        } else {
                trace("failed to get named item, %s", php_win_err(res));
        }
-       
-       return S_OK;
+       return res;
 }
 
 /* Bind to a type library */
@@ -1602,17 +1405,25 @@ STDMETHODIMP TPHPScriptingEngine::AddTypeLib(
        /* [in] */ DWORD dwMinor,
        /* [in] */ DWORD dwFlags)
 {
-       struct php_active_script_add_tlb_info info;
+       HRESULT ret;
+       ITypeLib *TypeLib;
        TSRMLS_FETCH();
 
-       info.rguidTypeLib = &rguidTypeLib;
-       info.dwMajor = dwMajor;
-       info.dwMinor = dwMinor;
-       info.dwFlags = dwFlags;
+       if (tsrm_thread_id() != m_enginethread)
+               return marshal_call(this, APHP_AddTypeLib, 4, rguidTypeLib, dwMajor, dwMinor, dwFlags);
 
-       SEND_THREAD_MESSAGE(this, PHPSE_ADD_TYPELIB, 0, (LPARAM)&info TSRMLS_CC);
+       ENGINE_THREAD_ONLY(IActiveScript, AddTypeLib);
+       
+       trace("AddTypeLib\n");
+       ret = LoadRegTypeLib(rguidTypeLib, (USHORT)dwMajor, (USHORT)dwMinor, LANG_NEUTRAL, &TypeLib);
 
-       return S_OK;
+       if (SUCCEEDED(ret)) {
+               php_com_import_typelib(TypeLib, CONST_CS, CP_ACP TSRMLS_CC);
+               TypeLib->Release();
+       }
+
+
+       return ret;
 }
 
 /* Returns an object representing the PHP Scripting Engine.
@@ -1624,34 +1435,40 @@ STDMETHODIMP TPHPScriptingEngine::GetScriptDispatch(
        /* [in] */ LPCOLESTR pstrItemName,
        /* [out] */ IDispatch **ppdisp)
 {
+       zend_function *func = NULL;
        TSRMLS_FETCH();
-       *ppdisp = NULL;
-       struct php_active_script_get_dispatch_info info;
-
-       info.pstrItemName = pstrItemName;
-       info.dispatch = NULL;
-       
-       /* This hack is required because the host is likely to query us
-        * for a dispatch if we use any of its objects from PHP script.
-        * Since the engine thread will be waiting for the return from
-        * a COM call, we need to deliberately poke a hole in thread
-        * safety so that it is possible to read the symbol table from
-        * outside the engine thread and give it a valid return value.
-        * This is "safe" only in this instance, since we are not modifying
-        * the engine state by looking up the dispatch (I hope).
-        * The scripting engine rules pretty much guarantee that this
-        * method is only called in the base thread.
-        * This appears to only happen when we set the threading to
-        * apartment. */
 
        if (tsrm_thread_id() != m_enginethread) {
-               tsrm_ls = m_tsrm_hack;
-               trace("HEY: hacking thread safety!\n");
+               return marshal_call(this, APHP_GetScriptDispatch, 2, pstrItemName, ppdisp);
        }
+       
+       *ppdisp = NULL;
+
+       if (pstrItemName != NULL) {
+               zval **tmp;
+               TWideString itemname(pstrItemName);
+       trace("GetScriptDispatch %s\n", itemname.ansi_string());
+
+               /* Get that item from the global namespace.
+                * If it is an object, export it as a dispatchable object.
+                * */
+
+               if (zend_hash_find(&EG(symbol_table), itemname.ansi_string(),
+                                       itemname.ansi_len() + 1, (void**)&tmp) == SUCCESS) {
+                       if (Z_TYPE_PP(tmp) == IS_OBJECT) {
+                               *ppdisp = php_com_wrapper_export(*tmp TSRMLS_CC);
+                       }
+               } else if (zend_hash_find(EG(function_table), itemname.ansi_string(),
+                                       itemname.ansi_len() + 1, (void**)&func) == SUCCESS) {
+                       trace("The host wants a function, but we don't have one\n");
+               }
 
-       if (S_OK == engine_thread_handler(PHPSE_GET_DISPATCH, 0, (LPARAM)&info, NULL TSRMLS_CC)) {
-               GIT_get(info.dispatch, IID_IDispatch, (void**)ppdisp);
+       } else {
+               trace("GetScriptDispatch NULL\n");
+               /* This object represents PHP global namespace */
+               *ppdisp = (IDispatch*)this;
        }
+
        
        if (*ppdisp) {
                return S_OK;
@@ -1662,10 +1479,8 @@ STDMETHODIMP TPHPScriptingEngine::GetScriptDispatch(
 STDMETHODIMP TPHPScriptingEngine::GetCurrentScriptThreadID( 
        /* [out] */ SCRIPTTHREADID *pstidThread)
 {
-//     tsrm_mutex_lock(m_mutex);
        trace("%08x: GetCurrentScriptThreadID()\n", this);
-       *pstidThread = m_enginethread;
-//     tsrm_mutex_unlock(m_mutex);
+       *pstidThread = GetCurrentThreadId();//m_enginethread;
        return S_OK;
 }
 
@@ -1673,10 +1488,8 @@ STDMETHODIMP TPHPScriptingEngine::GetScriptThreadID(
        /* [in] */ DWORD dwWin32ThreadId,
        /* [out] */ SCRIPTTHREADID *pstidThread)
 {
-//     tsrm_mutex_lock(m_mutex);
        trace("%08x: GetScriptThreadID()\n", this);
        *pstidThread = dwWin32ThreadId;
-//     tsrm_mutex_unlock(m_mutex);
        return S_OK;
 }
 
@@ -1684,11 +1497,8 @@ STDMETHODIMP TPHPScriptingEngine::GetScriptThreadState(
        /* [in] */ SCRIPTTHREADID stidThread,
        /* [out] */ SCRIPTTHREADSTATE *pstsState)
 {
-//     tsrm_mutex_lock(m_mutex);
-
        trace("%08x: GetScriptThreadState()\n", this);
        *pstsState = SCRIPTTHREADSTATE_NOTINSCRIPT;
-       
        switch(stidThread) {
                case SCRIPTTHREADID_BASE:
                        stidThread = m_basethread;
@@ -1702,7 +1512,6 @@ STDMETHODIMP TPHPScriptingEngine::GetScriptThreadState(
        } else if (stidThread == m_enginethread) {
                *pstsState = SCRIPTTHREADSTATE_NOTINSCRIPT;
        }
-//     tsrm_mutex_unlock(m_mutex);
        return S_OK;
 }
 
@@ -1724,31 +1533,34 @@ STDMETHODIMP TPHPScriptingEngine::InterruptScriptThread(
 STDMETHODIMP TPHPScriptingEngine::Clone( 
        /* [out] */ IActiveScript **ppscript)
 {
-       TPHPScriptingEngine *cloned = new TPHPScriptingEngine;
-       TSRMLS_FETCH();
+       HRESULT ret = E_FAIL;
+       IUnknown *punk;
        
-       trace("%08x: Clone()\n", this);
-
-       if (ppscript)
-               *ppscript = NULL;
-
-       if (cloned) {
-               cloned->InitNew();
-               SEND_THREAD_MESSAGE(cloned, PHPSE_CLONE, 0, (LPARAM)this TSRMLS_CC);
-               trace("%08x: Cloned OK, returning cloned object ptr %08x\n", this, cloned);     
-               *ppscript = (IActiveScript*)cloned;
-               return S_OK;
-
+       trace("*************  Clone this[%08x]\n", this);
+       
+       punk = create_scripting_engine(this);
+       
+       if (punk) {
+               ret = punk->QueryInterface(IID_IActiveScript, (void**)ppscript);
+               punk->Release();
        }
-               
-       return E_FAIL;
+
+       return ret;
 }
 
 
 STDMETHODIMP TPHPScriptingEngine::InitNew( void)
 {
        TSRMLS_FETCH();
-       SEND_THREAD_MESSAGE(this, PHPSE_INIT_NEW, 0, 0 TSRMLS_CC);
+       trace("InitNew() this=%08x\n", this);
+       if (m_done_init) {
+               if (tsrm_thread_id() != m_enginethread)
+                       return marshal_call(this, APHP_InitNew, 0);
+
+               zend_hash_destroy(&m_frags);
+               php_request_shutdown(NULL);
+               setup_engine_state();
+       }
        return S_OK;
 }
 
@@ -1765,22 +1577,102 @@ STDMETHODIMP TPHPScriptingEngine::AddScriptlet(
        /* [out] */ BSTR *pbstrName,
        /* [out] */ EXCEPINFO *pexcepinfo)
 {
-       struct php_active_script_add_scriptlet_info info;
+       HRESULT ret;
        TSRMLS_FETCH();
 
-       info.pstrDefaultName = pstrDefaultName;
-       info.pstrCode = pstrCode;
-       info.pstrItemName = pstrItemName;
-       info.pstrSubItemName = pstrSubItemName;
-       info.pstrEventName = pstrEventName;
-       info.pstrDelimiter = pstrDelimiter;
-       info.dwSourceContextCookie = dwSourceContextCookie;
-       info.ulStartingLineNumber = ulStartingLineNumber;
-       info.dwFlags = dwFlags;
-       info.pbstrName = pbstrName;
-       info.pexcepinfo = pexcepinfo;
-
-       return SEND_THREAD_MESSAGE(this, PHPSE_ADD_SCRIPTLET, 0, (LPARAM)&info TSRMLS_CC);
+       if (tsrm_thread_id() != m_enginethread)
+               return marshal_call(this, APHP_AddScriptlet, 11, pstrDefaultName, pstrCode, pstrItemName, pstrSubItemName, pstrEventName, pstrDelimiter, dwSourceContextCookie, ulStartingLineNumber, dwFlags, pbstrName, pexcepinfo);
+       
+       ENGINE_THREAD_ONLY(IActiveScriptParse, AddScriptlet);
+       
+trace("AddScriptlet\n");
+       
+       /* Parse/compile a chunk of script that will act as an event handler.
+        * If the host supports IActiveScriptParseProcedure, this code will
+        * not be called.
+        * The docs are (typically) vague: AFAICT, once the code has been
+        * compiled, we are supposed to arrange for an IConnectionPoint
+        * advisory connection to the item/subitem, once the script
+        * moves into SCRIPTSTATE_CONNECTED.
+        * That's a lot of work!
+        *
+        * FIXME: this is currently almost useless
+        * */
+
+       TWideString
+               default_name(pstrDefaultName),
+               code(pstrCode),
+               item_name(pstrItemName),
+               sub_item_name(pstrSubItemName),
+               event_name(pstrEventName),
+               delimiter(pstrDelimiter);
+
+       /* lets invent a function name for the scriptlet */
+       char sname[256];
+
+       /* should check if the name is already used! */
+       if (pstrDefaultName) 
+               strcpy(sname, default_name.ansi_string());
+       else {
+               sname[0] = 0;
+               strcat(sname, "__");
+               if (pstrItemName) {
+                       strcat(sname, item_name.ansi_string());
+                       strcat(sname, "_");
+               }
+               if (pstrSubItemName) {
+                       strcat(sname, sub_item_name.ansi_string());
+                       strcat(sname, "_");
+               }
+               if (pstrEventName)
+                       strcat(sname, event_name.ansi_string());
+       }
+
+
+       trace("%08x: AddScriptlet:\n state=%s\n name=%s\n code=%s\n item=%s\n subitem=%s\n event=%s\n delim=%s\n line=%d\n",
+                       this, scriptstate_to_string(m_scriptstate),
+                       default_name.safe_ansi_string(), code.safe_ansi_string(), item_name.safe_ansi_string(),
+                       sub_item_name.safe_ansi_string(), event_name.safe_ansi_string(), delimiter.safe_ansi_string(),
+                       ulStartingLineNumber);
+
+
+       code_frag *frag = compile_code_fragment(
+                       FRAG_SCRIPTLET,
+                       sname,
+                       pstrCode,
+                       ulStartingLineNumber,
+                       pexcepinfo,
+                       this
+                       TSRMLS_CC);
+
+       if (frag) {
+
+               frag->persistent = (dwFlags & SCRIPTTEXT_ISPERSISTENT);
+
+               zend_hash_next_index_insert(&m_frags, &frag, sizeof(code_frag*), NULL);
+
+               /*
+                       ScriptProcedureDispatch *disp = new ScriptProcedureDispatch;
+
+                       disp->AddRef();
+                       disp->m_frag = frag;
+                       disp->m_procflags = info->dwFlags;
+                       disp->m_engine = this;
+                       frag->ptr = disp;
+
+                *ppdisp = disp;
+                */
+               ret = S_OK;
+       } else {
+               ret = DISP_E_EXCEPTION;
+       }
+
+       *pbstrName = TWideString::bstr_from_ansi(sname);
+
+       trace("%08x: done with scriptlet %s\n", this, sname);
+
+       
+       return ret;
 }
 
 STDMETHODIMP TPHPScriptingEngine::ParseScriptText( 
@@ -1794,20 +1686,74 @@ STDMETHODIMP TPHPScriptingEngine::ParseScriptText(
        /* [out] */ VARIANT *pvarResult,
        /* [out] */ EXCEPINFO *pexcepinfo)
 {
-       struct php_active_script_parse_info info;
+trace("ParseScriptText\n");
+       int doexec;
+       TWideString
+               code(pstrCode),
+               item_name(pstrItemName),
+               delimiter(pstrDelimiter);
+       HRESULT ret;
        TSRMLS_FETCH();
 
-       info.pstrCode = pstrCode;
-       info.pstrItemName = pstrItemName;
-       info.punkContext = punkContext;
-       info.pstrDelimiter = pstrDelimiter;
-       info.dwSourceContextCookie = dwSourceContextCookie;
-       info.ulStartingLineNumber = ulStartingLineNumber;
-       info.dwFlags = dwFlags;
-       info.pvarResult = pvarResult;
-       info.pexcepinfo = pexcepinfo;
-
-       return SEND_THREAD_MESSAGE(this, PHPSE_PARSE_SCRIPT, 0, (LPARAM)&info TSRMLS_CC);
+       trace("pstrCode=%p pstrItemName=%p punkContext=%p pstrDelimiter=%p pvarResult=%p pexcepinfo=%p\n",
+                       pstrCode, pstrItemName, punkContext, pstrDelimiter, pvarResult, pexcepinfo);
+
+       if (tsrm_thread_id() != m_enginethread) {
+               return marshal_call(this, APHP_ParseScriptText, 9,
+                               pstrCode, pstrItemName, punkContext, pstrDelimiter,
+                               dwSourceContextCookie, ulStartingLineNumber,
+                               dwFlags, pvarResult, pexcepinfo);
+       }
+       
+       trace("%08x: ParseScriptText:\n state=%s\ncode=%.*s\n item=%s\n delim=%s\n line=%d\n",
+                       this, scriptstate_to_string(m_scriptstate),
+                       code.m_ansi_strlen,
+                       code.safe_ansi_string(), item_name.safe_ansi_string(), delimiter.safe_ansi_string(),
+                       ulStartingLineNumber);
+
+       code_frag *frag = compile_code_fragment(
+                       FRAG_MAIN,
+                       dwFlags & SCRIPTTEXT_ISEXPRESSION ? FRAG_CREATE_FUNC : NULL,
+                       pstrCode,
+                       ulStartingLineNumber,
+                       pexcepinfo,
+                       this
+                       TSRMLS_CC);
+
+       doexec = (dwFlags & SCRIPTTEXT_ISEXPRESSION) ||
+               m_scriptstate == SCRIPTSTATE_STARTED ||
+               m_scriptstate == SCRIPTSTATE_CONNECTED ||
+               m_scriptstate == SCRIPTSTATE_DISCONNECTED;
+
+       if (frag) {
+               frag->persistent = (dwFlags & SCRIPTTEXT_ISPERSISTENT);
+               ret = S_OK;
+
+               if (dwFlags & SCRIPTTEXT_ISEXPRESSION) {
+                       if (m_scriptstate == SCRIPTSTATE_INITIALIZED) {
+                               /* not allowed to execute code in this state */
+                               ret = E_UNEXPECTED;
+                               doexec = 0;
+                       }
+               }
+
+               if (doexec) {
+                       /* execute the code as an expression */
+                       if (!execute_code_fragment(frag, pvarResult, pexcepinfo TSRMLS_CC))
+                               ret = DISP_E_EXCEPTION;
+               }
+
+               zend_hash_next_index_insert(&m_frags, &frag, sizeof(code_frag*), NULL);
+       } else {
+               ret = DISP_E_EXCEPTION;
+       }
+
+       if (pvarResult) {
+               VariantInit(pvarResult);
+       }
+
+
+       return ret;
 }
 
 STDMETHODIMP TPHPScriptingEngine::ParseProcedureText( 
@@ -1822,37 +1768,69 @@ STDMETHODIMP TPHPScriptingEngine::ParseProcedureText(
                /* [in] */ DWORD dwFlags,
                /* [out] */ IDispatch **ppdisp)
 {
-       struct php_active_script_parse_proc_info info;
+trace("ParseProcedureText\n");
+       ENGINE_THREAD_ONLY(IActiveScriptParseProcedure, ParseProcedureText);
+       /* This is the IActiveScriptParseProcedure implementation.
+        * IE uses this to request for an IDispatch that it will invoke in
+        * response to some event, and tells us the code that it wants to
+        * run.
+        * We compile the code and pass it back a dispatch object.
+        * The object will then serialize access to the engine thread and
+        * execute the opcodes */
+       TWideString
+               formal_params(pstrFormalParams),
+               procedure_name(pstrProcedureName),
+               item_name(pstrItemName),
+               delimiter(pstrDelimiter);
        HRESULT ret;
        TSRMLS_FETCH();
 
-       info.pstrCode = pstrCode;
-       info.pstrFormalParams = pstrFormalParams;
-       info.pstrProcedureName = pstrProcedureName;
-       info.pstrItemName = pstrItemName;
-       info.punkContext = punkContext;
-       info.pstrDelimiter = pstrDelimiter;
-       info.dwSourceContextCookie = dwSourceContextCookie;
-       info.ulStartingLineNumber = ulStartingLineNumber;
-       info.dwFlags = dwFlags;
+       
+       trace("%08x: ParseProc:\n state=%s\nparams=%s\nproc=%s\nitem=%s\n delim=%s\n line=%d\n",
+                       this, scriptstate_to_string(m_scriptstate),
+                       formal_params.ansi_string(), procedure_name.ansi_string(),
+                       item_name.safe_ansi_string(), delimiter.safe_ansi_string(),
+                       ulStartingLineNumber);
+
+       code_frag *frag = compile_code_fragment(
+                       FRAG_PROCEDURE,
+                       NULL,
+                       pstrCode,
+                       ulStartingLineNumber,
+                       NULL,
+                       this
+                       TSRMLS_CC);
+
+       if (frag) {
+
+               frag->persistent = (dwFlags & SCRIPTTEXT_ISPERSISTENT);
+               zend_hash_next_index_insert(&m_frags, &frag, sizeof(code_frag*), NULL);
+
+               ScriptProcedureDispatch *disp = new ScriptProcedureDispatch;
+
+               disp->m_frag = frag;
+               disp->m_procflags = dwFlags;
+               disp->m_engine = this;
+               frag->ptr = disp;
+
+               *ppdisp = (IDispatch*)disp;
+       } else {
+               ret = DISP_E_EXCEPTION;
+       }
 
-       ret = SEND_THREAD_MESSAGE(this, PHPSE_PARSE_PROC, 0, (LPARAM)&info TSRMLS_CC);
 
-       if (ret == S_OK)
-               ret = GIT_get(info.dispcookie, IID_IDispatch, (void**)ppdisp);
-       
        trace("ParseProc: ret=%08x disp=%08x\n", ret, *ppdisp);
        return ret;
 }
 
-#if ACTIVEPHP_OBJECT_SAFETY
 STDMETHODIMP TPHPScriptingEngine::GetInterfaceSafetyOptions(
                /* [in]  */ REFIID      riid,                                           // Interface that we want options for
                /* [out] */ DWORD       *pdwSupportedOptions,           // Options meaningful on this interface
                /* [out] */ DWORD       *pdwEnabledOptions)                     // current option values on this interface
 {
        /* PHP isn't really safe for untrusted anything */
-       *pdwSupportedOptions = 0;
+       trace("GetInterfaceSafetyOptions called\n");
+       *pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_DATA|INTERFACESAFE_FOR_UNTRUSTED_CALLER;
        *pdwEnabledOptions = 0;
        return S_OK;
 }
@@ -1863,22 +1841,49 @@ STDMETHODIMP TPHPScriptingEngine::SetInterfaceSafetyOptions(
                /* [in]  */ DWORD               dwEnabledOptions)               // New option values
 {
        /* PHP isn't really safe for untrusted anything */
+       trace("SetInterfaceSafetyOptions mask=%08x enabled=%08x\n", dwOptionSetMask, dwEnabledOptions);
        if (dwEnabledOptions == 0) {
                return S_OK;
        }
+       trace("can't set those options; we're not safe enough\n");
        return E_FAIL;
 }
-#endif
-       
-extern "C" 
-void activescript_error_func(int type, const char *error_msg, ...)
+
+#if 0
+STDMETHODIMP TPHPScriptingEngine::GetUnmarshalClass(
+               REFIID riid, void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, CLSID *pCid)
 {
-       TSRMLS_FETCH();
+       return E_NOTIMPL;
+}
 
-       TPHPScriptingEngine *engine = (TPHPScriptingEngine*)SG(server_context);
+STDMETHODIMP TPHPScriptingEngine::GetMarshalSizeMax(
+               REFIID riid, void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, ULONG *pSize)
+{
+       return E_NOTIMPL;
+}
 
-       
+STDMETHODIMP TPHPScriptingEngine::MarshalInterface(
+               IStream *pStm, REFIID riid, void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshflags)
+{
+       return E_NOTIMPL;
+}
+
+STDMETHODIMP TPHPScriptingEngine::UnmarshalInterface(
+               IStream *pStm, REFIID riid, void **ppv)
+{
+       return E_NOTIMPL;
+}
+
+STDMETHODIMP TPHPScriptingEngine::ReleaseMarshalData(IStream *pStm)
+{
+       return E_NOTIMPL;
+}
+
+STDMETHODIMP TPHPScriptingEngine::DisconnectObject(DWORD dwReserved)
+{
+       return E_NOTIMPL;
 }
+#endif 
 
 class TActiveScriptError:
        public IActiveScriptError
@@ -1929,9 +1934,12 @@ public:
                        /* [out] */ ULONG *pulLineNumber,
                        /* [out] */ LONG *plCharacterPosition)
        {
-               *pdwSourceContext = 0;
-               *pulLineNumber = m_lineno;
-               *plCharacterPosition = 0;
+               if (pdwSourceContext)
+                       *pdwSourceContext = 0;
+               if (pulLineNumber)
+                       *pulLineNumber = m_lineno;
+               if (plCharacterPosition)
+                       *plCharacterPosition = 0;
                return S_OK;
        }
 
@@ -1971,10 +1979,19 @@ void activescript_error_handler(int type, const char *error_filename,
        int buflen;
        TPHPScriptingEngine *engine = (TPHPScriptingEngine*)SG(server_context);
        
-       buflen = vspprintf(&buf, PG(log_errors_max_len), format, args);
-       trace("%08x: Error: %s\n", engine, buf);
-
-       /* if it's a fatal error, report it using IActiveScriptError. */
+       /* ugly way to detect an exception for an eval'd fragment */
+       if (type == E_ERROR && EG(exception) && strstr("Exception thrown without a stack frame", format)) {
+               zend_exception_error(EG(exception) TSRMLS_CC);
+               /* NOTREACHED -- recursive E_ERROR */
+       } else {
+               buflen = vspprintf(&buf, PG(log_errors_max_len), format, args);
+               trace("%08x: PHP Error: %s\n", engine, buf);
+       }
+       
+       TActiveScriptError *eobj = new TActiveScriptError(error_filename, error_lineno, buf);
+       trace("raising error object!\n");
+       if (engine->m_pass)
+               engine->m_pass->OnScriptError(eobj);
        
        switch(type) {
                case E_ERROR:
@@ -1982,16 +1999,8 @@ void activescript_error_handler(int type, const char *error_filename,
                case E_COMPILE_ERROR:
                case E_USER_ERROR:
                case E_PARSE:
-                       /* trigger an error in the host */
-                       TActiveScriptError *eobj = new TActiveScriptError(error_filename, error_lineno, buf);
-                       trace("raising error object!\n");
-                       if (engine->m_pass_eng)
-                               engine->m_pass_eng->OnScriptError(eobj);
-
                        /* now throw the exception to abort execution */
-                       if (engine->m_err_trap)
-                               longjmp(*engine->m_err_trap, 1);
-                       
+                       zend_bailout(); 
                        break;
        }
        efree(buf);