I'm new to programming in C# (VS2010) .Net (4.0) and I'm encountering I couldn't solve by myself since some days already.
I'm using an external scripting language (Lua) in my C# code.
To do so I use LuaInterpreter built for .Net 4.0
First try:
The project is a console application -> the program works fine when I try to call a Lua class.
Second try:
The project is a class Librrary COM used from Excel -> The class library compile fine and my user defined functions work fine within Excel. But when I try to call a Lua class it crashed saying that the Lua assembly is missing.
Could not load file or assembly 'lua51, Version=0.0.0.0, Culture=neutral, PublicKeyToken=1e1fb15b02227b8a' or one of its dependencies. Strong name validation failed. (Exception from HRESULT: 0x8013141A)
To reproduce the problem :
1- You need to get LuaInterface .Net 4.0 from
http://www.mdome.org/2011/05/16/luainterface-for-csharp-net-4-custom-build/
2- Add LuaInterface as a reference in your project
3- Copy the Lua51 DLL in the building directory (I put my Excel sheet there too)
4- Copy the code for the Class Library
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using Excel = Microsoft.Office.Interop.Excel;
using LuaInterface;
namespace POC
{
[ClassInterface(ClassInterfaceType.AutoDual)]
[ComVisible(true)]
public class Functions
{
public int test()
{
Lua lua = new Lua();
return 0;
}
#region Class in Excel
[ComRegisterFunctionAttribute]
public static void RegisterFunction(Type type)
{
Registry.ClassesRoot.CreateSubKey(
GetSubKeyName(type, "Programmable"));
RegistryKey key = Registry.ClassesRoot.OpenSubKey(
GetSubKeyName(type, "InprocServer32"), true);
key.SetValue("",
System.Environment.SystemDirectory + #"\mscoree.dll",
RegistryValueKind.String);
}
[ComUnregisterFunctionAttribute]
public static void UnregisterFunction(Type type)
{
Registry.ClassesRoot.DeleteSubKey(
GetSubKeyName(type, "Programmable"), false);
}
private static string GetSubKeyName(Type type,
string subKeyName)
{
System.Text.StringBuilder s =
new System.Text.StringBuilder();
s.Append(#"CLSID\{");
s.Append(type.GUID.ToString().ToUpper());
s.Append(#"}\");
s.Append(subKeyName);
return s.ToString();
}
#endregion
}
}
The function that crashed is the test function when called from Excel
I would take any help on that
Thanks
SInce it appears to be signed, try to put Lua51 into the GAC and see if it works. Probably you can try even by putting Lua15.dll in the same path of excel.exe.
I've had lots of issues with .NET, LuaInterface, and Lua5.1 interracting on 64-bit machines. Lua5.1 only compiles 32-bit and this requires you to (I believe) build the LuaInterface project as 32-bit as well. Try changing "Project -> Properties -> Build -> Platform Target" to "x86" in your .NET projects.
Related
I've struggle several hours on that and I can't find what I'm doing wrong.
I created a new C# dll project, here is the content of the only class it contain:
using System;
using System.Runtime.InteropServices;
namespace PolygonSl {
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class Config {
[ComVisible(true)]
public string GetCompany() {
return "POL";
}
}
}
I basically remove everything from it trying to make it work, the only reference is System.
I checked the Make assembly COM-Visible flag on the Assembly Information and my project is signed (seams required for codebase).
It compiling fine, after that, I called RegAsm.exe, giving it my dll, I added /codebase and /tlb, the command is successful.
When I go to my VBA project, I can add my new tlb file to the references, working fine. After, I can use it in my code, the autocomplete is working and I can compile with no errors.
Then, when I execute, I got this:
Run-time error '430':
Class does not support Automation or does not support expected interface
Here is my code sample in the VBA:
Private Sub Button1_Click()
'With CreateObject("PolygonSl.Config")
With New PolygonSl.Config
MessBox .GetCompany, MB_OK, "Test"
End With
End Sub
I tried late binding and my code is running fine with it but I'd like to be able to use the autocomplete.
Anyone have a suggestion on what I could try to make it work?
Edit (Adding some details on my environment)
I work on VS2008 for projects related to Dynamics SL (one of the Microsoft ERPs)
I'm on Windows Server 2008 R8 Standard, running from VMWare
Compiling on Framework 3.5, Release, x86, Dynamics SL client is 32 bits
I tried my dll on Dynamics but also on Excel to be sure that the problem was not Dynamics ;)
I think you need to define an interface to be able to see getcompany.
using System;
using System.Runtime.InteropServices;
namespace PolygonSl
{
[Guid("6DC1808F-81BA-4DE0-9F7C-42EA11621B7E")]
[System.Runtime.InteropServices.ComVisible(true)]
[System.Runtime.InteropServices.InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IConfig
{
string GetCompany();
}
[Guid("434C844C-9FA2-4EC6-AB75-45D3013D75BE")]
[System.Runtime.InteropServices.ComVisible(true)]
[System.Runtime.InteropServices.ClassInterface(ClassInterfaceType.None)]
public class Config : IConfig
{
public string GetCompany()
{
return "POL";
}
}
}
You can generate the interface automatically by placing the cursor in the class definition and using Edit.Refactor.ExtractInterface.
I'd have to admit that I'm at the absolute edge of my abilities here and the above is put together based on examples I've seen elsewhere.
Edit
The following test code works fine on my PC
Option Explicit
Sub polygontest()
Dim my_polygon As SOPolygon.Config
Set my_polygon = New SOPolygon.Config
Debug.Print my_polygon.GetCompany
End Sub
Where SOPolygon is the project name.
I am getting started with developing an Excel-DNA addin using IronPython with some C# as a wrapper for the calls to IronPython. With the generous help of the Excel-DNA developer, I have worked through some of the initial kinks of getting a sample up and running, but now I am trying to debug the addin in SharpDevelop, and I'm running into some problems. As I'm completely new to most of this, I'm not really sure if it is an issue with SharpDevelop, .NET, Excel-DNA or IronPython.
I have created two projects in one solution, one is a C# class library. The other is a python class library. I setup the project to debug following a tutorial I found on a blog. I am able to step through the first few lines of C# code, so that is progress, but when I get to the following line:
pyEngine.Runtime.LoadAssembly(myclass);
I get an exception:
"Could not load file or assembly
'Microsoft.Dynamic, Version=1.0.0.0,
Culture=neutral,
PublicKeyToken=31bf3856ad364e35' or
one of its dependencies. The located
assembly's manifest definition does
not match the assembly reference.
(Exception from HRESULT: 0x80131040)"
But I'm pretty sure I have added the Microsoft.Dynamic reference to my project. It is version 1.1.0.20. This is included in the IronPython distribution but also in another location on my computer. I have tried setting the reference to both, but they both have the same version number and appear to be the same file size. Neither one works. Do I need version 1.0.0.0 or am I doing something else wrong? I don't really understand why anything pyEngine (the ScriptEngine returned by Python.CreateEngine()) would try to load a different version than the one included with the distribution.
Code is below. Let me know if you need any other information.
MyAddin.cs
/*
Added these references all as Local Copies - probably not necessary?
System.Windows.Forms
Microsoft.CSharp
ExcelDna.Integration (from Excel-DNA distribution folder)
IronPython (from IronPython folder)
IronPython.Modules (from IronPython folder)
Microsoft.Dynamic (from IronPython folder)
Microsoft.Scripting (from IronPython folder)
Microsoft.Scripting.Metadata (from IronPython folder)
mscorlib (I don't really know why I added this, but it was one of the references in my IronPython class library)
MyClass (this is the reference to my IronPython class - I checked to see that it gets copied in every time I rebuild the solution and it does)
These were automatically added by SharpDevelop when I created the project.
System
System.Core
System.Windows.Forms
System.Xml
System.Xml.Linq
*/
using System;
using System.IO;
using System.Windows.Forms;
using ExcelDna.Integration;
using System.Reflection;
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
public class MyAddIn : IExcelAddIn
{
public void AutoOpen()
{
try
{
string xllDirectory = Path.GetDirectoryName(#"C:/Users/myname/Documents/SharpDevelop Projects/IronPythonExcelDNATest/MyAddIn/bin/Debug/");
string dllPath = Path.Combine(xllDirectory,"MyClass.dll");
Assembly myclass = Assembly.LoadFile(dllPath);
ScriptEngine pyEngine = Python.CreateEngine();
pyEngine.Runtime.LoadAssembly(myclass);
ScriptScope pyScope = pyEngine.Runtime.ImportModule("MyClass");
object myClass = pyEngine.Operations.Invoke(pyScope.GetVariable("MyClass"));
IronTest.AddSomeStuff = pyEngine.Operations.GetMember<Func<double, double,double>>(myClass, "AddSomeStuff");
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
public void AutoClose()
{
}
}
public class IronTest
{
public static Func<double, double, double> AddSomeStuff;
public static double TestIPAdd(double val1, double val2)
{
try
{
return AddSomeStuff(val1, val2);
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
return double.NaN;
}
}
}
MyClass.py
class MyClass:
def __init__(self):
pass
def AddSomeStuff(self,x,y):
return x + y
Your IronPython stuff probably needs to run under the .NET 4 runtime. To tell Excel-DNA to load .NET 4, you have to explicitly add a RuntimeVersion attribute in the main .dna file. Your .dna file will start with something like:
<DnaLibrary RuntimeVersion="v4.0"> ... </DnaLibrary>
The default behaviour, if the attribute is omitted, is to load the .NET 2.0 version of the runtime (which is also used by .NET 3.0 and 3.5).
It might be possible to host IronPython under the .NET 2.0 runtime, but then you need to deal with the DLR libraries yourself, whereas they are built-in and already installed with .NET 4.
I have a simple class library that I use in Excel. Here is a simplification of my class...
using System;
using System.Runtime.InteropServices;
namespace SimpleLibrary
{
[ComVisible(true)]
public interface ISixGenerator
{
int Six();
}
public class SixGenerator : ISixGenerator
{
public int Six()
{
return 6;
}
}
}
In Excel 2007 I would create a macro enabled workbook and add a module with the following code:
Public Function GetSix()
Dim lib As SimpleLibrary.SixGenerator
lib = New SimpleLibrary.SixGenerator
Six = lib.Six
End Function
Then in Excel I could call the function GetSix() and it would return six. This no longer works in Excel 2010 64bit. I get a Run-time error '429': ActiveX component can't create object.
I tried changing the platform target to x64 instead of Any CPU but then my code wouldn't compile unless I unchecked the Register for COM interop option, doing so makes it so my macro enable workbook cannot see SimpleLibrary.dll as it is no longer regsitered.
Any ideas how I can use my library with Excel 2010 64 bit?
You haven't described in detail how your created your .NET assembly. However, there are a certain number of steps required to expose the assembly to COM:
Add the following attributes to your code:
using System;
using System.Runtime.InteropServices;
namespace SimpleLibrary
{
[ComVisible(true)]
[Guid("71F645D0-AA78-4447-BA26-3A2443FDA691")]
public interface ISixGenerator
{
int Six();
}
[ComVisible(true)]
[ProgId("SimpleLibrary.SixGenerator")]
[Guid("8D59E0F6-4AE3-4A6C-A4D9-DFE06EC5A514")]
[ClassInterface(ClassInterfaceType.AutoDispatch)]
public class SixGenerator : ISixGenerator
{
[DispId(1)]
public int Six()
{
return 6;
}
}
}
Your assembly must be signed (Project -> Properties... -> Signing, create a strong key file and check the box to sign the assembly
The following command is necessary to register the assembly (all in one line):
C:\Windows\Microsoft.NET\Framework64\v2.0.50727\RegAsm.exe
SimpleLibrary.dll /tlb SimpleLibrary.tlb /codebase
This creates a .tlb type library file which you will have to reference from your VBA project (Tools -> References -> Browse... in your VBA editor)
Adjust the VBA code:
Public Function GetSix()
Dim lib As SimpleLibrary.SixGenerator
Set lib = New SimpleLibrary.SixGenerator
GetSix = lib.Six
End Function
You will find the steps described in more detail in this article on Microsoft's support database:
How to call a Visual Basic .NET or Visual Basic 2005 assembly from Visual Basic 6.0
I have a C# DLL file project (my_cs_dll.dll) which defines a static class with a static member function.
namespace Foo
{
public static class Bar
{
public static double GetNumber() { return 1.0; }
}
}
I also have a C++ DLL project which is using /clr.
#using <my_cs_dll.dll>
double get_number_from_cs() { return Foo::Bar::GetNumber(); }
I've added a reference to 'my_cs_dll.dll' in the C++ project Common Properties references section (copy local/copy dependencies are both True).
And I've also added the path to 'my_cs_dll.dll' in the C++ project Configuration Properties C/C++ General 'Resolve#using References' section.
Everything builds without error, however at runtime I keep getting a 'System.IO.FileNotFound' exception from the system claiming it can't find the my_cs_dll.dll assembly.
Both DLL files are definitely present in the same directory from which I'm running.
I have tried all sorts of variations on the settings mentioned above and read everything I could find on manged/unmanaged interop, but I can't seem to get my brain around what is wrong...
I'm using Visual Studio 2008 and .NET 3.5.
It sounds like your C# assembly is not being resolved at runtime. Is your C# dll in the same directory as (or a subdirectory of) your executable? It's been a while since I did this, but my recollection is that unless your assembly is installed in the GAC, it must be in the directory (or a subdirectory) where your executable is located, as opposed to the location of the dll that's using it. This has to do with the .NET security features.
If you are still having problems, you can try using resolving the assembly yourself. In your clr-enabled C++ project, try adding the following:
using namespace System;
using namespace System.Reflection;
void Resolve()
{
AppDomain::CurrentDomain->AssemblyResolve +=
gcnew ResolveEventHandler(OnAssemblyResolve);
}
Assembly ^OnAssemblyResolve(Object ^obj, ResolveEventArgs ^args)
{
#ifdef _DEBUG
String ^path = gcnew String(_T("<path to your debug directory>"));
#else
String ^path = gcnew String(_T("<path to your release directory>"));
#endif
array<String^>^ assemblies =
System::IO::Directory::GetFiles(path, _T("*.dll"));
for (long ii = 0; ii < assemblies->Length; ii++) {
AssemblyName ^name = AssemblyName::GetAssemblyName(assemblies[ii]);
if (AssemblyName::ReferenceMatchesDefinition(gcnew AssemblyName(args->Name), name)) {
return Assembly::Load(name);
}
}
return nullptr;
}
You may have to tweak the code a little bit to get it to compile in your project. In my case, I made the two functions static methods of a class in my clr-enabled project. Just make sure you call the Resolve() function early on in your code, i.e., before you try to call get_number_from_cs().
While using COM is an option, it is not necessary. You're on the right path with your current approach. If you want some hand-holding, take a look at this CodeProject example. It's the one I following to get my unmanaged application to use my managed assemblies.
I am trying to call a C# dll from QTP (uses vbscript). I have tried a number of things with no success:
Visual Studio 2010
Create C# class libary (st.dll)
code:
using System;
using System.Collections.Generic;
using System.Text;
namespace st
{
public class Class1
{
public static int GetValue()
{
return 34;
}
}
}
regasm /codebase st.dll
fails 'because it is not a valid .NET assembly'
In QTP/vbscript, I have tried
extern.Declare micInteger, "GetValue", "e:\st.dll", "GetValue"
Returns message: 'Invalid procedure call or argument'
Regardless of QTP, I would greatly appreciate any insight on how to call the c# dll from a .vbs file.
I was able to get this working by doing the following:
Create a new C# dll in VS 2010.
namespace st4
{
public class st4_functions
{
public int GetValue()
{
return 34;
}
}
}
In QTP I added the following lines:
Set obj = DotNetFactory.CreateInstance("st4.st4_functions", "c:\\st4.dll")
MsgBox obj.GetValue()
Thanks to all that responded to my problem. Though I did not do the COM solution, it got me thinking that I could stay with .NET and led to this solution. Good job all!
EDIT:
I created a blog post to detail the steps and provide additional information:
http://www.solutionmaniacs.com/blog/2012/5/29/qtp-calling-c-dll-in-vbscript.html
As Marc said, but I think it merits an answer. If you ensure that your dll will be available though the COM mechanics, your script should be able to call into it with things like CreateObject.
How to register .NET assembly for COM interop
Your function is static. Static class members can't be matched up to interface members, and if it can't implement a .NET interface then it certainly won't implement a COM interface.