Jump to content

DLL extensions


towsim

Recommended Posts

Being tired of fighting against the unpredictable behavior of LUA scripts, I created a DLL to be invoked with the 'require' statement out of a small LUA script. After a view experiments I got it to run and it worked perfect. All complicated logic under the control of my own C++ program! I was happy for a short time. This DLL served my private cockpit extension. For my ATC radar project I needed a second DLL which should serve the network communication. And then the problems started up. The LUA interface seems to accept only one specific name for a DLL which is 'LUAext.dll ' all other names are rejected by the 'require' statement. I can bring the second DLL to run by simply renaming it to from LUAnet.dll to LUAext.dll. Is it the truth, that there is a naming convention for DLLs and I found the right name like a lottery win? I would be really happy if someone could explain this odd behavior.

Regards

Mike :helpsmilie:

[sIGPIC][/sIGPIC]

Link to comment
Share on other sites

  • 3 weeks later...

So I finally found out how the DLL extension works.

Situation

You want to call a self developed DLL (C/C++) out of a LUA script. The LUA script is invoked out of EXPORT.LUA with a 'dofile' instruction. Your DCS simulator is a 64 bit application and runs under Windows 7, 64 bit.

Solution DLL

 

First the DLL has to fulfill some requirements.

  • The DLL project must be a 64 bit version.
  • You have to link the library lua5.1.lib to your project. No other version is accepted.
  • The lua5.1.lib must be the 64 bit version!
  • The entry point in the DLL is the next critical point. The entry function must be exported with 'DllExport'.
  • The name of the entry function in your DLL is a combination of a keyword (lua_open_) and the name of your DLL module. If, for example, the DLL is named CockpitExt.DLL, the function must be:
    DllExport int lua_open_CockpitExt( lua_State *L)
    {
    ......
    }
  • So renaming the DLL afterwards will cause a 'file not found' error.
  • No link library is necessary for the DLL.
  • in the Linker's advanced settings, set the 'Base Address' to 0x68100000. I do not know the reason for it, but a LUA documentation specified this entry to be necessary.
  • To avoid the construction of a C++ wrapper module, I would recommend to use pure C code in the DLL.
  • Ensure, that only release versions are given to other users. I made the experience, that debug versions are sometimes rejected by the loader module.

Solution LUA script

 

In your LUA script you have to add the following statements to load and connect to your DLL:

  • Depending on the location where your DLL resides, extend the search path of the lua loader module with
    package.cpath = package.cpath..";.\\Config\\Export\\?.dll".
    This statement includes the Export folder in the search logic for DLLs. It implies, that you copy the DLL to the Export folder.
  • To load the DLL and to invoke the entry function add the statement:
    local MyDLLfunctions = require('CockpitExt')

If that all is done, the DLL should work as expected. The further code to register your C functions to LUA and the data exchange should be taken from the LUA documentation. There are no specifics for DCS.

Good luck,

Mike

  • Like 1

[sIGPIC][/sIGPIC]

Link to comment
Share on other sites

Awesome! Many, many thanks, this, together with all linking advice you provided, clarifies a lot. I haven't tested by myself, I'm still working on the library, but just some questions that came up when I digged into your code:

 

* Why this line?

 

#define DllExport   __declspec( dllexport )

* Why are all functions defined as static? Is there any reason behind?

 

* When you registered your functions

 

static const luaL_Reg Map[] = {
   {"MyDLLfunction", OwnShipPosRep},  
   {"EOapp", EndOfApplication},   
   {NULL, NULL}
}; 

I'm not very familiar with C implementation in Lua code, but I suppose that, if you register "OwnShipPosRep" C function as "MyDLLfunction", then if you want to have access to "OwnShipPosRep" from Lua you must call it as "Aries.MyDLLfunction". In fact, for EndOfApplication you do so:

 

LuaExportStop = function()
   [b]Aries.EOapp()[/b] [i][b]// You used the LUA name[/b][/i]
   if RadarExport.LuaExportStop then
       RadarExport.LuaExportStop()
   end
end

But shortly afterwards:

 

LuaExportActivityNextEvent = function(t)
   local tNext = t

   local os = LoGetSelfData()
[b]Aries.OwnShipPosRep(t,os.LatLongAlt.Lat,os.LatLongAlt.Long,os.LatLongAlt.Alt,os.Heading,os.Pitch,os.Bank) [/b]
[i][b] //This time you used the C call[/b][/i]
tNext = tNext + 0.2  expected in 200 ms
   if RadarExport.LuaExportActivityNextEvent then
       RadarExport.LuaExportActivityNextEvent(t)
   end
   return tNext
end

* Finally, in the entry function:

 

    luaL_register(L, "Radar",Map); 
   return 1;

What are the parameters of "luaL_register"? What does "Radar" mean?

 

------------

 

Besides, the library I'm working on is heavily OOC, so I hope there aren't major showstoppers with classes.

 

Many, many thanks for your support, this thread should have a pin!

 

Regards!



Link to comment
Share on other sites

As I said, I did not test it. I had to rename some items and therefore some mismatches.

#define DllExport __declspec( dllexport )

This is simply a macro for a Microsoft specific export declaration. You could even use __declspec( dllexport ) directly. In a big DLL not only one function must be accessible. Then the shorter form DLLexport is more comfortable.

static expression

In the LUA library header files, the typical LUA callable C function is declared as static. So I have to follow this requirement. Otherwise the LUA loader would reject the table with an error message. Static simply means, that this function has a fixed memory address over the entire session. Therefore its address can be used as address of a constant to be stored in a table. I even used this construction in other projects (not with LUA) without the static declaration. It works...

Name Mixup

In the DLL source code, "My Dllfunction" must be replaced with "OwnShipPosRep". This was a mistake while adjusting the source. (sorry)

"Radar"

Actually the C functions are accessible under two names. One is the name of the local variable 'local Aries = require('LUAext')', the other is 'radar'. So a function call could be aries.EOapp() or radar.EOapp(). The only difference is, that 'aries' is accessible only in CockpitExt.lua while 'radar' is a public variable, accessible from all other LUA scripts. That even means, you can call the DLL C functions out of other LUA scripts without any additional coding.

The parameters for ' lua_register ' are the state variable (manly used for stack management) and a user definable, public name for the table. In this case I used "radar" . The third variable is the address of the table itself.

regards

Mike


Edited by towsim

[sIGPIC][/sIGPIC]

Link to comment
Share on other sites

  • 8 years later...

Hi, I saw this topic and trying to build my own DLL to add it to my GUI lua file. My DLL is found but the module isn't loaded. Try with and without Base Address set in VS 2019 linker configuration. Not sure this Base Address is needed.

 

My Cpp code, nothing else than dllmain.cpp generated by VS 2019 :

 

#include "pch.h"

extern "C" {
#include "lua.h"		  // the includes for the 64 bit LUA libray
#include "lualib.h"
#include "luaconf.h"
#include "lauxlib.h"
}


static int imult(lua_State* L)
{
	double a, b;

	a = (double)lua_tonumber(L, 1);	// get all position report data from the stack
	b = (double)lua_tonumber(L, 2);
	
	lua_pushnumber(L, a * b);

	return 1;
}


static int  EndOfApplication(lua_State* L)
{
	// do all clean up for your DLL here in
	return 0;
}

/***********************************************************************************

	 COMMENT:  This is the 	standardized list of all functions in your DLL. The first
			   entry is the name that LUA understands, the second is the pointer to
			   the DLL function

	 GROUP:	   LUA DLL


	*/


static const luaL_Reg Map[] = {
	{"mult", imult},	 // receives a position report from the LUA script
	{NULL, NULL}
};


/***********************************************************************************

	 COMMENT:  This is the 	DLL entry point. The LUA loader searches for this function
			   to be called. Take special car to the name of the function. In this case
			   LUAext must be the name of the DLL

	 GROUP:	   LUA DLL

	 ENTRY:	   lua_State

	 EXIT:	   int loaded by luaL_register onto the stack

	*/




extern "C" __declspec(dllexport)  int luaopen_ATMEDll(lua_State* L)
{
	luaL_register(L, "Test", Map);	   // register the list of function
	return 1;
}

 

Actually :

  • Lua 5.1 compiled in X64 env -> Ok got my lua51.lib
  • VS 2019, create my dll in X64 Release conf -> Ok build ok.
  • The DLL has been copied in Saved Games\DCS.openbeta\Mods\tech\ATME\bin which name is ATMEDll.dll. In the folder, only that file is present.

My lua file is simple :

 

-- ATME GameGUI
-- tests


do
	-- local myPath = lfs.writedir()
	local dllATMEModPath = lfs.writedir()..'Mods\\tech\\ATME\\bin\\'
	package.cpath = package.cpath..";" .. dllATMEModPath .. "?.dll"

	local ATMEDll = require "ATMEDll"   -- loads the DLL

	local ATMECallbacks = {}

	function ATMECallbacks.onMissionLoadEnd()
		
	end

	function ATMECallbacks.onSimulationStart()
		if DCS.isMultiplayer() == false then
			log.write("[ATME]",log.INFO, "Mission starts in SP")
			trigger.action.setUserFlag("Test", 1)
		else
			log.write("[ATME]",log.INFO, "Mission starts in MP")
		end
	end

	function ATMECallbacks.onSimulationFrame()
		
	end

	function ATMECallbacks.onPlayerConnect(id)
		local player = net.get_player_info(id)
		log.write("[ATME]",log.INFO, "New player " .. player.name .. " connected")
	end
	
	function ATMECallbacks.onGameEvent(event, id, slot)
		if event == "change_slot" then
			if DCS.isMultiplayer() == true then
				local player = net.get_player_info(id)
				log.write("[ATME]",log.INFO, "New player " .. player.name .. " starts")
			else
				log.write("[ATME]",log.INFO, "Player starts in SP")
			end
		end
	end

	function ATMECallbacks.onPlayerDisconnect(id,err_code)
		
	end

	DCS.setUserCallbacks(ATMECallbacks)

	log.write("[ATME]",log.INFO, "ATME successfully loaded")

end

 

 My lua script works perfectly without the require line of course.

But when started DCS I have that :

 

2021-05-20 07:52:57.904 DEBUG   LuaGUI: Failed to exec user hook script ATMEGameGUI.lua: error loading module 'ATMEDll' from file '...\Saved Games\DCS.openbeta\Mods\tech\ATME\bin\ATMEDll.dll':
	Le module spécifié est introuvable.

 

... is the begining of the path, and good of course.    "Le module spécifié est introuvable" means "The specified module could not be found."

 

I think it's a calling convention problem or a VS environment configuration. 

 

Any idea ?

 

Thanks in advance

 


Edited by sunski34
  • Thanks 1
Link to comment
Share on other sites

  • 2 weeks later...
  • 11 months later...
  • 8 months later...
On 6/5/2021 at 7:40 PM, sunski34 said:

Hi,

 

I solved my problem. I just need to compile lua and generate a lua.dll and a lua.lib instead of a lua51.lib and dll. then I link my dll with the lib and now that's ok. 

Thank you for explaining and sharing how you fixed it. It's really appreciated. Most people wouldn't have bothered. 

Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...