Build Your Own Arcade Controls Forum
Main => Software Forum => Topic started by: DrakeTungsten on June 12, 2017, 04:53:30 pm
-
I wrote a front-end, for which I want to add support for uploading Ultrastik maps to the joysticks. I downloaded the DLL package form the Ultimarc site, but am having a problem with the linking step of building my exe.
I am compiling on windows 7, 64-bit, using MSYS2. The SDK from Ultimarc contains 7 folders, most of which which appear to represent various compilers. These folders are: C#, C++, Delphi, dlls, libsub, VB.net, VB6. Since none of these are specifically for mingw32, I figure my best bet is using whatever's in the generically-named C++ folder.
I copied the two header files in this generically-named C++ file to where my project source files are, and in the main cpp file for my project, I "#include"-ed these files. I then added two more lines to my source file, which I picked up from the Ultimarc documentation:
int deviceCount = UltraStik_Initialize();
UltraStik_Shutdown();
When I "make" the project, the step to create an object file from this works just fine. In case it helps, here is that complete line in my makefile:
g++ -c -g -std=c++0x nq.cpp
But it fails on linking (linking works when I recompile after commenting-out the two Ultrastik functions). Here is my link command in my makefile:
g++ nq.o hbsdl.o nq.res -o nq -static-libgcc -static-libstdc++ `sdl2-config --cflags --libs` -lSDL2_image -lSDL2_ttf -L /c/NoQuarter -lUltraStik64
And the messages I get are:
undefined reference to '__imp__Z2OUltrastik_Initializev'
undefined reference to '__imp__Z18Ultrastik_Shutdownv'
I do have the dll in the folder specified by the "-L /c/NoQuarter" part of the command. If I delete that dll, then I get a different message saying "cannot find Ultrastik64", so the problem is not that it doesn't see the dll.
My googling is telling me that the problem appears to be a mismatch in the way different compilers perform name-mangling, and some folks say it's hopeless to try to link object/library files created by different compilers, while others say that there are tools which should be able to convert the name mangling in one object to be compatible with a compiler other than the one which created it. I'd rather not resort to such tools. Any advice to get the Ultrastik DLLs to work with what I have? Just to grasp at straws, I tried some of the other DLLs in the SDK download, but no dice.
As an aside, I don't understand the need to link to DLLs. It seems to go against the point of DLLs. But I know my SDL functions also need the DLLs I'm using to be present when I link, so I suppose there's a good reason.
-
I'm the author of the SDK so I should be able to help here :)
Just be aware that internally the name of the dll is actually "UltraStik". I only append the 32/64 to differentiate between the two filenames.
I use Visual Studio to compile them so there may be issues linking them with MinGW. If there is a tool to convert the name mangling I don't see why you can't use one.
As a last resort you can always use LoadLibrary (https://msdn.microsoft.com/en-us/library/windows/desktop/ms684175(v=vs.85).aspx) to dynamically load the dll and GetProcAddress (https://msdn.microsoft.com/en-us/library/windows/desktop/ms683212(v=vs.85).aspx) to call the actual functions in the dll.
-
Thanks for the reply. I tried the conversion tools (gendef and dlltool) but they didn't help. I'm sill looking into how to use LoadLibrary and GetProcAddress.
-
Did you take a look at MSVC and MinGW DLLs (http://www.mingw.org/wiki/MSVC_and_MinGW_DLLs)?
-
headkaze, sorry to jump in on this thread but maybe you might be the one to ask on this outside of Andy.
The new king of fighters XIV on steam doesnt work by default with the ultrastiks. Someone had mentioned adding them to the gamecontrollerdb.txt which uses SDL2 configs.
I tried using the SDL2 Gamepad tool but it didnt seem to detect the up and down. I have everything working with the setup below except for up and down. Any thoughts on what setting might work? obviously I was guessing below and it didnt work.
09d21105000000000000504944564944,Ultimarc Ultra-Stik Player 1,a:b0,b:b1,x:b3,y:--BINGO! Either that, or I was attempting to say "before" but it was too many letters to type--,start:b7,leftshoulder:b2,rightshoulder:b5,dpup:a2,dpdown:a3,dpleft:a0,dpright:a1,platform:Windows,
09d21205000000000000504944564944,Ultimarc Ultra-Stik Player 2,a:b0,b:b1,x:--BINGO! Either that, or I was attempting to say "before" but it was too many letters to type--,y:b5,start:b7,leftshoulder:b2,rightshoulder:b5,dpup:a2,dpdown:a3,dpleft:a0,dpright:a1,platform:Windows,
Thanks for any help!
-
I figured this out. Sorry.
-
Yes, I saw that link. That's what lead me to try using gendef and dlltool. That link talks about a tool called pexports, but it wasn't installed on my msys2 install, and further research showed that gendef, which I did already have installed, does the same thing. Using the file created by these two tools gives me the same error. The other method, of using the reimp tool, successfully created another file which is supposed to be a MINGW compatible library, but linking against that library also gives me the same undefined reference errors I originally had.
I tried wrapping the two header files inside an 'extern "C"' block, but that resulted in the linker complaining about conflicting definitions. I think that's because the functions which had conflicting definitions are overloaded functions, so they require name mangling so they can be differentiated.
I haven't got around to trying GetProcAddress yet. It sounds like I need to know the name MSVC mangled the functions into, and I haven't found that yet. If that route doesn't work, I recall seeing that the Ultramap program is supposed to be able to upload maps from the command line, so if that can work without launching the Ultramap GUI, I can just build a command line for it and run it with a process call.
-
I haven't got around to trying GetProcAddress yet. It sounds like I need to know the name MSVC mangled the functions into.
No, you don't need to know the "mangled" function names. Just use the real ones as presented in the header file.
Here is an example of how MAME uses LoadLibrary/GetProcAddress for Raw Input.
// RawInput APIs
typedef /*WINUSERAPI*/ INT (WINAPI *get_rawinput_device_list_ptr)(OUT PRAWINPUTDEVICELIST pRawInputDeviceList, IN OUT PINT puiNumDevices, IN UINT cbSize);
typedef /*WINUSERAPI*/ INT (WINAPI *get_rawinput_data_ptr)(IN HRAWINPUT hRawInput, IN UINT uiCommand, OUT LPVOID pData, IN OUT PINT pcbSize, IN UINT cbSizeHeader);
typedef /*WINUSERAPI*/ INT (WINAPI *get_rawinput_device_info_ptr)(IN HANDLE hDevice, IN UINT uiCommand, OUT LPVOID pData, IN OUT PINT pcbSize);
typedef /*WINUSERAPI*/ BOOL (WINAPI *register_rawinput_devices_ptr)(IN PCRAWINPUTDEVICE pRawInputDevices, IN UINT uiNumDevices, IN UINT cbSize);
// RawInput variables
static get_rawinput_device_list_ptr get_rawinput_device_list;
static get_rawinput_data_ptr get_rawinput_data;
static get_rawinput_device_info_ptr get_rawinput_device_info;
static register_rawinput_devices_ptr register_rawinput_devices;
...
HMODULE user32;
// look in user32 for the raw input APIs
user32 = LoadLibrary(TEXT("user32.dll"));
if (user32 == NULL)
goto error;
// look up the entry points
register_rawinput_devices = (register_rawinput_devices_ptr)GetProcAddress(user32, "RegisterRawInputDevices");
get_rawinput_device_list = (get_rawinput_device_list_ptr)GetProcAddress(user32, "GetRawInputDeviceList");
get_rawinput_device_info = (get_rawinput_device_info_ptr)GetProcAddress(user32, "GetRawInputDeviceInfo" UNICODE_SUFFIX);
get_rawinput_data = (get_rawinput_data_ptr)GetProcAddress(user32, "GetRawInputData");
if (register_rawinput_devices == NULL || get_rawinput_device_list == NULL || get_rawinput_device_info == NULL || get_rawinput_data == NULL)
goto error;
osd_printf_verbose("RawInput: APIs detected\n");
// get the number of devices, allocate a device list, and fetch it
if ((*get_rawinput_device_list)(NULL, &device_count, sizeof(*devlist)) != 0)
goto error;
if (device_count == 0)
goto error;
devlist = global_alloc_array(RAWINPUTDEVICELIST, device_count);
if ((*get_rawinput_device_list)(devlist, &device_count, sizeof(*devlist)) == -1)
goto error;
...
No too difficult is it?