Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trouble using DOTNET from PHP.

I've been trying to call a .net assembly from PHP through com (using DOTNET()). It seems like php is finding the DLL and initializing properly, but I can't see/use the methods for some reason. Anyone know how I might be able to fix this?

Here is the php code I'm using to call the .net class. When I call it the output is "hello1 hello2". When I try to directly call the function by doing $csclass->ModelBuilder("","") I get a 500 server error specifying that it couldn't find the function.

<?php
echo "hello1";
try{
$csclass = new DOTNET("ModelBuilder, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1208136d23b48dc5",
                      "ModelBuilder.ModelBuilder2");

$class_methods = get_class_methods($csclass);

foreach ($class_methods as $method_name) {
    echo "$method_name\n";
}

} catch (Exception $e) {
    echo 'Caught exception: ',  $e->getMessage(), "\n";
}
echo "hello2";
?>

Here is the the class in the assembly I'm trying to call (built using .net 3.5, signed with a strong name, and registered with gacutil):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using OfficeOpenXml;
using System.Runtime.InteropServices;

namespace ModelBuilder
{    
    [ClassInterface(ClassInterfaceType.AutoDual)]
    [ComVisible(true)]
    public class ModelBuilder2
    {
        [ComVisible(true)]
        public Boolean BuildModel(String outputFileLoc,String excelTemplateFile)
        {
            try
            {
                //do stuff
            return true;
        }
        catch (Exception e)
        {                
            return false;
        }
    }

} 
like image 501
user1167650 Avatar asked Feb 20 '23 06:02

user1167650


1 Answers

The get_class_methods() will not work with a Dotnet class object. That thing is a proxy, and it does not behave the way a normal php object does.

If you really are focused on listing the methods of the object, then you need to look into implementing IDispatch on your .NET object. If, however, your main goal is to simply use the .NET object, and your attempt to list the methods was just a side effort to diagnose why that was not working, then I have some suggestions for you.

  1. rather than using gacutil, consider whether you can insert the required DLL into the php directory, the place where php.exe resides. If php.exe resides at c:\php5\php.exe, then copy your assembly into c:\php5.

  2. If you follow the above suggestion, the .NET assembly need not be strongly named. It needs to be strongly named if you plan to GAC it. As I said, you don't need to GAC it, in order to load it with php. If you copy the assembly into the PHP dir, then you can use an un-signed assembly.

  3. For early development and exploration with this, use the php.exe program from the command-line, rather than using IIS to launch php via a web request. This lets you see the error messages directly, rather than worrying about 500 errors from IIS.


Example

Suppose this is the C# code:

using System;
namespace Ionic
{
    public class MathEx
    {
        System.Random rnd;
        public MathEx ()
        {
            rnd = new System.Random();
        }

        public int RandomEven()
        {
            return rnd.Next()*2;
        }
    }
}

Compile it from the command-line like this:

c:\net3.5\csc.exe /t:library /out:Ionic.MathEx.dll MathEx.cs

copy the assembly to the dir that holds php.exe:

copy Ionic.MathEx.dll \php
        1 file(s) copied.

Then, suppose I have a php script named mathex.php with these contents:

<?php
$my_assembly = 'Ionic.MathEx'; // name of the dll without the .dll suffix
$clz = new DOTNET($my_assembly, 'Ionic.MathEx');
for ($i=0; $i<5; $i++) {
    echo $i . " " . $clz->RandomEven() . "\n";
}
?>

... and if I run it from the command line this way:

\php\php.exe  mathex.php  

...then I get these results:

0 -1083602762
1 1535669896
2 -86761710
3 -1204365564
4 459406052

Nota Bene

I tried doing this with an assembly compiled with .NET 4.0, but it did not work, giving me an error message about a .NET runtime mismatch. Like this:

Fatal error: Uncaught exception 'com_exception' with message 'Failed to instantiate .Net object
[CreateInstance] [0x8013101b] ' in C:\dev\php\mathEx.php:6
Stack trace:
#0 C:\dev\php\mathEx.php(6): dotnet->dotnet('Ionic.MathEx', 'Ionic.MathEx')
#1 {main}
  thrown in C:\dev\php\mathEx.php on line 6

The 0x8013101b indicates a runtime mismatch - the runtime version of the assembly does not match the runtime version of the app that is trying to load the assembly.

From this I conclude that php5 is compiled to support .NET 2.0 (which includes 3.0 and 3.5). The runtime version changed for .NET 4.0, so assemblies that you compile with a .NET 4.0 compiler (including VS2010) will not be usable with php5.exe. The workaround is to just compile with the .NET 2.0 or 3.5 compiler (VS2005, vs2008).

like image 183
Cheeso Avatar answered Feb 23 '23 00:02

Cheeso