08-29-2016 07:51 AM
Hello,
we use a big home built measurement software written in vb6 in our lab. Just recently we added a new detector which is read out using a labview vi that is called using a dll directly from vb6. Here we call the function e.g. read_detector which starts the data acquisition and in a next step writes the data into a string. When finished the called function returns a pointer to the string and its length. From vb6 we read out this pointer and process the data.
This operation is leaking memory. Could this be the case because we do not preallocate memory for labview to write into and therefore lab view just writes the data into the memory without freeing it up again? In the current configuration the function of the dll is just called over and over and every time lab view creates a new outputstring variable.
If we need to preallocate memory. How can we do this from vb6 ?
Best,
Julian
Solved! Go to Solution.
08-29-2016 08:03 AM - last edited on 07-18-2024 05:01 PM by Content Cleaner
First you should show the VI which was used to create the DLL. I guess you have the memory leak there in the VI itself...
edit: pls do not show a screenshot, but attach your actual VI file, or show as a snippet!
08-29-2016 08:06 AM - edited 08-29-2016 08:07 AM
It would be helpful to see the VI, the according LabVIEW project file and the resulting DLL and header files. If the parameter you pass to the LabVIEW DLL is a string or array buffer then yes you need to preallocate that in your calling program. Unless you do quite a bit of pointer vodoo (which requires calls to LabVIEW manager calls or Windows platform APIs) in the LabVIEW code, the caller is responsible to allocate the space before calling the function. If you do such vodoo then the caller is responsible to clean up after the call returns by using the correct memory manager functions. Since it is often not possible to always control what memory manager functions the callee has used to allocate the memory, it can be hard until impossible to decide what memory manager functions to call in the caller to properly deallocate the buffers. So if you had chosen that approach your DLL also should export a proper function to deallocate such buffers it has allocated and passed back to the caller.
Since this gets cumbersome and hard to control very quickly the more easy approach is to write APIs in such a way that the caller needs to allocate the memory and als deallocate it afterwards. This solves the problem of ambiguaties about what memory manger functions to call.
So without seeing how your actual VB and LabVIEW code is structured and what it actually does, there is no way to tell you if your VB program needs to preallocate the memory buffers or not.
08-29-2016 08:07 AM - edited 08-29-2016 08:09 AM
Oh of course!
08-29-2016 08:09 AM
What about the DLL header file and the VB sample code to call this DLL?
08-29-2016 08:10 AM
@jschmidtengler wrote:Oh of course!
Sorry, this will not help. You only show the main VI, you should attach your entire LV project including all subVIs...
08-29-2016 08:16 AM - edited 08-29-2016 08:21 AM
This should be all of the vi`s in question right?
I will also get the vb6 code that calls the function contained in the dll...
We actually do not preallocate the memory in vb6 before calling the labview dll. I tried to alter the vi that it has a string as input parameter which can be defined beforehand in vb6. LV wants to get this input as string handle pointer, but I do not know how to get this type of pointer from a string in vb6.
08-29-2016 08:19 AM
The .lvproj file would give us some information how you configured the build specification for the DLL. In there is the mapping between the VI connector pane and the DLL function interface.
The second best thing if the .lvproj file is to difficult to share would be the according .h file that the LabVIEW builder generated together with the DLL. That also shows to a less specific degree how the function parameters are configured. And yes the V6 source code that calls the function is also necessary.
08-29-2016 08:23 AM - edited 08-29-2016 08:26 AM
Surely you can have the project file! Thanks already for all of the comments! Your help is greatly appreciated!
The function is defined as follows:
Private Declare Function start_fpas Lib "DataAqFPAS_outputString.dll" Alias "FPAS2UniversalScanAcquireMX_twoDataAqFPAS_outputString" _ (ByVal bytSafeModeDisabled As Byte, _ ByVal dblElements As Double, _ ByVal dblTotalPointsToAcquireCHScans As Double, _ ByRef num_of_chars_ext As Long, _ ByRef StringHandle_ext As Long, _ ByRef num_of_chars_detector As Long, _ ByRef StringHandle_detector As Long) As Long
And called here:
Public Function read_ADC(ByRef ADBuffer() As Single, ByVal shots As Long) As Boolean On Error GoTo ProcError 'Dimming read_ADC = True 'Function input parameters Const bytNoExternals As Byte = 0 Const dblElements As Double = 128 Const nChan_detector As Long = 127 Const nChan_ext As Long = 15 Dim dblTotalPointsToAcquireCHScans As Double Dim shots2 As Double shots2 = CDbl(shots) Dim num_of_chars_ext As Long Dim num_of_chars_detector As Long Dim StringHandle_ext As Long Dim StringHandle_detector As Long dblTotalPointsToAcquireCHScans = dblElements * shots ReDim detector_out_long(127, shots - 1) As Long ReDim extern_out_long(15, shots - 1) As Long Dim n As Long Dim M As Long Dim i As Long Dim counter As Long Dim ausgabe As Long Dim Stringpointer_detector As Long Dim Stringpointer_ext As Long Dim outputstring_detector As String Dim outputstring_ext As String counter = 0 ausgabe = 0 convert2volt As Long convert2volt = 13107 'Call function and read out the pointers ausgabe = start_fpas(bytSafeModeDisabled, dblElements, dblTotalPointsToAcquireCHScans, num_of_chars_ext, StringHandle_ext, num_of_chars_detector, StringHandle_detector) T0 = Timer Do DoEvents Loop Until ((Timer - T0) > 0.01) GetMem4 ByVal StringHandle_detector, Stringpointer_detector GetMem4 ByVal StringHandle_ext, Stringpointer_ext outputstring_detector = SysAllocStringByteLen(ByVal Stringpointer_detector, num_of_chars_detector) outputstring_ext = SysAllocStringByteLen(ByVal Stringpointer_ext, num_of_chars_ext)
08-29-2016 08:55 AM - edited 08-29-2016 08:58 AM
Well first, a LabVIEW string handle is a very specific LabVIEW datatype. Not only does only LabVIEW itself know exactly how to allocate and deallocate it, but you also have to allocate and deallocate it in exactly the same LabVIEW instance. Since your LabVIEW DLL runs for this purpose inside the LabVIEW runtime DLL, you would have to also retrieve the according LabVIEW memory manager functions DSDisposeHandle() from the lvrt.dll and call that after you are done with the strings returned by the DLL function. This is however made extra complicated since there are circumstances where not lvrt.dll is actually used by your DLL but some other LabVIEW runtime system. And if you ever happen to have two LabVIEW DLLs created with different versions of LabVIEW, each of them will load and use its according LabVIEW runtime system DLL in a side by side load. Believe me, you do not want to know how side by side DLLs work and how to make that possibility work in an application. It's the Microsoft way of creating a monster to handle DLL hell and by doing so, creating the exponential version of hell instead.
You basically create two memory leaks for every DLL function call at the moment. LabVIEW creates a new string handle for each of the string parameters at each call, you dereference that pointer and then copy the contents into a SysString. But then you leave the LabVIEW created string handles hang around on the stack and at the next invocation of your function, you initialize new stack variables that get initialized to NULL and LabVIEW creates again two new handles. What you should do is also to define a function "Integer DSDisposeHandle(LStrHandle As Long)" exported from lvrt.dll and call it after you have copied the data from the handle into your SysString for both of those handles.
But basically calling a LabVIEW DLL from non LabVIEW code and using LabVIEW string or array handles as parameters is many times more trouble than it is worth. The most easy approach would be to just allocate a big enough buffer beforehand and pass those to the function, configuring the LabVIEW DLL function not to accept a LabVIEW string handle but a Pointer to a C string instead. Then you circumvent the problem about memory allocated by someone else than your own VB program and having to worry about deallocating it correctly. The drawback of this approach is, that you need to make sure that the allocated buffer is big enough for the maximum possible length that your DLL function may ever return.
But another approach might be to leave out LabVIEW altogether here and call the DAQmx APIs directly from VB.