diff --git a/Peachpie.sln b/Peachpie.sln
index 2ff24ff158..63a40b78f7 100644
--- a/Peachpie.sln
+++ b/Peachpie.sln
@@ -82,6 +82,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Peachpie.NET.SdkTests", "sr
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Peachpie.App.Tests", "src\Tests\Peachpie.App.Tests\Peachpie.App.Tests.csproj", "{C8A0A533-BB62-4CB5-8861-06522DCF5EC2}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Peachpie.Library.ComDotNet", "src\Peachpie.Library.ComDotNet\Peachpie.Library.ComDotNet.csproj", "{B9DE827D-79F5-4066-80C5-7535EB66B443}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -654,6 +656,26 @@ Global
{C8A0A533-BB62-4CB5-8861-06522DCF5EC2}.Release|x64.Build.0 = Release|Any CPU
{C8A0A533-BB62-4CB5-8861-06522DCF5EC2}.Release|x86.ActiveCfg = Release|Any CPU
{C8A0A533-BB62-4CB5-8861-06522DCF5EC2}.Release|x86.Build.0 = Release|Any CPU
+ {B9DE827D-79F5-4066-80C5-7535EB66B443}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B9DE827D-79F5-4066-80C5-7535EB66B443}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B9DE827D-79F5-4066-80C5-7535EB66B443}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {B9DE827D-79F5-4066-80C5-7535EB66B443}.Debug|ARM.Build.0 = Debug|Any CPU
+ {B9DE827D-79F5-4066-80C5-7535EB66B443}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {B9DE827D-79F5-4066-80C5-7535EB66B443}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {B9DE827D-79F5-4066-80C5-7535EB66B443}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {B9DE827D-79F5-4066-80C5-7535EB66B443}.Debug|x64.Build.0 = Debug|Any CPU
+ {B9DE827D-79F5-4066-80C5-7535EB66B443}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {B9DE827D-79F5-4066-80C5-7535EB66B443}.Debug|x86.Build.0 = Debug|Any CPU
+ {B9DE827D-79F5-4066-80C5-7535EB66B443}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B9DE827D-79F5-4066-80C5-7535EB66B443}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B9DE827D-79F5-4066-80C5-7535EB66B443}.Release|ARM.ActiveCfg = Release|Any CPU
+ {B9DE827D-79F5-4066-80C5-7535EB66B443}.Release|ARM.Build.0 = Release|Any CPU
+ {B9DE827D-79F5-4066-80C5-7535EB66B443}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {B9DE827D-79F5-4066-80C5-7535EB66B443}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {B9DE827D-79F5-4066-80C5-7535EB66B443}.Release|x64.ActiveCfg = Release|Any CPU
+ {B9DE827D-79F5-4066-80C5-7535EB66B443}.Release|x64.Build.0 = Release|Any CPU
+ {B9DE827D-79F5-4066-80C5-7535EB66B443}.Release|x86.ActiveCfg = Release|Any CPU
+ {B9DE827D-79F5-4066-80C5-7535EB66B443}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -684,6 +706,7 @@ Global
{8BE90675-F686-4327-8E13-BE3F7B540CF5} = {82A72490-8B53-4A22-BB14-B0C4D6C83D67}
{937F85EB-7F06-4A34-8C7D-BA3BF88E71AA} = {30104149-66C2-44B1-899F-F97E9ECA3860}
{C8A0A533-BB62-4CB5-8861-06522DCF5EC2} = {30104149-66C2-44B1-899F-F97E9ECA3860}
+ {B9DE827D-79F5-4066-80C5-7535EB66B443} = {30D6D91F-A671-4E73-A571-5614500FDE0B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A0FC0FC5-DA92-4FD6-9841-D8ABF963E7AD}
diff --git a/build/dummy/dummy.msbuildproj b/build/dummy/dummy.msbuildproj
index 4f2a8cb56c..b89ff85095 100644
--- a/build/dummy/dummy.msbuildproj
+++ b/build/dummy/dummy.msbuildproj
@@ -12,6 +12,7 @@
+
diff --git a/build/update-cache.ps1 b/build/update-cache.ps1
index b869fbecbe..ecc753f12c 100644
--- a/build/update-cache.ps1
+++ b/build/update-cache.ps1
@@ -13,7 +13,10 @@ $defaultArgs = "/p:VersionPrefix=$version,VersionSuffix=$suffix"
## Delete old nuget packages
Write-Host -f green "Deleting '$version-$suffix' packages from '$packagesSource' ..."
-@("Peachpie.Runtime", "Peachpie.Library", "Peachpie.Library.Scripting", "Peachpie.Library.MySql", "Peachpie.Library.MsSql", "Peachpie.Library.Graphics", "Peachpie.Library.Network", "Peachpie.Library.PDO", "Peachpie.Library.XmlDom", "Peachpie.App", "Peachpie.CodeAnalysis", "Peachpie.AspNetCore.Web", "Peachpie.AspNetCore.Mvc", "Peachpie.NET.Sdk", "Peachpie.Library.PDO.MySql", "Peachpie.Library.PDO.Sqlite", "Peachpie.Library.SqlSrv") | % {
+@("Peachpie.Runtime", "Peachpie.Library", "Peachpie.Library.Scripting", "Peachpie.Library.MySql", "Peachpie.Library.MsSql",
+ "Peachpie.Library.Graphics", "Peachpie.Library.Network", "Peachpie.Library.PDO", "Peachpie.Library.XmlDom", "Peachpie.App",
+ "Peachpie.CodeAnalysis", "Peachpie.AspNetCore.Web", "Peachpie.AspNetCore.Mvc", "Peachpie.NET.Sdk", "Peachpie.Library.PDO.MySql",
+ "Peachpie.Library.PDO.Sqlite", "Peachpie.Library.SqlSrv", "Peachpie.Library.ComDotNet") | % {
$installedFolder = "$packagesSource/$_/$version-$suffix"
if (Test-Path $installedFolder) {
Remove-Item -Recurse -Force $installedFolder
diff --git a/src/Peachpie.Library.ComDotNet/Com.cs b/src/Peachpie.Library.ComDotNet/Com.cs
new file mode 100644
index 0000000000..c4640e59ab
--- /dev/null
+++ b/src/Peachpie.Library.ComDotNet/Com.cs
@@ -0,0 +1,121 @@
+using System;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using Pchp.Core;
+
+namespace Peachpie.Library.ComDotNet
+{
+ ///
+ /// COM object
+ /// TODO : implements VARIANT and inherit from it, which would allow returning VARIANT object from __call and __get
+ /// and working with it, see test case com_dotnet/com.php
+ ///
+ [PhpType(PhpTypeAttribute.InheritName), PhpExtension("com_dotnet")]
+ public class COM
+ {
+ #region Private members
+
+ [PhpHidden]
+ internal protected object comObject = null;
+
+ [PhpHidden]
+ internal protected const BindingFlags MemberAccessCom =
+ BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase;
+
+ #endregion
+
+ #region Construction
+
+ public COM(Context ctx, string module_name)
+ {
+ __construct(ctx, module_name);
+ }
+
+ [PhpFieldsOnlyCtor]
+ protected COM()
+ {
+ }
+
+ public virtual void __construct(Context ctx, string module_name)
+ {
+ var comType = Type.GetTypeFromProgID(module_name);
+ if (comType == null)
+ return;
+
+ comObject = Activator.CreateInstance(comType);
+ }
+
+ public virtual void __construct(Context ctx, string module_name, string server_name)
+ {
+ var comType = Type.GetTypeFromProgID(module_name, server_name);
+ if (comType == null)
+ return;
+
+ comObject = Activator.CreateInstance(comType);
+ }
+
+ public virtual void __construct(Context ctx, string module_name, string server_name, int code_page)
+ {
+ throw new NotSupportedException();
+ }
+
+ public virtual void __construct(Context ctx, string module_name, string server_name, int code_page, string typelib)
+ {
+ throw new NotSupportedException();
+ }
+
+ #endregion
+
+ #region Magic methods
+
+ ///
+ /// Special field containing runtime fields.
+ ///
+ ///
+ /// The field is handled by runtime and is not intended for direct use.
+ /// Magic methods for property access are ignored without runtime fields.
+ ///
+ [CompilerGenerated]
+ internal PhpArray __peach__runtimeFields = null;
+
+ public PhpValue __call(string name, PhpArray arguments)
+ {
+ if (comObject == null)
+ return PhpValue.Null;
+
+ var parameters = arguments.GetValues().Select(v => v.ToClr()).ToArray();
+
+ var result = comObject.GetType().InvokeMember(
+ name, MemberAccessCom | BindingFlags.InvokeMethod, null, comObject, parameters);
+
+ return PhpValue.FromClr(result);
+ }
+
+ public virtual PhpValue __get(string name)
+ {
+ if (comObject == null)
+ return PhpValue.Null;
+
+ var prop = comObject.GetType().InvokeMember(
+ name, MemberAccessCom | BindingFlags.GetProperty, null, comObject, null);
+
+ return prop != null
+ ? PhpValue.FromClr(prop)
+ : PhpValue.Null;
+ }
+
+ public virtual bool __set(string name, PhpValue value)
+ {
+ if (comObject == null)
+ return false;
+
+ comObject.GetType().InvokeMember(
+ name, MemberAccessCom | BindingFlags.SetProperty, null, comObject, new object[1] { value.ToClr() });
+
+ return true;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Peachpie.Library.ComDotNet/ComDotNet.cs b/src/Peachpie.Library.ComDotNet/ComDotNet.cs
new file mode 100644
index 0000000000..cbb95206e7
--- /dev/null
+++ b/src/Peachpie.Library.ComDotNet/ComDotNet.cs
@@ -0,0 +1,21 @@
+using System;
+using Pchp.Core;
+
+namespace Peachpie.Library.ComDotNet
+{
+ ///
+ /// COM functions
+ ///
+ [PhpExtension("com_dotnet")]
+ public static class ComDotNet
+ {
+ ///
+ /// Generate a globally unique identifier (GUID)
+ ///
+ public static PhpString com_create_guid()
+ {
+ return Guid.NewGuid().ToString("B");
+ }
+ }
+
+}
diff --git a/src/Peachpie.Library.ComDotNet/Peachpie.Library.ComDotNet.csproj b/src/Peachpie.Library.ComDotNet/Peachpie.Library.ComDotNet.csproj
new file mode 100644
index 0000000000..7004bbccba
--- /dev/null
+++ b/src/Peachpie.Library.ComDotNet/Peachpie.Library.ComDotNet.csproj
@@ -0,0 +1,21 @@
+
+
+
+ netstandard2.0
+ Peachpie.Library.ComDotNet
+ Peachpie.Library.ComDotNet
+ peachpie;library;com;dotnet;com_dotnet
+ True
+ Peachpie PHP language library functions for COM/.Net interoperability on Windows.
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Peachpie.Library.ComDotNet/Properties/AssemblyInfo.cs b/src/Peachpie.Library.ComDotNet/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..2685374ebe
--- /dev/null
+++ b/src/Peachpie.Library.ComDotNet/Properties/AssemblyInfo.cs
@@ -0,0 +1,11 @@
+using System.Reflection;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyProduct("Peachpie.Library.ComDotNet")]
+[assembly: AssemblyTrademark("")]
+
+// annotates this library as a php extension,
+// all its public static methods with compatible signatures will be seen as global functions to php scope
+[assembly: Pchp.Core.PhpExtension("com_dotnet")]
\ No newline at end of file
diff --git a/src/Tests/Peachpie.ScriptTests/Peachpie.ScriptTests.csproj b/src/Tests/Peachpie.ScriptTests/Peachpie.ScriptTests.csproj
index b36d9e06c2..1ce69c2127 100644
--- a/src/Tests/Peachpie.ScriptTests/Peachpie.ScriptTests.csproj
+++ b/src/Tests/Peachpie.ScriptTests/Peachpie.ScriptTests.csproj
@@ -20,6 +20,7 @@
+
diff --git a/src/Tests/Peachpie.ScriptTests/ScriptsTest.cs b/src/Tests/Peachpie.ScriptTests/ScriptsTest.cs
index 46ff3d02f5..693c86a74d 100644
--- a/src/Tests/Peachpie.ScriptTests/ScriptsTest.cs
+++ b/src/Tests/Peachpie.ScriptTests/ScriptsTest.cs
@@ -29,6 +29,7 @@ public class ScriptsTest
typeof(Peachpie.Library.Scripting.Standard).Assembly.Location,
typeof(Peachpie.Library.XmlDom.XmlDom).Assembly.Location,
typeof(Peachpie.Library.Network.CURLFunctions).Assembly.Location,
+ typeof(Peachpie.Library.ComDotNet.COM).Assembly.Location,
};
static readonly Context.IScriptingProvider _provider = Context.DefaultScriptingProvider; // use IScriptingProvider singleton
diff --git a/tests/com_dotnet/com.php b/tests/com_dotnet/com.php
new file mode 100644
index 0000000000..0cc26ffa30
--- /dev/null
+++ b/tests/com_dotnet/com.php
@@ -0,0 +1,34 @@
+GetTempName()) . PHP_EOL;
+ // echo $fso->GetSpecialFolder(0) . PHP_EOL; // <- Fails (COM_Object)
+ $drives = $fso->Drives;
+ echo gettype($drives).PHP_EOL;
+ foreach($drives as $d) {
+ //echo $d.PHP_EOL; // <- Fails (COM_Object)
+ $dO = $fso->GetDrive($d);
+ //echo $dO->DriveLetter.PHP_EOL; // <- Fails (COM_Object)
+ break;
+ }
+
+ echo "testing Shell" .PHP_EOL;
+ $shell = new \COM('WScript.Shell');
+ echo $shell->CurrentDirectory . PHP_EOL;
+ $shell->CurrentDirectory = "C:\\";
+ echo $shell->CurrentDirectory . PHP_EOL;
+}
+
+test();
diff --git a/tests/com_dotnet/com_create_guid.php b/tests/com_dotnet/com_create_guid.php
new file mode 100644
index 0000000000..17b02cdbbb
--- /dev/null
+++ b/tests/com_dotnet/com_create_guid.php
@@ -0,0 +1,17 @@
+