PHP DotNet und .Net Framework 4.0

JeyB

Mitglied
Hallo,

ich möchte eine Instanz in PHP von einer DLL erstellen, welche in C# geschrieben wurde und mit dem .Net Framework 4.0 kompiliert wurde. PHP findet allerdings die zu ladende dll, welche sich im globalen Assembly Cache (neuerdings unter dem Microsoft.Net Verzeichnis) befindet, nicht:

Failed to instantiate .Net object [CreateInstance] [0x80070002] Das System kann die angegebene Datei nicht finden.

Ich vermute, dass PHP nur in dem globalen Assembly Cache unter dem "Windows\assembly" Verzeichnis nach der DLL schaut, nicht aber unter dem neuen Verzeichnis. Diese Vermutung hat sich bei mir verstärkt, da das Erstellen einer Instanz funktioniert hatte, sofern die DLL im globalen Assembly Cache unter Windows registriert war. Hat jemand eine Idee ob die DOTNET Klasse von PHP auch bei Assemblies, welche mit .Net Framework 4.0 kompiliert wurden, verwendet werden können? Oder gibt es eine Möglichkeit die .Net 4.0 DLL mittels gacutil 4.0 in den alten globalen assembly cache zu registrieren? Mein Aufruf sieht folgendermaßen aus:

PHP:
$lFullAssemblyName = "Assembly Name, Version=x.x.x.x, Culture=neutral, ".
"PublicKeyToken=xxxxxxxxxxxxxxxx, processorArchitecture=MSIL";
$lFullClassName = "Namespace.Classname";

try
{
        $lDotNet = new DOTNET($lFullAssemblyName, $lFullClassName);
}
catch(Exception $lEx)
{
        die("Error occurred:<br>".$lEx->getMessage());
}

Zur Erinnerung: Der Aufruf funktioniert bei Assemblies, die im alten globalen Assembly Cache installiert sind (windows\assembly). Die Abhängigkeiten der DLL wurden auch alle gefunden (überprüft mit dem Dependency Walker). Außerdem würde das Fehlen einer Abhängigkeit explizit in der Fehlermeldung erscheinen.

Gruß, JeyB
 
Zuletzt bearbeitet:
Soweit ich das einsehen kann, ruft die Klasse grundsätzlich nur DLLs aus dem GAC auf...

I can't explain the whole process as I'm not even sure. But as far as I know, in order to get PHP's DOTNET() to work with it, the dll must be registered in the GAC (see: en.wikipedia.org/wiki/Global_Assembly_Cache). Before that though, within the dll, you must jump through the required hoops to get your dll classes and methods to be COM visible (see: msdn.microsoft.com/en-us/library/zsfww439.aspx). Since you have the source, you can clean it up, set it up for COM usage, recompile, then register in the GAC where DOTNET() can find and use it.

Quelle
 
Das habe ich auch befürchtet. Gibt es aber keine andere Möglichkeit die .Net 4.0 DLL aus der PHP DotNet Klasse heraus zu verwenden oder diese in den alten GAC zu registrieren?

Vielen Dank für jede weitere Antwort.

Gruß, JeyB
 
Ich habe den Beitrag mal ins .NET Forum verschoben, weil ich vermute dass hier die Leute mit mehr Ahnung sitzen und nicht den PHP Bereich checken ;)
 
Also ich habe Änderungen in der com_dotnet.c Datei vorgenommen.
Statt die Assembly direkt vom GAC zu laden (mittels CreateInstance) habe ich die Funktion CreateInstanceFrom verwendet. Danach habe ich PHP 5.3.3 nts (not thread safe) kompiliert und getestet. Ich kann jetzt direkt statt den vollständigen Assemblynamen, den Pfad der DLL angeben und es wird eine Instanz der angegebenen Klasse erstellt. Mein Aufruf sieht jetzt folgendermaßen aus:

PHP:
$lAssemblyPath = "C:\\bin\\Assembly.dll";
$lFullClassName = "Namespace.Classname";

try
{
        $lDotNet = new DOTNET($lAssemblyPath, $lFullClassName);
}
catch(Exception $lEx)
{
        die("Error occurred:<br>".$lEx->getMessage());
}

Nachteile:
-Es dauert etwas länger bis die Assembly geladen wurde (laden aus dem GAC ist schneller)
-Ich kann nur eine Instanz von einer Assembly Klasse, welche mit .Net Framework < 4.0 kompiliert wurde, erzeugen.

Der Fehler, dass er die Assembly nicht findet, erscheint nicht mehr, dafür aber dieser Fehler:
Failed to instantiate .Net object [CreateInstance] [0x8013101b]

Anscheinend kann ich keine Instanz einer Klasse erzeugen, die mit .Net 4.0 kompiliert wurde.
Hat noch jemand eine Idee? Lasst euch nicht von dem [CreateInstance] verwirren. Das steht nur da drin, weil dieser Wert zuvor in einer Variablen gespeichert wurde. Ich verwende also wirklich die Funktion CreateInstanceFrom.

Gruß, JeyB
 
Zuletzt bearbeitet:
Habe noch nie was mit PHP gemacht. Den einzigen Satz, den ich verstehe, ist der über die COM-Sichtbarkeit. Besitzt du den Quellcode der Assembly bzw. wurde sie als COM-Sichtbar kompiliert? Das ist in VisualStudio nämlich standardmäßig deaktiviert.
 
Habe die Assembly COM-Sichtbar kompiliert. Es funktioniert ja, sofern ich für die Assembly .Net Framework < 4.0 verwende. Habe nun in der com_dotnet.c Datei gesehen, das die Schnittstelle von System.AppDomain für die Instanziierung einer .Net Klasse verwendet wird. Zum laden wird die GUID angegeben. Mittels oleview konnte ich die GUID der Schnittstelle, welche in der mscorlib.dll definiert ist, herausfinden und habe diese GUID von .Net 2.0 durch die von .Net 4.0 ersetzt.

PHP:
// .Net 2.0 GUID der Schnittstelle System.AppDomain
//const GUID IID_mscorlib_System_AppDomain = {
//0x05F696DC, 0x2B29, 0x3663, {0xAD, 0x8B, 0xC4, 0x38, 0x9C, 0xF2, 0xA7, 0x13 }};

// .Net 4.0 GUID der Schnittstelle System.AppDomain
const GUID IID_mscorlib_System_AppDomain = {
0x5FE0A145, 0xA82B, 0x3D96, {0x94, 0xE3, 0xFD, 0x21, 0x4C, 0x9D, 0x6E, 0xB9 }};

Nun erhalte ich folgenden Fehler:

Failed to init .Net runtime [QI: System._AppDomain] Schnittstelle nicht unterstützt

D.h. er scheitert beim Aufruf der Funktion "IUnknown_QueryInterface".

PHP:
static HRESULT dotnet_init(char **p_where TSRMLS_DC)
{
	HRESULT hr;
	struct dotnet_runtime_stuff *stuff;
	IUnknown *unk = NULL;
	char *where = "";

	stuff = malloc(sizeof(*stuff));
	memset(stuff, 0, sizeof(*stuff));

	where = "CoCreateInstance";
	hr = CoCreateInstance(&CLSID_CorRuntimeHost, NULL, CLSCTX_ALL,
			&IID_ICorRuntimeHost, (LPVOID*)&stuff->dotnet_host);

	if (FAILED(hr))
		goto out;

	/* fire up the host and get the domain object */
	where = "ICorRuntimeHost_Start\n";
	hr = ICorRuntimeHost_Start(stuff->dotnet_host);
	if (FAILED(hr))
		goto out;
	
	where = "ICorRuntimeHost_GetDefaultDomain";
	hr = ICorRuntimeHost_GetDefaultDomain(stuff->dotnet_host, &unk);
	if (FAILED(hr))
		goto out;

	where = "QI: System._AppDomain";
        // Dieser Aufruf verläuft nicht positiv
	hr = IUnknown_QueryInterface(unk, &IID_mscorlib_System_AppDomain, (LPVOID*)&stuff->dotnet_domain);
	if (FAILED(hr))
		goto out;
		
	COMG(dotnet_runtime_stuff) = stuff;

out:
	if (unk) {
		IUnknown_Release(unk);
	}
	if (COMG(dotnet_runtime_stuff) == NULL) {
		/* clean up */
		if (stuff->dotnet_domain) {
			IUnknown_Release(stuff->dotnet_domain);
		}
		if (stuff->dotnet_host) {
			ICorRuntimeHost_Stop(stuff->dotnet_host);
			ICorRuntimeHost_Release(stuff->dotnet_host);
		}
		free(stuff);

		*p_where = where;

		return hr;
	}

	return S_OK;
}

Im Anhang befindet sich die vollständige com_dotnet.c Datei.
Habe auch nachgeschaut ob in anderen Dateien eine Verwendung einer .Net 2.0 Assembly gemacht wird , konnte aber dies bezüglich nichts finden. Hat sonst noch jemand eine Idee? Bin für jede weitere Antwort dankbar.

Gruß, JeyB
 

Anhänge

  • com_dotnet.zip
    3,2 KB · Aufrufe: 34

Neue Beiträge

Zurück