Jump to content

DLL with Bundle input crashes


Recommended Posts

The task is to process a bundle by a DLL embedded in LABVIEW. LABVIEW crashes even if the coded is most simple.

For example, I created an array of double, bundled it and plugged it into the DLL.  The parameters of the DLL are "Adapt to Type". The prototype in Labview is "void  pointertest(void *source);"

The C program:

// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include "Header.h"


extern "C" __declspec(dllexport)void pointertest(LVCluster *source);

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
void pointertest(LVCluster *source) {
    source->dimSize = 2;
    source->arg[0] = 3.1;
    source->arg[1] = 4.2;
}

 The header: Header.h

#pragma pack(push,1)
typedef struct {
    __int64 dimSize;
    double arg[2];
}LVCluster;
#pragma pack(pop)

The LABVIEW VI is attached.  I checked the foren. However, I cannot figure out what I did wrong. Please give me a hint.

Your support is highly appreciated.

pointertest.vi

Link to comment

Have you tried right-clicking on the Call Library Function Node and choosing "Create C File"? That will give you a starting point with the correct function prototype. It appears that since your cluster contains only one element, the cluster is ignored, and you're passing only the array. You can see this by creating a C file from the existing configuration, then wiring the array directly without the bundle and creating a C file - the resulting C prototype is the same. Since you have configured the Call Library Function Node to pass the array parameter as an array data pointer, the value passed to the function is a pointer to the first element of the array, NOT a pointer to the LVCluster structure you've defined. If your function will not resize the array, then this works fine, and you can change your function to (although you might want to add a parameter for the array size):

void pointertest(double *source) {
    source[0] = 3.1;
    source[1] = 4.2;
}

However, if you plan to resize the array within your DLL, then you need a more complex solution. One possibility is to change the Call Library Function Node configuration to pass the parameter as "Handles by Value" so that you can use the LabVIEW memory manager functions such as NumericArrayResize.

Why are you bundling the array into a cluster at all here?

Link to comment

Thank you for giving me feedback.  Creating C-File by Call Library Function Node gives

Quote

/* Call Library source file */

#include "extcode.h"

void pointertest(void source[]);

void pointertest(void source[])
{

    /* Insert code here */

}

Therefore Labview also crashed when I change the function prototype from

Quote

void pointertest(LVCluster *source)

to

Quote

void pointertest(double *source)

Once this simple example works, the task will be to bundle a complex combination of data types.

Link to comment

Can you attach a ZIP archive containing all the pieces of your project - the code for the shared library, the compiled DLL, and the LabVIEW VIs?

In the Call Library Function Node configuration, try changing the Error Checking level to Maximum, and see if it gives you any helpful error messages.

Link to comment

I duplicated your code, including building a DLL, and now I can duplicate the crash as well. My apologies for leading you partly in the wrong direction. I see that despite the generated C file, the array is not getting passed as a simple pointer, unlike what I expected. With some help from this thread, I've confirmed that although Create C File generates different function prototypes depending on how you pass the cluster parameter, LabVIEW in fact still passes the parameter the same way. The following code, based on Create C File with the parameter passed as Handle by Value, works for me (even if when you actually call the function it's configured to pass the parameter as an Array Data Pointer):

#pragma pack(push,1)
typedef struct {
	int dimSize;
	double elt[1];
} TD2;
typedef TD2 **TD2Hdl;

typedef struct {
	TD2Hdl elt1;
} TD1;
#pragma pack(pop)

extern "C" __declspec(dllexport) void pointertest(TD1 *arg1);

void pointertest(TD1 *arg1) {
	(*arg1->elt1)->elt[0] = 3.1;
	(*arg1->elt1)->elt[1] = 4.2;
}

Note that you should not set the dimSize element directly in your C code; if you need to resize the array, use NumericArrayResize as I mentioned before.

Link to comment
#pragma pack(push,1)
typedef struct {
	int dimSize;
	double elt[1];
} TD2;
typedef TD2 **TD2Hdl;

typedef struct {
	TD2Hdl elt1;
} TD1;
#pragma pack(pop)

extern "C" __declspec(dllexport) void pointertest(TD1 *arg1);

MgErr pointertest(TD1 *arg1)
{
	if (!arg1->elt1 || (*arg1->elt1)->dimSize < 2)
	   return mgArgErr;
                                 
	(*arg1->elt1)->elt[0] = 3.1;
	(*arg1->elt1)->elt[1] = 4.2;
}

Defensive programming would use at least this extra code. Note the extra test that the handle is not NULL before testing the dimSize, since the array handle itself can be legitimately NULL, if you happen to assign an empty array to it on the diagram

Altneratively you should really make sure to properly resize the array with LabVIEW manager functions before attempting to write into them, just as ned mentioned:

MgErr pointertest(TD1 *arg1)
{
    MgErr err = NumericArrayResize(fD, 1, (UHandle*)&arg1->elt1, 2);
    if (err == noErr)
    {
        (*arg1->elt1)->elt[0] = 3.1;
        (*arg1->elt1)->elt[1] = 4.2;
    }
    return err;
}

 

Edited by rolfk
Link to comment

MgErr is defined in "extcode.h" found in the "cintools" directory within your LabVIEW installation. When you create a C file from a Call Library Function Node, you'll notice it includes that file - it includes definitions of standard LabVIEW data types to avoid any confusion over things such as the size of an integer.

Link to comment
On 4/11/2017 at 10:01 PM, Alexander Kocian said:

Ned, your method works. Only the declaration int I had to change against  __int64.  thank you. Now, I understand how Labview stores clusters.

rolfk, my VisualC compiler says that the syntax MgErr is unknown.Do I need to consider a special library?

Something about the __int64 sounds very wrong!

In fact the definition of the structure should really be like this with the #pragma pack() statements replaced with the correct LabVIEW header files. 

#include "extcode.h"

// Some stuff

#include "lv_prolog.h"
typedef struct {
	int32 dimSize;
	double elt[1];
} TD2;
typedef TD2 **TD2Hdl;

typedef struct {
    TD2Hdl elt[1];
} TD1;
#include "lv_epilog.h"

// Remaining code

This is because on 32-bit LabVIEW for Windows, structures are packed, but on 64-bit LabVIEW for Windows, they are not. The "lv_prolog.h" file sets the correct packing instruction depending on the platform as defined in "platdefines.h" which is included inside "extcode.h".

The __int64 only seems to solve the problem, but by accident. It works by the virtue of LabVIEW only using the lower 32 bits of that number anyway and the fact that x86 CPUs are little endian, so the lower 32-bit of the int64 also happen to be in the same location as the full 32-bit value LabVIEW really expects. But it will go wrong catastrophically if you ever try to compile this code for 32-bit LabVIEW.

And if you call any of the LabVIEW manager function defined in "extcode.h" such as the NumericArrayResize() you will also need to link your project with labview.lib (or labviewv.lib for the 32-bit case) inside the cintools directory. As long as you only use datatypes and macros from "extcode.h", this doesn't apply though.

Edited by rolfk
Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Similar Content

    • By Billy_G
      Hello, I wrote a LabVIEW program to communicate with a hardware sensor using vendor-provided LLB and a DLL files. The program runs fine on my workstation both from LabVIEW IDE and from a compiled executable. The problem starts when I copy the entire executable folder to a target host without a LabVIEW IDE (only with a runtime engine). The application opens with a broken Run arrow and a "missing external function" error message appears for every function call I made to the DLL (see attached).
      I have tested my application on 5 completely different Windows 10 computers managed by different people. On three of them with various versions of LabVIEW IDE my executable opened with a whole Run arrow and no error message. Two other machines previously had no LabVIEW, so I installed a Runtime Engine 2017f2 32-bit with default settings to match the version of my IDE. Both gave an identical error message.
      The DLL is always included in the application build. I have tried placing the DLL in every conceivable location on the target host: in the executable folder, in the /data folder, in the c:\Windows and system32 folders... I even created a full folder tree matching the location of the project on the developer workstation. Same error. When I intentionally hide the DLL, my executable prompts me to point to it upon being opened, and when I do, I get all the same error messages.
      Vendor documentation only asks to put the two files in the same folder. From programmer's manual: " The driver was written in LabWindows/CVI, version 4.0.1 and is contained in a dynamic link library which can be linked with a variety of programming languages." There is no vendor-provided support.
      One way I actually got rid of the error message was by editing every Call Library Function Node in every VI in the LLB to use relative path to DLL together with the Application Directory VI. However, I feel that there has got to be a better way to compile than by editing a vendor-provided library, especially since it works as-is on some computers. Can anyone suggest what it is?
      Thank you for your time!
       

    • By torekp
      DLL functions or shared variables?  Or something else?
      I have a Labview 2014-64 executable (or I can build a DLL) that runs one piece of equipment, the X-ray.  The other engineer has a large CVI Labwindows 2015 + MS Visual Studio 2012 (C++) executable that runs everything else.  I want the Labview code to be a slave of the CVI code, accepting commands to turn X-ray On or Off, reporting failures, and the like.  Translating the X-ray code into C++ would be possible in principle, but not fun.
      Shared variables look easy, but I'm kinda scared of them.  I would define all the shared variables in my LV code, since I'm more familiar with LV, then use them in both.  There's a thread in here called "Shared Variable Woes" so maybe I should be scared.  In the alternative, I tried building a proof-of-concept DLL in Labview, and calling its functions in CVI/C++, and it works, but it's kinda clunky.  (I'm attaching it below in case you want to play, or advise.)
      Your advice would be appreciated.
      XrayDLL.zip
    • By patufet_99
      To use a controller from LabVIEW I have to use some functions of a DLL.
      For one of the functions, according to the header file .h there is a structure data with parameters of different types that I have to pass to the dll. Some of the parameres are BYTE (1 Byte)  and WORD (2 Bytes).
      When compiling this kind of structure with Visual C++ and looking at it's size with "sizeof()" it seems to me that 4 Bytes variables have to start in a position multiple of 4. For example if there is a BYTE and then a DWORD, the 3 Bytes after the BYTE are ignored and the DWORD starts at Bytes 5 to 8.
      When defining a LabVIEW cluster to match the DLL structure, will LabVIEW do the same? If in my cluster there is a U8 variable and then a U32, will anyway the U8 take 4 bytes?
       
      Thank you.
    • By torekp
      So I created a DLL from a Labview VI that has a 2D array input (and some scalar inputs) and some 2D array outputs.  Labview creates a .h file with these lines

      And then it defines the 2D array arguments to my function as being of this type: "void __cdecl Linear_discrim_4dll(DoubleArray *dataObsFeat, int32_t grpAsz," etc etc.  Trouble is, I have no idea how to fill out this structure so that the DLL can use it.  Say for simplicity I had a 2 by 3 input called dataObsFeat with elements {1,2,3; 11,12,13}; how would I create the object with these values in C or C++ and pass it to the function?  I am a total C++ noob, in case it isn't obvious.
    • By Fred chen
      Hi
       
      When I call the DLL, there is a structure:
      struct Signal { uint32 nStartBit; uint32 nLen; double nFactor; double nOffset; double nMin; double nMax; double nValue; uint64 nRawValue; bool is_signed; char unit[11]; char strName[66]; char strComment[201]; };  
       
      There is another  Message structure to the above Signal:
       
      struct Message { uint32 nSignalCount; uint32 nID; uint8 nExtend; uint32 nSize; Signal vSignals[513]; char strName[66]; char strComment[201]; }  
        The point is  Signal vSignals[513];
       
      I've tried to solve it like this,but the program will crash.
       
      How to create struct Message ,any good suggestions?  Thanks a lot.
       

      message.vi
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.