JasonMel Posted June 5 Posted June 5 (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 June 5 by JasonMel
dmonds Posted June 17 Posted June 17 (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 June 17 by dmonds
dmonds Posted June 17 Posted June 17 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.
JasonMel Posted June 17 Posted June 17 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.
dmonds Posted June 17 Posted June 17 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.
dmonds Posted June 17 Posted June 17 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
JasonMel Posted June 17 Posted June 17 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
JasonMel Posted June 18 Posted June 18 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.
Recommended Posts