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:
-
Create a new C++ console project.
-
Install WebView2 via NuGet.
-
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:
- Create a new file named
BarcodeQRCodeSDK.targets
. -
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>
-
Set the include path.
<ItemDefinitionGroup> <ClCompile> <AdditionalIncludeDirectories> $(MSBuildThisFileDirectory)\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> </ClCompile> </ItemDefinitionGroup>
-
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>
-
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>
-
Save the
BarcodeQRCodeSDK.targets
file and then create aBarcodeQRCodeSDK.nuspec
file in the root folder for manually building the NuGet package. We usefiles
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>
-
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
- Create a new C++ project and add the NuGet package in Visual Studio.
-
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; }
-
Press
F5
to run the project.