IronPython, importing Modules - c#

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...

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.

Using C# DLL in managed C++ (command line)

I've looked over a few questions with a title just like this one, but they either do not talk about command line, or don't seem to work for me for some reason. From what I have read, it seemed as if I could "simply" do the following:
The dummy code (C#):
using System;
public static class Foo {
public static void Bar() {
Console.WriteLine("o3o");
}
}
More dummy code (Visual C++):
#using <test.dll>
int main() {
Foo::Bar();
return 0;
}
C# DLL compiled using:
csc /out:test.dll /t:library src\cs\Foo.cs
Visual C++ object file compiled using:
cl /Ox /clr /AI. /c src\vc\test.cpp
Executable compiled using:
link /out:test.exe test.obj
The following exception is thrown upon running the executable:
Unhandled Exception: System.TypeLoadException: Could not load type 'Foo' from assembly 'test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
at main()
at mainCRTStartup()
I have a strong feeling that I was supposed to also reference the DLL in the link phase, but I couldn't find any option for linking a DLL similar to GCC's -l option. Attempting to pass the DLL along with the .obj to link causes it to tell me that linking assemblies is not supported. Interestingly, though, if I build a .netmodule instead of a DLL, i.e. by:
csc /out:test.dll /t:module src\cs\Foo.cs
and changing the #using directive to
#using <test.netmodule>
the executable runs without error. This feels a bit weird, for I don't think anybody packages code in .netmodules (what are .netmodules, anyway?).
All of your steps should have worked, but one very simple issue is preventing your program from running.
Namely: Your C# DLL assembly name is test, and your C++/CLI exe assembly has the same name. They both have the same identity.
So when looking for Foo::Bar in the test assembly, the loader first checks if the assembly is loaded in the AppDomain. It is - it's your C++/CLI exe, and you can't have several assemblies with the same identity loaded simultaneously within the same AppDomain. Your C# dll wasn't even given a try.
Just change either one of them and everything will work fine:
link /out:test2.exe test.obj
As for what's a .netmodule, it's the format used for linking managed code statically, that's why you managed to link your C# code with your C++/CLI code without issues. It's roughly the equivalent of a .lib file for managed code.
And you're right, it's not used very often.

Call a C# .net .dll script using PHP

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

CodeDOM -- Execute compiled code, second time yields error

i'm currently struggeling with the CodeDOM code generator and executing compiled assemblies.
Everything works like a charm, except running the compiled code a second time.
Settings
The user "programs" a model which will be translated into a executable program. The user can define whether the assembly should created in memory only or on disc, whether to have source code or only an executable. When he clicks the "run" button, the CodeDOM tree is put together and compiled, written out to disc (if needed) and executed.
Exception
When he clicks the "run" button a second time, an exception is thrown:
error CS0016: Unable to write to output file '': --
"The process cannot access the file because it is being used by
another process."
As I can compile the code as often as I want without encountering the error, I would suggest it has something to do how I run the assembly. I searched the web for information on this topic, but all I came up was creating a separate AppDomain and unloading it afterwards.
Here is the snippet which executes the assembly:
if ( RunProject )
{
_log.info( "Compiled without errors, running..." );
Assembly compiledAssembly = res.CompiledAssembly;
AppDomain compiledAssemblyDomain = AppDomain.CreateDomain( "compiledAssemblyDomain" );
compiledAssemblyDomain.ExecuteAssemblyByName( compiledAssembly.GetName( ) );
AppDomain.Unload( compiledAssemblyDomain );
}
The executable file can only be removed if I quit the program, as if the file is locked by the current appdomain. What to do? Thanks for your help!
Update
When the above code executes, the main file is loaded into the executing assembly (or am I wrong?). The debugging console caputes the following information:
[13:42:19.5576171] i Compiled without errors, running...
'XXX.vshost.exe' (Managed (v4.0.30319)): Loaded 'C:\...\bin\main.exe'
and just a few seconds after quitting the executed assembly:
The thread '.NET SystemEvents' (0x20d0) has exited with code 0 (0x0).
The thread '<No Name>' (0x1d20) has exited with code 0 (0x0).
where XXX is the name of my main application compiling the code. Shouldn't the file loaded somewhere else? Doesn't XXX.vshost.exe open a handle and don't close it after unloading the AppDomain?
I have wrestled with this quite a bit (I did something similar with Scrolling Game Development Kit 2). You have to be very carefully to make sure that everything you do with that compiled code happens within that other AppDomain so that when you unload that AppDomain, all references to the DLL are unloaded with it. If you so much as refer to a type from the compiled code, that DLL will get loaded into your AppDomain as well and unloading the other domain will do no good. So what I have had to do was define interfaces in a common DLL which can get loaded into both domains so that I can call functions in the other DLL without loading types from the other DLL. Just make sure that every object you instantiate in the other DLL uses an interface defined in the shared DLL (or another public interface not defined in the user-defined DLL). Then cast each object you instantiate from that DLL to one of those interfaces. You can never use the types defined in that DLL directly.
EDIT: Observe the following note from MSDN documentation about the CompiledAssembly Property
Note
The get accessor for the CompiledAssembly property calls the Load method to load the
compiled assembly into the current application domain. After calling the get accessor,
the compiled assembly cannot be deleted until the current AppDomain is unloaded.

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.

Categories