LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Get string from DLL (DLL-allocated memory)

Solved!
Go to solution

Hi, I'm aware there are plenty of threads around this topic, but there are a lot of variations and I've never used LabVIEW before, and I seem to be struggling at a very basic level, so I'm hoping someone can help me out with the very simple specific test case below to put me on the right track, before I tear my remaining hair out.

 

I have created a DLL with a single function "GenerateGreeting". When called, it allocates sufficient memory for the string "Hello World!\0" to the pointer pGreeting, copies that string to the pointer, and sets the GreetingLength parameter to the number of bytes allocated (in the DLL I ultimately want to use, there is a DLL function to free memory allocated this way).

 

I created a header file to go with the DLL, containing the following line.

 

 

extern __declspec(dllimport) int __stdcall GenerateGreeting(char* &pGreeting, int &GreetingLength);

I then imported the file into LabVIEW using the Import Shared Library wizard. That created a "Generate Greeting.vi", and all looks somewhat sensible to me (although that doesn't mean much at the moment). When I run the vi, the "GreetingLength out" correctly displays "13", the length of the string, however "pGreeting out" shows only three or four junk characters (which vary on each run), instead of the expected string.

 

The pGreeting parameter is set to type "String", String format "C String Pointer", Minimum Size currently 4095. I'm thinking the problem is that the DLL wants to allocate the memory for pGreeting; the caller is supposed to pass an unallocated pointer, and let the DLL allocate the appropriate amount of memory for the string, but LabVIEW is expecting the DLL to write to its pre-allocated buffer. How does one do that with LabVIEW? Most of the functions in the DLL I ultimately want to use work that way, so I'm hoping it's possible. Or do I need to rewrite all my DLL functions to use caller-allocated buffers?

 

The vi , header and DLL are atteched, any tips appreciated. Edit - can't attach DLLs or headers.

 

 

 

0 Kudos
Message 1 of 10
(5,865 Views)

In most cases when you get a string out of a DLL call you need to pass in a blank LabVIEW string of the same length or longer.

 

Can you try it with a pGreeting in consisting of 13 spaces and a GreetingLength in of 13?

0 Kudos
Message 2 of 10
(5,856 Views)

Thanks for the reply. With pGreeting in set to thirteen spaces and GreetingLength in set to 13, IpGreeting out has four junk characters followed by nine spaces. The four junk characters change each run as before. I'm thinking those four characters are a representation of the pointer the DLL is trying to allocate.

0 Kudos
Message 3 of 10
(5,851 Views)

Is it possible that in the DLL code you are copying the pointer to the string instead of the string contents?

 

It's been quite a while since I actively developed anything in C so I'm trying to recall my college courses from 15 years ago.

0 Kudos
Message 4 of 10
(5,844 Views)

caller-allocated buffers

I will do that if i were you.

 

Or use LabVIEW string handle instead of c string pointer.

That way the string length is well defined.

 

On the other hand, the problem could be in your C code.

Your inputs are pointers.  You can do whatever you want in the C code.

 

 

George Zou
0 Kudos
Message 5 of 10
(5,831 Views)
Solution
Accepted by tony_si

@tony_si wrote:

 

 

extern __declspec(dllimport) int __stdcall GenerateGreeting(char* &pGreeting, int &GreetingLength);

 

 

 


Well that char * &pGreeting is in fact a C++ thing (no C compiler I know would accept that) and it basically means that the char pointer is passed as a reference. So technically it is a double referenced pointer, however nothing in C++ specifies that reference parameters need to be implemented as a pointer on hardware level. So a C compiler builder would be free to decide to use some other possible mechanisme that his target CPU architecture supports. However for the C++ compilers I know it is really just syntactic suger and is internally implemented as a pointer.

 

LabVIEW has no datatype that directly allows to configure this. You'll have to configure it as pointer sized integer passed as pointer to value and then use a MoveBlock() call or the GetValuePtr() support VI to copy the data out of the pointer into a LabVIEW string.

 

AND: You need to find out how the DLL allocates the pointer so that you can properly deallocate it after each call to this function. Otherwise you create most likely a memory leak, since you say that the first 4 bytes in the returned buffer always change, this function seems to allocate at each execution a new buffer that you need to properly deallocate. Unless the DLL uses a Windows API function such as HeapAlloc() for this it should also export an according function to deallocate the buffer. Functions like malloc() and free() from the C runtime may not always be implemented in the same version between caller and callee, so that calling free() in the caller on a buffer that was allocated with malloc() in the DLL may not work on the same heap and cause undefined behaviour.

Rolf Kalbermatter
My Blog
0 Kudos
Message 6 of 10
(5,824 Views)
Solution
Accepted by tony_si

tony_si wrote:

I have created a DLL with a single function "GenerateGreeting". When called, it allocates sufficient memory for the string "Hello World!\0" to the pointer pGreeting, copies that string to the pointer, and sets the GreetingLength parameter to the number of bytes allocated (in the DLL I ultimately want to use, there is a DLL function to free memory allocated this way).

 

I created a header file to go with the DLL, containing the following line.

extern __declspec(dllimport) int __stdcall GenerateGreeting(char* &pGreeting, int &GreetingLength);

I can't open your VI - I'm on LabVIEW 2012 - so these comments are based only on your text. Your header is a bit weird, normally you don't put the & in your function prototype, rather you should have char **hGreeting, int *pGreetingLength.

 

Everyone telling you to preallocate your string in LabVIEW seems to have missed your comment that your DLL allocates the memory and returns a pointer to it. So, the first parameter is actually a handle (let's call it hGreeting) and it should be configured as a pointer-sized integer, passed by pointer. The DLL will return in that parameter the memory location where it stored the string. This is why you get 4 funny characters with your current code - you are using a 32-bit development environment, 32 bits is 4 bytes/characters, what you think is the string is actually the address of the string. Once you change have the pointer-sized integer returned from the DLL, use GetValueByPointer or MoveBlock (https://decibel.ni.com/content/docs/DOC-9091) to retrieve the string from that address. When you call the function in your DLL to free that string, you'll may need to pass the address by value rather than by pointer, depending on how that function is configured.

0 Kudos
Message 7 of 10
(5,818 Views)

@nathand - thanks, I was finding it hard to believe that LabVIEW didn't have a way to deal with data returned that way, that makes sense now. I could see what LabVIEW was probably doing with the pointer, it just wasn't obvious how to make it dereference the pointer. So "it should be configured as a pointer-sized integer, passed by pointer" and "use GetValueByPointer or MoveBlock" I think is going to get me where I need to go.

 

Yes, the header is a verbatim translation from Delphi to C++, in Delphi it's "var pGreeting: PAnsiChar", where var means the parameter is passed by reference, I translate the var to "&" and PAnsiChar to "char*". I have to admit to not being a C or C++ expert, so maybe that's not quite kosher, but that form seems to work in the C++ compilers I deal with, maybe I need to revisit that.

0 Kudos
Message 8 of 10
(5,790 Views)

@rolfk - the DLL I'm going to be working with beyond this test case has its own routine for freeing memory allocated by its functoins; all its routines that allocate memory return a pointer to the memory and a size value, so I need to keep track of those in LabVIEW, and pass them back to the DLL routine to free the memory once its no longer needed. I'm still trying to get my head around the order that things happen in LabVIEW, so it's not exactly clear where I should make the call to free the memory, but hopefully I'll be able to figure that out soon. 

0 Kudos
Message 9 of 10
(5,783 Views)

I have published an entire MSVS2010SP1-Example, it includes a procedure to handle strings of different size:

Basic Hello World c++DLL-Sample

0 Kudos
Message 10 of 10
(5,755 Views)