]> granicus.if.org Git - php/commitdiff
Implement ActiveScript interfaces.
authorWez Furlong <wez@php.net>
Mon, 20 May 2002 01:35:29 +0000 (01:35 +0000)
committerWez Furlong <wez@php.net>
Mon, 20 May 2002 01:35:29 +0000 (01:35 +0000)
This allows use of PHP in:
  Client-side script in Internet Explorer
  Windows Scripting Host
  ASP and ASP.NET pages
It's mostly working... give it a go.
You will need to regsvr32 the php4activescript.dll manually.

sapi/activescript/classfactory.cpp [new file with mode: 0644]
sapi/activescript/php4activescript.c [new file with mode: 0644]
sapi/activescript/php4activescript.def [new file with mode: 0644]
sapi/activescript/php4activescript.dsp [new file with mode: 0644]
sapi/activescript/php4activescript.h [new file with mode: 0644]
sapi/activescript/php4as_classfactory.h [new file with mode: 0644]
sapi/activescript/php4as_scriptengine.h [new file with mode: 0644]
sapi/activescript/scriptengine.cpp [new file with mode: 0644]

diff --git a/sapi/activescript/classfactory.cpp b/sapi/activescript/classfactory.cpp
new file mode 100644 (file)
index 0000000..3d4f256
--- /dev/null
@@ -0,0 +1,361 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 4                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997-2002 The PHP Group                                |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 2.02 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available at through the world-wide-web at                           |
+   | http://www.php.net/license/2_02.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$ */
+
+/* IClassFactory Implementation, and DllXXX function implementation */
+
+#define INITGUID
+#define DEBUG_CLASS_FACTORY 0
+
+#include <windows.h>
+#include <initguid.h>
+
+extern "C" {
+HINSTANCE module_handle;
+}
+
+#include <comcat.h>
+#include "TSRM.h"
+#include "php4as_classfactory.h"
+#include "php4as_scriptengine.h"
+
+volatile LONG TPHPClassFactory::factory_count = 0;
+volatile LONG TPHPClassFactory::object_count = 0;
+
+/* {{{ Class Factory Implementation */
+TPHPClassFactory::TPHPClassFactory()
+{
+       m_refcount = 1;
+       InterlockedIncrement(&factory_count);
+}
+
+TPHPClassFactory::~TPHPClassFactory()
+{
+       InterlockedDecrement(&factory_count);
+}
+
+void TPHPClassFactory::AddToObjectCount(void)
+{
+       InterlockedIncrement(&object_count);
+}
+
+void TPHPClassFactory::RemoveFromObjectCount(void)
+{
+       InterlockedDecrement(&object_count);
+}
+
+STDMETHODIMP_(DWORD) TPHPClassFactory::AddRef(void)
+{
+       return InterlockedIncrement(&m_refcount);
+}
+
+STDMETHODIMP_(DWORD) TPHPClassFactory::Release(void)
+{
+       DWORD ret = InterlockedDecrement(&m_refcount);
+       if (ret == 0)
+               delete this;
+       return ret;
+}
+
+STDMETHODIMP TPHPClassFactory::QueryInterface(REFIID iid, void **ppvObject)
+{
+       *ppvObject = NULL;
+       
+       if (IsEqualGUID(IID_IClassFactory, iid)) {
+               *ppvObject = (IClassFactory*)this;
+       } else if (IsEqualGUID(IID_IUnknown, iid)) {
+               *ppvObject = this;      
+       }
+       if (*ppvObject) {
+               AddRef();
+               return S_OK;
+       }
+       
+       return E_NOINTERFACE;
+}
+
+STDMETHODIMP TPHPClassFactory::LockServer(BOOL fLock)
+{
+       return E_NOTIMPL;
+}
+
+STDMETHODIMP TPHPClassFactory::CreateInstance(IUnknown *pUnkOuter, REFIID iid, void **ppvObject)
+{
+       TPHPScriptingEngine *engine = new TPHPScriptingEngine;
+
+       HRESULT ret = engine->QueryInterface(iid, ppvObject);
+
+       engine->Release();
+       
+       return ret;
+}
+
+int TPHPClassFactory::CanUnload(void)
+{
+       return (object_count + factory_count) == 0 ? 1 : 0;
+}
+
+/* }}} */
+
+/* {{{ Registration structures */
+struct reg_entry {
+       HKEY root_key;
+       char *sub_key;
+       char *value_name;
+       char *data;
+};
+
+struct reg_class {
+       /* REFIID here causes compilation errors */
+       const GUID *class_id;
+       char *class_name;
+       char *threading;
+       const struct reg_entry *reg;
+       const GUID **cats;
+};
+
+#define MAX_REG_SUBST  8
+struct reg_subst {
+       int count;
+       char *names[MAX_REG_SUBST];
+       char *values[MAX_REG_SUBST];
+};
+/* }}} */
+
+/* {{{ Registration information */
+/* This registers the sapi as a scripting engine that can be instantiated using it's progid */
+static const struct reg_entry engine_entries[] = {
+       { HKEY_CLASSES_ROOT,    "CLSID\\[CLSID]",                                       NULL,                           "[CLASSNAME]" },
+       { HKEY_CLASSES_ROOT,    "CLSID\\[CLSID]\\InprocServer32",       NULL,                           "[MODULENAME]" },
+       { HKEY_CLASSES_ROOT,    "CLSID\\[CLSID]\\InprocServer32",       "ThreadingModel",       "[THREADING]" },
+       { HKEY_CLASSES_ROOT,    "CLSID\\[CLSID]\\OLEScript",            NULL,                           NULL },
+       { HKEY_CLASSES_ROOT,    "CLSID\\[CLSID]\\ProgID",                       NULL,                           "ActivePHP" },
+       { HKEY_CLASSES_ROOT,    "ActivePHP",                                            NULL,                           "ActivePHP" },
+       { HKEY_CLASSES_ROOT,    "ActivePHP\\CLSID",                                     NULL,                           "[CLSID]"},
+       { HKEY_CLASSES_ROOT,    "ActivePHP\\OLEScript",                         NULL,                           NULL},
+       
+       { 0, NULL, NULL, NULL }
+};
+
+static const GUID *script_engine_categories[] = {
+       &CATID_ActiveScript,
+       &CATID_ActiveScriptParse,
+       NULL
+};
+
+static const struct reg_class classes_to_register[] = {
+       { &CLSID_PHPActiveScriptEngine, "PHP Active Script Engine", "Both", engine_entries, script_engine_categories },
+       { NULL, NULL, NULL, 0, NULL }
+};
+/* }}} */
+
+/* {{{ Registration code */
+static void substitute(struct reg_subst *subst, char *srcstring, char *deststring)
+{
+       int i;
+       char *srcpos = srcstring;
+       char *destpos = deststring;
+       
+       while(1) {
+               char *repstart = strchr(srcpos, '[');
+
+               if (repstart == NULL) {
+                       strcpy(destpos, srcpos);
+                       break;
+               }
+
+               /* copy everything up until that character to the dest */
+               strncpy(destpos, srcpos, repstart - srcpos);
+               destpos += repstart - srcpos;
+               *destpos = 0;
+
+               /* search for replacement strings in the table */
+               for (i = 0; i < subst->count; i++) {
+                       int len = strlen(subst->names[i]);
+                       if (strncmp(subst->names[i], repstart + 1, len) == 0) {
+                               /* append the replacement value to the buffer */
+                               strcpy(destpos, subst->values[i]);
+                               destpos += strlen(subst->values[i]);
+                               srcpos = repstart + len + 2;
+
+                               break;
+                       }
+               }
+       }
+#if DEBUG_CLASS_FACTORY
+       MessageBox(0, deststring, srcstring, MB_ICONHAND);
+#endif
+}
+
+static int reg_string(BOOL do_reg, struct reg_subst *subst, const struct reg_entry *entry)
+{
+       char subbuf[MAX_PATH], valuebuf[MAX_PATH], databuf[MAX_PATH];
+       char *sub = NULL, *value = NULL, *data = NULL;
+       LRESULT res;
+       HKEY hkey;
+       DWORD disp;
+       
+       if (entry->sub_key) {
+               substitute(subst, entry->sub_key, subbuf);
+               sub = subbuf;
+       }
+
+       if (entry->value_name) {
+               substitute(subst, entry->value_name, valuebuf);
+               value = valuebuf;
+       }
+
+       if (entry->data) {
+               substitute(subst, entry->data, databuf);
+               data = databuf;
+       }
+
+       if (do_reg) {
+               res = RegCreateKeyEx(entry->root_key, sub, 0, NULL, REG_OPTION_NON_VOLATILE,
+                               KEY_WRITE, NULL, &hkey, &disp);
+               if (res == NOERROR) {
+                       if (data)
+                               res = RegSetValueEx(hkey, value, 0, REG_SZ, (LPBYTE)data, strlen(data) + 1);
+                       RegCloseKey(hkey);
+               }
+       } else {
+               res = RegDeleteKey(entry->root_key, sub);
+       }
+       return res == NOERROR ? 1 : 0;
+}
+
+static int perform_registration(BOOL do_reg)
+{
+       char module_name[MAX_PATH];
+       char classid[40];
+       int ret = 0;
+       int i, j;
+       struct reg_subst subst = {0};
+       LPOLESTR classidw;
+       ICatRegister *catreg = NULL;
+       CATID cats[8];
+       int ncats;
+
+       CoInitialize(NULL);
+       CoCreateInstance(CLSID_StdComponentCategoriesMgr, NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (LPVOID*)&catreg);
+
+       GetModuleFileName(module_handle, module_name, sizeof(module_name));
+               
+       subst.names[0] = "CLSID";
+       subst.values[0] = classid;
+
+       subst.names[1] = "MODULENAME";
+       subst.values[1] = module_name;
+
+       subst.names[2] = "CLASSNAME";
+       subst.names[3] = "THREADING";
+
+       subst.count = 4;
+
+       for (i = 0; classes_to_register[i].class_id; i++) {
+               StringFromCLSID(*classes_to_register[i].class_id, &classidw);
+               WideCharToMultiByte(CP_ACP, 0, classidw, -1, classid, sizeof(classid), NULL, NULL);
+               classid[39] = 0;
+               CoTaskMemFree(classidw);
+
+               subst.values[2] = classes_to_register[i].class_name;
+               subst.values[3] = classes_to_register[i].threading;
+
+               if (catreg && classes_to_register[i].cats) {
+                       ncats = 0;
+                       while(classes_to_register[i].cats[ncats]) {
+                               CopyMemory(&cats[ncats], classes_to_register[i].cats[ncats], sizeof(CATID));
+                               ncats++;
+                       }
+               }
+               
+               if (do_reg) {
+                       for (j = 0; classes_to_register[i].reg[j].root_key; j++) {
+                               if (!reg_string(do_reg, &subst, &classes_to_register[i].reg[j])) {
+                                       ret = 0;
+                                       break;
+                               }
+                       }
+                       
+                       if (catreg && ncats) {
+                               catreg->RegisterClassImplCategories(*classes_to_register[i].class_id, ncats, cats);
+                       }
+                       
+               } else {
+
+                       if (catreg && ncats) {
+                               catreg->UnRegisterClassImplCategories(*classes_to_register[i].class_id, ncats, cats);
+                       }
+
+                       /* do it in reverse order, so count the number of entries first */
+                       for (j = 0; classes_to_register[i].reg[j].root_key; j++)
+                               ;
+                       while(j-- > 0) {
+                               if (!reg_string(do_reg, &subst, &classes_to_register[i].reg[j])) {
+                                       ret = 0;
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       if (catreg)
+               catreg->Release();
+       CoUninitialize();
+       
+       return ret;
+}
+/* }}} */
+
+/* {{{ Standard COM server DllXXX entry points */
+
+STDAPI DllCanUnloadNow(void)
+{
+       if (TPHPClassFactory::CanUnload())
+               return S_OK;
+       return S_FALSE;
+}
+
+STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
+{
+       HRESULT ret = E_FAIL;
+
+       if (IsEqualCLSID(CLSID_PHPActiveScriptEngine, rclsid)) {
+               TPHPClassFactory *factory = new TPHPClassFactory();
+
+               if (factory) {
+                       ret = factory->QueryInterface(riid, ppv);
+                       factory->Release();
+               }
+       }
+       return ret;
+}
+
+
+
+STDAPI DllRegisterServer(void)
+{
+       return perform_registration(TRUE) ? S_OK : S_FALSE;
+}
+
+STDAPI DllUnregisterServer(void)
+{
+       return perform_registration(FALSE) ? S_OK : S_FALSE;
+}
+
+/* }}} */
diff --git a/sapi/activescript/php4activescript.c b/sapi/activescript/php4activescript.c
new file mode 100644 (file)
index 0000000..eed1d7e
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 4                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997-2002 The PHP Group                                |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 2.02 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available at through the world-wide-web at                           |
+   | http://www.php.net/license/2_02.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$ */
+
+#include "php.h"
+#include "php_main.h"
+#include "SAPI.h"
+#include "php_globals.h"
+#include "ext/standard/info.h"
+#include "php_variables.h"
+#include "php_ini.h"
+#include "php4activescript.h"
+
+/* SAPI definitions and DllMain */
+
+static int php_activescript_startup(sapi_module_struct *sapi_module)
+{
+       if (php_module_startup(sapi_module) == FAILURE ||
+                       zend_startup_module(&php_activescript_module) == FAILURE) {
+               return FAILURE;
+       } else {
+               return SUCCESS;
+       }
+}
+
+static int sapi_activescript_ub_write(const char *str, uint str_length TSRMLS_DC)
+{
+       /* In theory, this is a blackhole.  In practice, I wan't to see the output
+        * in the debugger! */
+
+       char buf[1024];
+       uint l, a = str_length;
+
+       while(a) {
+               l = a;
+               if (l > sizeof(buf) - 1)
+                       l = sizeof(buf) - 1;
+               memcpy(buf, str, l);
+               buf[l] = 0;
+               OutputDebugString(buf);
+               a -= l;
+       }
+
+       return str_length;
+}
+
+static void sapi_activescript_register_server_variables(zval *track_vars_array TSRMLS_DC)
+{
+}
+
+static char *sapi_activescript_read_cookies(TSRMLS_D)
+{
+       return NULL;
+}
+
+static int sapi_activescript_header_handler(sapi_header_struct *sapi_header, sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+       return SAPI_HEADER_ADD;
+}
+
+static int sapi_activescript_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+       return SAPI_HEADER_SENT_SUCCESSFULLY;
+}
+
+zend_module_entry php_activescript_module = {
+    STANDARD_MODULE_HEADER,
+       "ActiveScript",
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+    NULL,
+       STANDARD_MODULE_PROPERTIES
+};
+
+
+sapi_module_struct activescript_sapi_module = {
+       "activescript",                                         /* name */
+       "Active Script",                                        /* pretty name */
+                                                                       
+       php_activescript_startup,                               /* startup */
+       php_module_shutdown_wrapper,    /* shutdown */
+
+       NULL,                                                   /* activate */
+       NULL,                                                   /* deactivate */
+
+       sapi_activescript_ub_write,                     /* unbuffered write */
+       NULL,                                                   /* flush */
+       NULL,                                                   /* get uid */
+       NULL,                                                   /* getenv */
+
+       zend_error,                                             /* error handler */
+
+       sapi_activescript_header_handler,               /* header handler */
+       sapi_activescript_send_headers,         /* send headers handler */
+       NULL,                                                   /* send header handler */
+
+       NULL,                   /* read POST data */
+       sapi_activescript_read_cookies,         /* read Cookies */
+
+       sapi_activescript_register_server_variables,    /* register server variables */
+       NULL,                                                                   /* Log message */
+
+       NULL,                                                                   /* Block interruptions */
+       NULL,                                                                   /* Unblock interruptions */
+
+       STANDARD_SAPI_MODULE_PROPERTIES
+};
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+       switch (fdwReason) {
+               case DLL_PROCESS_ATTACH:
+                       module_handle = hinstDLL;
+
+                       tsrm_startup(128, 1, TSRM_ERROR_LEVEL_CORE, "C:\\TSRM.log");
+
+                       sapi_startup(&activescript_sapi_module);
+                       if (activescript_sapi_module.startup) {
+                               activescript_sapi_module.startup(&sapi_module);
+                       }
+                       break;
+               case DLL_THREAD_ATTACH:
+                       break;
+               case DLL_THREAD_DETACH:
+                       ts_free_thread();
+                       break;
+               case DLL_PROCESS_DETACH:
+                       if (activescript_sapi_module.shutdown) {
+                               activescript_sapi_module.shutdown(&sapi_module);
+                       }
+                       tsrm_shutdown();
+                       break;
+       }
+       return TRUE;
+}
+
+
diff --git a/sapi/activescript/php4activescript.def b/sapi/activescript/php4activescript.def
new file mode 100644 (file)
index 0000000..1f8e945
--- /dev/null
@@ -0,0 +1,5 @@
+EXPORTS
+DllCanUnloadNow @1 PRIVATE
+DllGetClassObject @2 PRIVATE
+DllRegisterServer @3 PRIVATE
+DllUnregisterServer @4 PRIVATE
diff --git a/sapi/activescript/php4activescript.dsp b/sapi/activescript/php4activescript.dsp
new file mode 100644 (file)
index 0000000..cbfafa1
--- /dev/null
@@ -0,0 +1,185 @@
+# Microsoft Developer Studio Project File - Name="php4activescript" - Package Owner=<4>\r
+# Microsoft Developer Studio Generated Build File, Format Version 6.00\r
+# ** DO NOT EDIT **\r
+\r
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102\r
+\r
+CFG=php4activescript - Win32 Debug_TS\r
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,\r
+!MESSAGE use the Export Makefile command and run\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "php4activescript.mak".\r
+!MESSAGE \r
+!MESSAGE You can specify a configuration when running NMAKE\r
+!MESSAGE by defining the macro CFG on the command line. For example:\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "php4activescript.mak" CFG="php4activescript - Win32 Debug_TS"\r
+!MESSAGE \r
+!MESSAGE Possible choices for configuration are:\r
+!MESSAGE \r
+!MESSAGE "php4activescript - Win32 Debug_TS" (based on "Win32 (x86) Dynamic-Link Library")\r
+!MESSAGE "php4activescript - Win32 Release_TS" (based on "Win32 (x86) Dynamic-Link Library")\r
+!MESSAGE "php4activescript - Win32 Release_TS_inline" (based on "Win32 (x86) Dynamic-Link Library")\r
+!MESSAGE "php4activescript - Win32 Release_TSDbg" (based on "Win32 (x86) Dynamic-Link Library")\r
+!MESSAGE \r
+\r
+# Begin Project\r
+# PROP AllowPerConfigDependencies 0\r
+# PROP Scc_ProjName ""\r
+# PROP Scc_LocalPath ""\r
+CPP=cl.exe\r
+MTL=midl.exe\r
+RSC=rc.exe\r
+\r
+!IF  "$(CFG)" == "php4activescript - Win32 Debug_TS"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 1\r
+# PROP BASE Output_Dir "Debug_TS"\r
+# PROP BASE Intermediate_Dir "Debug_TS"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 1\r
+# PROP Output_Dir "..\..\Debug_TS"\r
+# PROP Intermediate_Dir "Debug_TS"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PHP4ISAPI_EXPORTS" /YX /FD /GZ /c\r
+# ADD CPP /nologo /MDd /Ze /W3 /Gm /GX /ZI /Od /Ob0 /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\main" /I "..\..\TSRM" /D "_DEBUG" /D "COMPILE_LIBZEND" /D ZEND_DEBUG=1 /D "_WINDOWS" /D "_USRDLL" /D "PHP4ISAPI_EXPORTS" /D "MSVC5" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "WIN32" /D "_MBCS" /FR /YX /FD /GZ /c\r
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32\r
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32\r
+# ADD BASE RSC /l 0x40d /d "_DEBUG"\r
+# ADD RSC /l 0x40d /d "_DEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept\r
+# ADD LINK32 wsock32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib php4ts_debug.lib /nologo /version:4.0 /dll /debug /machine:I386 /nodefaultlib:"libcmt" /pdbtype:sept /libpath:"..\..\Debug_TS"\r
+\r
+!ELSEIF  "$(CFG)" == "php4activescript - Win32 Release_TS"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 0\r
+# PROP BASE Output_Dir "Release_TS"\r
+# PROP BASE Intermediate_Dir "Release_TS"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 0\r
+# PROP Output_Dir "..\..\Release_TS"\r
+# PROP Intermediate_Dir "Release_TS"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PHP4ISAPI_EXPORTS" /YX /FD /c\r
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\main" /I "..\..\TSRM" /D "NDEBUG" /D ZEND_DEBUG=0 /D "_WINDOWS" /D "_USRDLL" /D "PHP4ISAPI_EXPORTS" /D "MSVC5" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "WIN32" /D "_MBCS" /FR /YX /FD /c\r
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32\r
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32\r
+# ADD BASE RSC /l 0x40d /d "NDEBUG"\r
+# ADD RSC /l 0x40d /d "NDEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386\r
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib wsock32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib php4ts.lib /nologo /version:4.0 /dll /machine:I386 /libpath:"..\..\Release_TS"\r
+\r
+!ELSEIF  "$(CFG)" == "php4activescript - Win32 Release_TS_inline"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 0\r
+# PROP BASE Output_Dir "php4activescript___Win32_Release_TS_inline"\r
+# PROP BASE Intermediate_Dir "php4activescript___Win32_Release_TS_inline"\r
+# PROP BASE Ignore_Export_Lib 0\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 0\r
+# PROP Output_Dir "..\..\Release_TS_inline"\r
+# PROP Intermediate_Dir "Release_TS_inline"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /MD /W3 /GX /O2 /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /D "NDEBUG" /D "_WINDOWS" /D "_USRDLL" /D "PHP4ISAPI_EXPORTS" /D "MSVC5" /D "ZTS" /D "WIN32" /D "_MBCS" /D ZEND_DEBUG=0 /FR /YX /FD /c\r
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\main" /I "..\..\TSRM" /D "NDEBUG" /D ZEND_DEBUG=0 /D "ZEND_WIN32_FORCE_INLINE" /D "_WINDOWS" /D "_USRDLL" /D "PHP4ISAPI_EXPORTS" /D "MSVC5" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "WIN32" /D "_MBCS" /FR /YX /FD /c\r
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32\r
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32\r
+# ADD BASE RSC /l 0x40d /d "NDEBUG"\r
+# ADD RSC /l 0x40d /d "NDEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib wsock32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib php4ts.lib /nologo /dll /machine:I386 /libpath:"..\..\Release_TS"\r
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib wsock32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib php4ts.lib /nologo /version:4.0 /dll /machine:I386 /libpath:"..\..\Release_TS_inline"\r
+\r
+!ELSEIF  "$(CFG)" == "php4activescript - Win32 Release_TSDbg"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 0\r
+# PROP BASE Output_Dir "php4activescript___Win32_Release_TSDbg"\r
+# PROP BASE Intermediate_Dir "php4activescript___Win32_Release_TSDbg"\r
+# PROP BASE Ignore_Export_Lib 0\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 0\r
+# PROP Output_Dir "..\..\Release_TSDbg"\r
+# PROP Intermediate_Dir "Release_TSDbg"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /MD /W3 /GX /O2 /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\main" /I "..\..\TSRM" /D "NDEBUG" /D ZEND_DEBUG=0 /D "_WINDOWS" /D "_USRDLL" /D "PHP4ISAPI_EXPORTS" /D "MSVC5" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "WIN32" /D "_MBCS" /FR /YX /FD /c\r
+# ADD CPP /nologo /MD /W3 /GX /Zi /Od /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\main" /I "..\..\TSRM" /D "NDEBUG" /D ZEND_DEBUG=0 /D "_WINDOWS" /D "_USRDLL" /D "PHP4ISAPI_EXPORTS" /D "MSVC5" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "WIN32" /D "_MBCS" /FR /YX /FD /c\r
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32\r
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32\r
+# ADD BASE RSC /l 0x40d /d "NDEBUG"\r
+# ADD RSC /l 0x40d /d "NDEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib wsock32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib php4ts.lib /nologo /version:4.0 /dll /machine:I386 /libpath:"..\..\Release_TS"\r
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib wsock32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib php4ts.lib /nologo /version:4.0 /dll /debug /machine:I386 /libpath:"..\..\Release_TSDbg"\r
+\r
+!ENDIF \r
+\r
+# Begin Target\r
+\r
+# Name "php4activescript - Win32 Debug_TS"\r
+# Name "php4activescript - Win32 Release_TS"\r
+# Name "php4activescript - Win32 Release_TS_inline"\r
+# Name "php4activescript - Win32 Release_TSDbg"\r
+# Begin Group "Source Files"\r
+\r
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"\r
+# Begin Source File\r
+\r
+SOURCE=.\classfactory.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=.\php4activescript.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=.\php4activescript.def\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=.\scriptengine.cpp\r
+# End Source File\r
+# End Group\r
+# Begin Group "Header Files"\r
+\r
+# PROP Default_Filter "h;hpp;hxx;hm;inl"\r
+# Begin Source File\r
+\r
+SOURCE=.\php4activescript.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=.\php4as_classfactory.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=.\php4as_scriptengine.h\r
+# End Source File\r
+# End Group\r
+# End Target\r
+# End Project\r
diff --git a/sapi/activescript/php4activescript.h b/sapi/activescript/php4activescript.h
new file mode 100644 (file)
index 0000000..423958f
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 4                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997-2002 The PHP Group                                |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 2.02 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available at through the world-wide-web at                           |
+   | http://www.php.net/license/2_02.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$ */
+
+extern zend_module_entry php_activescript_module;
+extern sapi_module_struct activescript_sapi_module;
+extern HINSTANCE module_handle;
+extern void activescript_error_func(int type, const char *error_msg, ...);
+extern void activescript_error_handler(int type, const char *error_filename,
+               const uint error_lineno, const char *format, va_list args);
+
diff --git a/sapi/activescript/php4as_classfactory.h b/sapi/activescript/php4as_classfactory.h
new file mode 100644 (file)
index 0000000..bc4d530
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 4                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997-2002 The PHP Group                                |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 2.02 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available at through the world-wide-web at                           |
+   | http://www.php.net/license/2_02.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$ */
+
+/* IClassFactory Implementation */
+
+#include <unknwn.h>
+
+// {A0AD8E7A-95EC-4819-986F-78D93895F2AE}
+DEFINE_GUID(CLSID_PHPActiveScriptEngine, 
+0xa0ad8e7a, 0x95ec, 0x4819, 0x98, 0x6f, 0x78, 0xd9, 0x38, 0x95, 0xf2, 0xae);
+
+class TPHPClassFactory:
+       public IClassFactory
+{
+protected:
+       volatile LONG m_refcount;
+
+       static volatile LONG factory_count;
+       static volatile LONG object_count;
+       
+public: /* IUnknown */
+       STDMETHODIMP QueryInterface(REFIID iid, void **ppvObject);
+       STDMETHODIMP_(DWORD) AddRef(void);
+       STDMETHODIMP_(DWORD) Release(void);
+public: /* IClassFactory */
+       STDMETHODIMP CreateInstance(IUnknown *pUnkOuter, REFIID iid, void **ppvObject);
+       STDMETHODIMP LockServer(BOOL fLock);
+
+       TPHPClassFactory();
+       ~TPHPClassFactory();
+
+       static void AddToObjectCount(void);
+       static void RemoveFromObjectCount(void);
+       static int CanUnload(void);
+};
+
diff --git a/sapi/activescript/php4as_scriptengine.h b/sapi/activescript/php4as_scriptengine.h
new file mode 100644 (file)
index 0000000..1d2314b
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 4                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997-2002 The PHP Group                                |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 2.02 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available at through the world-wide-web at                           |
+   | http://www.php.net/license/2_02.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$ */
+
+#include <activscp.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
+};
+
+struct php_active_script_get_dispatch_info {
+       LPCOLESTR pstrItemName;
+       LPSTREAM dispatch;
+};
+
+struct php_active_script_add_named_item_info {
+       LPCOLESTR pstrName;
+       DWORD dwFlags;
+       IUnknown *punk;
+       ITypeInfo *ptyp;
+       IDispatch *pdisp;
+       LPSTREAM marshal;
+};
+
+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;
+};
+
+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;
+};
+
+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;
+       /* [out] */ IDispatch **ppdisp; 
+};
+
+struct php_active_script_add_tlb_info {
+       /* [in] */ const GUID * rguidTypeLib;
+       /* [in] */ DWORD dwMajor;
+       /* [in] */ DWORD dwMinor;
+       /* [in] */ DWORD dwFlags;
+};
+
+class TPHPScriptingEngine:
+       public IActiveScript,
+       public IActiveScriptParse,
+       public IActiveScriptParseProcedure
+{
+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;
+
+       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);
+       
+public: /* IUnknown */
+       STDMETHODIMP QueryInterface(REFIID iid, void **ppvObject);
+       STDMETHODIMP_(DWORD) AddRef(void);
+       STDMETHODIMP_(DWORD) Release(void);
+public: /* IActiveScript */
+       STDMETHODIMP SetScriptSite( 
+               /* [in] */ IActiveScriptSite *pass);
+       
+       STDMETHODIMP GetScriptSite( 
+               /* [in] */ REFIID riid,
+               /* [iid_is][out] */ void **ppvObject);
+       
+       STDMETHODIMP SetScriptState( 
+               /* [in] */ SCRIPTSTATE ss);
+       
+       STDMETHODIMP GetScriptState( 
+               /* [out] */ SCRIPTSTATE *pssState);
+       
+       STDMETHODIMP Close( void);
+       
+       STDMETHODIMP AddNamedItem( 
+               /* [in] */ LPCOLESTR pstrName,
+               /* [in] */ DWORD dwFlags);
+       
+       STDMETHODIMP AddTypeLib( 
+               /* [in] */ REFGUID rguidTypeLib,
+               /* [in] */ DWORD dwMajor,
+               /* [in] */ DWORD dwMinor,
+               /* [in] */ DWORD dwFlags);
+       
+       STDMETHODIMP GetScriptDispatch( 
+               /* [in] */ LPCOLESTR pstrItemName,
+               /* [out] */ IDispatch **ppdisp);
+       
+       STDMETHODIMP GetCurrentScriptThreadID( 
+               /* [out] */ SCRIPTTHREADID *pstidThread);
+       
+       STDMETHODIMP GetScriptThreadID( 
+               /* [in] */ DWORD dwWin32ThreadId,
+               /* [out] */ SCRIPTTHREADID *pstidThread);
+       
+       STDMETHODIMP GetScriptThreadState( 
+               /* [in] */ SCRIPTTHREADID stidThread,
+               /* [out] */ SCRIPTTHREADSTATE *pstsState);
+       
+       STDMETHODIMP InterruptScriptThread( 
+               /* [in] */ SCRIPTTHREADID stidThread,
+               /* [in] */ const EXCEPINFO *pexcepinfo,
+               /* [in] */ DWORD dwFlags);
+       
+       STDMETHODIMP Clone( 
+               /* [out] */ IActiveScript **ppscript);
+       
+public: /* IActiveScriptParse */
+       STDMETHODIMP InitNew( void);
+       
+       STDMETHODIMP AddScriptlet( 
+               /* [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);
+       
+       STDMETHODIMP ParseScriptText( 
+               /* [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);
+public: /* IActiveScriptParseProcedure */
+       STDMETHODIMP ParseProcedureText( 
+               /* [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,
+               /* [out] */ IDispatch **ppdisp);
+public:
+       TPHPScriptingEngine();
+       ~TPHPScriptingEngine();
+
+};
+
diff --git a/sapi/activescript/scriptengine.cpp b/sapi/activescript/scriptengine.cpp
new file mode 100644 (file)
index 0000000..b0dc40d
--- /dev/null
@@ -0,0 +1,1813 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 4                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997-2002 The PHP Group                                |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 2.02 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available at through the world-wide-web at                           |
+   | http://www.php.net/license/2_02.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$ */
+
+/* Implementation Notes:
+ *
+ * PHP stores scripting engine state in thread-local storage. That means
+ * that we need to create a dedicated thread per-engine so that a host can
+ * use more than one engine object per thread.
+ *
+ * There are some interesting synchronization issues: Anything to do with
+ * running script in the PHP/Zend engine must take place on the engine
+ * thread.  Likewise, calling back to the host must take place on the base
+ * thread - the thread that set the script site.
+ *
+ * For talking to the site from engine thread, we use an invisible window:
+ * the window processing is guaranteed to occur in the correct thread,
+ * and the message queue provides a useful synchronization device.
+ *
+ * For talking to the engine from any other thread, the engine thread waits
+ * for messages to arrive at it's message queue.  Since the only API for
+ * dealing with thread messages is asynchronous, we use a mutex to ensure
+ * that only one thread can talk to the engine at a time, and an event
+ * object to signal to it that the processing is complete.
+ *
+ * */
+
+#define _WIN32_DCOM
+
+#include "php.h"
+extern "C" {
+#include "php_main.h"
+#include "SAPI.h"
+#include "zend.h"
+#include "zend_execute.h"
+#include "zend_compile.h"
+#include "php_globals.h"
+#include "php_variables.h"
+#include "php_ini.h"
+#include "php4activescript.h"
+#include "ext/com/com.h"
+#include "ext/com/php_COM.h"
+#include "ext/com/conversion.h"
+}
+#include "php4as_scriptengine.h"
+#include "php4as_classfactory.h"
+#include <objbase.h>
+
+enum fragtype {
+       FRAG_MAIN,
+       FRAG_SCRIPTLET,
+       FRAG_PROCEDURE
+};
+
+typedef struct {
+       enum fragtype fragtype;
+       zend_op_array *opcodes;
+       char *code;
+       int persistent; /* should be retained for Clone */
+       int executed;   /* for "main" */
+       char *functionname;
+       unsigned int codelen;
+       unsigned int starting_line;
+       TPHPScriptingEngine *engine;
+       void *ptr;
+} code_frag;
+
+#define FRAG_CREATE_FUNC       (char*)-1
+static code_frag *compile_code_fragment(
+               enum fragtype fragtype,
+               char *functionname,
+               LPCOLESTR code,
+               ULONG starting_line,
+               EXCEPINFO *excepinfo,
+               TPHPScriptingEngine *engine
+               TSRMLS_DC);
+
+static int execute_code_fragment(code_frag *frag,
+               VARIANT *varResult,
+               EXCEPINFO *excepinfo
+               TSRMLS_DC);
+static void free_code_fragment(code_frag *frag);
+static code_frag *clone_code_fragment(code_frag *frag, TPHPScriptingEngine *engine TSRMLS_DC);
+
+/* 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));
+}
+       
+
+
+/* {{{ scriptstate_to_string */
+static const char *scriptstate_to_string(SCRIPTSTATE ss)
+{
+       switch(ss) {
+               case SCRIPTSTATE_UNINITIALIZED: return "SCRIPTSTATE_UNINITIALIZED";
+               case SCRIPTSTATE_INITIALIZED: return "SCRIPTSTATE_INITIALIZED";
+               case SCRIPTSTATE_STARTED: return "SCRIPTSTATE_STARTED";
+               case SCRIPTSTATE_CONNECTED: return "SCRIPTSTATE_CONNECTED";
+               case SCRIPTSTATE_DISCONNECTED: return "SCRIPTSTATE_DISCONNECTED";
+               case SCRIPTSTATE_CLOSED: return "SCRIPTSTATE_CLOSED";
+               default:
+                       return "unknown";
+       }
+}
+/* }}} */
+
+/* {{{ trace */
+static inline void trace(char *fmt, ...)
+{
+       va_list ap;
+       char buf[4096];
+
+       sprintf(buf, "T=%08x ", tsrm_thread_id());
+       OutputDebugString(buf);
+       
+       va_start(ap, fmt);
+       vsnprintf(buf, sizeof(buf), fmt, ap);
+
+       OutputDebugString(buf);
+       
+       va_end(ap);
+}
+/* }}} */
+
+/* {{{ TWideString */
+/* This class helps manipulate strings from OLE.
+ * It does not use emalloc, so it is better suited for passing pointers
+ * between threads. */
+class TWideString {
+       public:
+               LPOLESTR m_ole;
+               char *m_ansi;
+               int m_ansi_strlen;
+
+               TWideString(LPOLESTR olestr) {
+                       m_ole = olestr;
+                       m_ansi = NULL;
+               }
+               TWideString(LPCOLESTR olestr) {
+                       m_ole = (LPOLESTR)olestr;
+                       m_ansi = NULL;
+               }
+
+               ~TWideString() {
+                       if (m_ansi) {
+                               CoTaskMemFree(m_ansi);
+                       }
+                       m_ansi = NULL;
+               }
+
+               char *safe_ansi_string() {
+                       char *ret = ansi_string();
+                       if (ret == NULL)
+                               return "<NULL>";
+                       return ret;
+               }
+
+               int ansi_len(void) { return m_ansi_strlen; }
+
+               static BSTR bstr_from_ansi(char *ansi) {
+                       OLECHAR *ole = NULL;
+                       BSTR bstr = NULL;
+
+                       int req = MultiByteToWideChar(CP_ACP, 0, ansi, -1, NULL, 0);
+                       if (req) {
+                               ole = (OLECHAR*)CoTaskMemAlloc((req + 1) * sizeof(OLECHAR));
+                               if (ole) {
+                                       req = MultiByteToWideChar(CP_ACP, 0, ansi, -1, ole, req);
+                                       req--;
+                                       ole[req] = 0;
+
+                                       bstr = SysAllocString(ole);
+                                       CoTaskMemFree(ole);
+                               }
+                       }
+                       return bstr;
+               }
+
+               char *ansi_string(void)
+               {
+                       if (m_ansi)
+                               return m_ansi;
+
+                       if (m_ole == NULL)
+                               return NULL;
+
+                       int bufrequired = WideCharToMultiByte(CP_ACP, 0, m_ole, -1, NULL, 0, NULL, NULL);
+                       if (bufrequired) {
+
+                               m_ansi = (char*)CoTaskMemAlloc(bufrequired + 1);
+                               if (m_ansi) {
+                                       m_ansi_strlen = WideCharToMultiByte(CP_ACP, 0, m_ole, -1, m_ansi, bufrequired + 1, NULL, NULL);
+
+                                       if (m_ansi_strlen) {
+                                               m_ansi_strlen--;
+                                               m_ansi[m_ansi_strlen] = 0;
+
+                                       } else {
+                                               trace("conversion failed with return code %08x\n", GetLastError());     
+                                       }
+                               }
+                       }
+                       return m_ansi;
+               }
+};
+/* }}} */
+
+/* {{{ A generic stupid IDispatch implementation */
+class IDispatchImpl:
+       public IDispatch
+{
+protected:
+       volatile LONG m_refcount;
+public:
+       /* IUnknown */
+       STDMETHODIMP QueryInterface(REFIID iid, void **ppvObject) {
+               *ppvObject = NULL;
+
+               if (IsEqualGUID(IID_IDispatch, iid)) {
+                       *ppvObject = (IDispatch*)this;
+               } else if (IsEqualGUID(IID_IUnknown, iid)) {
+                       *ppvObject = this;      
+               }
+               if (*ppvObject) {
+                       AddRef();
+                       return S_OK;
+               }
+               return E_NOINTERFACE;
+       }
+       
+       STDMETHODIMP_(DWORD) AddRef(void) {
+               return InterlockedIncrement(&m_refcount);
+       }
+       
+       STDMETHODIMP_(DWORD) Release(void) {
+               DWORD ret = InterlockedDecrement(&m_refcount);
+               trace("%08x: IDispatchImpl: release ref count is now %d\n", this, ret);
+               if (ret == 0)
+                       delete this;
+               return ret;
+       }
+       /* IDispatch */
+       STDMETHODIMP GetTypeInfoCount(unsigned int *  pctinfo) {
+               *pctinfo = 0;
+               trace("%08x: IDispatchImpl: GetTypeInfoCount\n", this);
+               return S_OK;
+       }
+       STDMETHODIMP GetTypeInfo( unsigned int iTInfo, LCID lcid, ITypeInfo **ppTInfo) {
+               trace("%08x: IDispatchImpl: GetTypeInfo\n", this);
+               return DISP_E_BADINDEX;
+       }
+       STDMETHODIMP GetIDsOfNames( REFIID  riid, OLECHAR **rgszNames, unsigned int cNames, LCID lcid, DISPID *rgDispId)
+       {
+               unsigned int i;
+               trace("%08x: IDispatchImpl: GetIDsOfNames: \n", this);
+               for (i = 0; i < cNames; i++) {
+                       TWideString name(rgszNames[i]);
+                       trace("      %s\n", name.ansi_string());
+               }
+               trace("----\n");
+               return DISP_E_UNKNOWNNAME;
+       }
+       STDMETHODIMP Invoke( DISPID  dispIdMember, REFIID  riid, LCID  lcid, WORD  wFlags,
+               DISPPARAMS FAR*  pDispParams, VARIANT FAR*  pVarResult, EXCEPINFO FAR*  pExcepInfo,
+               unsigned int FAR*  puArgErr)
+       {
+               trace("%08x: IDispatchImpl: Invoke dispid %08x\n", this, dispIdMember);
+               return S_OK;
+       }
+
+
+       IDispatchImpl() {
+               m_refcount = 1;
+       }
+
+       virtual ~IDispatchImpl() {
+       }
+};
+/* }}} */
+
+/* {{{ 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
+{
+public:
+       ScriptDispatch() {
+               m_refcount = 1;
+       }
+};
+/* }}} */
+
+/* {{{ This object is used in conjunction with IActiveScriptParseProcedure to
+ * allow scriptlets to be bound to events.  IE uses this for declaring
+ * event handlers such as onclick="...".
+ * The compiled code is stored in this object; IE will call
+ * IDispatch::Invoke when the element is clicked.
+ * */
+class ScriptProcedureDispatch:
+       public IDispatchImpl
+{
+public:
+       code_frag               *m_frag;
+       DWORD                   m_procflags;
+       TPHPScriptingEngine *m_engine;
+
+       STDMETHODIMP Invoke( DISPID  dispIdMember, REFIID  riid, LCID  lcid, WORD  wFlags,
+               DISPPARAMS FAR*  pDispParams, VARIANT FAR*  pVarResult, EXCEPINFO FAR*  pExcepInfo,
+               unsigned int FAR*  puArgErr)
+       {
+               TSRMLS_FETCH();
+
+               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);
+               }
+               return S_OK;
+       }
+       ScriptProcedureDispatch() {
+               m_refcount = 1;
+       }
+};
+/* }}} */
+
+/* {{{ code fragment management */
+static code_frag *compile_code_fragment(
+               enum fragtype fragtype,
+               char *functionname,
+               LPCOLESTR code,
+               ULONG starting_line,
+               EXCEPINFO *excepinfo,
+               TPHPScriptingEngine *engine
+               TSRMLS_DC)
+{
+       zval pv;
+       int code_offs = 0;
+       char namebuf[256];
+       
+       code_frag *frag = (code_frag*)CoTaskMemAlloc(sizeof(code_frag));
+       memset(frag, 0, sizeof(code_frag));
+
+       frag->engine = engine;
+       
+       /* handle the function name */
+       if (functionname) {
+               int namelen;
+               if (functionname == FRAG_CREATE_FUNC) {
+                       ULONG n = ++engine->m_lambda_count;
+
+                       sprintf(namebuf, "__frag_%08x_%u", engine, n);
+                       functionname = namebuf; 
+               }
+
+               namelen = strlen(functionname);
+               code_offs = namelen + sizeof("function (){");
+
+               frag->functionname = (char*)CoTaskMemAlloc((namelen + 1) * sizeof(char));
+               memcpy(frag->functionname, functionname, namelen+1);
+       }
+
+       frag->functionname = functionname;
+       
+trace("%08x: COMPILED FRAG\n", frag);
+       
+       frag->codelen = WideCharToMultiByte(CP_ACP, 0, code, -1, NULL, 0, NULL, NULL);
+       frag->code = (char*)CoTaskMemAlloc(sizeof(char) * (frag->codelen + code_offs + 1));
+
+       if (functionname) {
+               sprintf(frag->code, "function %s(){ ", functionname);
+       }
+       
+       frag->codelen = WideCharToMultiByte(CP_ACP, 0, code, -1, frag->code + code_offs, frag->codelen, NULL, NULL) - 1;
+       
+       if (functionname) {
+               frag->codelen += code_offs + 1;
+               frag->code[frag->codelen-1] = '}';
+               frag->code[frag->codelen] = 0;
+       }
+
+trace("code to compile is:\ncode_offs=%d  func=%s\n%s\n", code_offs, functionname, frag->code);        
+       
+       frag->fragtype = fragtype;
+       frag->starting_line = starting_line;
+       
+       pv.type = IS_STRING;
+       pv.value.str.val = frag->code;
+       pv.value.str.len = frag->codelen;
+
+       frag->opcodes = compile_string(&pv, "fragment" TSRMLS_CC);
+
+       if (frag->opcodes == NULL) {
+               free_code_fragment(frag);
+
+               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 frag;
+}
+
+static void free_code_fragment(code_frag *frag)
+{
+       switch(frag->fragtype) {
+               case FRAG_PROCEDURE:
+                       if (frag->ptr) {
+                               ScriptProcedureDispatch *disp = (ScriptProcedureDispatch*)frag->ptr;
+                               disp->Release();
+                               CoDisconnectObject((IUnknown*)disp, 0);
+                               frag->ptr = NULL;
+                       }
+                       break;
+       }
+       
+       if (frag->opcodes)
+               destroy_op_array(frag->opcodes);
+       if (frag->functionname)
+               CoTaskMemFree(frag->functionname);
+       CoTaskMemFree(frag->code);
+       CoTaskMemFree(frag);
+}
+
+static code_frag *clone_code_fragment(code_frag *frag, TPHPScriptingEngine *engine TSRMLS_DC)
+{
+       zval pv;
+       code_frag *newfrag = (code_frag*)CoTaskMemAlloc(sizeof(code_frag));
+       memset(newfrag, 0, sizeof(code_frag));
+
+       newfrag->engine = engine;
+trace("%08x: CLONED FRAG\n", newfrag);
+
+       newfrag->persistent = frag->persistent;
+       newfrag->codelen = frag->codelen;
+       newfrag->code = (char*)CoTaskMemAlloc(sizeof(char) * frag->codelen + 1);
+       memcpy(newfrag->code, frag->code, frag->codelen + 1);
+
+       if (frag->functionname) {
+               int namelen = strlen(frag->functionname);
+               newfrag->functionname = (char*)CoTaskMemAlloc(sizeof(char) * (namelen + 1));
+               memcpy(newfrag->functionname, frag->functionname, namelen+1);
+       } else {
+               newfrag->functionname = NULL;
+       }
+       
+       newfrag->fragtype = frag->fragtype;
+       newfrag->starting_line = frag->starting_line;
+       
+       pv.type = IS_STRING;
+       pv.value.str.val = newfrag->code;
+       pv.value.str.len = newfrag->codelen;
+
+       newfrag->opcodes = compile_string(&pv, "fragment" TSRMLS_CC);
+
+       if (newfrag->opcodes == NULL) {
+               free_code_fragment(newfrag);
+/*
+               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,
+               VARIANT *varResult,
+               EXCEPINFO *excepinfo
+               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) {
+               trace("*** Executing code in thread %08x\n", tsrm_thread_id());
+
+               if (frag->functionname) {
+
+                       zval fname;
+
+                       fname.type = IS_STRING;
+                       fname.value.str.val = frag->functionname;
+                       fname.value.str.len = strlen(frag->functionname);
+
+                       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;
+               }
+       } else {
+               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;
+
+       if (frag->fragtype == FRAG_MAIN)
+               frag->executed = 1;
+
+       if (varResult)
+               VariantInit(varResult);
+       
+       if (retval_ptr) {
+               if (varResult)
+                       php_pval_to_variant(retval_ptr, varResult, CP_ACP TSRMLS_CC);
+               zval_ptr_dtor(&retval_ptr);
+       }
+
+       return 1;
+}
+
+static void frag_dtor(void *pDest)
+{
+       code_frag *frag = *(code_frag**)pDest;
+       free_code_fragment(frag);
+}
+/* }}} */
+
+/* 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);
+       CloseHandle(m_engine_thread_handle);
+}
+
+/* Synchronize with the engine thread */
+HRESULT TPHPScriptingEngine::SendThreadMessage(LONG msg, WPARAM wparam, LPARAM lparam)
+{
+       HRESULT ret;
+
+       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"); 
+                               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;
+}
+
+TPHPScriptingEngine::~TPHPScriptingEngine()
+{
+       trace("\n\n *** Engine Destructor Called\n\n");
+       if (m_scriptstate != SCRIPTSTATE_UNINITIALIZED && m_scriptstate != SCRIPTSTATE_CLOSED && m_enginethread)
+               Close();
+
+       PostThreadMessage(m_enginethread, WM_QUIT, 0, 0);
+
+       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)
+{
+       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;
+}
+       
+HRESULT TPHPScriptingEngine::engine_thread_handler(LONG msg, WPARAM wparam, LPARAM lParam, int *handled TSRMLS_DC)
+{
+       HRESULT ret = S_OK;
+
+       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;
+       
+       if (m_enginethread == 0)
+               return E_UNEXPECTED;
+       
+       switch(msg) {
+               case PHPSE_ADD_TYPELIB:
+                       {
+                               struct php_active_script_add_tlb_info *info = (struct php_active_script_add_tlb_info*)lParam;
+                               ITypeLib *TypeLib;
+
+                               if (SUCCEEDED(LoadRegTypeLib(*info->rguidTypeLib, (USHORT)info->dwMajor,
+                                                               (USHORT)info->dwMinor, LANG_NEUTRAL, &TypeLib))) {
+                                       php_COM_load_typelib(TypeLib, CONST_CS 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);
+                               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;
+
+                       }
+                       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;
+                               }
+                               zend_hash_destroy(&m_frags);
+                               php_request_shutdown(NULL);
+
+                               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;
+                               char *itemname;
+                               unsigned int itemlen;
+
+                               if (info->pstrItemName != NULL) {
+                                       zval **tmp;
+
+                                       itemname = php_OLECHAR_to_char((OLECHAR*)info->pstrItemName, &itemlen, CP_ACP TSRMLS_CC);
+
+                                       /* 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, itemlen+1, (void**)&tmp) == SUCCESS) {
+                                               if (Z_TYPE_PP(tmp) == IS_OBJECT) {
+                                                       disp = php_COM_export_object(*tmp TSRMLS_CC);
+                                               }
+                                       }
+                                       trace("%08x: GetScriptDispatch(%s --> %08x)\n", this, itemname, disp);
+
+                                       efree(itemname);
+
+                               } else {
+#if 0
+                                       zval *obj;
+
+                                       MAKE_STD_ZVAL(obj);
+                                       object_init(obj);
+                                       disp = php_COM_export_object(obj TSRMLS_CC);
+#else
+
+                                       disp = (IDispatch*) new ScriptDispatch;
+#endif
+                                       trace("%08x: GetScriptDispatch(NULL --> %08x)\n", this, disp);
+                               }
+
+                               if (disp) {
+                                       trace("--- Marshaling to stream\n");
+                                       ret = CoMarshalInterThreadInterfaceInStream(IID_IDispatch, disp, &info->dispatch);
+                                       disp->Release();
+                               } else {
+                                       ret = S_FALSE;
+                               }
+                       }
+                       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;
+                               
+                               if (SUCCEEDED(CoGetInterfaceAndReleaseStream(info->marshal, IID_IDispatch, (void**)&disp))) 
+                                       add_to_global_namespace(disp, info->dwFlags, name.ansi_string() TSRMLS_CC);
+
+                       }
+                       break;
+               case PHPSE_SET_SITE:
+                       {
+                               LPSTREAM stream = (LPSTREAM)lParam;
+
+                               if (m_pass_eng) {
+                                       m_pass_eng->Release();
+                                       m_pass_eng = NULL;
+                               }
+
+                               if (stream)
+                                       CoGetInterfaceAndReleaseStream(stream, IID_IActiveScriptSite, (void**)&m_pass_eng);
+
+                               trace("%08x: site (engine-side) is now %08x (base=%08x)\n", this, m_pass_eng, m_pass);
+
+                       }
+                       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
+                                       code(info->pstrCode),
+                               formal_params(info->pstrFormalParams),
+                               procedure_name(info->pstrProcedureName),
+                               item_name(info->pstrItemName),
+                               delimiter(info->pstrDelimiter);
+
+                               trace("%08x: ParseProc:\n state=%s\ncode=%s\n params=%s\nproc=%s\nitem=%s\n delim=%s\n line=%d\n",
+                                               this, scriptstate_to_string(m_scriptstate),
+                                               code.safe_ansi_string(), 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->AddRef();
+                                       disp->m_frag = frag;
+                                       disp->m_procflags = info->dwFlags;
+                                       disp->m_engine = this;
+                                       frag->ptr = disp;
+
+                                       *info->ppdisp = disp;
+                                       /*
+                                       ret = CoMarshalInterThreadInterfaceInStream(IID_IDispatch, disp, &info->disp);
+                                       disp->Release();
+                                       */
+                               } else {
+                                       ret = DISP_E_EXCEPTION;
+                               }
+
+                       }
+                       break;
+
+               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;
+
+                                       if (info->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, info->pvarResult, info->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 (info->pvarResult) {
+                                       VariantInit(info->pvarResult);
+                               }
+
+
+                       }
+                       break;
+               default:
+                       trace("unhandled message type %08x\n", msg);
+                       if (handled)
+                               *handled = 0;
+       }
+       return ret;
+}
+
+/* The PHP/Zend state actually lives in this thread */
+void TPHPScriptingEngine::engine_thread_func(void)
+{
+       TSRMLS_FETCH();
+       int handled;
+       int terminated = 0;
+       MSG msg;
+
+       trace("%08x: engine thread started up!\n", this);
+
+       CoInitializeEx(0, COINIT_MULTITHREADED);
+       
+       m_tsrm_hack = tsrm_ls;
+
+       while(!terminated) {
+               DWORD result = MsgWaitForMultipleObjects(0, NULL, FALSE, 4000, QS_ALLINPUT);
+
+               switch(result) {
+                       case WAIT_OBJECT_0:
+                               while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+
+                                       if (msg.message == WM_QUIT) {
+                                               terminated = 1;
+                                       } 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);
+                                       }
+
+                               }
+                               break;
+                       case WAIT_TIMEOUT:
+                               trace("thread wait timed out\n");
+                               break;
+                       default:
+                               trace("some strange value\n");
+               }
+       }
+       
+#if 0
+       while(GetMessage(&msg, NULL, 0, 0)) {
+
+               if (msg.message == WM_QUIT)
+                       break;
+               
+               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("%08x: engine thread exiting!!!!!\n", this);
+#endif
+       m_enginethread = 0;
+
+       CoUninitialize();
+}
+
+/* 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.
+ * */
+void TPHPScriptingEngine::add_to_global_namespace(IDispatch *disp, DWORD flags, char *name TSRMLS_DC)
+{
+       zval *val;
+       ITypeInfo *typ;
+       int i;
+       unsigned int namelen;
+       FUNCDESC *func;
+       BSTR funcname;
+       TYPEATTR *attr;
+       DISPPARAMS dispparams;
+       VARIANT vres;
+       ITypeInfo *rettyp;
+       TYPEATTR *retattr;
+
+trace("Add %s to global namespace\n", name);
+       
+       val = php_COM_object_from_dispatch(disp, NULL TSRMLS_CC);
+       
+       if (val == NULL) {
+               disp->Release();
+               return;
+       }
+
+       ZEND_SET_SYMBOL(&EG(symbol_table), name, val);
+
+       if (flags & SCRIPTITEM_GLOBALMEMBERS == 0) {
+               disp->Release();
+               return;
+       }
+
+       /* Enumerate properties and add those too */
+       if (FAILED(disp->GetTypeInfo(0, 0, &typ))) {
+               disp->Release();
+               return;
+       }
+
+       if (SUCCEEDED(typ->GetTypeAttr(&attr))) {
+               for (i = 0; i < attr->cFuncs; i++) {
+                       if (FAILED(typ->GetFuncDesc(i, &func)))
+                               continue;
+
+                       /* Look at it's type */
+                       if (func->invkind == INVOKE_PROPERTYGET
+                                       && VT_PTR == func->elemdescFunc.tdesc.vt
+                                       && VT_USERDEFINED == func->elemdescFunc.tdesc.lptdesc->vt
+                                       && SUCCEEDED(typ->GetRefTypeInfo(func->elemdescFunc.tdesc.lptdesc->hreftype, &rettyp)))
+                       {
+                               if (SUCCEEDED(rettyp->GetTypeAttr(&retattr))) {
+                                       if (retattr->typekind == TKIND_DISPATCH) {
+                                               /* It's dispatchable */
+
+                                               /* get the value */
+                                               dispparams.cArgs = 0;
+                                               dispparams.cNamedArgs = 0;
+                                               VariantInit(&vres);
+
+                                               if (SUCCEEDED(disp->Invoke(func->memid, IID_NULL, 0, func->invkind,
+                                                                               &dispparams, &vres, NULL, NULL))) {
+
+                                                       /* Get it's dispatch */
+                                                       IDispatch *sub = NULL;
+
+                                                       if (V_VT(&vres) == VT_UNKNOWN)
+                                                               V_UNKNOWN(&vres)->QueryInterface(IID_IDispatch, (void**)&sub);
+                                                       else if (V_VT(&vres) == VT_DISPATCH)
+                                                               sub = V_DISPATCH(&vres);
+
+                                                       if (sub) {
+                                                               /* find out it's name */
+                                                               typ->GetDocumentation(func->memid, &funcname, NULL, NULL, NULL);
+                                                               name = php_OLECHAR_to_char(funcname, &namelen, CP_ACP TSRMLS_CC);
+
+                                                               /* add to namespace */
+                                                               zval *subval = php_COM_object_from_dispatch(sub, NULL TSRMLS_CC);
+                                                               if (subval) {
+                                                                       ZEND_SET_SYMBOL(&EG(symbol_table), name, subval);       
+                                                               }
+
+                                                               efree(name);
+                                                               SysFreeString(funcname);
+                                                       }
+                                                       VariantClear(&vres);
+                                               }
+                                       }
+                                       rettyp->ReleaseTypeAttr(retattr);
+                               }
+                               rettyp->Release();
+                       }
+                       typ->ReleaseFuncDesc(func);
+               }
+               typ->ReleaseTypeAttr(attr);
+       }
+       disp->Release();
+}
+
+STDMETHODIMP_(DWORD) TPHPScriptingEngine::AddRef(void)
+{
+       return InterlockedIncrement(&m_refcount);
+}
+
+STDMETHODIMP_(DWORD) TPHPScriptingEngine::Release(void)
+{
+       DWORD ret = InterlockedDecrement(&m_refcount);
+       if (ret == 0) {
+               trace("%08x: Release: zero refcount, destroy the engine!\n", this);
+               delete this;
+       }
+       return ret;
+}
+
+STDMETHODIMP TPHPScriptingEngine::QueryInterface(REFIID iid, void **ppvObject)
+{
+       *ppvObject = NULL;
+       
+       if (IsEqualGUID(IID_IActiveScript, iid)) {
+               *ppvObject = (IActiveScript*)this;
+       } else if (IsEqualGUID(IID_IActiveScriptParse32, iid)) {
+               *ppvObject = (IActiveScriptParse32*)this;
+       } else if (IsEqualGUID(IID_IActiveScriptParseProcedure32, iid)) {
+               *ppvObject = (IActiveScriptParseProcedure*)this;
+       } else if (IsEqualGUID(IID_IUnknown, iid)) {
+               *ppvObject = this;      
+       } else {
+               LPOLESTR guidw;
+               StringFromCLSID(iid, &guidw);
+               {
+                       TWideString guid(guidw);
+                       trace("%08x: QueryInterface for unsupported %s\n", this, guid.ansi_string());
+               }
+               CoTaskMemFree(guidw);
+       }
+       if (*ppvObject) {
+               AddRef();
+               return S_OK;
+       }
+       
+       return E_NOINTERFACE;
+}
+
+/* This is called by the host to set the scrite site.
+ * It also defines the base thread. */
+STDMETHODIMP TPHPScriptingEngine::SetScriptSite(IActiveScriptSite *pass)
+{
+       TSRMLS_FETCH();
+
+       tsrm_mutex_lock(m_mutex);
+
+       trace("%08x: -----> Base thread is %08x\n", this, tsrm_thread_id());
+       
+       if (m_pass) {
+               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 = pass;
+       if (m_pass) {
+               m_pass->AddRef();
+
+               LPSTREAM stream;
+               if (SUCCEEDED(CoMarshalInterThreadInterfaceInStream(IID_IActiveScriptSite, m_pass, &stream)))
+                       SEND_THREAD_MESSAGE(this, PHPSE_SET_SITE, 0, (LPARAM)stream TSRMLS_CC);
+               
+               if (m_scriptstate == SCRIPTSTATE_UNINITIALIZED)
+                       SEND_THREAD_MESSAGE(this, PHPSE_STATE_CHANGE, 0, SCRIPTSTATE_INITIALIZED TSRMLS_CC);
+       }
+
+       tsrm_mutex_unlock(m_mutex);
+       return S_OK;
+}
+
+STDMETHODIMP TPHPScriptingEngine::GetScriptSite(REFIID riid, void **ppvObject)
+{
+       HRESULT ret = S_FALSE;
+       
+       trace("%08x: GetScriptSite()\n", this);
+       tsrm_mutex_lock(m_mutex);
+
+
+       if (m_pass)
+               ret = m_pass->QueryInterface(riid, ppvObject);
+
+       tsrm_mutex_unlock(m_mutex);
+       return ret;
+}
+
+STDMETHODIMP TPHPScriptingEngine::SetScriptState(SCRIPTSTATE ss)
+{
+       TSRMLS_FETCH();
+       trace("%08x: SetScriptState(%s)\n", this, scriptstate_to_string(ss));
+       return SEND_THREAD_MESSAGE(this, PHPSE_STATE_CHANGE, 0, ss TSRMLS_CC);
+}
+
+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();
+
+       if (m_pass) {
+               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. */
+STDMETHODIMP TPHPScriptingEngine::AddNamedItem(LPCOLESTR pstrName, DWORD dwFlags)
+{
+       struct php_active_script_add_named_item_info info;
+       TSRMLS_FETCH();
+
+       info.pstrName = pstrName;
+       info.dwFlags = dwFlags;
+
+       m_pass->GetItemInfo(pstrName, SCRIPTINFO_IUNKNOWN, &info.punk, NULL);
+       if (SUCCEEDED(CoMarshalInterThreadInterfaceInStream(IID_IDispatch, info.punk, &info.marshal))) {
+               SEND_THREAD_MESSAGE(this, PHPSE_ADD_NAMED_ITEM, 0, (LPARAM)&info TSRMLS_CC);
+       }
+       info.punk->Release();
+       
+       return S_OK;
+}
+
+/* Bind to a type library */
+STDMETHODIMP TPHPScriptingEngine::AddTypeLib( 
+       /* [in] */ REFGUID rguidTypeLib,
+       /* [in] */ DWORD dwMajor,
+       /* [in] */ DWORD dwMinor,
+       /* [in] */ DWORD dwFlags)
+{
+       struct php_active_script_add_tlb_info info;
+       TSRMLS_FETCH();
+
+       info.rguidTypeLib = &rguidTypeLib;
+       info.dwMajor = dwMajor;
+       info.dwMinor = dwMinor;
+       info.dwFlags = dwFlags;
+
+       SEND_THREAD_MESSAGE(this, PHPSE_ADD_TYPELIB, 0, (LPARAM)&info TSRMLS_CC);
+
+       return S_OK;
+}
+
+/* Returns an object representing the PHP Scripting Engine.
+ * Optionally, a client can request a particular item directly.
+ * For the moment, we only do the bare minimum amount of work
+ * for the engine to work correctly; we can flesh out this part
+ * a little later. */
+STDMETHODIMP TPHPScriptingEngine::GetScriptDispatch( 
+       /* [in] */ LPCOLESTR pstrItemName,
+       /* [out] */ IDispatch **ppdisp)
+{
+       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 it's 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. */
+
+       if (tsrm_thread_id() != m_enginethread) {
+               tsrm_ls = m_tsrm_hack;
+               trace("HEY: hacking thread safety!\n");
+       }
+
+       if (S_OK == engine_thread_handler(PHPSE_GET_DISPATCH, 0, (LPARAM)&info, NULL TSRMLS_CC)) {
+               CoGetInterfaceAndReleaseStream(info.dispatch, IID_IDispatch, (void**)ppdisp);
+       }
+       
+       if (*ppdisp) {
+               return S_OK;
+       }
+       return S_FALSE;
+}
+
+STDMETHODIMP TPHPScriptingEngine::GetCurrentScriptThreadID( 
+       /* [out] */ SCRIPTTHREADID *pstidThread)
+{
+//     tsrm_mutex_lock(m_mutex);
+       trace("%08x: GetCurrentScriptThreadID()\n", this);
+       *pstidThread = m_enginethread;
+//     tsrm_mutex_unlock(m_mutex);
+       return S_OK;
+}
+
+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;
+}
+
+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;
+                       break;
+               case SCRIPTTHREADID_CURRENT:
+                       stidThread = m_enginethread;
+                       break;
+       };
+       if (stidThread == m_basethread) {
+               *pstsState = SCRIPTTHREADSTATE_NOTINSCRIPT;
+       } else if (stidThread == m_enginethread) {
+               *pstsState = SCRIPTTHREADSTATE_NOTINSCRIPT;
+       }
+//     tsrm_mutex_unlock(m_mutex);
+       return S_OK;
+}
+
+STDMETHODIMP TPHPScriptingEngine::InterruptScriptThread( 
+       /* [in] */ SCRIPTTHREADID stidThread,
+       /* [in] */ const EXCEPINFO *pexcepinfo,
+       /* [in] */ DWORD dwFlags)
+{
+       /* do not serialize this method, or call into the script site */
+       trace("%08x: InterruptScriptThread()\n", this);
+       return S_OK;
+}
+
+/* Clone is essential when running under Windows Script Host.
+ * It creates an engine to parse the code.  Once it is parsed,
+ * the host clones other engines from the original and runs those.
+ * It is intended to be a fast method of running the same script
+ * multiple times in multiple threads. */
+STDMETHODIMP TPHPScriptingEngine::Clone( 
+       /* [out] */ IActiveScript **ppscript)
+{
+       TPHPScriptingEngine *cloned = new TPHPScriptingEngine;
+       TSRMLS_FETCH();
+       
+       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;
+
+       }
+               
+       return E_FAIL;
+}
+
+
+STDMETHODIMP TPHPScriptingEngine::InitNew( void)
+{
+       TSRMLS_FETCH();
+       SEND_THREAD_MESSAGE(this, PHPSE_INIT_NEW, 0, 0 TSRMLS_CC);
+       return S_OK;
+}
+
+STDMETHODIMP TPHPScriptingEngine::AddScriptlet( 
+       /* [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)
+{
+       struct php_active_script_add_scriptlet_info info;
+       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);
+}
+
+STDMETHODIMP TPHPScriptingEngine::ParseScriptText( 
+       /* [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)
+{
+       struct php_active_script_parse_info info;
+       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);
+}
+
+STDMETHODIMP TPHPScriptingEngine::ParseProcedureText( 
+               /* [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,
+               /* [out] */ IDispatch **ppdisp)
+{
+       struct php_active_script_parse_proc_info info;
+       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;
+       info.ppdisp = ppdisp;
+
+       ret = SEND_THREAD_MESSAGE(this, PHPSE_PARSE_PROC, 0, (LPARAM)&info TSRMLS_CC);
+
+       /*
+       if (ret == S_OK) {
+               ret = CoGetInterfaceAndReleaseStream(info.disp, IID_IDispatch, (void**)ppdisp);
+
+       }
+       */
+       trace("ParseProc: ret=%08x disp=%08x\n", ret, *ppdisp);
+       return ret;
+}
+
+extern "C" 
+void activescript_error_func(int type, const char *error_msg, ...)
+{
+       TSRMLS_FETCH();
+
+       TPHPScriptingEngine *engine = (TPHPScriptingEngine*)SG(server_context);
+
+       
+}
+
+class TActiveScriptError:
+       public IActiveScriptError
+{
+protected:
+       volatile LONG m_refcount;
+public:
+       /* IUnknown */
+       STDMETHODIMP QueryInterface(REFIID iid, void **ppvObject) {
+               *ppvObject = NULL;
+
+               if (IsEqualGUID(IID_IActiveScriptError, iid)) {
+                       *ppvObject = (IActiveScriptError*)this;
+               } else if (IsEqualGUID(IID_IUnknown, iid)) {
+                       *ppvObject = this;      
+               }
+               if (*ppvObject) {
+                       AddRef();
+                       return S_OK;
+               }
+               return E_NOINTERFACE;
+       }
+       
+       STDMETHODIMP_(DWORD) AddRef(void) {
+               return InterlockedIncrement(&m_refcount);
+       }
+       
+       STDMETHODIMP_(DWORD) Release(void) {
+               DWORD ret = InterlockedDecrement(&m_refcount);
+               trace("Release: errobj refcount=%d\n", ret);
+               if (ret == 0)
+                       delete this;
+               return ret;
+       }
+
+       HRESULT STDMETHODCALLTYPE GetExceptionInfo( 
+                       /* [out] */ EXCEPINFO *pexcepinfo)
+       {
+               memset(pexcepinfo, 0, sizeof(EXCEPINFO));
+               pexcepinfo->bstrDescription = SysAllocString(m_message);
+               pexcepinfo->bstrSource = SysAllocString(m_filename);
+               pexcepinfo->wCode = 1000;       
+               return S_OK;
+       }
+
+       HRESULT STDMETHODCALLTYPE GetSourcePosition( 
+                       /* [out] */ DWORD *pdwSourceContext,
+                       /* [out] */ ULONG *pulLineNumber,
+                       /* [out] */ LONG *plCharacterPosition)
+       {
+               *pdwSourceContext = 0;
+               *pulLineNumber = m_lineno;
+               *plCharacterPosition = 0;
+               return S_OK;
+       }
+
+       HRESULT STDMETHODCALLTYPE GetSourceLineText( 
+                       /* [out] */ BSTR *pbstrSourceLine)
+       {
+               *pbstrSourceLine = NULL;
+               return E_FAIL;
+       }
+
+       BSTR m_filename, m_message;
+       UINT m_lineno;
+       
+       TActiveScriptError(const char *filename, const uint lineno, const char *message)
+       {
+               m_refcount = 0; /* start with zero refcount because this object is passed
+                                                * directly to the script site; it will call addref */
+               m_filename = TWideString::bstr_from_ansi((char*)filename);
+               m_message = TWideString::bstr_from_ansi((char*)message);
+               m_lineno = lineno;
+       }
+
+       ~TActiveScriptError()
+       {
+               trace("%08x: cleaning up error object\n", this);
+               SysFreeString(m_filename);
+               SysFreeString(m_message);
+       }
+};
+
+extern "C"
+void activescript_error_handler(int type, const char *error_filename,
+               const uint error_lineno, const char *format, va_list args)
+{
+       TSRMLS_FETCH();
+       char *buf;
+       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. */
+       
+       switch(type) {
+               case E_ERROR:
+               case E_CORE_ERROR:
+               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);
+                       
+                       break;
+       }
+       efree(buf);
+}
+