LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

LabView VB6 Dll Interfacing causes memory leak

Solved!
Go to solution

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

0 Kudos
Message 1 of 13
(5,016 Views)

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!

0 Kudos
Message 2 of 13
(5,006 Views)

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. 

Rolf Kalbermatter
My Blog
0 Kudos
Message 3 of 13
(4,997 Views)

Oh of course!

 

 

0 Kudos
Message 4 of 13
(4,996 Views)

What about the DLL header file and the VB sample code to call this DLL?

Rolf Kalbermatter
My Blog
0 Kudos
Message 5 of 13
(4,992 Views)

@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...

0 Kudos
Message 6 of 13
(4,990 Views)

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.

0 Kudos
Message 7 of 13
(4,985 Views)

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.

Rolf Kalbermatter
My Blog
0 Kudos
Message 8 of 13
(4,978 Views)

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)
0 Kudos
Message 9 of 13
(4,974 Views)
Solution
Accepted by topic author jschmidtengler

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.

Rolf Kalbermatter
My Blog
Message 10 of 13
(4,946 Views)