LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Passing a string buffer pointer to a DLL in LabVIEW

Hello there!

I'm trying to call a DLL supplied from hardware manufacturer and all previous calls to the DLL have been of the form:

long hasp(long service, long seed, long lptnum, long pass1, long pass2, long *Par1, long *Par2, long *Par3, long *Par4);

The Call Library Node is using stdcall (WINAPI).

Changing the input parameters Par1 to Par4 have worked well and behaved as described.

One particular function I wish to access requires that a pointer to a string buffer be known and then passed to the DLL. I've tried this function in VB6 and CVI 6.0 under Window 2000 Srv Pck 4 and it works fine but I'm having problems to convert the same VB/CVI code into G. CVI code as follows:

p1 = 0;
/* start address */
p2 = MEMO_BUFFER_SIZE >> 1; /* buffer length in words */
p3 = 0;
p4 = (int)&MemoBuffer;
#endif

/* fill buffer with zeroes to ensure that buffer content */
/* was modified by the READBLOCK service */
memset(&MemoBuffer, 0, sizeof(MemoBuffer));

hasp(MEMOHASP_READBLOCK, SeedCode, PortNum, Pass1, Pass2, &p1, &p2, &p3, &p4)

Input parameter 4 (p4) is the problematic one. I've enclosed the data that is passed to the CVI version of the code as a .jpgs.

I've tried creating a 48 U8 byte buffer (the 48 comes from the CVI .jpg - see &MemoBuffer) using the Initialize Array VI and then type casting it to string and then passing it to the DLL node input. In the Call Library Function, I changed the p4 input to "String" and have tried different "String Formats" but these either make LabVIEW unstable or crash LabVIEW completely.

Can anyone tell me how to pass an unsigned char buffer of 48 chars into
the DLL?

Any help greatly appreciated.

Regards,
Chroma
Download All
0 Kudos
Message 1 of 15
(8,516 Views)
Did you try pass an array of U8 byte integers as it is without casting it to string? you'll probably need to modify the parameter to 1D array of U8.

Joe
0 Kudos
Message 2 of 15
(8,516 Views)
Hey Joe - thanks for the suggestion!

It tried what you said using the following prototype for the Call Library Function in LabVIEW:

long hasp(long service, long seed, long lptnum, long pass1, long pass2, long *Par1, long *Par2, long *Par3, unsigned char *Par4);

As an aside, Par3 returns the Status of the call to the DLL and for other "services" (see above prototype) they have worked well. I continually get "-9" back as the status, which the DLL supplier references to "Invalid Pointer. The pointer passed to the service is not valid".

When I tried using your suggested method adn then creating a 48-byte buffer of zero U8s, the DLL responded with the -9 status error.

For kicks, I also tried changing the Array Format under the Call Libr
ary Function to "Array Handle" and to "Array Handle Pointer" (doesn't make sense based on the C code) and LabVIEW prompts with "Not enough memory to complete this operation" and crashes with "memory.cpp line 593" respectively.

I was thinking this was going to be simple (think again!) as all we doing is:

1) Define a buffer of 48 bytes
2) Populate it with zeros
3) Get a pointer to the buffer in LabVIEW
4) Pass this pointer to the DLL

Any more suggestions? I forgot to mention that I'm using LabVIEW 7.0 and the DLL needs some hardware for any testing.

Regards,
Chroma
0 Kudos
Message 3 of 15
(8,516 Views)
When you passing the arry to the DLL, it is dynamically created or from a variable? Sometimes the DLL does not like the dynamically created memory. Also, when you pass the array, it is the array itself you are passing, not the handle to the array. You might also need to increase the size of the array, because C string terminates with a binary '0'.

Joe
0 Kudos
Message 4 of 15
(8,516 Views)
Hey Joe - thanks for your feedback again!

Please see the enclosed zip file (tested & v-scanned!) for the LV HTML documentation of the code. As you see there's not much too it! The CVI code defines MEMOBUFFER as follows:

#define MEMO_BUFFER_SIZE 48 /* size in bytes */
unsigned char MemoBuffer[MEMO_BUFFER_SIZE];

To answer your questions:

1) I guess I'm trying to create a static blank string buffer (or array of unsigned chars - whatever is your preferred definition!). For the LabVIEW testing, all I have been doing is to create a 48 byte unsigned char array (0-47, set byte 47 to equal zero on the LV front panel).

2) As the CVI code suggests, it's not the buffer contents I am passing but the pointer or memory location
of where the buffer is. The DLL needs the location so that the hardware driver can pass its contents. The CVI and VB example code sets a buffer size of 48 bytes, so I'm not sure what I'd need to set it higher(?). Also you mentioned about "C string terminates with a binary '0'" - what do you mean? Not sure I follow. The clearing of the buffer is really just to check that we are getting new data from the DLL. I tried passing an "Array Handle Pointer" (Array1DUnsigned char ***Par4) and this crashed LabVIEW.

Any thoughts?

Regards,
Chroma
0 Kudos
Message 5 of 15
(8,515 Views)
If you just want to pass a character array (string) to the DLL, please see the attached image. The illustrated function is a standard Win32 API call, it take a drive letter as input (string), and return the drive type (integer).

Following is the equivalent code in VB:

Declare Function GetDriveType Lib "kernel32" Alias "GetDriveTypeA" (ByVal nDrive As String) As Long

Hope this time helps,

Joe
0 Kudos
Message 6 of 15
(8,515 Views)
Hi,

just curious: p4 is a pointer to int. Is it really meant to pass the address of this pointer in the function call?

Regards
Matthias
0 Kudos
Message 7 of 15
(8,515 Views)
Hello there!

Some more information. I've snaped the params that are being passed to the DLL and return from the DLL in CVI and VB.


For the record LabWindows/CVI 6.0 passes the following to the DLL:

p1 0 int*
p2 24 int*
p3 0 int*
p4 4252524 (0x0040E368) int*

Memory buffer initialized to 48 chars

The DLL returns with:

p1 0 int*
p2 38 int*
p3 0 int*
p4 4252572 (0x0040E368) int*

See CVI-BEFORE.jpg and CVI-AFTER.jpg to see the p4 pointer being passed to the DLL.



For the record Visual Basic 6.0 passes the following to the DLL:

p1 0 int*
p2 12 int*
p3 0 int*
p4 75763456 int*

Memory buffer initialized to 500 chars

The DLL returns with:

p1 0 int*
p2 12 int*
p3 0 int*
p4 75763456
int*

See VB-BEFORE.jpg and VB-AFTER.jpg to see the p4 pointer being passed to the DLL. VB has a "helper" fucntion that prepares memory for the WriteBlock service (shown below in the function prototype)

' The main hasp API function
Declare Sub hasp Lib "haspvb32.dll" (ByVal Service&, ByVal seed&, ByVal lpt&, ByVal pass1&, ByVal pass2&, retcode1&, retcode2&, retcode3&, retcode4 As Any)

' ReadHaspBlock function prepares memory for the WriteBlock service
Declare Sub ReadHaspBlock Lib "haspvb32.dll" (ByVal Service&, Buff As Any, ByVal Length&)


Ideally I want LabVIEW to pass the "4252524" or "75763456" value to the DLL (p4). I've tried to pass a value of "4252524" straight into the DLL and that doesn't work either (believe me - at this stage I'm trying anything that's reasonable!). I've even re-flashed (so to speak) the hardware and swapped it for another similar unit but same results.

I'm at the stage were I'm going to completely re-write the G code, to see if that is th
e problem - as you know, LabVIEW sometimes can get corrupt....

Regards,
Chroma

P.S. The snaps are in a zip file, the NI uploader crunches my 100k .jpgs for some reason!
0 Kudos
Message 8 of 15
(8,515 Views)
Hello again...

Back to looking at this problem. Have still been unable to get LabVIEW to create a blank data buffer, pass this buffer address to the DLL and then have the DLL return data from the hardware to this buffer address. I have however been able to read data (two chars) at a time from the hardware device until I have read the entire device's memory. This is not the optimal solution and I would prefer if LabVIEW could read the entire buffer in one Call Library Node VI call.

Also buried in the manufacturer's API detail was a brief note that stated the DLL uses the "PASCAL calling convention". This I have been using in the Call Library Node configuration i.e. par4 - using a Pascal String Pointer.... but when I pass an empty buffer the D
LL returns with

Trawling through the Developer Zone once more yielded this question - "Does Call Library Function convert LV strings to null-terminated before calling the dll?" (search on this for more details). In the LVWUtil32 library (NI FTP site), there is a handy VI called "Generate String Buffer". This generates a blank string buffer which can then be sent to the DLL.

Could it be possible that LabVIEW 7.0 is not prepending the length correctly to the blank buffer string when using the Pascal String Pointer? Is there any way to check what the Pascal String Pointer value and contents are when it is executed in the Call Library Node VI?

Here's the configuration I'm using for the DLL:

void hasp(unsigned long service, unsigned long seed, unsigned long lptnum, unsigned long pass1, unsigned long pass2, unsigned long *Par1, unsigned long *Par2, unsigned long *Par3, PStr Par4);


-Chroma
0 Kudos
Message 9 of 15
(8,515 Views)
> Could it be possible that LabVIEW 7.0 is not prepending the length
> correctly to the blank buffer string when using the Pascal String
> Pointer? Is there any way to check what the Pascal String Pointer
> value and contents are when it is executed in the Call Library Node
> VI?
>
> Here's the configuration I'm using for the DLL:
>
> void hasp(unsigned long service, unsigned long seed, unsigned long
> lptnum, unsigned long pass1, unsigned long pass2, unsigned long *Par1,
> unsigned long *Par2, unsigned long *Par3, PStr Par4);
>

You don't give much information about how this fails to work? LV grew
up on the Mac, and uses pascal strings all over the place internally.
Anything is possible, but I think that the pascal string probably gives
a 256 byte buffer with the contents of the input string copied in and
the size set accordingly. If you can set a breakpoint in the DLL, even
in assembly, it shouldn't be too hard to spot it, especially if you pass
in a unique input string.

As for generating a blank string buffer, you can use this VI I suppose,
but a constant or any other string will work as long as it is of the
expected length. And this is the most common mistake made using the
Call Library Node. LabVIEW has no idea how big the buffer is supposed
to be, or even if it is an input/output, versus an input, versus an
output. The C language leaves that up the the programmer of the DLL and
the user of the DLL. It is expected that the caller of the DLL allocate
the right buffer size, and LV cannot help here. If you don't pass in a
big enough buffer, the DLL will overwrite the buffer and corrupt other
memory or get an access fault, depending on the layout of memory. Also,
check if any of your input parameters describe the buffer size --
telling the DLL how much data you want returned, or telling it the size
of the buffer. If you are inadvertantly telling it you only want two
bytes, that might explain what you are seeing.

Sorry I can't be more helpful. If you have more problems, please
provide more information about the failures you are seeing.

Greg McKaskle
0 Kudos
Message 10 of 15
(7,951 Views)