How to Link MSVC DLLs with MinGW GCC in Windows
Tags: mingw, msvc, windows, dll
Published: 2023-08-03 00:54:23

Recently, Dynamsoft rolled out [C++ Barcode SDK v10.0](https://www.dynamsoft.com/barcode-reader/docs/server/programming/cplusplus/user-guide.html). This version has been entirely re-written in C++, providing an entirely new suite of APIs for barcode reading. However, if you're using **MinGW(Minimalist GNU for Windows)** to code with this SDK, such as integrating it into a Qt project, you may encounter linking issues in Windows. To address this challenge, this article will guide you on how to create a **shim DLL** - a bridging shared library that exports C interfaces, ensuring successful DLL linking with MinGW GCC.

## About Dynamsoft C++ Barcode SDK v10.0
- Download the [SDK](https://www.dynamsoft.com/barcode-reader/downloads/).
- Request a [free trial license](https://www.dynamsoft.com/customer/license/trialLicense?product=dbr&source=codepool).

## Why Linking DLL Files Fails with MinGW GCC
Dynamsoft's C++ Barcode SDK for Windows is compiled with **Microsoft's Visual C++ (MSVC)**. Due to the differences in **ABI (Application Binary Interface)**, C++ libraries built using the MSVC compiler may not be directly compatible with MinGW. Prior to version 10.0, the Dynamsoft Barcode Reader offered C APIs compatible with MinGW. However, these C APIs were discontinued starting from version 10.0. As a result, when you try to build the HelloWorld sample with MinGW, you'll encounter numerous errors related to `undefined references`.

![dbr10 mingw error](https://www.dynamsoft.com/codepool/img/2023/08/mingw-link-msvc-dll.png)

To address this issue, one viable solution is to create a **shim DLL** by writing bridging code in C, which enables the invocation of Dynamsoft Barcode Reader's C++ APIs.

## Creating C Interfaces for Dynamsoft Barcode Reader v10.0
We create a CMake project named `bridge`, which contains a `bridge.h` file, a `bridge.cpp` file, and a `CMakeLists.txt` file.

Copy the header and library files of Dynamsoft Barcode Reader v10.0 into the bridging project. The directory structure should look like this:

```
- bridge
    - bridge.h
    - bridge.cpp
    - CMakeLists.txt
    - include
        - DynamsoftBarcodeReader.h
        - DynamsoftCaptureVisionRouter.h
        - DynamsoftCodeParser.h
        - DynamsoftCore.h
        - DynamsoftDocumentNormalizer.h
        - DynamsoftLabelRecognizer.h
        - DynamsoftLicense.h
        - DynamsoftUtility.h
    - platforms
        - win
            - bin
                - DynamicImagex64.dll
                - DynamicPdfCorex64.dll
                - DynamicPdfx64.dll
                - DynamsoftBarcodeReaderx64.dll
                - DynamsoftCaptureVisionRouterx64.dll
                - DynamsoftCorex64.dll
                - DynamsoftImageProcessingx64.dll
                - DynamsoftLicensex64.dll
                - DynamsoftUtilityx64.dll
            - lib
                - DynamsoftBarcodeReaderx64.lib
                - DynamsoftCaptureVisionRouterx64.lib
                - DynamsoftCorex64.lib
                - DynamsoftLicensex64.lib
                - DynamsoftUtilityx64.lib
```

Configure the `CMakeLists.txt` file for building a shared library:

```cmake
cmake_minimum_required(VERSION 3.0.0)

project(bridge VERSION 0.1.0)

if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
    link_directories("${PROJECT_SOURCE_DIR}/platforms/win/bin/") 
else()
    link_directories("${PROJECT_SOURCE_DIR}/platforms/win/lib/") 
endif()

INCLUDE_DIRECTORIES("${CMAKE_CURRENT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/include/")
add_library(bridge SHARED bridge.cpp)
target_link_libraries (${PROJECT_NAME} "DynamsoftLicensex64" "DynamsoftBarcodeReaderx64" "DynamsoftCaptureVisionRouterx64")
```

We employ the conditional statement `if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")` to determine whether the compiler in use is **MinGW GCC** or **MSVC**. In the case of MinGW GCC, the DLL files in the `bin` folder are linked. Conversely, for MSVC, we link the LIB files in the `lib` folder..

We can look to the header files of Dynamsoft Barcode Reader v9.6.20 for guidance in defining the C API in the `bridge.h` file. The advantage of this approach is to maintain the API design consistent with that of v9.6.20. 

```c
#ifndef C_BRIDGING_H
#define C_BRIDGING_H

#include <iostream>
#include <string>

#include "DynamsoftCaptureVisionRouter.h"

using namespace std;
using namespace dynamsoft::license;
using namespace dynamsoft::cvr;
using namespace dynamsoft::dbr;

#define C_API __declspec(dllexport)
typedef struct
{
    CCaptureVisionRouter *cvr;
    CCapturedResult *result;
} BarcodeReader;

typedef struct
{

    /**Barcode type in BarcodeFormat group 1 as string */
    char *barcodeFormatString;

    /**The barcode text, ends by '\0' */
    char *barcodeText;

    /**Reserved memory for the struct. The length of this array indicates the size of the memory reserved for this struct. */
    char reserved[44];
} TextResult;

typedef struct
{
    /**The total count of text result */
    int resultsCount;

    /**The text result array */
    TextResult *results;
} TextResultArray;

#ifdef __cplusplus
extern "C"
{
#endif

    C_API int DBR_InitLicense(const char *pLicense, char errorMsgBuffer[], const int errorMsgBufferLen);
    C_API int DBR_DecodeFile(void *barcodeReader, const char *pFileName, const char *pTemplateName);
    C_API void *DBR_CreateInstance();
    C_API void DBR_DestroyInstance(void *barcodeReader);
    C_API const char *DBR_GetVersion();
    C_API int DBR_GetAllTextResults(void *barcodeReader, TextResultArray **pResults);
    C_API void DBR_FreeTextResults(TextResultArray **pResults);
#ifdef __cplusplus
}
#endif

#endif
```

In order to provide a C API, we encapsulate the C++ code within `extern "C"` blocks. We utilize the `__declspec(dllexport)` keyword to export the functions from the DLL.

In the `bridge.cpp` file, we can implement the C interfaces as follows:
- `DBR_InitLicense()`: Set the license key for Dynamsoft Barcode Reader.
    
    ```c
    C_API int DBR_InitLicense(const char *pLicense, char errorMsgBuffer[], const int errorMsgBufferLen)
    {
        return CLicenseManager::InitLicense(pLicense, errorMsgBuffer, 512);
    }
    ```

- `DBR_CreateInstance()`: Create an instance of `BarcodeReader` struct that contains a `CCaptureVisionRouter` object and a `CCapturedResult` object.

    ```c
    C_API void *DBR_CreateInstance()
    {
        BarcodeReader *barcodeReader = (BarcodeReader *)calloc(1, sizeof(BarcodeReader));
        CCaptureVisionRouter *cvr = new CCaptureVisionRouter;
        barcodeReader->cvr = cvr;
        barcodeReader->result = NULL;
        return (void *)barcodeReader;
    }
    ```

- `DBR_DestroyInstance()`: Destroy the `BarcodeReader` instance.

    ```c
    C_API void DBR_DestroyInstance(void *barcodeReader)
    {
        if (barcodeReader != NULL)
        {
            BarcodeReader *reader = (BarcodeReader *)barcodeReader;
            if (reader->cvr != NULL)
            {
                delete reader->cvr;
                reader->cvr = NULL;
            }

            if (reader->result != NULL)
            {
                delete reader->result;
                reader->result = NULL;
            }
        }
    }
    ```

- `DBR_DecodeFile()`: Decode barcodes from an image file.

    ```c
    C_API int DBR_DecodeFile(void *barcodeReader, const char *pFileName, const char *pTemplateName)
    {
        BarcodeReader *reader = (BarcodeReader *)barcodeReader;
        if (!reader || !reader->cvr)
            return -1;

        CCapturedResult *result = reader->cvr->Capture(pFileName, CPresetTemplate::PT_READ_BARCODES);
        int errorCode = result->GetErrorCode();
        if (result->GetErrorCode() != 0)
        {
            cout << "Error: " << result->GetErrorCode() << "," << result->GetErrorString() << endl;
        }

        reader->result = result;

        return errorCode;
    }
    ```

- `DBR_GetVersion()`: Get the version of Dynamsoft Barcode Reader.

    ```c
    C_API const char *DBR_GetVersion()
    {
        return CBarcodeReaderModule::GetVersion();
    }
    ```

- `DBR_GetAllTextResults()`: Get all text results from the `CCapturedResult` object.

    ```c
    C_API int DBR_GetAllTextResults(void *barcodeReader, TextResultArray **pResults)
    {
        BarcodeReader *reader = (BarcodeReader *)barcodeReader;
        if (!reader || !reader->cvr || !reader->result)
            return -1;

        CCapturedResult *result = reader->result;

        int capturedResultItemCount = result->GetCount();
        if (capturedResultItemCount == 0)
            return -1;

        TextResultArray *textResults = (TextResultArray *)calloc(1, sizeof(TextResultArray));
        textResults->resultsCount = capturedResultItemCount;
        textResults->results = (TextResult *)calloc(capturedResultItemCount, sizeof(TextResult));
        *pResults = textResults;

        for (int j = 0; j < capturedResultItemCount; j++)
        {
            const CCapturedResultItem *capturedResultItem = result->GetItem(j);
            CapturedResultItemType type = capturedResultItem->GetType();
            if (type == CapturedResultItemType::CRIT_BARCODE)
            {
                const CBarcodeResultItem *barcodeResultItem = dynamic_cast<const CBarcodeResultItem *>(capturedResultItem);
                char *barcodeFormatString = (char *)barcodeResultItem->GetFormatString();
                char *barcodeText = (char *)barcodeResultItem->GetText();
                textResults->results[j].barcodeFormatString = (char *)malloc(strlen(barcodeFormatString) + 1);
                strcpy(textResults->results[j].barcodeFormatString, barcodeFormatString);
                textResults->results[j].barcodeText = (char *)malloc(strlen(barcodeText) + 1);
                strcpy(textResults->results[j].barcodeText, barcodeText);
            }
        }

        delete result;
        reader->result = NULL;

        return 0;
    }
    ```

- `DBR_FreeTextResults()`: Free the memory allocated for the `TextResultArray` object.

    ```c
    C_API void DBR_FreeTextResults(TextResultArray **pResults)
    {
        if (pResults)
        {
            if (*pResults)
            {
                if ((*pResults)->results)
                {
                    for (int i = 0; i < (*pResults)->resultsCount; i++)
                    {
                        if ((*pResults)->results[i].barcodeFormatString)
                        {
                            free((*pResults)->results[i].barcodeFormatString);
                        }
                        if ((*pResults)->results[i].barcodeText)
                        {
                            free((*pResults)->results[i].barcodeText);
                        }
                    }
                    free((*pResults)->results);
                }
            }
        }
    }
    ```

Compile the shared library using Microsoft Visual C++ through CMake with the following command:

```bash
mkdir build
cd build
cmake -DCMAKE_GENERATOR_PLATFORM=x64 ..
cmake --build . --config release
```

We now have a `bridge.dll` file and a `bridge.lib` file located in the `build/Release` folder. The `bridge.lib` file can be used for linkage in MSVC projects, while the `bridge.dll` file can be utilized for linkage in MinGW projects.

## Establishing DLL Linkage with MinGW GCC
1. Create a new CMake project.
2. Transfer `bridge.h`, `bridge.dll` and the complete Dynamsoft Barcode Reader development package into the target project directory.
3. Create a `BarcodeReader.cpp` file. You need to update the license key in the `DBR_InitLicense()` function and the image path in the `DBR_DecodeFile()` function.

    ```cpp
    #include "bridge.h"

    #include <iostream>

    int main()
    {
        std::cout << "Version: " << DBR_GetVersion() << std::endl;
        char errorMsgBuffer[512];
        int errorCode = DBR_InitLicense("LICENSE-KEY", errorMsgBuffer, 512);
        std::cout << "InitLicense errorCode: " << errorCode << std::endl;

        void* barcodeReader = DBR_CreateInstance();

        errorCode = DBR_DecodeFile(barcodeReader, "../images/UPC-E.jpg", "");

        if (errorCode != 0) {
            std::cout << "Failed to read barcode: " << errorCode << std::endl;
            DBR_DestroyInstance(barcodeReader);
            return -1;
        }
        
        TextResultArray *paryResult = NULL;
        DBR_GetAllTextResults(barcodeReader, &paryResult);

        std::cout << "Barcode count: " << paryResult->resultsCount << std::endl;

        for (int index = 0; index < paryResult->resultsCount; index++)
        {
            printf("Barcode %d:\n", index + 1);
            printf("    Type: %s\n", paryResult->results[index].barcodeFormatString);
            printf("    Text: %s\n", paryResult->results[index].barcodeText);
        }

        DBR_FreeTextResults(&paryResult);
        DBR_DestroyInstance(barcodeReader);
        return 0;
    }
    ```
4. Configure the `CMakeLists.txt` file for building the executable file:

    ```cmake
    cmake_minimum_required(VERSION 3.0.0)

    project(BarcodeReader VERSION 0.1.0)

    link_directories("${PROJECT_SOURCE_DIR}/")
    INCLUDE_DIRECTORIES("${PROJECT_SOURCE_DIR}/")

    add_executable(BarcodeReader BarcodeReader.cpp)
    target_link_libraries(BarcodeReader "bridge")
    ```
5. Build and run the project:

    ```bash
    mkdir build
    cd build
    cmake -G "MinGW Makefiles" ..
    cmake --build . --config release
    .\BarcodeReader.exe
    ```

    ![link DLL via MinGW gcc](https://www.dynamsoft.com/codepool/img/2023/08/dynamsoft-barcode-reader-10-mingw-windows.png)

## Source Code
[https://github.com/yushulx/cmake-cpp-barcode-qrcode/tree/dbr10-mingw](https://github.com/yushulx/cmake-cpp-barcode-qrcode/tree/dbr10-mingw)