Browse Source

Add ***EXPERIMENTAL*** Zend OO Extension support for Microsoft.Net

PHP-4.0.5
Sam Ruby 26 years ago
parent
commit
a0b745a053
  1. 12
      EXTENSIONS
  2. 32
      ext/dotnet/README
  3. 243
      ext/dotnet/dotnet.cpp
  4. 167
      ext/dotnet/dotnet.dsp
  5. 8
      ext/dotnet/dotnet.php
  6. 21
      ext/dotnet/php_dotnet.h
  7. 14
      ext/rpc/com/COM.c
  8. 32
      ext/rpc/dotnet/README
  9. 243
      ext/rpc/dotnet/dotnet.cpp
  10. 167
      ext/rpc/dotnet/dotnet.dsp
  11. 8
      ext/rpc/dotnet/dotnet.php
  12. 21
      ext/rpc/dotnet/php_dotnet.h
  13. 1
      ext/standard/info.c

12
EXTENSIONS

@ -65,8 +65,9 @@ MAINTENANCE: Unknown
STATUS: Unknown
-------------------------------------------------------------------------------
EXTENSION: servlet
MAINTENANCE: Unknown
STATUS: Unknown
PRIMARY MAINTAINER: Sam Ruby <rubys@us.ibm.com>
MAINTENANCE: Maintained
STATUS: Working
-------------------------------------------------------------------------------
EXTENSION: thttpd
MAINTENANCE: Unknown
@ -199,6 +200,11 @@ PRIMARY MAINTAINER: Uwe Steinmann <steinm@php.net>
MAINTENANCE: Maintained
STATUS: Experimental
-------------------------------------------------------------------------------
EXTENSION: dotnet
PRIMARY MAINTAINER: Sam Ruby <rubys@us.ibm.com>
MAINTENANCE: Maintained
STATUS: Experimental
-------------------------------------------------------------------------------
EXTENSION: exif
MAINTENANCE: Unknown
STATUS: Unknown
@ -233,7 +239,7 @@ STATUS: Working
EXTENSION: java
PRIMARY MAINTAINER: Sam Ruby <rubys@us.ibm.com>
MAINTENANCE: Maintained
STATUS: Unknown
STATUS: Working
-------------------------------------------------------------------------------
EXTENSION: ldap
PRIMARY MAINTAINER: Rasmus Lerdorf <rasmus@php.net>

32
ext/dotnet/README

@ -0,0 +1,32 @@
Warning
=======
This support is EXPERIMENTAL. In fact, it integrates code that
Microsoft labels as pre-beta. Use at your own risk.
Build instructions
==================
Download and install the .NET Framework SDK Technology Preview from
http://msdn.microsoft.com/net/#sdk. Once installed, copy Mscoree.h
(typically found in C:\Program Files\NGWSSDK\Include to ext\dotnet).
Do not simply add the NGWSSDK\Include directory to the include path
as this will cause compilation failures.
Download and unzip the source to the dm.net COM Moniker from
http://staff.develop.com/jasonw/clr/readme.htm. Copy mscorlib.h
to ext\dotnet. There is no need to register the clrmonsrv.dll as
it is not used.
At this point, the dotnet project can be built like any other
project, from either VisualStudio 6's GUI or from the command line.
Example command line invocation:
msdev dotnet.dsp /MAKE "dotnet - Win32 Debug_TS"
Execution instructions:
=======================
Add "extension=php_dotnet.dll" into php.ini.
Sample program can be found at dotnet.php

243
ext/dotnet/dotnet.cpp

@ -0,0 +1,243 @@
/*
+----------------------------------------------------------------------+
| PHP version 4.0 |
+----------------------------------------------------------------------+
| Copyright (c) 1997, 1998, 1999, 2000 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: Sam Ruby <rubys@us.ibm.com> |
+----------------------------------------------------------------------+
*/
/*
* This module implements support for Microsoft .Net components.
*/
#ifdef PHP_WIN32
#include <iostream.h>
#include <math.h>
#include <comdef.h>
extern "C" {
#include "php.h"
#include "php_ini.h"
pval php_COM_get_property_handler(zend_property_reference *property_reference);
int php_COM_set_property_handler(zend_property_reference *property_reference, pval *value);
char *php_COM_error_message(HRESULT hr);
void php_COM_call_function_handler(INTERNAL_FUNCTION_PARAMETERS, zend_property_reference *property_reference);
int php_COM_get_le_idispatch();
}
#include <stdio.h>
#include "Mscoree.h"
#include "mscorlib.h"
using namespace mscorlib;
static ICorRuntimeHost *pHost;
static mscorlib::_AppDomain *pDomain;
static zend_class_entry dotnet_class_entry;
static OLECHAR *php_char_to_OLECHAR(char *C_str, uint strlen)
{
OLECHAR *unicode_str = (OLECHAR *) emalloc(sizeof(OLECHAR)*(strlen+1));
OLECHAR *unicode_ptr = unicode_str;
while (*C_str) {
*unicode_ptr++ = (unsigned short) *C_str++;
}
*unicode_ptr = 0;
return unicode_str;
}
HRESULT dotnet_init() {
HRESULT hr;
hr = CoCreateInstance(CLSID_CorRuntimeHost, NULL, CLSCTX_ALL,
IID_ICorRuntimeHost, (void **)&pHost);
if (FAILED(hr)) return hr;
hr = pHost->Start();
if (FAILED(hr)) return hr;
IUnknown *uDomain;
hr = pHost->GetDefaultDomain(&uDomain);
if (FAILED(hr)) return hr;
hr = uDomain->QueryInterface(__uuidof(_AppDomain), (void**) &pDomain);
if (FAILED(hr)) return -1;
uDomain->Release();
return ERROR_SUCCESS;
}
HRESULT dotnet_create(OLECHAR *assembly, OLECHAR *datatype, struct IDispatch **object) {
HRESULT hr;
_ObjectHandle *pHandle;
hr = pDomain->CreateInstance(_bstr_t(assembly), _bstr_t(datatype), &pHandle);
if (FAILED(hr)) return hr;
if (!pHandle) return hr;
_variant_t unwrapped;
hr = pHandle->Unwrap(&unwrapped);
pHandle->Release();
if (FAILED(hr)) return hr;
*object = unwrapped.pdispVal;
return ERROR_SUCCESS;
}
void dotnet_term() {
if (pHost) pHost->Stop();
if (pHost) pHost->Release();
if (pDomain) pDomain->Release();
pHost = 0;
pDomain = 0;
}
/* {{{ proto int dotnet_load(string module_name)
Loads a DOTNET module */
PHP_FUNCTION(DOTNET_load)
{
HRESULT hr;
pval *assembly_name, *datatype_name;
OLECHAR *assembly, *datatype;
IDispatch FAR *i_dispatch = NULL;
if (ZEND_NUM_ARGS() != 2) WRONG_PARAM_COUNT;
getParameters(ht, 2, &assembly_name, &datatype_name);
convert_to_string(assembly_name);
assembly = php_char_to_OLECHAR(assembly_name->value.str.val, assembly_name->value.str.len);
convert_to_string(datatype_name);
datatype = php_char_to_OLECHAR(datatype_name->value.str.val, datatype_name->value.str.len);
/* obtain IDispatch */
hr=dotnet_create(assembly, datatype, &i_dispatch);
efree(assembly);
efree(datatype);
if (FAILED(hr)) {
char *error_message;
error_message = php_COM_error_message(hr);
php_error(E_WARNING,"Error obtaining .Net class for %s in assembly %s: %s",datatype_name->value.str.val,assembly_name->value.str.val,error_message);
LocalFree(error_message);
RETURN_FALSE;
}
if (!i_dispatch) {
php_error(E_WARNING,"Unable to locate %s in assembly %s",datatype_name->value.str.val,assembly_name->value.str.val);
RETURN_FALSE;
}
RETURN_LONG(zend_list_insert(i_dispatch,php_COM_get_le_idispatch()));
}
/* }}} */
void php_DOTNET_call_function_handler(INTERNAL_FUNCTION_PARAMETERS, zend_property_reference *property_reference)
{
pval *object = property_reference->object;
zend_overloaded_element *function_name = (zend_overloaded_element *) property_reference->elements_list->tail->data;
if (zend_llist_count(property_reference->elements_list)==1
&& !strcmp(function_name->element.value.str.val, "dotnet")) { /* constructor */
pval *object_handle;
PHP_FN(DOTNET_load)(INTERNAL_FUNCTION_PARAM_PASSTHRU);
if (!zend_is_true(return_value)) {
var_reset(object);
return;
}
ALLOC_ZVAL(object_handle);
*object_handle = *return_value;
pval_copy_constructor(object_handle);
INIT_PZVAL(object_handle);
zend_hash_index_update(object->value.obj.properties, 0, &object_handle, sizeof(pval *), NULL);
pval_destructor(&function_name->element);
} else {
php_COM_call_function_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, property_reference);
}
}
void php_register_DOTNET_class()
{
INIT_OVERLOADED_CLASS_ENTRY(dotnet_class_entry, "DOTNET", NULL,
php_DOTNET_call_function_handler,
php_COM_get_property_handler,
php_COM_set_property_handler);
zend_register_internal_class(&dotnet_class_entry);
}
function_entry DOTNET_functions[] = {
{NULL, NULL, NULL}
};
static PHP_MINFO_FUNCTION(DOTNET)
{
DISPLAY_INI_ENTRIES();
}
PHP_INI_BEGIN()
PHP_INI_END()
PHP_MINIT_FUNCTION(DOTNET)
{
HRESULT hr;
CoInitialize(0);
hr = dotnet_init();
if (FAILED(hr)) return hr;
php_register_DOTNET_class();
REGISTER_INI_ENTRIES();
return SUCCESS;
}
PHP_MSHUTDOWN_FUNCTION(DOTNET)
{
dotnet_term();
CoUninitialize();
UNREGISTER_INI_ENTRIES();
return SUCCESS;
}
zend_module_entry dotnet_module_entry = {
"dotnet", DOTNET_functions, PHP_MINIT(DOTNET), PHP_MSHUTDOWN(DOTNET), NULL, NULL, PHP_MINFO(DOTNET), STANDARD_MODULE_PROPERTIES
};
extern "C" {
ZEND_GET_MODULE(dotnet)
}
void php_register_DOTNET_class();
#endif

167
ext/dotnet/dotnet.dsp

@ -0,0 +1,167 @@
# Microsoft Developer Studio Project File - Name="dotnet" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
CFG=dotnet - Win32 Debug_TS
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "dotnet.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "dotnet.mak" CFG="dotnet - Win32 Debug_TS"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "dotnet - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
!MESSAGE "dotnet - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
!MESSAGE "dotnet - Win32 Debug_TS" (based on "Win32 (x86) Dynamic-Link Library")
!MESSAGE "dotnet - Win32 Release_TS" (based on "Win32 (x86) Dynamic-Link Library")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
MTL=midl.exe
RSC=rc.exe
!IF "$(CFG)" == "dotnet - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "..\..\Release"
# PROP BASE Intermediate_Dir "..\..\Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "..\..\Release"
# PROP Intermediate_Dir "..\..\Release"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MD /W3 /GX /O2 /D "PHP_WIN32" /D "ZEND_WIN32" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c
# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\.." /I "..\..\Zend" /I "$(JAVA_HOME)\include\win32" /I "$(JAVA_HOME)\include" /I "..\..\..\bindlib_w32" /D "NDEBUG" /D ZEND_DEBUG=0 /D "PHP_WIN32" /D "ZEND_WIN32" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "COMPILE_DL_JAVA" /D HAVE_JAVA=1 /YX /FD /c
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x40d /d "NDEBUG"
# ADD RSC /l 0x40d /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# 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
# ADD 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 /out:"..\..\Release/php_dotnet.dll" /libpath:"$(JAVA_HOME)\lib" /libpath:"..\..\Release"
!ELSEIF "$(CFG)" == "dotnet - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "..\..\Debug"
# PROP BASE Intermediate_Dir "..\..\Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "..\..\Debug"
# PROP Intermediate_Dir "..\..\Debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "PHP_WIN32" /D "ZEND_WIN32" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /GZ /c
# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\.." /I "..\..\Zend" /I "$(JAVA_HOME)\include\win32" /I "$(JAVA_HOME)\include" /I "..\..\..\bindlib_w32" /D "_DEBUG" /D ZEND_DEBUG=1 /D "PHP_WIN32" /D "ZEND_WIN32" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "COMPILE_DL_JAVA" /D HAVE_JAVA=1 /FR /YX /FD /GZ /c
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x40d /d "_DEBUG"
# ADD RSC /l 0x40d /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# 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
# ADD 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 /out:"..\..\Debug/php_dotnet.dll" /pdbtype:sept /libpath:"$(JAVA_HOME)\lib" /libpath:"..\..\Debug"
!ELSEIF "$(CFG)" == "dotnet - Win32 Debug_TS"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "..\..\Debug_TS"
# PROP BASE Intermediate_Dir "..\..\Debug_TS"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "..\..\Debug_TS"
# PROP Intermediate_Dir "..\..\Debug_TS"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\.." /I "..\..\..\Zend" /I "$(JAVA_HOME)\include\win32" /I "$(JAVA_HOME)\include" /I "..\..\..\bindlib_w32" /D "_DEBUG" /D "PHP_WIN32" /D "ZEND_WIN32" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "COMPILE_DL_JAVA" /FR /YX /FD /GZ /c
# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\..\\" /I "..\..\main" /I "..\..\Zend" /I "$(JAVA_HOME)\include\win32" /I "$(JAVA_HOME)\include" /I "..\..\..\bindlib_w32" /D "_DEBUG" /D ZEND_DEBUG=1 /D "ZTS" /D "PHP_WIN32" /D "ZEND_WIN32" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "COMPILE_DL_JAVA" /D HAVE_JAVA=1 /FR /YX /FD /GZ /c
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x40d /d "_DEBUG"
# ADD RSC /l 0x40d /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# 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
# ADD 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 php4ts_debug.lib /nologo /dll /debug /machine:I386 /out:"..\..\Debug_TS/php_dotnet.dll" /pdbtype:sept /libpath:"$(JAVA_HOME)\lib" /libpath:"..\..\Debug_TS"
!ELSEIF "$(CFG)" == "dotnet - Win32 Release_TS"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "..\..\Release_TS"
# PROP BASE Intermediate_Dir "..\..\Release_TS"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "..\..\Release_TS"
# PROP Intermediate_Dir "..\..\Release_TS"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MD /W3 /GX /O2 /I "..\.." /I "..\..\..\Zend" /I "$(JAVA_HOME)\include\win32" /I "$(JAVA_HOME)\include" /I "..\..\..\bindlib_w32" /D "NDEBUG" /D "PHP_WIN32" /D "ZEND_WIN32" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "COMPILE_DL_JAVA" /YX /FD /c
# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\\" /I "..\..\main" /I "..\..\Zend" /I "$(JAVA_HOME)\include\win32" /I "$(JAVA_HOME)\include" /I "..\..\..\bindlib_w32" /D "NDEBUG" /D ZEND_DEBUG=0 /D "ZTS" /D "PHP_WIN32" /D "ZEND_WIN32" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "COMPILE_DL_JAVA" /D HAVE_JAVA=1 /YX /FD /c
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x40d /d "NDEBUG"
# ADD RSC /l 0x40d /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# 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
# ADD 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 php4ts.lib /nologo /dll /machine:I386 /out:"..\..\Release_TS/php_dotnet.dll" /libpath:"$(JAVA_HOME)\lib" /libpath:"..\..\Release_TS"
!ENDIF
# Begin Target
# Name "dotnet - Win32 Release"
# Name "dotnet - Win32 Debug"
# Name "dotnet - Win32 Debug_TS"
# Name "dotnet - Win32 Release_TS"
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File
SOURCE=.\dotnet.cpp
# End Source File
# Begin Source File
SOURCE=.\..\..\win32\winutil.c
# End Source File
# End Group
# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File
SOURCE=.\php_dotnet.h
# End Source File
# End Group
# End Target
# End Project

8
ext/dotnet/dotnet.php

@ -0,0 +1,8 @@
<?
$stack = new DOTNET("mscorlib","System.Collections.Stack");
$stack->Push(".Net");
$stack->Push("Hello ");
echo $stack->Pop() . $stack->Pop();
?>

21
ext/dotnet/php_dotnet.h

@ -0,0 +1,21 @@
#ifndef PHP_DOTNET_H
#define PHP_DOTNET_H
#if WIN32|WINNT
extern PHP_MINIT_FUNCTION(DOTNET);
extern PHP_MSHUTDOWN_FUNCTION(DOTNET);
PHP_FUNCTION(DOTNET_load);
extern zend_module_entry DOTNET_module_entry;
#define DOTNET_module_ptr &DOTNET_module_entry
#else
#define DOTNET_module_ptr NULL
#endif /* Win32|WINNT */
#define phpext_DOTNET_ptr DOTNET_module_ptr
#endif /* PHP_DOTNET_H */

14
ext/rpc/com/COM.c

@ -66,6 +66,10 @@ function_entry COM_functions[] = {
{NULL, NULL, NULL}
};
__declspec(dllexport)
int php_COM_get_le_idispatch() {
return le_idispatch;
}
static PHP_MINFO_FUNCTION(COM)
{
@ -81,7 +85,8 @@ void php_register_COM_class();
static int php_COM_load_typelib(char *typelib_name, int mode);
static char *php_COM_error_message(HRESULT hr)
__declspec(dllexport)
char *php_COM_error_message(HRESULT hr)
{
void *pMsgBuf;
@ -428,7 +433,7 @@ static void php_variant_to_pval(VARIANTARG *var_arg, pval *pval_arg, int persist
var_arg->pdispVal->lpVtbl->Release(var_arg->pdispVal);
/* fallthru */
default:
php_error(E_WARNING,"Unsupported variant type");
php_error(E_WARNING,"Unsupported variant type: %d (0x%X)", var_arg->vt, var_arg->vt);
var_reset(pval_arg);
break;
}
@ -834,6 +839,7 @@ VARIANTARG _php_COM_get_property_handler(zend_property_reference *property_refer
}
__declspec(dllexport)
pval php_COM_get_property_handler(zend_property_reference *property_reference)
{
pval result;
@ -844,6 +850,7 @@ pval php_COM_get_property_handler(zend_property_reference *property_reference)
}
__declspec(dllexport)
int php_COM_set_property_handler(zend_property_reference *property_reference, pval *value)
{
pval result;
@ -907,6 +914,7 @@ int php_COM_set_property_handler(zend_property_reference *property_reference, pv
__declspec(dllexport)
void php_COM_call_function_handler(INTERNAL_FUNCTION_PARAMETERS, zend_property_reference *property_reference)
{
zend_overloaded_element *overloaded_property;
@ -934,6 +942,8 @@ void php_COM_call_function_handler(INTERNAL_FUNCTION_PARAMETERS, zend_property_r
int arg_count = ZEND_NUM_ARGS();
VARIANTARG var_result;
var_result.vt = VT_EMPTY;
if (object_handle.vt != VT_DISPATCH) {
/* that shouldn't happen */
return;

32
ext/rpc/dotnet/README

@ -0,0 +1,32 @@
Warning
=======
This support is EXPERIMENTAL. In fact, it integrates code that
Microsoft labels as pre-beta. Use at your own risk.
Build instructions
==================
Download and install the .NET Framework SDK Technology Preview from
http://msdn.microsoft.com/net/#sdk. Once installed, copy Mscoree.h
(typically found in C:\Program Files\NGWSSDK\Include to ext\dotnet).
Do not simply add the NGWSSDK\Include directory to the include path
as this will cause compilation failures.
Download and unzip the source to the dm.net COM Moniker from
http://staff.develop.com/jasonw/clr/readme.htm. Copy mscorlib.h
to ext\dotnet. There is no need to register the clrmonsrv.dll as
it is not used.
At this point, the dotnet project can be built like any other
project, from either VisualStudio 6's GUI or from the command line.
Example command line invocation:
msdev dotnet.dsp /MAKE "dotnet - Win32 Debug_TS"
Execution instructions:
=======================
Add "extension=php_dotnet.dll" into php.ini.
Sample program can be found at dotnet.php

243
ext/rpc/dotnet/dotnet.cpp

@ -0,0 +1,243 @@
/*
+----------------------------------------------------------------------+
| PHP version 4.0 |
+----------------------------------------------------------------------+
| Copyright (c) 1997, 1998, 1999, 2000 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: Sam Ruby <rubys@us.ibm.com> |
+----------------------------------------------------------------------+
*/
/*
* This module implements support for Microsoft .Net components.
*/
#ifdef PHP_WIN32
#include <iostream.h>
#include <math.h>
#include <comdef.h>
extern "C" {
#include "php.h"
#include "php_ini.h"
pval php_COM_get_property_handler(zend_property_reference *property_reference);
int php_COM_set_property_handler(zend_property_reference *property_reference, pval *value);
char *php_COM_error_message(HRESULT hr);
void php_COM_call_function_handler(INTERNAL_FUNCTION_PARAMETERS, zend_property_reference *property_reference);
int php_COM_get_le_idispatch();
}
#include <stdio.h>
#include "Mscoree.h"
#include "mscorlib.h"
using namespace mscorlib;
static ICorRuntimeHost *pHost;
static mscorlib::_AppDomain *pDomain;
static zend_class_entry dotnet_class_entry;
static OLECHAR *php_char_to_OLECHAR(char *C_str, uint strlen)
{
OLECHAR *unicode_str = (OLECHAR *) emalloc(sizeof(OLECHAR)*(strlen+1));
OLECHAR *unicode_ptr = unicode_str;
while (*C_str) {
*unicode_ptr++ = (unsigned short) *C_str++;
}
*unicode_ptr = 0;
return unicode_str;
}
HRESULT dotnet_init() {
HRESULT hr;
hr = CoCreateInstance(CLSID_CorRuntimeHost, NULL, CLSCTX_ALL,
IID_ICorRuntimeHost, (void **)&pHost);
if (FAILED(hr)) return hr;
hr = pHost->Start();
if (FAILED(hr)) return hr;
IUnknown *uDomain;
hr = pHost->GetDefaultDomain(&uDomain);
if (FAILED(hr)) return hr;
hr = uDomain->QueryInterface(__uuidof(_AppDomain), (void**) &pDomain);
if (FAILED(hr)) return -1;
uDomain->Release();
return ERROR_SUCCESS;
}
HRESULT dotnet_create(OLECHAR *assembly, OLECHAR *datatype, struct IDispatch **object) {
HRESULT hr;
_ObjectHandle *pHandle;
hr = pDomain->CreateInstance(_bstr_t(assembly), _bstr_t(datatype), &pHandle);
if (FAILED(hr)) return hr;
if (!pHandle) return hr;
_variant_t unwrapped;
hr = pHandle->Unwrap(&unwrapped);
pHandle->Release();
if (FAILED(hr)) return hr;
*object = unwrapped.pdispVal;
return ERROR_SUCCESS;
}
void dotnet_term() {
if (pHost) pHost->Stop();
if (pHost) pHost->Release();
if (pDomain) pDomain->Release();
pHost = 0;
pDomain = 0;
}
/* {{{ proto int dotnet_load(string module_name)
Loads a DOTNET module */
PHP_FUNCTION(DOTNET_load)
{
HRESULT hr;
pval *assembly_name, *datatype_name;
OLECHAR *assembly, *datatype;
IDispatch FAR *i_dispatch = NULL;
if (ZEND_NUM_ARGS() != 2) WRONG_PARAM_COUNT;
getParameters(ht, 2, &assembly_name, &datatype_name);
convert_to_string(assembly_name);
assembly = php_char_to_OLECHAR(assembly_name->value.str.val, assembly_name->value.str.len);
convert_to_string(datatype_name);
datatype = php_char_to_OLECHAR(datatype_name->value.str.val, datatype_name->value.str.len);
/* obtain IDispatch */
hr=dotnet_create(assembly, datatype, &i_dispatch);
efree(assembly);
efree(datatype);
if (FAILED(hr)) {
char *error_message;
error_message = php_COM_error_message(hr);
php_error(E_WARNING,"Error obtaining .Net class for %s in assembly %s: %s",datatype_name->value.str.val,assembly_name->value.str.val,error_message);
LocalFree(error_message);
RETURN_FALSE;
}
if (!i_dispatch) {
php_error(E_WARNING,"Unable to locate %s in assembly %s",datatype_name->value.str.val,assembly_name->value.str.val);
RETURN_FALSE;
}
RETURN_LONG(zend_list_insert(i_dispatch,php_COM_get_le_idispatch()));
}
/* }}} */
void php_DOTNET_call_function_handler(INTERNAL_FUNCTION_PARAMETERS, zend_property_reference *property_reference)
{
pval *object = property_reference->object;
zend_overloaded_element *function_name = (zend_overloaded_element *) property_reference->elements_list->tail->data;
if (zend_llist_count(property_reference->elements_list)==1
&& !strcmp(function_name->element.value.str.val, "dotnet")) { /* constructor */
pval *object_handle;
PHP_FN(DOTNET_load)(INTERNAL_FUNCTION_PARAM_PASSTHRU);
if (!zend_is_true(return_value)) {
var_reset(object);
return;
}
ALLOC_ZVAL(object_handle);
*object_handle = *return_value;
pval_copy_constructor(object_handle);
INIT_PZVAL(object_handle);
zend_hash_index_update(object->value.obj.properties, 0, &object_handle, sizeof(pval *), NULL);
pval_destructor(&function_name->element);
} else {
php_COM_call_function_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, property_reference);
}
}
void php_register_DOTNET_class()
{
INIT_OVERLOADED_CLASS_ENTRY(dotnet_class_entry, "DOTNET", NULL,
php_DOTNET_call_function_handler,
php_COM_get_property_handler,
php_COM_set_property_handler);
zend_register_internal_class(&dotnet_class_entry);
}
function_entry DOTNET_functions[] = {
{NULL, NULL, NULL}
};
static PHP_MINFO_FUNCTION(DOTNET)
{
DISPLAY_INI_ENTRIES();
}
PHP_INI_BEGIN()
PHP_INI_END()
PHP_MINIT_FUNCTION(DOTNET)
{
HRESULT hr;
CoInitialize(0);
hr = dotnet_init();
if (FAILED(hr)) return hr;
php_register_DOTNET_class();
REGISTER_INI_ENTRIES();
return SUCCESS;
}
PHP_MSHUTDOWN_FUNCTION(DOTNET)
{
dotnet_term();
CoUninitialize();
UNREGISTER_INI_ENTRIES();
return SUCCESS;
}
zend_module_entry dotnet_module_entry = {
"dotnet", DOTNET_functions, PHP_MINIT(DOTNET), PHP_MSHUTDOWN(DOTNET), NULL, NULL, PHP_MINFO(DOTNET), STANDARD_MODULE_PROPERTIES
};
extern "C" {
ZEND_GET_MODULE(dotnet)
}
void php_register_DOTNET_class();
#endif

167
ext/rpc/dotnet/dotnet.dsp

@ -0,0 +1,167 @@
# Microsoft Developer Studio Project File - Name="dotnet" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
CFG=dotnet - Win32 Debug_TS
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "dotnet.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "dotnet.mak" CFG="dotnet - Win32 Debug_TS"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "dotnet - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
!MESSAGE "dotnet - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
!MESSAGE "dotnet - Win32 Debug_TS" (based on "Win32 (x86) Dynamic-Link Library")
!MESSAGE "dotnet - Win32 Release_TS" (based on "Win32 (x86) Dynamic-Link Library")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
MTL=midl.exe
RSC=rc.exe
!IF "$(CFG)" == "dotnet - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "..\..\Release"
# PROP BASE Intermediate_Dir "..\..\Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "..\..\Release"
# PROP Intermediate_Dir "..\..\Release"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MD /W3 /GX /O2 /D "PHP_WIN32" /D "ZEND_WIN32" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c
# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\.." /I "..\..\Zend" /I "$(JAVA_HOME)\include\win32" /I "$(JAVA_HOME)\include" /I "..\..\..\bindlib_w32" /D "NDEBUG" /D ZEND_DEBUG=0 /D "PHP_WIN32" /D "ZEND_WIN32" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "COMPILE_DL_JAVA" /D HAVE_JAVA=1 /YX /FD /c
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x40d /d "NDEBUG"
# ADD RSC /l 0x40d /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# 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
# ADD 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 /out:"..\..\Release/php_dotnet.dll" /libpath:"$(JAVA_HOME)\lib" /libpath:"..\..\Release"
!ELSEIF "$(CFG)" == "dotnet - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "..\..\Debug"
# PROP BASE Intermediate_Dir "..\..\Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "..\..\Debug"
# PROP Intermediate_Dir "..\..\Debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "PHP_WIN32" /D "ZEND_WIN32" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /GZ /c
# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\.." /I "..\..\Zend" /I "$(JAVA_HOME)\include\win32" /I "$(JAVA_HOME)\include" /I "..\..\..\bindlib_w32" /D "_DEBUG" /D ZEND_DEBUG=1 /D "PHP_WIN32" /D "ZEND_WIN32" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "COMPILE_DL_JAVA" /D HAVE_JAVA=1 /FR /YX /FD /GZ /c
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x40d /d "_DEBUG"
# ADD RSC /l 0x40d /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# 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
# ADD 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 /out:"..\..\Debug/php_dotnet.dll" /pdbtype:sept /libpath:"$(JAVA_HOME)\lib" /libpath:"..\..\Debug"
!ELSEIF "$(CFG)" == "dotnet - Win32 Debug_TS"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "..\..\Debug_TS"
# PROP BASE Intermediate_Dir "..\..\Debug_TS"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "..\..\Debug_TS"
# PROP Intermediate_Dir "..\..\Debug_TS"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\.." /I "..\..\..\Zend" /I "$(JAVA_HOME)\include\win32" /I "$(JAVA_HOME)\include" /I "..\..\..\bindlib_w32" /D "_DEBUG" /D "PHP_WIN32" /D "ZEND_WIN32" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "COMPILE_DL_JAVA" /FR /YX /FD /GZ /c
# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\..\\" /I "..\..\main" /I "..\..\Zend" /I "$(JAVA_HOME)\include\win32" /I "$(JAVA_HOME)\include" /I "..\..\..\bindlib_w32" /D "_DEBUG" /D ZEND_DEBUG=1 /D "ZTS" /D "PHP_WIN32" /D "ZEND_WIN32" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "COMPILE_DL_JAVA" /D HAVE_JAVA=1 /FR /YX /FD /GZ /c
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x40d /d "_DEBUG"
# ADD RSC /l 0x40d /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# 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
# ADD 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 php4ts_debug.lib /nologo /dll /debug /machine:I386 /out:"..\..\Debug_TS/php_dotnet.dll" /pdbtype:sept /libpath:"$(JAVA_HOME)\lib" /libpath:"..\..\Debug_TS"
!ELSEIF "$(CFG)" == "dotnet - Win32 Release_TS"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "..\..\Release_TS"
# PROP BASE Intermediate_Dir "..\..\Release_TS"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "..\..\Release_TS"
# PROP Intermediate_Dir "..\..\Release_TS"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MD /W3 /GX /O2 /I "..\.." /I "..\..\..\Zend" /I "$(JAVA_HOME)\include\win32" /I "$(JAVA_HOME)\include" /I "..\..\..\bindlib_w32" /D "NDEBUG" /D "PHP_WIN32" /D "ZEND_WIN32" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "COMPILE_DL_JAVA" /YX /FD /c
# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\\" /I "..\..\main" /I "..\..\Zend" /I "$(JAVA_HOME)\include\win32" /I "$(JAVA_HOME)\include" /I "..\..\..\bindlib_w32" /D "NDEBUG" /D ZEND_DEBUG=0 /D "ZTS" /D "PHP_WIN32" /D "ZEND_WIN32" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "COMPILE_DL_JAVA" /D HAVE_JAVA=1 /YX /FD /c
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x40d /d "NDEBUG"
# ADD RSC /l 0x40d /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# 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
# ADD 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 php4ts.lib /nologo /dll /machine:I386 /out:"..\..\Release_TS/php_dotnet.dll" /libpath:"$(JAVA_HOME)\lib" /libpath:"..\..\Release_TS"
!ENDIF
# Begin Target
# Name "dotnet - Win32 Release"
# Name "dotnet - Win32 Debug"
# Name "dotnet - Win32 Debug_TS"
# Name "dotnet - Win32 Release_TS"
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File
SOURCE=.\dotnet.cpp
# End Source File
# Begin Source File
SOURCE=.\..\..\win32\winutil.c
# End Source File
# End Group
# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File
SOURCE=.\php_dotnet.h
# End Source File
# End Group
# End Target
# End Project

8
ext/rpc/dotnet/dotnet.php

@ -0,0 +1,8 @@
<?
$stack = new DOTNET("mscorlib","System.Collections.Stack");
$stack->Push(".Net");
$stack->Push("Hello ");
echo $stack->Pop() . $stack->Pop();
?>

21
ext/rpc/dotnet/php_dotnet.h

@ -0,0 +1,21 @@
#ifndef PHP_DOTNET_H
#define PHP_DOTNET_H
#if WIN32|WINNT
extern PHP_MINIT_FUNCTION(DOTNET);
extern PHP_MSHUTDOWN_FUNCTION(DOTNET);
PHP_FUNCTION(DOTNET_load);
extern zend_module_entry DOTNET_module_entry;
#define DOTNET_module_ptr &DOTNET_module_entry
#else
#define DOTNET_module_ptr NULL
#endif /* Win32|WINNT */
#define phpext_DOTNET_ptr DOTNET_module_ptr
#endif /* PHP_DOTNET_H */

1
ext/standard/info.c

@ -396,6 +396,7 @@ void php_print_credits(int flag)
CREDIT_LINE("DBA", "Sascha Schumann");
CREDIT_LINE("DBM", "Rasmus Lerdorf, Jim Winstead");
CREDIT_LINE("dBase", "Jim Winstead");
CREDIT_LINE("dotnet", "Sam Ruby");
CREDIT_LINE("FDF", "Uwe Steinmann");
CREDIT_LINE("FilePro", "Chad Robinson");
CREDIT_LINE("FTP", "Andrew Skalski");

Loading…
Cancel
Save