02-14-2023 11:13 AM
Hello!
For a DLL call I need an array of cluster (cluster definition below) in size 18. While doing the opposite, to define an array of specific size by using "Array to cluster" and just set the cluster size is simple to do, I couldn't yet figure out how to do this challenge.
My first idea was to wire the cluster to "Cluster to Array" and then to "Array to Cluster" and set the size, but "Cluster to Array" refuses my cluster.
I can, of course, programmatically initialize an array of size 18, but I need to finally create a constant from it to be passed to the DLL call.
Solved! Go to Solution.
02-14-2023 11:50 AM
Are you sure the elements are all 8 bit integers?
In that case I would simply create an array of u8 with 3 + 18 * 2 + 1 = 40 elements and pass that as a Array of u8, pass Array data pointer to the DLL.
Insert the data that you want at the right offsets to pass to the DLL and retrieve the data that you need from the array after the CLN call.
02-15-2023 02:38 AM
"Are you sure the elements are all 8 bit integers?" - In this case, yes.
Of course, my first idea was to create the cluster as defined in the header file and then use "An Typ anpassen" which cannot work anymore when replacing parts or the entire cluster by either an array of size X or a straight cluster. The advantage of this method is, of course, you can address single cluster elements by their name.
So your solution would be to count all the bytes in the struct, then create an "array to cluster" of that total size and pass it as array of U8 to the DLL? If that works, then this will be easier as I thought, though on the other side, where I feed the cluster/array with data it would mean much more code to index and combined two U8 indexes for a U16 value etc. Well, you can't have everything...
02-15-2023 05:37 AM - edited 02-15-2023 05:38 AM
Second obstacle: not sure how to declare the input of the pointer pDpData in
DPOpenSlave(unsigned char ucSlvAddr, DP_DATA FAR * pDpData, unsigned int uOpenTimeout)
From my understanding I shall choose this:
02-15-2023 06:03 AM
@MaSta wrote:
Second obstacle: not sure how to declare the input of the pointer pDpData in
DPOpenSlave(unsigned char ucSlvAddr, DP_DATA FAR * pDpData, unsigned int uOpenTimeout)
From my understanding I shall choose this:
I dont know either since I don't know what API you are trying to interface and have no idea what DP_DATA really means. Without the according header it could be anything.
But it is DEFINITELY and ABSOLUTELY NOT a InstanceDataPtr. That is a pointer sized value that is specially managed by the Call Library Node in conjunction with the Callback functions in the Callbacks tab in the Call Library Node dialog. Unless you are a real C guru you don't even want to know what that is and how it works.
02-16-2023 03:07 AM
My struggle currently: I came from C coding (embedded C application) to LabVIEW and for the most things, LV is easy to grasp. But NI must have thought something making it so hard. They know, from their own C/C++ back then and now C# or whatever, what data types a DLL could use.
So, no matter what DP_DATA (in fact it's a struct) is, if the DLL requires a pointer to DP_DATA, I should be able to set it as a pointer, but I can't. I read through the LV help file, the section for DLL calls. I also read some threads here suggesting to not pass a string, but an array of ASCII bytes with a 0 at the end. Why? LV simply has to pass it correctly when I define a parameter as type string and the format as C string pointer.
In the Profibus DLL from Bihl & Wiedemann's Profibus Master Simulator (thats my API), which I'm trying to integrate into LV, there is simpler function call: EXPO1 DP_RESULT EXPO2 DPInit(LPCTSTR pDllName, LPCTSTR pInitSting) which resolves into
ulong DPInit(const char *pDllName, const char* pInitSting). The call in the Visual Studio example is DPInit(NULL, Buffer).
The first attempt by myself was
Which doesn't work. It always returns an error. The same COM port in the compiled Visual Studio EXE works. So it's merely the call in LV.
Next step was to accidentallly find the DLL importer of LV and start the DLL import with the DLL and header file which created most of the function as VI, but got stuck on one and I had to kill the task. But DPInit was created. The difference is that a) the importer set stdcall and b) gave the pointers a huge size.
However, the call still returns that error. My best guess is the DllName input. It's supposed to be empty or rs232. But even if I put rs232 into it, it doesn't change anything. The difference between the C call and stdcall is that the C call crashes LV.
02-16-2023 03:29 AM - edited 02-16-2023 03:30 AM
The problem is that LabVIEW is a VERY strictly typed language. You don't have pointers of things and even less void* datatypes. C on the other hand is the pure opposite. A pointer is a pointer is a pointer and where it points at is both interpretation and context specific.
The Call Library Node gives you the ability to bridge that but there is no way it can know for you what needs to be done and there is also no way to simply match anything to a LabVIEW datatype. You have the similar problem in all higher level languages including C# and Python. In C# you must do Interop or C++ CLI (obsolete by now) on Python it is ctypes and in most other languages it is some form of libffi or similar vodoo.
If DP-DATA is a struct you need to build this struct accordingly and if it has pointers in it things get hairy since LabVIEW doesn't really know pointers. Pointers is the complete antagonisme to data flow programming!!
02-16-2023 03:54 AM - edited 02-16-2023 03:56 AM
Addition: the error code from the DPInit tells me loading of another DLL failed, the one defined by pDllName. It's in the same directory as the DLL I call, so the called DLL should find it. At this point, I'm lost.
The other things are try and error. Thanks for your help so far, Rolf.
02-16-2023 04:36 AM - edited 02-16-2023 04:38 AM
@MaSta wrote:
Addition: the error code from the DPInit tells me loading of another DLL failed, the one defined by pDllName. It's in the same directory as the DLL I call, so the called DLL should find it. At this point, I'm lost.
No, Windows DLL loading doesn't work like that! Unless your DLL does specific attempts to load the DLL from the same directory as it is located, the Windows load rules are pretty strict.
If you pass a full path, the loading fails if the DLL can't be loaded in that location, otherwise Windows searches following directories:
1) Already loaded into the process, use it
2) In the same directory as where the current process executable is located, for the LabVIEW IDE that would be the folder where LabVIEW.exe is
3) The Windows System folder
4) The Windows Windows folder
5) The current folder (forget this one, trying to rely on this is an exercise in self torture)
6) The directories listed in the PATH environment variable
None of these contains the directory in which the calling DLL is located. Unless that DLL does do custom path resolution by determining where it is located and then appends the desired DLL name, the DLL in the same folder as the calling DLL will be usually not possible to be found!
And that is not LabVIEW specific. If you want your DLL to be able to load another DLL from the same directory it is in even if that DLL is not located in one of the above locations, your DLL has to do this explicitly itself. If your DLL doesn't do that you have to talk with the manufacturer of your DLL why it won't support that.
02-16-2023 04:49 AM - edited 02-16-2023 04:52 AM
I see. Ok, so I tried to put the secondary DLL
OK, one step further. Now the call works. I'm thinking about adding the actual VI/executable path to the PATH variable, as copying DLLs into the system paths isn't the right way for me.
IMHO, the first place for Windows to look for a DLL should always be the folder of the process calling the DLL.