How to Make a NuGet Package for C++ Development in Visual Studio

Xiao Ling
7 min readMar 30, 2023

A NuGet package is a single ZIP file that can be easily distributed and installed in Visual Studio projects. It typically includes compiled code (such as DLLs), as well as other resources, metadata, and configuration files. While NuGet packages are commonly used for .NET projects, they can also be used for distributing C++ libraries, as noted in Microsoft’s official documentation. A Visual Studio C++ projects can install dependent native C++ packages via NuGet. All available native C++ packages can be found on NuGet.org by filtering with tag:native. In this article, we will demonstrate how to modify the BarcodeQRCodeSDK package to support both .NET and C++ barcode app development in Visual Studio.

About BarcodeQRCodeSDK

The BarcodeQRCodeSDK is a .NET wrapper that provides interfaces for using Dynamsoft Barcode Reader C++ SDK to recognize barcodes and QR codes in .NET projects.

What’s the Difference between *.csproj and *.nuspec Files?

Both *.csproj and *.nuspec files are used in the context of NuGet packages, but they serve different purposes.

  • *.csproj files are used in .NET projects. If you set <GeneratePackageOnBuild>true</GeneratePackageOnBuild> in the *.csproj file, the NuGet package will be generated automatically when you build the project.
  • *.nuspec files are used to define the metadata and contents of a NuGet package. It is used to create a NuGet package manually.

To create a NuGet package for both .NET and C++ projects, we need to use a *.csproj to build .NET assemblies and a *.nuspec to pack .NET and C++ DLLs into the final NuGet package.

A NuGet Package for .NET and C++

While Microsoft does not provide extensive documentation on how to create a native NuGet package, it does offer the Microsoft.Web.WebView2 package as an example that can be used as a reference. This package demonstrates the basic structure and content that is required for a native NuGet package, and can serve as a helpful starting point for developers who are new to creating these types of packages.

Test WebView2 NuGet Package for C++ Project

Here are the steps you can follow to verify whether the package will work correctly in a C++ project:

  1. Create a new C++ console project.

2. Install WebView2 via NuGet.

3. Include the header files. If the header files are found and included correctly, there should be no syntax errors or red squiggly lines in your code editor.

Analyze the WebView2 NuGet Package

If you want to learn more about how the WebView2 package is structured and how it can be used in a C++ project, you can open it in NuGet Package Explorer to examine its contents. By doing so, you can see how the package is organized and what files are included, which can provide useful insights and guidance for creating your own NuGet packages.

In the WebView2 package, the build/native folder contains C++ header files and DLL files that are necessary for building and using the package in a C++ project. Additionally, the package includes a Microsoft.Web.WebView2.targets file, which is used to configure the build environment for the package.

Modify the BarcodeQRCodeSDK Package for C++ Development

The BarcodeQRCodeSDK package already contains the necessary C++ libraries for .NET invocation, located in the runtimes folder. However, to create a NuGet package that can be used in a C++ project, we will need to add the appropriate header files and create a *.targets file that specifies the necessary configurations.

Here are the steps:

  1. Create a new file named BarcodeQRCodeSDK.targets.
  2. Open the file in a text editor and add the following code to check the platform and architecture.
<MyPlatform Condition="'$(Platform)'=='x64'">x64</MyPlatform>

<IsWindows Condition="'$(OS)'=='Windows_NT'">true</IsWindows>
<IsLinux Condition="'$(OS)'=='Unix' and '$(OS)'!='Darwin'">true</IsLinux>
<IsMacOS Condition="'$(OS)'=='Darwin'">true</IsMacOS>

3. Set the include path.

<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>
$(MSBuildThisFileDirectory)\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>

4. Link the dependent libraries.

<ItemDefinitionGroup>
<Link Condition="'$(MyPlatform)' == 'x64' and '$(IsWindows)'=='true'">
<AdditionalDependencies>DBRx64.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>
$(MSBuildThisFileDirectory)\..\..\runtimes\win-x64\native;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>

<ItemDefinitionGroup>
<Link Condition="'$(MyPlatform)' == 'x64' and '$(IsLinux)'=='true'">
<AdditionalDependencies>libDynamsoftBarcodeReader.so;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>
$(MSBuildThisFileDirectory)\..\..\runtimes\linux-x64\native;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>

<ItemDefinitionGroup>
<Link Condition="'$(MyPlatform)' == 'x64' and '$(IsMacOS)'=='true'">
<AdditionalDependencies>libDynamsoftBarcodeReader.dylib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>
$(MSBuildThisFileDirectory)\..\..\runtimes\osx-x64\native;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>

5. Copy the library files to the output directory.

<ItemGroup>
<Content Include="$(MSBuildThisFileDirectory)\..\..\runtimes\win-x64\native\*.dll"
Condition="'$(MyPlatform)' == 'x64' and '$(IsWindows)'=='true'">
<Link>%(RecursiveDir)%(FileName)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>

<ItemGroup>
<Content Include="$(MSBuildThisFileDirectory)\..\..\runtimes\linux-x64\native\*.so"
Condition="'$(MyPlatform)' == 'x64' and '$(IsLinux)'=='true'">
<Link>%(RecursiveDir)%(FileName)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>

<ItemGroup>
<Content Include="$(MSBuildThisFileDirectory)\..\..\runtimes\win-x64\native\*.dylib"
Condition="'$(MyPlatform)' == 'x64' and '$(IsMacOS)'=='true'">
<Link>%(RecursiveDir)%(FileName)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>

6. Save the BarcodeQRCodeSDK.targets file and then create a BarcodeQRCodeSDK.nuspec file in the root folder for manually building the NuGet package. We use files element to specify the files to be included in the package.

<files>
<file src="README.md" target="docs\" />
<file src="LICENSE.txt" target=""/>
<file src="platform\win\**\*.*" target="runtimes\win-x64\native" />

<file src="platform\macos\**\*.*" target="runtimes\osx-x64\native" />

<file src="platform\linux\**\*.*" target="runtimes\linux-x64\native" />

<file src="bin\Release\net6.0\BarcodeQRCodeSDK.dll" target="lib\net6.0" />

<file src="include\**\*.*" target="build\native\include" />
<file src="BarcodeQRCodeSDK.targets" target="build\native" />
</files>

7. Now, we can build the NuGet package for .NET and C++ as follows:

dotnet build --configuration Release
nuget pack BarcodeQRCodeReader.nuspec

Create a Simple C++ Barcode Reader with the NuGet Package

  1. Create a new C++ project and add the NuGet package in Visual Studio.
  2. Add the following code to the main.cpp file.
#include <iostream>
#include <fstream>

#include "DynamsoftBarcodeReader.h"
#include "DynamsoftCommon.h"

using namespace dynamsoft::dbr;

typedef struct BarcodeFormatSet
{
int barcodeFormatIds;
int barcodeFormatIds_2;
}BarcodeFormatSet;

unsigned long GetTime()
{
#if defined(_WIN64) || defined(_WIN32)
return GetTickCount64();
#else
struct timeval timing;
gettimeofday(&timing, NULL);
return timing.tv_sec * 1000 + timing.tv_usec / 1000;
#endif
}

void ToHexString(unsigned char* pSrc, int iLen, char* pDest)
{
const char HEXCHARS[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };

int i;
char* ptr = pDest;

for (i = 0; i < iLen; ++i)
{
snprintf(ptr, 4, "%c%c ", HEXCHARS[(pSrc[i] & 0xF0) >> 4], HEXCHARS[(pSrc[i] & 0x0F) >> 0]);
ptr += 3;
}
}

void OutputResult(CBarcodeReader& reader, int errorcode, float time)
{
char* pszTemp = NULL;
char* pszTemp1 = NULL;
char* pszTemp2 = NULL;
int iRet = errorcode;
pszTemp = (char*)malloc(4096);
if (iRet != DBR_OK && iRet != DBRERR_MAXICODE_LICENSE_INVALID && iRet != DBRERR_AZTEC_LICENSE_INVALID && iRet != DBRERR_LICENSE_EXPIRED && iRet != DBRERR_QR_LICENSE_INVALID && iRet != DBRERR_GS1_COMPOSITE_LICENSE_INVALID &&
iRet != DBRERR_1D_LICENSE_INVALID && iRet != DBRERR_PDF417_LICENSE_INVALID && iRet != DBRERR_DATAMATRIX_LICENSE_INVALID && iRet != DBRERR_GS1_DATABAR_LICENSE_INVALID && iRet != DBRERR_PATCHCODE_LICENSE_INVALID &&
iRet != DBRERR_POSTALCODE_LICENSE_INVALID && iRet != DBRERR_DOTCODE_LICENSE_INVALID && iRet != DBRERR_DPM_LICENSE_INVALID && iRet != DBRERR_IRT_LICENSE_INVALID && iRet != DMERR_NO_LICENSE && iRet != DMERR_TRIAL_LICENSE)
{
snprintf(pszTemp, 4096, "Failed to read barcode: %s\r\n", CBarcodeReader::GetErrorString(iRet));
printf("%s", pszTemp);
free(pszTemp);
return;
}

TextResultArray* paryResult = NULL;
reader.GetAllTextResults(&paryResult);

if (paryResult->resultsCount == 0)
{
snprintf(pszTemp, 4096, "No barcode found. Total time spent: %.3f seconds.\r\n", time);
printf("%s", pszTemp);
free(pszTemp);
CBarcodeReader::FreeTextResults(&paryResult);
return;
}

snprintf(pszTemp, 4096, "Total barcode(s) found: %d. Total time spent: %.3f seconds\r\n\r\n", paryResult->resultsCount, time);
printf("%s", pszTemp);
for (int iIndex = 0; iIndex < paryResult->resultsCount; iIndex++)
{
snprintf(pszTemp, 4096, "Barcode %d:\r\n", iIndex + 1);
printf("%s", pszTemp);
snprintf(pszTemp, 4096, " Type: %s\r\n", paryResult->results[iIndex]->barcodeFormatString);

printf("%s", pszTemp);
snprintf(pszTemp, 4096, " Value: %s\r\n", paryResult->results[iIndex]->barcodeText);
printf("%s", pszTemp);

pszTemp1 = (char*)malloc(paryResult->results[iIndex]->barcodeBytesLength * 3 + 1);
pszTemp2 = (char*)malloc(paryResult->results[iIndex]->barcodeBytesLength * 3 + 100);
ToHexString(paryResult->results[iIndex]->barcodeBytes, paryResult->results[iIndex]->barcodeBytesLength, pszTemp1);
snprintf(pszTemp2, paryResult->results[iIndex]->barcodeBytesLength * 3 + 100, " Hex Data: %s\r\n", pszTemp1);
printf("%s", pszTemp2);
free(pszTemp1);
free(pszTemp2);
}

free(pszTemp);
CBarcodeReader::FreeTextResults(&paryResult);
}

int main(int argc, const char* argv[])
{
int iIndex = 0;
int iRet = -1;
unsigned long ullTimeBegin = 0;
unsigned long ullTimeEnd = 0;

char szErrorMsg[256];
PublicRuntimeSettings runtimeSettings;

printf("*************************************************\r\n");
printf("Welcome to Dynamsoft Barcode Reader Demo\r\n");
printf("*************************************************\r\n");
printf("Hints: Please input 'Q' or 'q' to quit the application.\r\n");

iRet = CBarcodeReader::InitLicense("DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTE2NDk4Mjk3OTI2MzUiLCJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSIsInNlc3Npb25QYXNzd29yZCI6IndTcGR6Vm05WDJrcEQ5YUoifQ==", szErrorMsg, 256);
if (iRet != DBR_OK)
{
printf("InitLicense Failed: %s\n", szErrorMsg);
}
CBarcodeReader reader;


while (1)
{
std::string input;
std::cout << "\r\n>> Step 1: Input your image file's full path:\r\n";
std::cin >> input;

if (input._Equal("q") || input._Equal("Q"))
{
return true;
}

std::ifstream file(input);

if (!file.good()) {
std::cout << "Please input a valid path.\r\n" << std::endl;
continue;
}

reader.InitRuntimeSettingsWithString("{\"ImageParameter\":{\"Name\":\"BestCoverage\",\"BarcodeFormatIds\": [\"BF_ALL\"],\"BarcodeFormatIds_2\": [\"BF2_POSTALCODE\", \"BF2_DOTCODE\"] , \"DeblurLevel\":9,\"ExpectedBarcodesCount\":512,\"ScaleDownThreshold\":100000,\"LocalizationModes\":[{\"Mode\":\"LM_CONNECTED_BLOCKS\"},{\"Mode\":\"LM_SCAN_DIRECTLY\"},{\"Mode\":\"LM_STATISTICS\"},{\"Mode\":\"LM_LINES\"},{\"Mode\":\"LM_STATISTICS_MARKS\"}],\"GrayscaleTransformationModes\":[{\"Mode\":\"GTM_ORIGINAL\"},{\"Mode\":\"GTM_INVERTED\"}]}}", CM_OVERWRITE, szErrorMsg, 256);

reader.GetRuntimeSettings(&runtimeSettings);
runtimeSettings.barcodeFormatIds = BF_ALL;
runtimeSettings.barcodeFormatIds_2 = BF2_POSTALCODE | BF2_DOTCODE;
iRet = reader.UpdateRuntimeSettings(&runtimeSettings, szErrorMsg, 256);
if (iRet != DBR_OK)
{
printf("Error code: %d. Error message: %s\n", iRet, szErrorMsg);
return -1;
}

ullTimeBegin = GetTime();
iRet = reader.DecodeFile(input.c_str(), "");
ullTimeEnd = GetTime();
OutputResult(reader, iRet, (((float)(ullTimeEnd - ullTimeBegin)) / 1000));

}

return 0;
}

3. Press F5 to run the project.

Source Code

https://github.com/yushulx/dotnet-barcode-qr-code-sdk

Originally published at https://www.dynamsoft.com on March 30, 2023.

--

--

Xiao Ling

Manager of Dynamsoft Open Source Projects | Tech Lover