Jump to content

Recommended Posts

Posted (edited)

Hey all (and @ivanwfr),

The renewed interest by @dmonds made me curious enough to try again to make DLL calls, and I did actually get it to work this time. Here's how I did it:

1. Compile a simple C++ project to a DLL in Visual Studio, such as one consisting entirely of this .cpp source file:

extern "C" __declspec(dllexport) int __stdcall TestFunction()
{
    return 42;
}

I created a few solutions, but some resulting DLLs were accepted as plugins and some were not, when plopped into the Plugins folder. When it's accepted, you'll see "Loaded plugin module <full path to DLL>" when opening the script editor. I'm not sure whether the DLL needs to be accepted this way, or whether it even needs to be in the Plugins folder at all. It does not seem to matter that compiling produces the message "Failed to map plugin module <full path to DLL>" since, as we'll see, we can accomplish the mapping step in code. It's very possible that TARGET will be finicky about the compiler options used to build the DLL, and we can work out which ones are important.

2. Create a simple, bare-bones TARGET script, such as this one:

include "target.tmh"

int fonctionne(){}

//program startup
int main()
{
	if(Init(&EventHandle)) return 1; // declare the event handler, return on error

	printf( "Hello world!\xa");

	int lib = LoadLibrary("LibraryLoadTest.dll");
	printf( "Library address: %d\xa", lib );

	int funk = GetProcAddress(lib, "_TestFunction@0");
	printf( "Library function address: %d\xa", funk );

	int mapped = Map(&fonctionne, funk, MAP_IPTR_VPN); // returns object. mode:MAP_NORMAL=1, MAP_IPTR=2, MAP_IPTR_VPN=3, MAP_THISCALL=4
	printf( "Mapped function address: %d\xa", mapped );

	printf( "%d", fonctionne(), "\xa");
	
	//add initialization code here
}

//event handler
int EventHandle(int type, alias o, int x)
{
	DefaultMapping(&o, x);
	
	//add event handling code here
}

You don't really need all the print statements, of course, and I'm sure you don't need to save the address returned by Map(). But if all goes well, you should see the number 42 in the editor's output window after the three large integer addresses.

The string "_TestFunction@0" can be discovered by running >dumpbin /EXPORTS <DLL file>. I tried unsuccessfully to compose the C++ in such a way that the exported function name is simply the same as in the source file, but there may be a way to do that.

I was so close before. I don't know why it took a three-year gap to hit on the right syntax. I certainly wish the jerks at ThrustMaster had made this procedure more clear. It wouldn't have taken much.

Edited by JasonMel
  • 2 weeks later...
Posted (edited)

Hi @JasonMel,

Using x86 Native Tools Command Prompt for VS run the command:

cl /nologo /W3 /EHsc /MD /LD TestFunction.cpp /link /EXPORT:TestFunction /OUT:TestFunction.dll

This produces TestFunction.dll which exports "TestFunction".

...and here's the modified .tmc file using 'TestFunction.dll' and 'TestFunction' exported function
 

include "target.tmh"

int fonctionne(){}

//program startup
int main()
{
	if(Init(&EventHandle)) return 1; // declare the event handler, return on error

	printf( "Hello world!\xa");

	int lib = LoadLibrary("TestFunction.dll");
	printf( "Library address: %d\xa", lib );

	int funk = GetProcAddress(lib, "TestFunction");
	printf( "Library function address: %d\xa", funk );

	int mapped = Map(&fonctionne, funk, MAP_IPTR_VPN); // returns object. mode:MAP_NORMAL=1, MAP_IPTR=2, MAP_IPTR_VPN=3, MAP_THISCALL=4
	printf( "Mapped function address: %d\xa", mapped );

	printf( "%d\xa", fonctionne());
	
	//add initialization code here
}

//event handler
int EventHandle(int type, alias o, int x)
{
	DefaultMapping(&o, x);
	
	//add event handling code here
}

I think you are correct about TARGET being finicky about compiler options when building a .dll to drop into the Plugins folder.
That option would be much more elegant to be able to drop the .dll in and do no other TARGET coding.

I have reached out to Thrustmaster and asked some questions, inlcuding if I might get access to the TARGET SDK which some people believe exists.
I'll wait for an answer and post back if/when they reply.

This method that you've outlined does work and will allow me to do what I need in the meantime.

Many thanks

dmonds

Edited by dmonds
Posted

One other thing, whist it's top of mind...

It seems to be implied that sys.dll (in the plugins folder) contains the functions which are defined within sys.tmh.
In @ivanwfr posts he mentions dropping a second sys.dll file which contains a fixed version of strstr(){} and which gets mapped ahead of the latest one (which has a broke strstr(){} ) function. I would assume that the working .dll was either built by ivanwfr, provided by Thrustmaster or came from a previous working version of sys.dll.

Either way, sys.dll does not publicly export the functions listed via sys.tmh and rather, exports only 'systemMap' which presumably is a gateway into the mapped functions.
I suspect there's more under the hood but I'm only a beginner when it comes to C coding.

Posted

Hey, looks good! I was actually wondering whether the automatic mapping that fails (with "Failed to map plugin module <full path to DLL>") might succeed if each exported function in the .dll has a corresponding function in the TARGET code with a matching function signature, including the function name, return type, and parameter types, which might most sensibly be put in their own header file. I just haven't tested this yet.

Posted

I just tested this using a slightly modified .tmc

include "target.tmh"

int TestFunction(){}

//program startup
int main()
{
	if(Init(&EventHandle)) return 1; // declare the event handler, return on error

	printf( "Hello world!\xa");

	int lib = LoadLibrary("TestFunction.dll");
	printf( "Library address: %d\xa", lib );

	int funk = GetProcAddress(lib, "TestFunction");
	printf( "Library function address: %d\xa", funk );

	int mapped = Map(&TestFunction, funk, MAP_IPTR_VPN); // returns object. mode:MAP_NORMAL=1, MAP_IPTR=2, MAP_IPTR_VPN=3, MAP_THISCALL=4
	printf( "Mapped function address: %d\xa", mapped );

	printf( "%d", TestFunction(), "\xa");
	
	//add initialization code here
}

//event handler
int EventHandle(int type, alias o, int x)
{
	DefaultMapping(&o, x);
	
	//add event handling code here
}

Here's the cut/paste of the console output;

Open TARGET Script Editor:

Loaded plugin module "C:\Program Files (x86)\Thrustmaster\TARGET\Plugins\sys.dll"(*)
Loaded plugin module "C:\Program Files (x86)\Thrustmaster\TARGET\Plugins\TestFunction.dll"(*)

Compile;

Loaded plugin module "C:\Program Files (x86)\Thrustmaster\TARGET\Plugins\sys.dll"(*)
Loaded plugin module "C:\Program Files (x86)\Thrustmaster\TARGET\Plugins\TestFunction.dll"(*)
Compiling script: TestFunction.tmc
Mapped plugin module "C:\Program Files (x86)\Thrustmaster\TARGET\Plugins\sys.dll"
Compile Succeeded.
Failed to map plugin module "C:\Program Files (x86)\Thrustmaster\TARGET\Plugins\TestFunction.dll" !

Now run (!!!!)

Running script: D:\Users\Den\OneDrive\Personal\Thrustmaster\TARGET\Clicker\Development\Sandpit\TestFunction.tmc
Mapped plugin module "C:\Program Files (x86)\Thrustmaster\TARGET\Plugins\sys.dll"
Failed to map plugin module "C:\Program Files (x86)\Thrustmaster\TARGET\Plugins\TestFunction.dll" !
Compile Succeeded.
Physical USB HID devices managed by script!
Currently plugged USB HID devices[3]:
1: "T-Rudder" - "USB\VID_044F&PID_B679&REV_0110"
2: "Throttle - HOTAS Warthog" - "USB\VID_044F&PID_0404&REV_0100"
3: "Joystick - HOTAS Warthog" - "USB\VID_044F&PID_0402&REV_0100"
USB HID device "Throttle - HOTAS Warthog"(USB\VID_044F&PID_0404\7&338B91D0&0&4) selected
USB HID device "Joystick - HOTAS Warthog"(USB\VID_044F&PID_0402\6&19FF8499&0&1) selected
USB HID device with hardware id "VID_044F&PID_0403" cannot be found
USB HID device with hardware id "VID_044F&PID_b351" cannot be found
USB HID device with hardware id "VID_044F&PID_b352" cannot be found
USB HID device with hardware id "VID_044F&PID_0400" cannot be found
USB HID device with hardware id "VID_044F&PID_B10A" cannot be found
USB HID device with hardware id "VID_044F&PID_B10B" cannot be found
USB HID device with hardware id "VID_044F&PID_B687" cannot be found
USB HID device "T-Rudder"(USB\VID_044F&PID_B679\7&338B91D0&0&1) selected
USB HID device with hardware id "VID_044F&PID_B68F" cannot be found
USB HID device with hardware id "VID_044F&PID_0405" cannot be found
USB HID device with hardware id "VID_044F&PID_0406" cannot be found
USB HID device with hardware id "VID_044F&PID_0407" cannot be found
USB HID device with hardware id "VID_044F&PID_0408" cannot be found
USB HID device with hardware id "VID_044F&PID_040A" cannot be found
USB HID device with hardware id "VID_044F&PID_040B" cannot be found
USB HID device with hardware id "VID_044F&PID_0409" cannot be found
USB HID device with hardware id "VID_044F&PID_0412" cannot be found
USB HID device with hardware id "VID_044F&PID_0413" cannot be found
USB HID device with hardware id "VID_044F&PID_040E" cannot be found
USB HID device with hardware id "VID_044F&PID_040F" cannot be found
USB HID device with hardware id "VID_044F&PID_0416" cannot be found
USB HID device with hardware id "VID_044F&PID_0417" cannot be found
USB HID device with hardware id "VID_044F&PID_0430" cannot be found
USB HID device with hardware id "VID_044F&PID_0438" cannot be found
USB HID device with hardware id "VID_044F&PID_0431" cannot be found
USB HID device with hardware id "VID_044F&PID_0439" cannot be found
USB HID device with hardware id "VID_044F&PID_0432" cannot be found
USB HID device with hardware id "VID_044F&PID_043A" cannot be found
USB HID device with hardware id "VID_044F&PID_0420" cannot be found
USB HID device with hardware id "VID_044F&PID_0428" cannot be found
USB HID device with hardware id "VID_044F&PID_0421" cannot be found
USB HID device with hardware id "VID_044F&PID_0429" cannot be found
USB HID device with hardware id "VID_044F&PID_0422" cannot be found
USB HID device with hardware id "VID_044F&PID_042A" cannot be found
USB HID device with hardware id "VID_044F&PID_041B" cannot be found
USB HID device with hardware id "VID_044F&PID_041C" cannot be found
Virtual HID devices managed by script!
Connecting virtual joystick...Done
Device name set to Thrustmaster Combined
Connecting virtual keyboard...Done
Connecting virtual mouse (absolute axes)...Done
Hello world!
Library address: 1917059072
Library function address: 1917063168
Mapped function address: 63452096
42
main returned 0

So despite not being mapped as a plugin .dll the dll is still loaded by TARGET and mapped via your .tmc file
This simply means you can drop .dll files into the plugin folder and TARGET will look there at compile/run time.

Posted

I have also found it necessary, when faffing about in the plugins folder, to restart the FAST service.

Much faster than having to reboot the PC.

Here's a batch file I created previously which I run as admin...

@echo off

net stop TmWinService
net start TmWinService

timeout /t 5 /nobreak
exit

 

Posted
4 hours ago, dmonds said:

I have also found it necessary, when faffing about in the plugins folder, to restart the FAST service.

Right, sorry, I forgot to mention that.

What I meant in my comment above was to try using matching function signatures to get the automatic mapping to work without having to go the GetProcAddress-Map route for every desired function, which could mean a lot of boilerplate code. I guess that doesn't work, but thanks for trying it.

I just tried mapping systemMap to a local no-argument function, and got this when calling it:

Runtime Error: Memory access violation (0x32bb0) in SystemMap ( line 35 in LoadLibraryTest.tmc )

I'm guessing it's trying to return some kind of struct or object, but I haven't allocated any memory for the return value. I'll try to figure out how to do that at some point. My guess is it's a struct with function names and/or relative source addresses, analagous to the output of dumpbin. I didn't see "systemMap" in any of the header files, so it must be getting called by the TARGET system itself when a script is compiled.

A web search of "systemMap" produced some interesting information that seems to support this: https://en.wikipedia.org/wiki/System.map

Posted

Dug into it some more. It turns out that systemMap internally calls the builtin Map() on every DLL function that it wants to expose (including Map() itself, in the case of sys.dll). I think the memory access violation comes from the expectation that it will be run by TARGET rather than a script. If it's even possible to replicate this in a home-brewed DLL, it's almost certainly not worth the effort.

  • Recently Browsing   0 members

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