Call a C# .net .dll script using PHP - c#

I have a C# .net dll script that calls a SQL stored procedure that SELECTs data in order to do run relevant methods.
I need to run the dll using PHP as my entire application is built in PHP.
What is the best way of doing this?
I'm not experienced at all with C#.
EDIT
I successfully registered the .net dll using:
RegAsm.exe DllName.dll /tlb:DllName.tlb
I should now be able to use PHP's COM() method as described below to call the dll's functions/methods.
But will these functions still be accessible through the COM() method as the .net dll was registered as an assembly? Does it make a difference?
EDIT
After registering the .net dll assembly I get an error message when I try to call the method using COM():
"PHP Fatal error: Uncaught exception 'com_exception' with message 'Failed
to create COM object `DllName.ClassName': Invalid syntax"
EDIT
Tried using:
new DOTNET('DllName, Version=4.0.30319.33440, Culture=neutral,
PublicTokenKey=14843e0419858c21', 'ClassName');
got an internal server 500 error
Is this because PHP doesn't communicate with .net 4 assemblies?

Option 1: Use the DLL
You can call the function using PHP's COM class.
You'll need to be running PHP on Windows, which I assume you are if you're using a C# DLL.
Steps:
Register the DLL using the command regasm yourdllname.dll in Command Prompt or the Run dialog.
You have one RegAsm.exe for each version of .NET installed on your computer, so make sure to execute the one for the version of .NET that the DLL targets by running it from %windir%\Microsoft.NET\AppropriateVersion.
Create a COM object in PHP that references the class name of the DLL.
Call the function, which will be available as a method of the COM object you've created.
Example:
$object = new COM('MyDllProjectName.MyDllClassName');
$object->myMethod();
Option 2: Rewrite in PHP
As has been mentioned, the cleaner, cross-platform option is to re-implement the SQL query in PHP directly, especially if your only reason for using the DLL is to run a query.

Using COM directly has many caveats and issues.
There is a library called NetPhp that abstracts on top of COM using reflection, so that you can call ANY .dll from within PHP without hassle:
http://www.drupalonwindows.com/en/blog/calling-net-framework-and-net-assemblies-php

Related

Why does instantiating COM object in C# throw exception

I'm trying to instantiate a COM object, defined in a x86 dll written in Borland C++, in a testing program i write in C# (.net 4.7.2). The COM dll (server) is working, I have a windows service also written in C++ Borland that can use it and instantiate a COM object from the class (using CoCreateInstance). The dll is registered and the InprocServer32 entry has the correct path to the dll. There is no coclass existing in a typelib, only interfaces (those exist in the typelib). I have used the TlbImp to create dll:s which i reference in the c# project. In the project the target platform is set to x86. The way i try to instantiate an object is:
var comType = Type.GetTypeFromProgID("ins.MyComType");
object comObj = Activator.CreateInstance(comType);
however the second line gives me
"Exception thrown: 'System.IO.FileNotFoundException' in mscorlib.dll"
with the message 'Retrieving the COM class factory for component with
CLSID {C4363C5E-3831-46DF-8701-60C8D1B612BA} failed due to the
following error: 8007007e The specified module could not be found.
(Exception from HRESULT: 0x8007007E).".
It does not matter if i try to run the app as administrator. I have a vague memory of trying out a similar thing a couple of years ago and that it at that time worked. It was probably on a Win 7 machine (might even have been a 32-bit system). I have tried to open the project in DependencyWalker but i'm not sure what i'm looking at. I get a couple of errors:
*Error: At least one required implicit or forwarded dependency was not found.
*Error: Modules with different CPU types were found.
*Error: A circular dependency was detected.
*Warning: At least one delay-load dependency module was not found.
*Warning: At least one module has an unresolved import due to a missing export function in a delay-load dependent module.
Does any one have any idea on what it could be causing the exception? Or if i could get some hints as of how to dig deeper into dependencywalker? I get a gigantic tree of systemassembly stuff but i cannot see any obvious assembly standing out, though DW refers to many of them as being 64 bit. My guess is some dependent dll(s) somewhere should be x86 but which one(s). Is there a redist similar thingi i should have installed for this to work?
best regards
/Erik
You should write a simple VBScript that contains these lines:
set obj = CreateObject("ins.MyComType")
MsgBox TypeName(obj)
Name the file test.vbs
Execute the command:
c:\windows\syswow64\wscript.exe test.vbs
Using the version from syswow64 ensures that it uses the 32-bit version of wscript.exe which can instantiate 32-bit COM objects. The version in c:\windows\system32 can only instantiate 64-bit In-process COM objects in DLLs...you said your object is a 32-bit COM DLL server.
If the vbscript fails, it could be that the object is not automation compatible--implements IDispatch. Otherwise you will get an error message why it fails.
If it succeeds, you will know there is probably nothing on the C++ side causing problems. You THINK this is the case...but where is the runtime for Borland C++? Is everything statically linked, or is some of it dynamically linked? If it is dynamically linked, where is it in the path? It could be that the C++ service you have has the library in its path so that when it loads your COM object, the library is available. But, when you try to load from a 3rd party, like .NET or VBScript then the path to the library manifests itself. Who knows? I'm just making suggestions.
If you use ProcMon, it can see where the problems are. It will show you what registry entries are being read and which files are trying to be loaded.

Fixing PlatformNotSupportedException when referencing System.Data.SqlClient from C# Azure Function

I am creating an Azure Function in C# using a target framework of netstandard2.0 in a Windows 10 environment. The function calls a method that's in another class library and that method creates an instance of SqlConnection. When I run the function I get the following exception:
Microsoft.Azure.WebJobs.Host.FunctionInvocationException : Exception
while executing function: Functions.RefreshImages --->
System.Reflection.TargetInvocationException : Exception has been
thrown by the target of an invocation. --->
System.PlatformNotSupportedException : System.Data.SqlClient is not
supported on this platform. at
System.Data.SqlClient.SqlConnection..ctor(String connectionString)......
Obviously SqlConnection is supported on Windows so I assume there's something else going on here.
This happens when a .Net Standard lib that uses a SqlConnection is loaded dynamically via reflection. The .Net Standard lib will typically reference System.Data.SqlClient which seems to be a dummy lib without actual implementation. It apparently ensures that the lib will compile on all platforms, including those without Registry and other platform specific stuff that the real SqlClient implementation relies on.
The easiest solution I can find is to add a reference to the Microsoft.Data.SqlClient NuGet package in the host application (the .Net core application that dynamically loads the .Net standard lib).
You may see a small yellow warning exclamation icon in the Solution explorer because Visual Studio thinks you are not using the lib and if you use the "Remove Unused References" feature it will also suggest removing the package. There is a feature to suppress warnings in the PropertyGrid, but I cannot figure out wat number should be filled in since the warning does not appear in the error list when compiling...
It looks like this is related to loading a SQL connection via reflection in .NET core (you are running on netstandard2.0 but the principle should still be the same).

CoCreateInstance fail with error "Class not registered" while using COM visible .NET class

Hello
I created COM visible class MyClass on C#
registered by regasm command - registration passed OK
And I can see in registry progid corresponding to MyClass
In the client C++ code :
1) call of CLSIDFromProgID passed OK
2) call of CoCreateInstance fail with error "Class not registered" (80040154)
What can be possible reasons of such behavior - your help will be very valuable
Thanks in advance
I just want to add that the "class not registered" problem may happen because one uses the incorrect bit-depth version of regasm. The .NET framework comes with 2 versions of regasm.exe. One version in the “Framework” path, and the other in “Framework64″ path.
On my 64 bit machine i got the error when using the 32-bit regasm to register the dll and tried to get an instance from a 64-bit C++ exe.
I had the same issue, solved executing these commands from the console:
C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\regasm dllname.dll /tlb:dllname.tlb
C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\regasm dllname.dll /regfile
then doing a double click on the reg file generated.
to test if everything is ok you can have a very small vb script file which created the object, just put a command like this in a .vbs file:
SET testObj = CreateObject("Namespace.ClassName")
your COM visible assembly should be strongly named.

IronPython, importing Modules

I follow the example from the best answer here to a T, compiling with Pyc.py.
Build Python scripts and call methods from C#
I get an exception at pyScope = pyEngine.ImportModule("MyClass");
no module named MyClass
I believe this to be a bug as sometimes recompilation with Pyc.py will produce a dll ImportModule recognizes, but other times it doesn't.
CONCLUSION: As noted below by digEmAll, compiling modules with Pyc.py to be used in this fashion produces random results. Call clr.CompileModules manually instead.
OK,
I got it.
The module name is the (case sensitive) name of the original .py module, not the compiled dll.
I mean, if your original module name was myClass.py, then you compiled it in MyClass.dll, you must ImportModule("myClass") not ImportModule("MyClass")
EDIT:
the previous code refers to the following compile method:
import clr
clr.CompileModules("CompiledScript.dll", "script.py")
On the contrary, using pyc.py, the generated dll contains a module called __main__ instead of the .py file name.
That's very strange...
IIRC, in python a module call itself __main__ if it's running standalone (i.e. not called by another), but I still don't grasp the connection...

how to call a C# dll from unmanaged c++ using IDispatch?

I have a C# dll that I need to call from unmanaged C++. The main problem that I have is that my c++ code corresponds to an excel add-in, that can be installed for excel 2003 and excel 2007, when I install my add-in in excel 2007, and I try to call my C# dll, it works just fine, but for some reason that I still haven't been able to find, in excel 2003 it crashes, excel show me a Runtime Error message, and when debugging my c++ code I can see that the code fails when trying to create an instance of my C# dll, it says that the class is not registered even if I registered with regasm.
this is my C# code:
namespace ManagedDLL
{
[
Guid("3C80EE60-D9B8-4daf-89BE-6C7B748F613C"),
InterfaceType( ComInterfaceType.InterfaceIsDual),
ComVisible(true)
]
public interface ICalculator
{
[DispId(1)]
int main(string args, IntPtr _handle);
};
[
Guid("5134F342-5B7F-4db2-94F0-F450610419CF"),
ProgId("myapp.CCOMEntryPoint"),
ClassInterface(ClassInterfaceType.None),
ComDefaultInterface(typeof(ICalculator)),
ComVisible(true)
]
public class COMEntryPoint : ICalculator
{
public int main(string args, IntPtr _handle)
{
string[] _args = args.Split(new char[] { ':' });
Program.handle = _handle;
return Program.Main(_args);
}
}
}
and in C++ what I do is to import the .tlb file that is generated when I use regasm to register my C# dll, like this:
\#import "..\bin\release\ManagedDLL.tlb" raw_interfaces_only
using namespace ManagedDLL;
.
.
.
int callMyDll()
{
long handle = 0, result = 0;
BSTR args;
HRESULT hr = CoInitialize(NULL);
ICalculatorPtr pICalc(__uuidof(COMEntryPoint));
pICalc->main(bstrStr, handle, &result);
return result;
}
But as I mentioned before, this code doesn't work for excel 2003, so my questions are:
I'm I doing something wrong in the way in which I declare my C# dll that is causing me problems in excel 2003?
Just as it is now, can my C# dll be considered an ActiveX object?
How can I call my C# dll in another way from c++? like using IDIspatch for example
Thanks
I've had a similar problem before. I wasn't calling C# from C++, but the concept is the same.
I had to load a .NET dll into a host application via COM, which looks like what you are trying to do. The problem was the host application (in your case excel) was loading the .NET runtime 1.1. Our dll was compiled for .NET 2.0.
It could be that Excel 2003 is loading the 1.1 runtime and 2007 loads a more recent version. Check out this forum this:
Excel selects wrong .NET runtime.
You could also test this by using MSBee to target the 1.1 runtime and then try load your dll in Excel 2003.
I'm not a C++ coder, so I can't comment on that part, but to answer it from the C# side:
"I'm I doing something wrong in the
way in which I declare my C# dll that
is causing me problems in excel 2003?"
No, your attribute usage looks exactly correct. Well done.
"Just as it is now, can my C# dll be
considered an ActiveX object?"
By compiling with the attributes you show and then registering via RegAsm, you have created and properly exposed your assembly to COM, which is what you want. (The term "ActiveX" is usually used in reference to COM controls, and your class is not a control.)
"How can I call my C# dll in another
way from c++? like using IDIspatch for
example."
You are using the [InterfaceType(ComInterfaceType.InterfaceIsDual)] attribute, which means that the interface is exposed to both early binding and late binding via IDispatch.
In short, I don't know what's wrong here, so I would try dequadin's idea to check that the .NET Framework version being loaded is at or above the framework on which you are building.
If that isn't it, the only other thing I can think of is the fact that you are getting a straight crash, without a recoverable error, suggests to me that there could be some sort of mis-alignment between the registered interface vs. the interface on which the caller was compiled. This can happen because the GUID does not change if you change the interface -- you have explicitly set the GUID via an attribute -- so if the interface changes at all without everything being re-built and re-registered from bottom-to-top, all hell breaks loose. Therefore, if you changed your interface in any way, then you need to re-build the C# assembly, re-register with RegAsm, and then re-compile your C++ add-in that is referencing it.
This is just my best guess though. And does not explain the Excel 2003 vs. 2007 issue if you are using the same exact assembly for each. In short, it's hard to know what's wrong because your C# code looks 100% clean.
-- Mike

Categories