2

In my "LuaTest" namespace I have a class called "Planet". The C# code reads like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LuaInterface;

namespace LuaTest
{
    public class Planet
    {
        public Planet(string name)
        {
            this.Name = name;
        }
        public Planet() : this("NoName") { }
        public string Name
        {
            get;
            private set;
        }

        public void printName()
        {
            Console.WriteLine("This planet's name is {0}", Name);
        }
    }
}

Then I built LuaTest.dll and copied this file to the same folder where my Lua script is saved. In the Lua script I wrote:

--define Path for required dlls
package.cpath = package.cpath .. ";" .. "/?.dll"
package.path = package.path .. ";" .. "/?.dll/"
require 'luanet'
luanet.load_assembly("LuaTest")
local Planet = luanet.import_type("LuaTest.Planet")
local planet = Planet("Earth")
planet.printName()

However, this piece of code does not work. Lua interpreter throws this error:

lua: dllTest.lua:7: attempt to call local 'Planet' (a nil value)

I suspect that my LuaTest assembly is not loaded at all. Could anyone point out where I did wrong? I would very much appreciate it, since I've been stuck by this problem for days.

Also it might be helpful to add that my LuaInterface.dll is the rebuilt version in .NET4.0 environment.

bames53
  • 86,085
  • 15
  • 179
  • 244
proudnoldo
  • 71
  • 1
  • 4
  • I had some real issues doing this when I used LuaInterface. You may want to look into C# Compiling on the fly. – Steffan Donal Nov 19 '12 at 08:51
  • @Ruirize Thanks for the comment. Did those 'real issues' you have made you abandon this approach in the end? I'll have a look at C# Compiling on the fly. Hope it's not complicated. – proudnoldo Nov 19 '12 at 08:59
  • Basically, I couldn't expose my C# classes to Lua without a long workaround that made the resulting Lua code very ugly. (I have since lost the project, this was a while ago) C# Compiling is super easy, and you can control what gets exposed just by using the access modifiers. – Steffan Donal Nov 19 '12 at 09:01
  • The problem is that we have to stick to Lua in this matter. The story is, we have a quite complicated ToolBox written in Lua and now we want to migrate to .NET framework. But still all the methods in the ToolBox have to be accessible in Lua script as in the good old way. That's where LuaInterface comes into play. However, it does not work in this particular application. – proudnoldo Nov 19 '12 at 15:00
  • I wish you happy hunting and the best of luck. – Steffan Donal Nov 19 '12 at 15:03

3 Answers3

0

So I spent a LOT of time similarly. What really drove me bonkers was trying to get Enums working. Eventually I ditched my project for a very simplified console application, very similar (ironically also named 'LuaTest').

Edit: I've noted that the initial "luanet.load_assembly("LuaTest")" appears superfluous. Works with it, or surprisingly without it.

Another Edit: As in my badly edited comment below, when I removed:

print(luanet.LuaTest.Pointless)

It all stopped working (LuaTest.Pointless became nil). But adding the luanet.load_assembly("LuaTest") then makes it work. It may be that there is some sort of odd implicit load in the print or in just expressing they type. Very Strange(tm).

In any case, it seems to work for me (note: after a lot of experimentation). I don't know why yours is failing, I don't note any real difference, but here's all my code in case someone else can spot the critical difference:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using LuaInterface;

namespace LuaTest
    {
    public class Program
        {
        static void Main(string[] args)
            {
            Lua lua = new Lua();
            lua.DoFile("test.lua");
            }

        public int some_member = 3;
        }

    public class Pointless
        {
        public enum AnEnum
            {
            One,
            Two,
            Three
            };

        public static string aStaticInt = "This is static.";
        public double i;
        public string n = "Nice";
        public AnEnum oneEnumVal = AnEnum.One;
        private AnEnum twoEnumVal = AnEnum.Two;
        private string very;

        public Pointless(string HowPointLess)
            {
            i = 3.13;
            very = HowPointLess;
            }



        public class MoreInnerClass
            {
            public string message = "More, please!";
            }


        public void Compare(AnEnum inputEnum)
            {
            if (inputEnum == AnEnum.Three)
                Console.WriteLine("Match.");
            else
                Console.WriteLine("Fail match.");
            }
        }



    }

and test.lua:

luanet.load_assembly("LuaTest")

--Pointless is a class in LuaTest assembly
local Pointless = luanet.import_type("LuaTest.Pointless")

print(Pointless)
--Gives 'ProxyType(LuaTest.Pointless): 46104728


print(Pointless.aStaticInt)
--'This is static.'
--Fails if not static, as we expect




--Instantiate a 'Pointless'.  
local p = Pointless("Very")

print(p)
--Gives 'LuaTest.Pointless: 12289376'



--Now we can get at the items inside the Pointless
--class (well, this instance, anyway).

local e = p.AnEnum;
print(e)
--ProxyType(LuaTest.Pointless+AnEnum): 23452342
--I guess the + must designate that it is a type?

print(p.i)
--3.14

print(p.oneEnumVal)
--Gives 'One: 0'

print(p.twoEnumVal)
--Gives 'twoEnumVal'... private
--behaves very differently.


print(e.Two:ToString())
--Gives 'Two'

local more = p.MoreInnerClass()
print(more.message)
--'More, Please!'

--create an enum value here in the script,
--pass it back for a comparison to 
--the enum.
local anotherEnumVal = p.AnEnum.Three

p:Compare(anotherEnumVal)
--outputs 'Match'
Will Gore
  • 96
  • 1
  • 4
  • Curious. I'm working with this today, messing with subclasses of the CLR objects. Calling items on the subclass (PointlessTwo) was working until I removed the line: `print(luanet.LuaTest.Pointless)` When I removed that line it stopped working. But adding luanet.load_assembly("LuaTest") made it work again! Is there some kind of implicit load going on with print(luanet.LuaTest.Pointless)? I wonder. – Will Gore Feb 02 '13 at 04:13
0

Having spent the last several days working on a project that required this exact functionality from LuaInterface, I stumbled across a piece of Lua code that turned out to be the perfect solution (see Reference 1). Whilst searching for this solution, I noticed this question and figured I'd drop my two cents in.

To apply this solution, I merely run the CLRPackage code while initializing my LuaInterface Lua object. However, the require statement works just as well.

The code provided in reference 1 allows the use of import statements, similar to C# using statements. Once an assembly is imported, its members are accessible in the global namespace. The import statement eliminates the need to use load_assembly or import_type (except in situations in which you need to use members of the same name from different assemblies. In this scenario, import_type would be used similar to C# using NewTypeName = Assembly.OldTypeName).

import "LuaTest"
planet = Planet("Earth")
planet:printName()

This package also works great with enums!

Further information regarding the use of this package may be found at Reference 2.

Hope this helps!

Reference 1: https://github.com/stevedonovan/MonoLuaInterface/blob/master/bin/lua/CLRPackage.lua Reference 2: http://penlight.luaforge.net/project-pages/penlight/packages/LuaInterface/

Larkspur
  • 11
  • 2
0

I spent some time in binding C# dll to lua. Your posts were helpful but something was missing. The following solution should work:

(Make sure to change your compiler to .NET Framework 3.5 or lower!)

Planet.dll:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Planets
{
    public class Planet
    {
        private string name;
        public string Name
        {
            get { return name; }
            set { this.name = value; }
        }

        private float diameter;
        public float Diameter
        {
            get { return diameter; }
            set { this.diameter = value; }
        }
        private int cntContinents;
        public int CntContinents
        {
            get { return cntContinents; }
            set { this.cntContinents = value; }
        }

        public Planet()
        {
            Console.WriteLine("Constructor 1");
            this.name = "nameless";
            this.diameter = 0;
            this.cntContinents = 0;
        }

        public Planet(string n, float d, int k)
        {
            Console.WriteLine("Constructor 2");
            this.name = n;
            this.diameter = d;
            this.cntContinents = k;
        }

        public void testMethod()
        {
            Console.WriteLine("This is a Test!");
        }
    }
}

Use the code above, paste it into your class library project and compile it with .NET smaller or equal 3.5.

The location of the generated DLL needs to be known by the lua enviroment. Paste it e.g at "clibs"-folder or another well known lua system path. Then try to use the following lua example. It should work.

Test1.lua: (Option 1 with "import" from CLRPackage)

require "luanet"
require "CLRPackage"
import "Planet"
local PlanetClass = luanet.import_type("Planets.Planet")
print(PlanetClass)
local PlanetObject1 = PlanetClass()
print(PlanetObject1)
local PlanetObject2 = PlanetClass("Earth",6371.00*2,7)
print(PlanetObject1.Name)
PlanetObject1.Name = 'Mars'
print(PlanetObject1.Name)
print(  "Planet " .. 
        PlanetObject2.Name .. 
        " is my home planet. Its diameter is round about " .. 
        PlanetObject2.Diameter .. "km." .. 
        " Our neighour is " .. 
        PlanetObject1.Name)

Test2.lua: (Option 2 with "load_assembly")

require "luanet"
require "CLRPackage"
luanet.load_assembly("Planet")
local PlanetClass = luanet.import_type("Planets.Planet")
print(PlanetClass)
local PlanetObject1 = PlanetClass()
print(PlanetObject1)
local PlanetObject2 = PlanetClass("Earth",6371.00*2,7)
print(PlanetObject1.Name)
PlanetObject1.Name = 'Mars'
print(PlanetObject1.Name)
print(  "Planet " .. 
        PlanetObject2.Name .. 
        " is my home planet. Its diameter is round about " .. 
        PlanetObject2.Diameter .. "km." .. 
        " Our neighour is " .. 
        PlanetObject1.Name)

In both cases the console output will look like this:

ProxyType(Planets.Planet): 18643596
Constructor 1
Planets.Planet: 33574638
Constructor 2
nameless
Mars
Planet Earth is my home planet. Its diameter is round about 12742km. Our neighbour is Mars

I hope its helps some of you.

Edit 1: by the way, a method call from lua looks like this:

PlanetObject1:testMethod()
PlanetObject2:testMethod()

Edit 2: I found different dll's whitch needed to be handled differently. One needed the "import"-function and another needed the "load_assembly"-function. Keep that maybe in mind!

Chris
  • 63
  • 6