Wrapper DLL for Labview teaser

Create a Wrapper DLL for Labview to read DataLayer

kuldeepM
Long-established Member
Introduction

In this article, we will use the built-in functionality of Labview 2023 to run our user-defined DLL inside labview and we will also go through a simple example of creating required DLLs with the help of Visual Studio 2022.

Before you go through this article, I would recommend to first have a look at the article 
"How to read Data Layer Nodes through Visual Studio".
In this article you will find a description of how to solve dependency issues in Visual Studio.

Prerequisites 
Configuration

Use Case --> First, we are going to build DLL around our required functions and then we will build another wrapper DLL around the first DLL and we will import this wrapper DLL inside LabView and use DLL functions. Below is a step-by-step description of how to implement this flow.

Step 1: Create a Dynamic-Link Library project in Visual Studio

Open a Visual Studio and click on create a new project as shown in the picture below.

Create a new projectCreate a new project

Now you can see the templates library for creating a new project. In the search bar type in "DLL" and choose the ICON shown in the picture below.

Choose DLL Project for C++Choose DLL Project for C++

After clicking on the Next button, you will encounter configure window. There you can give your project a relevant name and click on Create. Now your project window should look like in the picture below.

DLL project windowDLL project window

Step 2: Write and build DLL project

In the C++ code below, I have created a SharedLib class and its functions. For the shared lib class there are different functions for getting a connection string, connecting a client, reading a value at the datalayer node and deleting a client. And there are two additional functions to define the constructor and destructor of the C++ class. Within those functions, I am using already defined datalayer functions from datalayer.dll.

Code for cpp file:

#include "pch.h"
#include <iostream>
#include <cstring>
#include <string>
#include <sstream>
#include "comm/datalayer/datalayer.h"
#include "comm/datalayer/datalayer_system.h"
#include "shared_lib.h"

extern "C" {
__declspec(dllexport) SharedLib::SharedLib(const const char* ip, const const char* user, const char* password, int sslPort)
{
_ip = ip;
_user = user;
_password = password;
_sslPort = sslPort;

}

__declspec(dllexport) SharedLib::~SharedLib()
{
Deleteclient();
}

const char* SharedLib::getConnectionString(const char* ip, const char* user, const char* password, int sslPort)
{
std::string connectionString = DL_TCP + std::string(user) + ":" + std::string(password) + "@" + std::string(ip);
if (sslPort == 443)
{
return _strdup(connectionString.c_str());
}

std::string connection = connectionString + "?sslport=" + std::to_string(sslPort);
return _strdup(connection.c_str());
}


comm::datalayer::IClient* SharedLib::Connectclient(const char* constr)
{
if (_client == nullptr) {
_datalayerSystem.start(false);
std::string connectionString = "tcp://" + std::string(_user) + ":" + std::string(_password) + "@" + std::string(_ip);
//std::string connectionString = "tcp://boschrexroth:boschrexroth@192.168.1.1";
_client = _datalayerSystem.factory()->createClient(std::string(constr));
}
if (_client->isConnected())
{
std::cout << "client has been generated" << std::endl;
return _client;
}

delete _client;

return nullptr;
}

const char* SharedLib::Readclient(const char* str)
{
_output.str("");
if (_client == nullptr) {
return nullptr;
}
//std::stringstream output;
//comm::datalayer::Variant data;
//comm::datalayer::DlResult result;
_result = _client->readSync(std::string(str), &_data);
if (_result == comm::datalayer::DlResult::DL_OK) {
_output << "value at given node address is: " << double(_data) << std::endl;
}
else {
_output << "Reading Datalayernode failed with: " << _result.toString() << std::endl;
}
return _strdup(_output.str().c_str());
}

void SharedLib::Deleteclient()
{
if (_client != nullptr) {
delete _client;
_client = nullptr;
}
}
}

Code for Header file: In this file, there is an export function which will export the class as a whole with its variable and function to dll.

#pragma once
#include"pch.h"
#include <iostream>
#include <cstring>
#include <string>
#include <sstream>
#include "comm/datalayer/datalayer.h"
#include "comm/datalayer/datalayer_system.h"




#ifdef __cplusplus
extern "C" {
#endif

#ifdef BUILD_FOR_X64
#ifdef _WIN64
#define SHARED_LIB __declspec(dllexport)
#endif
#else
#ifdef _WIN64
#define SHARED_LIB __declspec(dllimport)
#endif
#endif

class SHARED_LIB SharedLib
{
public:
const char* _ip;
const char* _user;
const char* _password;
int _sslPort;
std::stringstream _output;
comm::datalayer::IClient* _client;
comm::datalayer::DlResult _result;
comm::datalayer::Variant _data;
comm::datalayer::DatalayerSystem _datalayerSystem;
SharedLib(const const char* ip, const const char* user, const char* password, int sslPort);

const char* getConnectionString(const char* ip, const char* user, const char* password, int sslPort);

comm::datalayer::IClient* Connectclient(const char* constr);

const char* Readclient(const char* str);

void Deleteclient();
~SharedLib();
};

}
#ifdef __cplusplus
#endif

You will also notice that there is some defined global variable. We need them to carry client and other login detail from one function to another.

Now you can define the dependencies exactly as shown in the linked article in Introduction.

Save this project and build DLL in Release mode by clicking on "Menubar-->build -->build Shared_Lib" or pressing ctrl+B.

Build DLLBuild DLL

You can realise 4 new files in the dll project directory of your File Explorer as shown below.

Release folderRelease folder

Remember the location of these files because we will copy them as dependencies for wrapper DLL.

Step 3: Create and build Wrapper DLL project

This DLL is not directly readable by LabVIEW because Call Library Function can not interpret the function of the DLL if these functions are in class and we needed to build a class in order to carry global variables. So to solve this problem there is a terminology called Wrapper Dll. Basically, it is just the same DLL as we created in Step 2 but this wrapper DLL will import that DLL and call functions from it. That way we can create a DLL, which does not include class.

--> To create a wrapper DLL first, we need to make a DLL project the same way we did in Step 1.

In the cpp file of this project define imported functions from DLL, which we have built earlier in Step 2.  And use those functions inside the newly defined function of this wrapper DLL as can be seen in the C++ code below:

#include "pch.h"
#include <iostream>
#include <cstring>
#include <string>
#include <sstream>
#include "comm/datalayer/datalayer.h"
#include "comm/datalayer/datalayer_system.h"
#include "shared_lib.h"

SharedLib Toast("192.168.1.1", "boschrexroth", "boschrexroth", 443);

extern "C" {
__declspec(dllexport) const char* Connectstring(const char* ip, const char* user, const char* password,int myPort) {
const char* a = Toast.getConnectionString(ip,user,password,443);
return a;
}
__declspec(dllexport) const char* read(const char* str, const char* corepipe) {
//comm::datalayer::IClient* client = Toast.Connectclient();
Toast.Connectclient(corepipe);
const char* b = "";
b = Toast.Readclient(str);
return b;
}
__declspec(dllexport) void Looseclient() {
Toast.Deleteclient();
}

}

In this code, I have defined an object name Toast to run functions defined in the SharedLib class. 

And in order to share function across DLL define *.h header file too.

#pragma once
#include "pch.h"
#include <iostream>
#include <cstring>
#include <string>
#include <sstream>
#include "comm/datalayer/datalayer.h"
#include "comm/datalayer/datalayer_system.h"
#include "shared_lib.h"
#ifdef __cplusplus
extern "C" {
#endif

#ifdef BUILD_FOR_X64
#ifdef _WIN64
#define WRAP_FUN __declspec(dllexport)
#endif
#else
#ifdef _WIN64
#define WRAP_FUN __declspec(dllimport)
#endif
#endif

SharedLib Toast("192.168.1.1", "boschrexroth", "boschrexroth", 443);
__declspec(dllexport) const char* Connectstring(const char* ip, const char* user, const char* password, int myPort);
__declspec(dllexport) const char* read(const char* str, const char* corepipe);
__declspec(dllexport) void Looseclient();


}
#ifdef __cplusplus
#endif

As you realize that this project could not detect shared_lib.h file but it will be gone when you configure dependencies as described below.

At the time of the configuration dependencies for this project we need to add one more dependency for DLL that we have created in Step 2. Via linking the .lib file of that project and copying 4 files that we generated in the release folder earlier.

Now build this project in release mode and you will see the .dll file created in the directory of file explorer. In my case, I have named the project "wrapfun".

Wrapper DLLWrapper DLL

Step 4: Call library function in LabVIEW 

If you do not want to use call library function then one can also directly import dll function inside LabVIEW via "tools-->import-->shared library (.dll).  It will generate vis for each function, which you also make accessible in pallet. But for call Library function vi, first open LabVIEW and create a new VI.

Import vis in palletImport vis in pallet

In this VI go to the block diagram window and right-click on the whiteboard and the Function dialogue box will pop up. Go to Connectivity --> Libraries & Executables --> Call Library Function Node. 

Call library function nodeCall library function node

Double click on this node and the configuration dialogue box for the call library function will open. There you can define the path for wrapper DLL and import the function from the drop-down as shown in the picture below.

Configure dialogue box for CLFConfigure dialogue box for CLFConfigure dialogue box for CLFConfigure dialogue box for CLF

Before hitting the ok button go to the parameters tab and there you have to create and define the input-output variables of the function that you have chosen as can be seen in the picture below.

Configuration of variablesConfiguration of variables

Now click on ok and your function is ready to use inside LabVIEW. You can import each useful function using the "Call library function node"

Step 5: Read the data layer node

To read the datalayer node from ctrlX CORE, you have to create a flow of call library functions first as can be seen in the picture below.

Labview flowLabview flow

Or with the help of imported dll in tools, it will look something like below.

Imported vi connectionImported vi connection

Now click on the "run" button on the top-left of the block diagram window and view on your front panel as shown in picture below.

Front panelFront panel

This how-to example is just for reading a single datalayer node, depending on your use case further development is possible 🙂 

If you have any questions, comments or encounter any unusual problems with the project, feel free to leave them in the comments section below. I would love to hear from you and continue the conversation.

Must Read
Icon--AD-black-48x48Icon--address-consumer-data-black-48x48Icon--appointment-black-48x48Icon--back-left-black-48x48Icon--calendar-black-48x48Icon--center-alignedIcon--Checkbox-checkIcon--clock-black-48x48Icon--close-black-48x48Icon--compare-black-48x48Icon--confirmation-black-48x48Icon--dealer-details-black-48x48Icon--delete-black-48x48Icon--delivery-black-48x48Icon--down-black-48x48Icon--download-black-48x48Ic-OverlayAlertIcon--externallink-black-48x48Icon-Filledforward-right_adjustedIcon--grid-view-black-48x48IC_gd_Check-Circle170821_Icons_Community170823_Bosch_Icons170823_Bosch_Icons170821_Icons_CommunityIC-logout170821_Icons_Community170825_Bosch_Icons170821_Icons_CommunityIC-shopping-cart2170821_Icons_CommunityIC-upIC_UserIcon--imageIcon--info-i-black-48x48Icon--left-alignedIcon--Less-minimize-black-48x48Icon-FilledIcon--List-Check-grennIcon--List-Check-blackIcon--List-Cross-blackIcon--list-view-mobile-black-48x48Icon--list-view-black-48x48Icon--More-Maximize-black-48x48Icon--my-product-black-48x48Icon--newsletter-black-48x48Icon--payment-black-48x48Icon--print-black-48x48Icon--promotion-black-48x48Icon--registration-black-48x48Icon--Reset-black-48x48Icon--right-alignedshare-circle1Icon--share-black-48x48Icon--shopping-bag-black-48x48Icon-shopping-cartIcon--start-play-black-48x48Icon--store-locator-black-48x48Ic-OverlayAlertIcon--summary-black-48x48tumblrIcon-FilledvineIc-OverlayAlertwhishlist