LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

call DLLs created by VC++ in CVI

Solved!
Go to solution
I have the header file (.h), the library file (.lib), and the DLL file.  The problem is these files are generated by VC++.  What is the best way to call the functions defined in the DLL using CVI 6.0 or 7.0?  I've read this link http://zone.ni.com/devzone/cda/tut/p/id/3341#toc4 and many related links it provides, but they don't help much.  How come LabVIEW can call C++ DLL files easily and straight forward but not LabWIndows?  Please help!
 
0 Kudos
Message 1 of 11
(7,301 Views)
If the C++ dll isn't allready made C compatible by marking its exported functions as "extern C" (look into the header file for checking that) , you have to write a wrapper library with C++ which imports the functions of the C++ library and reexports them in  C compatible way.
 
0 Kudos
Message 2 of 11
(7,297 Views)
I don't have VC++.  I don't have the source codes to regenerate the DLL, either.  Why does NI make it so difficult for LabWindows to call DLLs created by VC++? 
0 Kudos
Message 3 of 11
(7,300 Views)

Hello dcl9000,

I apologize that this issue is so frustrating and unfortunately I don't believe that the answer is going to make it less difficult. 

The obstacle is associated with the ANSI C environment.  C++ is not directly backward compatible with C.  The message posted by markus kossmann was correct; specifically, if you want to use a C++ dll with an ANSI C environment the source of the C++ dll has to be modified so that the C compiler recognizes it as C.  The incompatibility is not specific to LabWindows/CVI but to any ANSI C compiler when including any C++ source code.

Unlike LabWindows/CVI, LabVIEW is not limited to the ANSI C environment and therefore does not have the same incompatibilities making C++ dll calls.

Regards,
Ben M
Applications Engineer
National Instruments
Message 4 of 11
(7,273 Views)

ALAS!!! 

This is really a bad news for all LabWINDOWS users.  After using LabWINDOWS/CVI for more than 13 years, this problem suddenly becomes the main reason to make me believe LabVIEW is better than LabWINDOWS.  Really regret to know this.  Alas!

0 Kudos
Message 5 of 11
(7,267 Views)

One technical reason for the incompatibility is that the C++-Compiler needs to use name mangling to support function overloading and still using the same linker interface as a C-compiler. Name mangling means that the compiler codes number and type of the arguments of a function into the internally used symbol name. 

For example : If you have a function int foo(int) a C-compiler may use _foo as internal name for that function. But a C++ must be able to handle a function int foo(double) in the same code. So it may use _foo_1a for int foo(int) and  _foo_1b for int foo(double). That means also that a library compiled with one C++-compiler may be incompatible to another C++-compiler if that compiler uses a different name mangling scheme.

 

0 Kudos
Message 6 of 11
(7,250 Views)
I have a c++ dll for vc++ with header and lib file. In the thread it was mentioned that one could write a wrapper application in vc++ to use the dlls in cvi. Could anyone give more details how one would write this wrapper application (I have vc++) , and maybe provide an example ? I copy the header file of the dll I have for information. Thanks for all help.

// BNSBoard.h   header file to declare CBNSFactory for generating various type of board objects
//                and the base class BNSBoard

#undef AFX_DATA
#define AFX_DATA AFX_EXT_DATA

#ifndef  BNSBOARD
#define BNSBOARD



// This class is exported from the BNSboard.dll
class AFX_EXT_CLASS CBNSBoard
{
private:
    SBoard_Spec        m_BoardSpec;
    SRun_Status        m_RunStatus;

public:
    virtual SBoard_Spec        *BoardSpec();
public:
    CBNSBoard(void);
    virtual ~CBNSBoard(void);

    virtual int BoardReady();

    virtual int SetupDialog(int *LCType, int *TrueFrames);

    virtual int WriteFrameBuffer(unsigned short frameNum, unsigned char *buffer);

    virtual int SetRunParameters(unsigned short FrameRate, unsigned short LaserDuty, unsigned short TrueLaserGain, unsigned short InverseLaserGain);
    virtual int SelectImage(int FrameNum);

    virtual int SetRunMode(unsigned char runMode, int NumImages, unsigned short *FrameArray, bool Loop);
    virtual int GetRunStatus(SRun_Status *runStatus);

    virtual int SetDownloadMode(bool ContinuousDownload);

    virtual int SetTrueFrames(unsigned short TF);

    virtual int SetPower(bool powerOn);
    virtual int GetPower();

    virtual int GetTrueFrames();
    virtual int GetImageFrames();

};


class AFX_EXT_CLASS CBNSFactory
{
public:
    CString *TypeList;            // number of board type supported
    int        TypeCount;            // list of define constant board names

public:
    CBNSFactory(void);
    ~CBNSFactory(void);

    CBNSBoard *BuildBoard(CString rqstBoardName, unsigned short LC_Type, unsigned short TrueFrames, bool *VerifyHardware);
};

#endif



#undef AFX_DATA
#define AFX_DATA

0 Kudos
Message 7 of 11
(7,113 Views)

Hello WLangbein,

Take a look at this article for explanation and examples of how to compile, link, and run C++ code from C.

http://developers.sun.com/sunstudio/articles/mixing.html

Regards,
Ben M
Applications Engineer
National Instruments
0 Kudos
Message 8 of 11
(7,080 Views)
Solution
Accepted by topic author dcl9000

WLangbein,

You may look at this link for more info: http://zone.ni.com/devzone/cda/tut/p/id/3341#toc8

.

 

Meanwhile, if you know the data types and function names defined in the C++ header file and

the DLL, you can call the functions dynamically in a C program.  You need to write a new C

header file though according to the C++ header file.  The lib file doesn't seem to be needed

if you do so.

 

I've attached the original C++ header file here.  A new header file (partially) I wrote according

to this header file to be included in my C program looks like this :

 

 

/*my_c_header.h ----------------------------------------*/

 

typedef struct
        {
          double xLocation;
          double yLocation;
          double lightPeak;
          short  alignedOk;
        }
        HillClimbData;
       

typedef struct
        {
          double channelA_Power;
          double channelB_Power;
          short  Status;
        }
        DualPowerData;       

 

typedef long (CALLBACK* MY_SPC_Start)(short);
typedef long (CALLBACK* MY_SPC_End)(void);
typedef long (CALLBACK* MY_SPC_ToolboxMode)(short);
typedef long (CALLBACK* MY_SPC_ToolboxStart)(void);
typedef long (CALLBACK* MY_SPC_ToolboxEnd)(void);

typedef long (CALLBACK* MY_SPC_SetSide)(short);
typedef long (CALLBACK* MY_SPC_Home)(short);
typedef long (CALLBACK* MY_SPC_2DAlign)(double setSize, short checkPoints,
                                          short iterations, short searchType,
                                          HillClimbData *puData);

typedef long (CALLBACK* MY_SPC_SetChannel)(short);
typedef long (CALLBACK* MY_SPC_ReadPower)(double *lightPeak);
typedef long (CALLBACK* MY_SPC_ReadDualPower)(DualPowerData *puData);
typedef long (CALLBACK* MY_SPC_RTPowerMonitor)(char para_name[]);

 

HINSTANCE                  SPCLib;                   

MY_SPC_Start               my_SPC_start;
MY_SPC_End                 my_SPC_end;
MY_SPC_ToolboxMode         my_SPC_toolbox_mode;
MY_SPC_ToolboxStart        my_SPC_toolbox_start;
MY_SPC_ToolboxEnd          my_SPC_toolbox_end;
MY_SPC_SetSide             my_SPC_set_side;
MY_SPC_Home                my_SPC_home;
MY_SPC_2DAlign             my_SPC_2d_align;
MY_SPC_SetChannel          my_SPC_set_channel;
MY_SPC_ReadPower           my_SPC_read_power;
MY_SPC_ReadDualPower       my_SPC_read_dual_power;
MY_SPC_RTPowerMonitor      my_SPC_RT_power_monitor;

 

/*-------------------------------------------------------------------------------------------*/

 

 

 

 

Now I can call the original function defined in the C++ dll file this way in my C program.

First define a function to load these functions needed in your program:

 

/*-------------------------------------------------------------------------------------------*/

void Load_SPC_functions (void)
{
   SPCLib = LoadLibrary("C:\\SPC.dll");
  
   my_SPC_start = (MY_SPC_Start)GetProcAddress(SPCLib, "SPC_Start");
   my_SPC_end = (MY_SPC_End)GetProcAddress(SPCLib, "SPC_End");
   my_SPC_toolbox_mode = (MY_SPC_ToolboxMode)GetProcAddress(SPCLib, "SPC_ToolboxMode");
   my_SPC_toolbox_start = (MY_SPC_ToolboxStart)GetProcAddress(SPCLib, "SPC_ToolboxStart");
   my_SPC_toolbox_end = (MY_SPC_ToolboxEnd)GetProcAddress(SPCLib, "SPC_ToolboxEnd");
  
   my_SPC_set_side = (MY_SPC_SetSide)GetProcAddress(SPCLib, "SPC_SetSide");
   my_SPC_home = (MY_SPC_Home)GetProcAddress(SPCLib, "SPC_Home");
   my_SPC_2d_align = (MY_SPC_2DAlign)GetProcAddress(SPCLib, "SPC_HillClimbArg");

   my_SPC_set_channel = (MY_SPC_SetChannel)GetProcAddress(SPCLib, "SPC_SetChannel");
   my_SPC_read_power = (MY_SPC_ReadPower)GetProcAddress(SPCLib, "SPC_ReadPower");
   my_SPC_read_dual_power = (MY_SPC_ReadDualPower)GetProcAddress(SPCLib, "SPC_ReadDualPower");
   my_SPC_RT_power_monitor = (MY_SPC_RTPowerMonitor)GetProcAddress(SPCLib, "SPC_RTPowerMonitor");
}

/*-------------------------------------------------------------------------------------------*/

 

Then I can call this function, which is originally defined as "SPC_HillClimbArg" in C++ dll file,

directly in my C program like this:

 

my_spc_2d_align(setSize, checkPoints, iterations, searchType, &puData);

 

 

 

Hope this help.

 

 

0 Kudos
Message 9 of 11
(7,047 Views)
WLangBein:
 
If your problem is how to instantiate and invoke a C++ class in order to suit the C environment, here is a very simple (real) example:
 
Suppose you have a class defined in a .h file as:
 
class SPLMaker
{
public:
 SPLMaker(int isSlow);
 ~SPLMaker();
 void SPLProcess(float *inData, float *outData, int nSamples);
 int publicValue;

private:
 double alpha;
 double alpha1;
 double lastval;
};
 
Then one way of defining a C dll interface to this class is to have the following in a suitable .cpp file in a VC++ DLL. This DLL may be the original DLL augmented with the extra code, or it may be a separate interface DLL:
 
extern "C" {
 
__declspec(dllexport) void * _stdcall NewSPLMaker(int isSlow)
{
 return new SPLMaker(isSlow);
}
 
__declspec(dllexport) void _stdcall DisposeSPLMaker(void *splmakerHdl)
{
 delete (SPLMaker *)splmakerHdl;
}
 
__declspec(dllexport) void _stdcall SPLMaker_Process(void *splmakerHdl, float *rawData, float *splData, int nSamples)
{
 ((SPLMaker *)splmakerHdl)->SPLProcess(rawData,splData,nSamples);
}
 
__declspec(dllexport) int _stdcall SPLMaker_get_publicValue(void *splmakerHdl)
{
 return (SPLMaker *)splmakerHdl->publicValue;
}
 
__declspec(dllexport) void _stdcall SPLMaker_put_publicValue(void *splmakerHdl, int newValue)
{
 (SPLMaker *)splmakerHdl->publicValue = newValue;
}
 
}
 
You can see that the function NewSPLMaker() instantiates the class, and returns the address of the instance (as a void * 'handle') to the caller. All the other functions have this address passed to them as the first parameter of the function call.
 
The function DisposeSPLMaker() calls the class destructor when you are finished with it.
 
The function SPLMaker_Process() calls the method SPLMaker::SPLProcess
 
The functions SPLMaker_get_publicValue() and SPLMaker_put_publicValue() provide access to the public member publicValue.
 
Of course, you would also have to generate a suitable .h file so that the C calling program knows about the functions, e.g.:
 
#ifdef __cplusplus
extern "C" {
#endif
 
void * _stdcall NewSPLMaker(int isSlow);
void _stdcall SPLMaker_Process(void *splmakerHdl, float *rawData, float *splData, int nSamples);
void _stdcall DisposeSPLMaker(void *splmakerHdl);
int _stdcall SPLMaker_get_publicValue(void *splmakerHdl);
void _stdcall SPLMaker_put_publicValue(void *splmakerHdl, int newValue);
 
#ifdef __cplusplus
}
#endif
 
So a typical code fragment to use this interface from a C program would then do something like:
 
void *mySPLMaker = NewSPLMaker(0);
int initPublicValue = SPLMaker_get_publicValue (mySPLMaker);
SPLMaker_put_publicValue (mySPLMaker, initPublicValue+1000);
SPLMaker_Process (mySPLMaker, rawData, splData, 1024);
DisposeSPLMaker(mySPLMaker);
 
Hope that helps.
--
Martin
Certified CVI Developer
0 Kudos
Message 10 of 11
(6,995 Views)