I am attempting to access an external DLL and DyLib so I can interface with custom hardware. Each of the devices I need to support use identical APIs, but different DLLs. I’m running into a problem where the first function call I make locks Xojo into using that specific DLL and it will not use the others. This makes it impossible for me to support multiple different devices as the user would have to quit the application when changing devices.
// Two devices, A, and B, each with their own DLL, A.dll, B.dll, both use the same function of “Connect”.
soft declare function Connect_A lib “A.dll” alias “Connect” () as Boolean
soft declare function Connect_B lib “B.dll” alias “Connect” () as Boolean
dim resultA as Boolean = Connect_A()
// Successfully connects to device A
dim resultB as Boolean = Connect_B()
// Fails to connect to B, but instead connects to A
If I reverse the order and connect to B first, then the following attempt to connect to A will also connect to B. I’ve tested this with multiple different functions in the device API, in every case, whichever DLL I call first for a given function name call will be the DLL used for all subsequent function calls to that same name. It seems to ignore the fact that I’m attempting to use a different DLL.
I have tried this as soft declares and hard declares. I have tried splitting the declares into different methods. I have tried splitting them into different classes. I’ve tried destroying the class instance after making a call so nothing should still be referencing the DLL. Nothing I do seems to get this to work. There is a similar thread from 2017 where someone was trying to do this and seemed to indicate in the end they had success, but it does not work for me. (https://forum.xojo.com/40569-loading-two-same-api-dll-and-declare-usage)
Any ideas? Am I doing this wrong, or is this just something Xojo can’t handle due to the way it loads the DLLs? Anyone have any suggestions on a work around? Ultimately I need to support these different devices with the users dynamically swapping between them without having to quit in order to connect to the new device. I have no control over the DLLs so I have to work with what I am given.
Without knowing what happens as a result of the calls, this may be no use, but:
Have you considered making helper apps?
Launch the relevant helper, and the helper uses the relevant DLL, does its thing, then exits?
One would do… just to handle the ‘lesser used’ of the two devices.
Alternatively, and much easier, if you ship the DLLs and they are not likely to be replaced without your knowledge, you could open one of them with a Hex Editor and change the function name of one of them to be Connec2
I’ve tried everything from that thread you linked to including creating separate classes to trying to unload the dll’s by hand using windows declares and none of it works. I’ve also checked and it is the same in macos using dylib’s so I think its definitely a framework issue rather than a windows implementation issue.
Other than what Jeff has suggested, the other thing you could do is create three wrapper dlls that changes the exposed names, but that is not very practical if the dll’s are huge, complex and/or change often. As Jeff alluded to, you could spin up a helper app for each device, leave them running while the main app is running and talk to those via IPC. Although, that might be too slow or not an option if there’s huge data transfer between the dll and the app, but maybe you could look into shared memory instead.
IMhumbleO, if its just a feature to support three different devices I don’t think a restart of the app is totally abhorrent for a change once per week/month/year etc. but if its a frequent change, multiple times a day/minute in the app then that would obviously be a bit naff.
If you’re feeling mildly ambitious and want to try delegates and declares, this is fairly simple on windows using LoadLibraryA and GetProcAddress.
Of course if you have a lot of functions to deal with, it could be some work…
(there is no error checking below… leaving that to you)
Declare Function LoadLibraryA lib "Kernel32" (fileName as CString) as ptr
Declare Function GetProcAddress lib "Kernel32" (hmodule as ptr, procName as CString) as ptr
dim hmodA as ptr=LoadLibraryA("A.dll") //get a handle to the module
dim funcAddressA as ptr=GetProcAddress(hmodA,"Connect") //get the address of the desired function
dim myConnectDelegateA as new ConnectDelegate(funcAddressA) //create a new delegate for the function
dim hmodB as ptr=LoadLibraryA("B.dll") //get a handle to the module
dim funcAddressB as ptr=GetProcAddress(hmodB,"Connect") //get the address of the desired function
dim myConnectDelegateB as new ConnectDelegate(funcAddressB) //create a new delegate for the function
Add a delegate to your class/module ConnectDelegate() as boolean
Thank you everyone for your suggestions. It does sound like I am doing things correctly and there is a bug with Xojo that is preventing it from working.
The helper app is a potential solution. They need the ability to swap devices, but not constantly, so I could let the app start up once, and just control shutting it down at the point of device change. However, part of the solution is to let the main app automatically determine which device is connected, and that hunt process will be problematic with a helper app that needs to quit and restart between each device test.
Hacking the DLLs is actually pretty ingenious, and I might otherwise think about that except I don’t want to deal with potential problems introduced if I hack something wrong (or worse, when something inevitably is wrong, not being able to firmly point the finger at the DLL if that is where it looks to be). Plus then I have to worry about hacking them each time they are updated by the device team.
Calling the functions myself via OS level calls could work, although since I have access to Christian’s plethora of MBS plugins, I’m going to give them a try before trying to deal with OS level calls. My guess is he is doing something similar internally to MBS. Since it looks like MBS creates a separate object instance for each DLL, it will hopefully not suffer from this confusion issue. It is going to make my code a little more convoluted so in the long run I really hope Xojo can fix the issue, but I suspect it is not a common problem so may very well not be added to their fix list for a while.
Worst case scenario I guess I can abandon the auto detect feature, make the user to select the specific model device they are connecting, and force them to restart the app in order to change devices. My understanding is most users use just one device all the time, and there are not too many that use multiple devices. Those that do hopefully don’t have to change them often. But this is a last resort for me as it prevents us from abstracting the device selection entirely and letting the user simply connect a device, have it be recognized, and just start working.
Thanks again everyone for your help and advice. I am off to file a bug report with Xojo and then start seeing if I can get it working with MBS’s SoftDeclareMBS routine.
Chris - do your dll’s define a MODULE name ?
If not I wonder if they have the same module name and because of that the Windows DLL loader believes that it already has the right module loaded and so just uses the same DLL
That would explain the behaviour your seeing where it uses one or the other but not both
Sorry, got side tracked on another project and just looping back to this.
@Greg: Multiple helper apps could be an option, but right now there are 4 devices, and that is expected to continue growing. One helper app per device, all running in the background is going to get messy. Plus it will eventually reach a point where there is more app space consumed by device helper apps than the main app itself. I’m not ruling this out entirely, but it is not a preferred solution.
@Norman: I don’t have source code to check the device DLLs that I am ultimately trying to talk to, but I did create two sample DLLs and I believe they should have different module names. I’m a bit out of my element with creating DLLs, but to keep them super simple I used Visual Studio 2019 to create two new DLL projects, then just change the name of the sample function in one of them to the same name as the other. The prefab DLL code that VS uses does not use def files, but does use __declspec and prefaces everything with what I am assuming is the Module name. So my two lines are:
extern “C” GETWHICHDLL_API int fnGetWhichDLL(void);
extern “C” NOTTHESAMEDLL_API int fnGetWhichDLL(void);
It still occurs even with two distinctly different DLLs with an overlapping function name.
(The sample code I included on my feedback case was not setup like this and probably does have duplicate module names. I will amend my feedback case to use this version)
The way the sample code for the device DLLs appears to work is it calls a LoadLibrary and FreeLibrary before trying to connect and after it is done. Is there any analogous call in Xojo that can release the library and let me change which one it is using without having to quit and re-launch the app? In my further testing it seems when using soft declare it will lock on to whatever the last different DLL is that was called. So for my 4 devices it will stick to the 4th one. So it looks like it knows the next call is going to a different DLL, correctly loads that DLL, but then on subsequent attempts to talk to an earlier DLL it gets confused and just assumes the last one loaded is the same thing so uses it. If I could somehow tell Xojo to unload the soft declares and start over I could likely work around this issue.
You can use LoadLibrary and FreeLibrary directly from Xojo code. Using Run-Time Dynamic Linking gives you an example, if needed, on how to get a function entry address but I suppose that the sample code show you how to deal with this topic.
You need to map the address returned from GetProcAddress to a Xojo Delegate and use the Invoke method to call the function.
Pay attention to all the functions parameters type.
I ran into this issue recently and I resolved it with DeclareFunctionMBS.
It worked so well I just wanted to pop in here and plug it. It honestly works better than standard Xojo declares and allows me to so flexible with my declares, allowing me to create classes that load DLLs and Dylibs dynamically at runtime.
Those MBS plugins are worth every penny. Thank you Christian!