/*
   +----------------------------------------------------------------------+
   | 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.               |
   +----------------------------------------------------------------------+
   | Author: James Moore <jmoore@php.net>                                 |
   +----------------------------------------------------------------------+
 */

/* $Id: w32api.c,v 1.3.2.1 2002/04/16 14:20:47 sander Exp $ */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#if HAVE_W32API
#include <stdio.h>
#include <stdlib.h>
#define  WIN32_LEAN_AND_MEAN
#include <windows.h>

#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "ext/standard/php_string.h"
#include "php_w32api.h"

/* {{{ w32api_functions[]
 */
function_entry w32api_functions[] = {
	PHP_FE(w32api_register_function,				NULL)
	PHP_FE(w32api_deftype,							NULL)
	PHP_FE(w32api_init_dtype,						NULL)
	PHP_FE(w32api_set_call_method,					NULL)
	PHP_FE(w32api_invoke_function,					NULL)
    {NULL, NULL, NULL}
};
/* }}} */

/* {{{ w32api_module_entry
 */
zend_module_entry w32api_module_entry = {
	STANDARD_MODULE_HEADER,
	"w32api",
	w32api_functions,
	PHP_MINIT(w32api),
	PHP_MSHUTDOWN(w32api),
	NULL,
	NULL,
	PHP_MINFO(w32api),
    "0.1", /* Replace with version number for your extension */
	STANDARD_MODULE_PROPERTIES
};
/* }}} */

ZEND_DECLARE_MODULE_GLOBALS(w32api)

#ifdef COMPILE_DL_W32API
ZEND_GET_MODULE(w32api)
#endif

/* {{{ PHP_MINFO_FUNCTION
 */
PHP_MINFO_FUNCTION(w32api)
{
	php_info_print_table_start();
    php_info_print_table_row(2, "Win32 API Support", "enabled" );
    php_info_print_table_end();
}
/* }}} */

/* {{{ PHP_MINIT_FUNCTION
 */
PHP_MINIT_FUNCTION(w32api)
{
	ZEND_INIT_MODULE_GLOBALS(w32api, php_w32api_init_globals, NULL);
	register_constants(module_number);

	W32_G(le_dynaparm) = zend_register_list_destructors_ex(w32api_free_dynaparm, NULL, "dynaparm", module_number);

	return SUCCESS;
}
/* }}} */

/* {{{ PHP_MSHUTDOWN_FUNCTION
 */
PHP_MSHUTDOWN_FUNCTION(w32api)
{
	if(W32_G(regfuncs))
	{
		FREE_HASHTABLE(W32_G(regfuncs));
	}

	if(W32_G(libraries))
	{
		php_w32api_unload_libraries();
		FREE_HASHTABLE(W32_G(libraries));
	}

	if(W32_G(libraries))
	{
		FREE_HASHTABLE(W32_G(types));
	}

	return SUCCESS;
}
/* }}} */

/* {{{ DYNAPARM dtor */
static void w32api_free_dynaparm(zend_rsrc_list_entry *rsrc TSRMLS_DC)
{
	DYNAPARM *dparam;

	dparam = (DYNAPARM *)rsrc->ptr;

	if(dparam->pArg)
		efree(dparam->pArg);

	efree(dparam);
}
/* }}} */

/* {{{ */
static void php_w32api_unload_libraries()
{
	TSRMLS_FETCH();
	zend_hash_destroy(W32_G(libraries));
}
/* }}} */

/* {{{ */
static void php_w32api_dtor_library(void *data)
{
	FreeLibrary((HINSTANCE)data);
}
/* }}} */

/* {{{ */
static void php_w32api_init_globals(zend_w32api_globals *w32api_globals)
{
	TSRMLS_FETCH();

	w32api_globals->regfuncs = NULL;
	w32api_globals->libraries = NULL;
	w32api_globals->types = NULL;
	w32api_globals->call_type = DC_CALL_STD;

	ALLOC_HASHTABLE(W32_G(regfuncs));
	zend_hash_init(W32_G(regfuncs), 1, NULL, NULL, 1); 

	ALLOC_HASHTABLE(W32_G(libraries));
	zend_hash_init(W32_G(libraries), 1, NULL, php_w32api_dtor_library, 1);

	ALLOC_HASHTABLE(W32_G(types));
	zend_hash_init(W32_G(types), 5, NULL, NULL, 1);

}
/* }}} */

/* {{{ */
static void register_constants(int module_number)
{
	TSRMLS_FETCH();

	W32_REG_CONST(DC_MICROSOFT)
	W32_REG_CONST(DC_BORLAND)
	W32_REG_CONST(DC_CALL_CDECL)
	W32_REG_CONST(DC_CALL_STD)
	W32_REG_CONST(DC_RETVAL_MATH4)
	W32_REG_CONST(DC_RETVAL_MATH8)
	W32_REG_CONST(DC_CALL_STD_BO)
	W32_REG_CONST(DC_CALL_STD_MS)
	W32_REG_CONST(DC_CALL_STD_M8)
	W32_REG_CONST(DC_FLAG_ARGPTR)

}
/* }}} */

/* {{{ proto void w32api_set_call_method(int method)
	Sets the calling method used */
PHP_FUNCTION(w32api_set_call_method)
{
	zval **method;

	if(zend_get_parameters_ex(1, &method) == FAILURE)
	{
		WRONG_PARAM_COUNT
	}

	switch((*method)->value.lval)
	{
		case DC_CALL_CDECL:
			W32_G(call_type) = DC_CALL_CDECL;
			break;
		default:
			W32_G(call_type) = DC_CALL_STD;
			break;
	}

	RETURN_TRUE
	
}
/* }}} */

/* {{{ proto bool w32api_register_function(string libary, string function_name)
	Registers function function_name from library with PHP */
PHP_FUNCTION(w32api_register_function)
{
	HINSTANCE hinstLib;
	FARPROC ProcAdd;
	W32APIFE *fe;
	BOOL fRunTimeLinkSuccess = FALSE;
	zval **libname, **funcname, **retval_type;
	void *tmp;
	runtime_struct *rst;

	if(zend_get_parameters_ex(3, &libname, &funcname, &retval_type) == FAILURE)
	{
		WRONG_PARAM_COUNT
	}

	convert_to_string_ex(libname);
	convert_to_string_ex(funcname);
	convert_to_string_ex(retval_type);

	fe = (W32APIFE *)emalloc(sizeof(W32APIFE));
	fe->retvaltype = 0;
	//TODO: Check library isnt alreay loaded

	hinstLib = LoadLibrary((*libname)->value.str.val);

	if(hinstLib == NULL)
	{
		php_error(E_WARNING, "Could not load dynamic link library %s", (*libname)->value.str.val);
		RETURN_FALSE
	}

	zend_hash_add(W32_G(libraries),
				  (*libname)->value.str.val,
				  strlen((*libname)->value.str.val),
				  &hinstLib,
				  sizeof(HINSTANCE),
				  NULL);

	//TODO: Check function handle isnt already loaded

	ProcAdd = (FARPROC) GetProcAddress(hinstLib, (*funcname)->value.str.val);

	if(ProcAdd == NULL)
	{
		php_error(E_WARNING, "Could not get handle for function %s", (*funcname)->value.str.val);
		RETURN_FALSE
	}

	fe->fp = ProcAdd;

	if(!strcmp((*retval_type)->value.str.val, "long"))
	{
		fe->rettype = malloc(5*sizeof(char));
		fe->rettype  = strdup("long\0");
	}
	else if (!strcmp((*retval_type)->value.str.val, "int"))
	{
		fe->rettype = malloc(4*sizeof(char));
		fe->rettype = strdup("long\0");
	}
	else if (!strcmp((*retval_type)->value.str.val, "string"))
	{
		fe->rettype = malloc(7*sizeof(char));
		fe->rettype = strdup("string\0");
	}
	else if (!strcmp((*retval_type)->value.str.val, "byte"))
	{
		fe->rettype = malloc(5*sizeof(char));
		fe->rettype = strdup("byte\0");
	}
	else if (!strcmp((*retval_type)->value.str.val, "double"))
	{
		fe->rettype = malloc(7*sizeof(char));
		fe->rettype = strdup("double\0");
	}
	else if (!strcmp((*retval_type)->value.str.val, "bool"))
	{
		fe->rettype = malloc(5*sizeof(char));
		fe->rettype = strdup("bool\0");
	}
	else
	{
			/**
			 * this could be a userdef'd type so lets
			 * search the ht for that.
			 */
			if(zend_hash_find(W32_G(types), (*retval_type)->value.str.val, (*retval_type)->value.str.len, (void **) &tmp) == FAILURE)
			{
				php_error(E_WARNING, "Unknown type %s", (*retval_type)->value.str.val);
				RETURN_FALSE;
			}
			rst = tmp;
			fe->rettype = malloc(sizeof(char) * strlen(rst->name) + 1);
			memcpy(fe->rettype, rst->name, strlen(rst->name) + 1);
			fe->retvaltype = 1;
	}
	
	if(zend_hash_add(W32_G(regfuncs), php_strtolower((*funcname)->value.str.val, (*funcname)->value.str.len), (*funcname)->value.str.len, fe, sizeof(W32APIFE), NULL) == FAILURE)
	{
		php_error(E_WARNING, "Could not register function %s into hash");
		RETURN_FALSE;
	}	

	/**
	 * We now need to add the function into the global namespace, the best way to do this is
	 * to register it as a new module then it will definatly be removed on shutdown
	 */
	{
		zend_module_entry *temp_module_entry;
		char *fname;
		function_entry *tmp_functions;

		tmp_functions = malloc(sizeof(function_entry) * 2);
		fname = malloc((*funcname)->value.str.len + 1);
		memcpy(fname, (*funcname)->value.str.val, (*funcname)->value.str.len + 1);

		tmp_functions[0].fname = php_strtolower(fname, (*funcname)->value.str.len);
		tmp_functions[0].handler = zif_w32api_invoke_function;
		tmp_functions[0].func_arg_types = NULL;
		tmp_functions[1].fname = NULL;
		tmp_functions[1].handler = NULL; 
		tmp_functions[1].func_arg_types = NULL;


		temp_module_entry = malloc(sizeof(zend_module_entry));
		temp_module_entry->size = sizeof(zend_module_entry);
		temp_module_entry->zend_api = ZEND_MODULE_API_NO;
		temp_module_entry->zend_debug = ZEND_DEBUG;
		temp_module_entry->zts = USING_ZTS;
		temp_module_entry->name = fname;
		temp_module_entry->functions = tmp_functions;
		temp_module_entry->module_startup_func = temp_module_entry->module_shutdown_func =
		temp_module_entry->request_startup_func = temp_module_entry->request_shutdown_func = NULL;
		temp_module_entry->info_func = NULL;
		temp_module_entry->version = NULL;
		temp_module_entry->global_startup_func = temp_module_entry->global_shutdown_func = NULL;
		temp_module_entry->globals_id = 0;
		temp_module_entry->module_started = 0;
		temp_module_entry->type = 0;
		temp_module_entry->handle = NULL;
		temp_module_entry->module_number = 0;

		if(zend_register_module(temp_module_entry) != SUCCESS)
		{
			php_error(E_WARNING, "could not register function %s into the function table", (*funcname)->value.str.val);
			RETURN_FALSE
		}


	}



	RETURN_TRUE
};
/* }}} */

/* {{{ proto mixed w32api_invoke_function(string funcname, ....)
	Invokes function funcname with the arguments passed after the function name */
PHP_FUNCTION(w32api_invoke_function)
{
	zval ***args = (zval ***)NULL;
	void *tmp;
	W32APIFE *fe;
	char *funcname;
	int argc = ZEND_NUM_ARGS();
	runtime_struct *rst;
	RESULT retval;
	DYNAPARM *Param, *drval;
	LPVOID pVParam;
	int VParamsz;
	int i;

	args = emalloc(argc * sizeof(zval **));
	Param = (DYNAPARM *)emalloc((argc) * sizeof(DYNAPARM));

	if(zend_get_parameters_array_ex(argc, args) == FAILURE)
	{
		WRONG_PARAM_COUNT
	}

	funcname = get_active_function_name(TSRMLS_C);

	if(zend_hash_find(W32_G(regfuncs), funcname, strlen(funcname), (void **) &tmp) == FAILURE)
	{
		php_error(E_WARNING, "Could not find function handle for function %s", funcname);
		RETURN_FALSE;
	}

	fe = (W32APIFE *)tmp;

	// Build the DYNPARAM array.
	for(i = 0; i < (argc); i++)
	{
		Param[i] = w32api_convert_zval_to_dynparam(args[(i)] TSRMLS_CC);
	}


	/**
	 * We need to check the return type, if its a complex return type then we need to sort out pVParam and
	 * VParamsz and pass them as the last two parameters of the call to the invoke of the function.
	 */
	if(fe->retvaltype) // Complex return type
	{
		tmp = NULL;
		if(zend_hash_find(W32_G(types), fe->rettype, strlen(fe->rettype), (void **) &tmp) == FAILURE)
		{
			php_error(E_WARNING, "Unknown type %s", fe->rettype);
			RETURN_FALSE;
		}
		rst = tmp;
		VParamsz = rst->size;
		pVParam = malloc(rst->size);
	}
	else
	{
		pVParam = NULL;
		VParamsz = 0;
	}

	retval = php_w32api_dynamic_dll_call(W32_G(call_type), (ulong)(fe->fp), (argc), Param, pVParam, VParamsz);

	if(!strcmp(fe->rettype, "long"))
	{
		RETURN_LONG(retval.Long);
	}
	else if (!strcmp(fe->rettype, "int"))
	{
		RETURN_LONG(retval.Int);
	}
	else if (!strcmp(fe->rettype, "string"))
	{
		RETURN_STRING(retval.Pointer, 1);
	}
	else if (!strcmp(fe->rettype, "byte"))
	{
		php_error(E_WARNING, "byte return values are not supported right now");
		RETURN_FALSE;
	}
	else if (!strcmp(fe->rettype, "double"))
	{
		RETURN_DOUBLE(retval.Double);
	}
	else if (!strcmp(fe->rettype, "bool"))
	{
		if(retval.Int)
		{
			RETURN_TRUE;
		}
		else
		{
			RETURN_FALSE;
		}
	}
	else
	{
		/**
		 * This is returned in pRet, we need to get type and build a DYNAPARM for
		 * the return value and return the RESOURCE.
		 */
		
		drval = malloc(sizeof(DYNAPARM));
		drval->pArg = pVParam;
		drval->nWidth = VParamsz;
		drval->dwFlags = 0;
		ZEND_REGISTER_RESOURCE(return_value, drval, W32_G(le_dynaparm));
	}

}
/* }}} */

/* {{{ Dynamic calling of dll functions by pushing onto the stack manually. */
PHP_W32API_API RESULT php_w32api_dynamic_dll_call( int Flags, DWORD lpFunction, 
												   int nArgs, DYNAPARM Param[], 
												   LPVOID pRet, int nRetSize)
{
	/**
	 * Here we dont know the function we are calling or the arguments
	 * it expects so we must do quite a lot of work, normally done by
	 * the compiler ourselves, this is far easier to do it Assembly than
	 * in C.. here goes (jmoore - 05/11/2001).
	 *
	 * Based on the code by Ton Plooy <tonp@xs4all.nl>
	 * See Also MSFT KB Article ID: Q171729 for more background.
	 *
	 * We will support two calling mechanisms, __stdcall and __cdecl(WINAPIV).
	 */
	RESULT Res = { 0 };
	int i, nInd, nSize;										
	DWORD dwEAX, dwEDX, dwVal, *pStack, dwStSize = 0;
	BYTE *pArg;

	_asm {
		mov pStack, esp
		sub esp, 0x100
	}

    for (i = nArgs; i > 0; i--) {
        nInd  = i - 1;
        nSize = (Param[nInd].nWidth + 3) / 4 * 4;
        pArg  = (BYTE *)Param[nInd].pArg + nSize - 4;
        dwStSize += (DWORD)nSize;
        while (nSize > 0) {
            if (Param[nInd].dwFlags & DC_FLAG_ARGPTR) {
                dwVal = *(DWORD *)pArg;
                pArg -= 4;             
			}
            else {

                dwVal = Param[nInd].dwArg;
            }

            pStack--;           
            *pStack = dwVal;    
            nSize -= 4;
        }
    }

	if((pRet != NULL) && ((Flags & DC_BORLAND) || (nRetSize > 8))) 
	{
	    dwStSize += 4;
        pStack--;     
        *pStack = (DWORD)pRet; 
    }

	_asm {
		add esp, 0x100
		sub esp, dwStSize
		call [lpFunction]
		mov dwEAX, eax
		mov dwEDX, edx
	}

    if (Flags & DC_CALL_CDECL) {
        _asm add esp, dwStSize
    }

    if (Flags & DC_RETVAL_MATH4) {
        _asm fstp dword ptr [Res]
    }
    else if (Flags & DC_RETVAL_MATH8) {
        _asm fstp qword ptr [Res]
    }
    else if (pRet == NULL) {
        _asm{
			mov  eax, [dwEAX]
			mov  DWORD PTR [Res], eax
			mov  edx, [dwEDX]
			mov  DWORD PTR [Res + 4], edx
		}
    }
    else if (((Flags & DC_BORLAND) == 0) && (nRetSize <= 8)) {
        // Microsoft optimized less than 8-bytes structure passing
        _asm {
			mov ecx, DWORD PTR [pRet]
			mov eax, [dwEAX]
			mov DWORD PTR [ecx], eax
			mov edx, [dwEDX]
			mov DWORD PTR [ecx + 4], edx
		}
    }
    return Res;
}
/* }}} */

/* {{{ Conversion function for zvals to dynparams */
DYNAPARM w32api_convert_zval_to_dynparam(zval ** carg TSRMLS_DC)
{
	DYNAPARM dparam, *tparam;
	dparam.dwFlags = 0;

	switch((*carg)->type)
	{
		case IS_RESOURCE:
			tparam = (DYNAPARM *) zend_fetch_resource(carg TSRMLS_CC, -1, "dynaparm", NULL, 1, W32_G(le_dynaparm));
			if(!tparam)
			{
				php_error(E_ERROR, "Error when fetching argument");
			}
			dparam = *tparam;
			break;
		case IS_LONG:
			dparam.nWidth = sizeof(long);
			dparam.dwArg = (*carg)->value.lval;
			break;
		case IS_DOUBLE:
			dparam.nWidth = sizeof(float);
			dparam.pArg = &(*carg)->value.dval;
			dparam.dwFlags = DC_FLAG_ARGPTR;
			break;
		case IS_STRING:
			dparam.nWidth = sizeof(char *);
			dparam.pArg = (*carg)->value.str.val;
			break;
		case IS_BOOL:
			dparam.nWidth = sizeof(BOOL);
			dparam.dwArg = ((*carg)->value.lval == 0)?FALSE:TRUE;
			break;
		case IS_NULL:
			dparam.nWidth = sizeof(void *);
			dparam.pArg = NULL;
			break;
		default:
			php_error(E_ERROR, "Cant convert variable to type dynparam");
	}

	return dparam;
}
/* }}} */

/**
 * Typedef functions, We need to be flexible about what types we are going
 * to pass and retrive from functions in the win32 api. this means we need
 * to be able to create structs of any different type at runtime. We can do
 * this in asm. For example:
 *
 * typedef struct james {
 *		char firstname[81];
 *      char lastname[81];
 * }
 *
 * An instance of the above struct (lets call this instance iJames. iJames
 * is a pointer to the first letter of firstname (the base address), firstname
 * then fills the following 81 bytes (some of these may be empty), lastname is
 * at the offset iJames+81, 
 *
 * |-  81 Bytes  -|-  81 Bytes  -|
 * +------//------+------//------+
 * | James\0      | Moore\0      |
 * +------//------+------//------+
 * ^              ^
 * iJames         iJames[81]
 *
 * We could store a value in ax in this field by
 * the assembly command:
 * 
 * move ac iJames[81]
 *
 * Unions are easy in asm as the length of memory they use is equal to the size
 * of their largest member. For example:
 *
 * typedef union foo {
 *      int i;
 *      char j;
 * }
 *
 * The length of an int might be 4 bytes, the length of a char might be 1 byte. 
 * So if we create an instance of foo called bar then it would have the following 
 * layout in memory:
 *
 * +---+------------+
 * |   ¦            |
 * +---+------------+
 * ^^^^^
 * Memory area for char
 * ^^^^^^^^^^^^^^^^^^
 * Memory area for int
 *
 * Therefore even if there was only a char held in this section and the union was within 
 * a struct the next offset would still be base address + 4 not +1 so we need to deal
 * with this too.
 *
 * When defining types the user can call the w32api_deftype() function, this takes 2n+1 args where
 * n is the number of members the type has. The first argument is the name of the struct struct
 * after that is the type of the member followed by the members name (in pairs).
 *	
 *
 * James Moore <jmoore@php.net> 6/11/2001
 *
 */

/* {{{ proto int w32api_deftype(string typename, string member1_type, string member1_name, ...)
	Defines a type for use with other w32api_functions. */
PHP_FUNCTION(w32api_deftype)
{
	zval ***args;
	int argc = ZEND_NUM_ARGS();
	int i;
	runtime_struct *rst, *orst;
	void *tmp;
	field *fields, *ptr;

	args = (zval ***)emalloc(sizeof(zval **) * argc);
	rst = malloc(sizeof(runtime_struct));

	ptr = (field *)emalloc(sizeof(field) *((argc-1)/2));

	fields = ptr;

	if((zend_get_parameters_array_ex(argc, args) ==  FAILURE) || ((argc % 2) !=  1))
	{
		WRONG_PARAM_COUNT
	}

	for(i=2; i<argc; i++)
	{
		convert_to_string_ex(args[i]);
	}

	convert_to_string_ex(args[0]);

	rst->name = (*args[0])->value.str.val;
	rst->size = 0;

	/**
	 * We now take each parameter pair and fill out the field struct
	 * for each parameter pair.
	 */
	for(i=1; i<argc; i += 2)
	{
		ptr->type = malloc((*args[i])->value.str.len + 1);
		memcpy(ptr->type, (*args[i])->value.str.val, (*args[i])->value.str.len + 1);
		
		ptr->fname = malloc((*args[i+1])->value.str.len + 1);
		memcpy(ptr->fname, (*args[i+1])->value.str.val, (*args[i+1])->value.str.len + 1);

		ptr->fsize = 0;

		if(!strcmp(ptr->type, "long"))
		{
			ptr->fsize = sizeof(long);
		}
		else if (!strcmp(ptr->type, "int"))
		{
				ptr->fsize = sizeof(int);
		}
		else if (!strcmp(ptr->type, "string"))
		{
				ptr->fsize = sizeof(char *);
		}
		else if (!strcmp(ptr->type, "byte"))
		{
				ptr->fsize = 1;
		}
		else if (!strcmp(ptr->type, "double"))
		{
				ptr->fsize = sizeof(double);
		}
		else if (!strcmp(ptr->type, "bool"))
		{
				ptr->fsize = sizeof(BOOL);
		}
		else
		{
				/**
				 * this could be a userdef'd type so lets
				 * search the ht for that.
				 */
				if(zend_hash_find(W32_G(types), ptr->type, strlen(ptr->type), (void **) &tmp) == FAILURE)
				{
					php_error(E_WARNING, "Unknown type %s", ptr->type);
					RETURN_FALSE;
				}
				orst = tmp;
				ptr->fsize = orst->size;
		}

		rst->size += ptr->fsize;
		ptr++;
	}

	rst->fields = fields;

	if(zend_hash_add(W32_G(types), rst->name, strlen(rst->name), rst, sizeof(runtime_struct), NULL) == FAILURE)
	{
		php_error(E_WARNING, "Error registering type %s", rst->name);
		RETURN_FALSE;
	}

	RETURN_TRUE;

}
/* }}} */

/* {{{ proto resource w32api_init_dtype(string typename, mixed val1, mixed val2);
	Creates an instance to the data type typename and fills it with the values val1, val2, the function
	then returns a DYNAPARM which can be passed when invoking a function as a parameter.*/
PHP_FUNCTION(w32api_init_dtype)
{
	DYNAPARM *dparam, *tparam;
	void *rtstruct, *tmp;
	runtime_struct *rst;
	field *ptr;
	char *m;
	zval ***args;
	zval **curarg;
	int i, j,argc = ZEND_NUM_ARGS();

	args = emalloc(sizeof(zval **) * argc);
	dparam = emalloc(sizeof(DYNAPARM));

	if(zend_get_parameters_array_ex(argc, args) != SUCCESS)
	{
		WRONG_PARAM_COUNT
	}

	convert_to_string_ex(args[0]);

	if(zend_hash_find(W32_G(types), (*args[0])->value.str.val, (*args[0])->value.str.len, (void **)&tmp) == FAILURE)
	{
		php_error(E_WARNING, "Unknown type %s",(*args[0])->value.str.val);
		RETURN_FALSE
	}

	rst = (runtime_struct *)tmp;

	rtstruct = emalloc(rst->size);
	rtstruct = memset(rtstruct, 0, rst->size);
	tmp = rtstruct;
	curarg = args[1];
	ptr = rst->fields;

	i = 0;
	j = (argc-1);

	while(i<j)
	{
		if(!strcmp(ptr->type, "long"))
		{
			convert_to_long_ex(curarg);
			memcpy(tmp, &(*curarg)->value.lval, ptr->fsize);
		}
		else if (!strcmp(ptr->type, "int"))
		{
			convert_to_long_ex(curarg);
			memcpy(tmp, &(*curarg)->value.lval, ptr->fsize);
		}
		else if (!strcmp(ptr->type, "string"))
		{
			convert_to_string_ex(curarg);
			m = emalloc(sizeof(char) * (*curarg)->value.str.len + 1);
			memcpy(m, (*curarg)->value.str.val, (*curarg)->value.str.len + 1);
			memcpy(tmp, &m, ptr->fsize);
		}
		else if (!strcmp(ptr->type, "byte"))
		{
			/* use Lower order bytes */
			convert_to_long_ex(curarg);
			memcpy(tmp, &(*curarg)->value.lval, ptr->fsize);
		}
		else if (!strcmp(ptr->type, "double"))
		{
			convert_to_double_ex(curarg);
			memcpy(tmp, &(*curarg)->value.dval, ptr->fsize);
		}
		else if (!strcmp(ptr->type, "bool"))
		{
			convert_to_boolean_ex(curarg);
			memcpy(tmp, &(*curarg)->value.lval, ptr->fsize);
		}
		else
		{
			/**
			 * OK we have a user type here, we need to treat the param
			 * as a resource and fetch the DYNAPARM its contained in
			 * then copy the contents of its LPVOID pointer into our
			 * memory space.
			 */
			ZEND_FETCH_RESOURCE(tparam, DYNAPARM *, curarg, -1, "dynaparm", W32_G(le_dynaparm));
			memcpy(tmp, tparam->pArg, ptr->fsize);
		}

		/**
		 * We need somthing that is 1 byte 
		 */
		(char)tmp += ptr->fsize;
		(void *)tmp;

		curarg++;
		ptr++;
		i++;
	}

	dparam->dwFlags = 0;
	dparam->nWidth = rst->size;
	dparam->pArg = rtstruct;
	
	ZEND_REGISTER_RESOURCE(return_value, dparam, W32_G(le_dynaparm));

}
/* }}} */


#endif /* HAVE_W32API */
