How to Make a NuGet Package for C++ Development in Visual Studio
Tags: nuget, cpp
Published: 2023-04-03 02:21:29

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](https://learn.microsoft.com/en-us/nuget/consume-packages/finding-and-choosing-packages#native-c-packages). 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](https://www.nuget.org/packages/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](https://www.dynamsoft.com/barcode-reader/sdk-desktop-server/) 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](https://learn.microsoft.com/en-us/nuget/guides/native-packages), it does offer the [Microsoft.Web.WebView2](https://www.nuget.org/packages/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.

    ![visual studio cpp console app](https://www.dynamsoft.com/codepool/img/2023/03/visual-cpp-console-app.png)

2. Install WebView2 via NuGet.

    ![webview nuget install](https://www.dynamsoft.com/codepool/img/2023/03/nuget-install-webview.png)

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.

    ![Include webview header files](https://www.dynamsoft.com/codepool/img/2023/03/include-webview-header.png)

    

### 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. 

![NuGet webview package](https://www.dynamsoft.com/codepool/img/2023/03/nuget-webview-package.png)

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.
    ```xml
    <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.
    ```xml
    <ItemDefinitionGroup>
        <ClCompile>
        <AdditionalIncludeDirectories>
            $(MSBuildThisFileDirectory)\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
        </ClCompile>
    </ItemDefinitionGroup>
    ```
4. Link the dependent libraries.
    ```xml
    <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.
    ```xml
    <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. 
    ```xml
    <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:

    ```bash
    dotnet build --configuration Release
    nuget pack BarcodeQRCodeReader.nuspec
    ```

    ![NuGet cpp barcode sdk package](https://www.dynamsoft.com/codepool/img/2023/03/nuget-cpp-barcode-sdk-package.png)


### 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.
    ```cpp
    #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.

    ![Visual C++ barcode reader](https://www.dynamsoft.com/codepool/img/2023/03/visual-cpp-barcode-reader.png)

## Source Code
[https://github.com/yushulx/dotnet-barcode-qr-code-sdk](https://github.com/yushulx/dotnet-barcode-qr-code-sdk)