LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

Creating dll, lib, fp's to be used in other CVI programs

Disclaimer: I am a self-taught CVI (and C) programmer in which I learned from examples, NI forums, and google 🙂 

 

Main question: What is the proper way to create a dll, lib, and fp to be used in other CVI programs?  What I am doing is creating a "wrapper class" called PowerSupplies (and other wrapper classes) that takes arguments from the user and call the correct functions based on the power supply model type.

 

Inside my Power Supplies project, I have included each power supply's function panel.  In the C file, I have added my functions and include file.  for example, here's one

 

int _VI_FUNC Supply_InitSupply (int devs, char *resourceName, int IDQuery, int resetDevice, int *instrumentID) {
	
	switch (devs) {
		case AG663XXA:
			return ag663xxa_init(resourceName,IDQuery,resetDevice,instrumentID);
		case HPE364XA:
			return hpe364xa_init (resourceName, IDQuery, resetDevice, instrumentID);		  //Has Voltage and Current
	}	
	return -1;
}

 

and then of course at the bottom, there resides the DllMain and DllEntryPoint functions.  What the heck are these, and am I suppose to rename these?  I ask because I created another wrapper classes with DMM's and I get the Multiply Defined Symbol Error with these two functions when using together in a separate program.

 

Additionally, I have created a header file, which can be included in other CVI programs to know what functions there are.  I noticed I could "Generate Prototypes" after the fact and wasn't sure if this was the correct way or not.  My header file is below:

 

 

#ifndef __PowerSupplies_H__
#define __PowerSupplies_H__

#ifdef __cplusplus
    extern "C" {
#endif

//==============================================================================
// Include files
#include "cvidef.h"
#include "ivi.h"

//==============================================================================
// Constants
#define		POWER_SUPPLY_ENUM_FACTOR 200
typedef enum
	{
		AG663XXA = 200,
		HPE364XA
	} power_supply_type;

static IviStringValueTable power_supply_table =
{
    {AG663XXA,  "AG-663##X"},
    {HPE364XA,  "HP-E364#A"}
};	


//==============================================================================
// Types
		
/************** Static Function Declarations **************/

/************** Global Variable Declarations **************/

/************** Global Function Declarations **************/
int _VI_FUNC Supply_InitSupply(int devs,char *resourceName, int IDQuery, int resetDevice,int *instrumentID);

/*commented out long list of functions for this forum's sake*/

int _VI_FUNC Supply_Close(int devs, int instrumentID);
int __stdcall DllMain(HINSTANCE hinstDLL,DWORD fdwReason, LPVOID lpvReserved);
int __stdcall DllEntryPoint(HINSTANCE hinstDLL,DWORD fdwReason, LPVOID lpvReserved);

#ifdef __cplusplus
    }
#endif

#endif  /* ndef __PowerSupplies_H__ */

 

I then have built it as a static library and dynamic link library.  I then generated a Function Panel from the header file with Prefix Supply_ and Qualifier as _VI_FUNC.

 

All worked nice and dandy, copied files to appropriate places in the IVI directory.

 

In my other CVI program in which uses these "wrapper classes", I have included the function panel's that I created.

 

Well with the error i mentioned above about Multiple Defined Symbol, I have come here to figure out what went wrong.

What are the DllMain and entry point functions?

Is my c and h files created correctly with proper prefixs?  I tried extern once, and got errors I believe.

Does the function panel use the .lib files? Am I going about this correctly or is there an easier/more efficient way?

 

Any and all advice is greatly appreciated!!!

 

Thanks!

 

 

 

0 Kudos
Message 1 of 4
(4,116 Views)

There's info in the CVI help on how to make DLL,s in CVI.

 

There's quite a bit more to it than you might imagine at first.

 

I've pasted a DllMain that I wrote.

 

Some key issues:

 

You can put a type library into your DLL, that way if you add the DLL as a reference in Visual Studio, VS will know the prototype without your having to bring in a header file or import library of any kind.

 

CVI created DLL's are not "active" DLL's and should not be registered with regsvr32, this will just make a mess.

 

You can tell CVI what you're exporting in a couple of different ways, I always use "symbols marked for exports"  and use the macros DLLEXPORT DLLSTDCALL, but NI says using a header file is preferable, I forget why.

 

The DLL search path is a wonderful thing - it's easy to wind up using a different copy of the DLL than you intended due to the way windows searches for the DLL.  Different versions of windows have slightly different rules.  Some allow redirection to help you manage the search path.

 

CVI will not automatically switch between debug and release versions of the import library - you have to call out the correct version in your project and link it to any executable using it. 

 

You can do "dynamic" loading / linking of a CVI DLL without binding to an import library using GetProcAddress function, but it's easier to use the import library.

 

A DllMain isn't necessary but is good practice.

 

Good luck.




/*== PRAGMAS =====================================================================================================*/

#if defined (_CVI_) && (_CVI_ >= 850)

  #if defined _CVI_DEBUG_
    #pragma C99_extensions_on  
  #endif

#endif

/*== INCLUDE FILES ===============================================================================================*/

#include <windows.h>
#include <userint.h>
#include <ansi_c.h>                                                                                     
#include <cvirte.h>
#include <stdio.h>
#include <utility.h>
#include <analysis.h>
#include <toolbox.h>
#include <formatio.h>      

/*== MACROS ======================================================================================================*/

#define MAX_STRING_SIZE 256   
#define MAX_MESSAGE_SIZE 256
#undef  MAX_LOG_MESSAGE_SIZE
#define MAX_LOG_MESSAGE_SIZE 256                                                


#undef  WriteFile   // To deconflict formatio (CVI) version of this function
#undef  ReadFile    // To deconflict formatio (CVI) version of this function             


/*== TYPEDEFS ====================================================================================================*/
// Data type providing reference structure for thread local storage (TLS).

typedef struct ThreadData {
 
  CHAR ErrorString[MAX_STRING_SIZE];
 
} ThreadData, * LPTHREADDATA;

/*== MODULE SCOPE VARIABLES ======================================================================================*/   

static FILE * logFileStream;                      
static HANDLE hModule = INVALID_HANDLE_VALUE;

/*== GLOBAL (HEAP) VARIABLES =====================================================================================*/

// TLS index

DWORD g_dwTlsIndex;

/*== PROTOTYPES ==================================================================================================*/    

/*== CODE ========================================================================================================*/  


/*******************************************************************************************************************
*
* Function: DllMain
*
* Description:
*    DLL Main entry point.  Provides for DLL initialization.  
*
* Limitations:
*
* Parameters:  System defined.
*
* Return Value:
*
*
********************************************************************************************************************/   

BOOL WINAPI DllMain (HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpvReserved) {

  LPTHREADDATA lpThreadData;      
                                  
  switch (fdwReason) {
    
    case DLL_PROCESS_ATTACH:
      
      // Allocate a TLS index.
 
      if ((g_dwTlsIndex = TlsAlloc ()) == 0xFFFFFFFF) return FALSE;
      
      hModule = hInstDLL; 
      
    
      // No break - fall through and initialize for main thread
 
    case DLL_THREAD_ATTACH:
      
      lpThreadData = (LPTHREADDATA) calloc (1, sizeof (ThreadData));
      if (lpThreadData != NULL) TlsSetValue (g_dwTlsIndex, lpThreadData);
      
      lpThreadData = TlsGetValue (g_dwTlsIndex);
      lpThreadData->ErrorString[0] = '\0';     
      
      break;
      
    case DLL_THREAD_DETACH:
      
      // Release the allocated memory for this thread.
 
      lpThreadData = TlsGetValue (g_dwTlsIndex);
      if (lpThreadData != NULL) free (lpThreadData);
      
      break;
      
    case DLL_PROCESS_DETACH:
      
      // Free the allocated memory for this thread (main thread).
 
      lpThreadData = TlsGetValue (g_dwTlsIndex);
      if (lpThreadData != NULL) free (lpThreadData);

      // Release the TLS index.

      TlsFree (g_dwTlsIndex);
      
      break;
      
    }
 
  return TRUE;
 
} // end, DllMain

INT DLLSTDCALL DllEntryPoint (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
 
  // Included for compatibility with Borland

  return DllMain (hinstDLL, fdwReason, lpvReserved);
 
} // end, DllEntryPoint

 

implement dll functions like this:

 

 

int DLLEXPORT DLLSTDCALL myFunc (int iParam) {

 

  return 1;

 

}

 

 

 

 

0 Kudos
Message 2 of 4
(4,112 Views)

Thanks for the info, it seems the easiest thing to do in my case, since I'm not a c guru is to just use a static library to reference my Power Supply and DMM libraries.

 

As I pursue this, I mentioned I created function panel's from the header files.  Is it necessary then to even have the DllMain and DllEntry Point functions in my c and h files?  I was wondering because it seems I am getting the multiply defined symbol error with those calls from my DMM and Power Supply header files.

0 Kudos
Message 3 of 4
(4,088 Views)

The first reason for creating a function panel is to allow its use when creating code.

 

The second reason is that if you want you can use the function panels to create a type library that you can include in your DLL (it's a target setting option).

 

Function panels and DllMain are not related in any way.

 

To create a DLL in CVI, you don't need to use DllMain or function panles, either one.

 

Multiple defined symbol means just that - just do a editor search for the symbol through your compilation unit , incuding header files, to see where the symbol is defined, then determine which one you want to use.  The compiler is confused because it can't tell which symbol definition you intended to use.

 

Menchar

0 Kudos
Message 4 of 4
(4,069 Views)