How to Use SWIG to Link Windows DLL with Golang

SWIG (Simplified Wrapper and Interface Generator) is a software development tool that connects C/C++ libraries with a variety of high-level programming languages such as Golang, Java, Python, PHP, Ruby and so on.  In this post, I’m going to demo how to use SWIG to automatically generate C wrapper code for linking Dynamsoft Barcode Reader DLL with cgo on Windows.

Prerequisites

How to Use SWIG to Generate C Wrapper Code for Cgo on Windows

Run cmd.exe and set GOPATH as the location of your workspace:

set GOPATH=f:\go-project
set DBR=<Dynamsoft Barcode Reader Directory>

Create your package path.

mkdir %GOPATH%\src\github.com\dynamsoftsamples\go-barcode-reader\dbr

Copy DynamsoftBarcodeReaderx64.dll from %DBR%\Redist\C_C++ to %GOPATH%\src\github.com\dynamsoftsamples\go-barcode-reader\dbr\bin

Copy header files from %DBR%\Include to %GOPATH%\src\github.com\dynamsoftsamples\go-barcode-reader\dbr\include

Create dbr.c:

#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include "include/If_DBR.h"

__int64 GetFormat(int iIndex)
{
	__int64 llFormat = 0;

	switch(iIndex)
	{
	case 1:
		llFormat = OneD | QR_CODE | PDF417 |DATAMATRIX;
		break;
	case 2:
		llFormat = OneD;
		break;
	case 3:
		llFormat = QR_CODE;
		break;
	case 4:
		llFormat = CODE_39;
		break;
	case 5:
		llFormat = CODE_128;
		break;
	case 6:
		llFormat = CODE_93;
		break;
	case 7:
		llFormat = CODABAR;
		break;
	case 8:
		llFormat = ITF;
		break;
	case 9:
		llFormat = INDUSTRIAL_25;
		break;
	case 10:
		llFormat = EAN_13;
		break;
	case 11:
		llFormat = EAN_8;
		break;
	case 12:
		llFormat = UPC_A;
		break;
	case 13:
		llFormat = UPC_E;
		break;
	case 14:
		llFormat = PDF417;
		break;
	case 15:
		llFormat = DATAMATRIX;
		break;
	default:
		break;
	}

	return llFormat;
}

const char * GetFormatStr(__int64 format)
{
	if (format == CODE_39)
		return "CODE_39";
	if (format == CODE_128)
		return "CODE_128";
	if (format == CODE_93)
		return "CODE_93";
	if (format == CODABAR)
		return "CODABAR";
	if (format == ITF)
		return "ITF";
	if (format == UPC_A)
		return "UPC_A";
	if (format == UPC_E)
		return "UPC_E";
	if (format == EAN_13)
		return "EAN_13";
	if (format == EAN_8)
		return "EAN_8";
	if (format == INDUSTRIAL_25)
		return "INDUSTRIAL_25";
	if (format == QR_CODE)
		return "QR_CODE";
	if (format == PDF417)
		return "PDF417";
	if (format == DATAMATRIX)
		return "DATAMATRIX";

	return "UNKNOWN";
}

int decode_file(const char *pszImageFile)
{
	if (!pszImageFile)
	{
		return -1;
	}

  // const char *pszImageFile = "f:\\AllSupportedBarcodeTypes.tif";
	__int64 llFormat = (OneD | QR_CODE | PDF417 | DATAMATRIX);
	char pszBuffer[512] = {0};

	int iMaxCount = 0x7FFFFFFF;
	int iIndex = 0;
	ReaderOptions ro = {0};
	pBarcodeResultArray paryResult = NULL;
	int iRet = -1;
	char * pszTemp = NULL;
	char * pszTemp1 = NULL;
	unsigned __int64 ullTimeBegin = 0;
	unsigned __int64 ullTimeEnd = 0;
	size_t iLen;
	FILE* fp = NULL;
	int iExitFlag = 0;

	DBR_InitLicense("38B9B94D8B0E2B41DB1CC80A58946567");

	// Read barcode
	ullTimeBegin = GetTickCount();
	ro.llBarcodeFormat = llFormat;
	ro.iMaxBarcodesNumPerPage = iMaxCount;
	iRet = DBR_DecodeFile(pszImageFile, &ro, &paryResult);
	ullTimeEnd = GetTickCount();

	// Output barcode result
	pszTemp = (char*)malloc(4096);
	if (iRet != DBR_OK)
	{
		sprintf(pszTemp, "Failed to read barcode: %s\r\n", DBR_GetErrorString(iRet));
		printf(pszTemp);
		free(pszTemp);
		return 0;
	}

	if (paryResult->iBarcodeCount == 0)
	{
		sprintf(pszTemp, "No barcode found. Total time spent: %.3f seconds.\r\n", ((float)(ullTimeEnd - ullTimeBegin)/1000));
		printf(pszTemp);
		free(pszTemp);
		DBR_FreeBarcodeResults(&paryResult);
		return 0;
	}

	sprintf(pszTemp, "Total barcode(s) found: %d. Total time spent: %.3f seconds\r\n\r\n", paryResult->iBarcodeCount, ((float)(ullTimeEnd - ullTimeBegin)/1000));
	printf(pszTemp);
	for (iIndex = 0; iIndex < paryResult->iBarcodeCount; iIndex++)
	{
		sprintf(pszTemp, "Barcode %d:\r\n", iIndex + 1);
		printf(pszTemp);
		sprintf(pszTemp, "    Page: %d\r\n", paryResult->ppBarcodes[iIndex]->iPageNum);
		printf(pszTemp);
		sprintf(pszTemp, "    Type: %s\r\n", GetFormatStr(paryResult->ppBarcodes[iIndex]->llFormat));
		printf(pszTemp);
		pszTemp1 = (char*)malloc(paryResult->ppBarcodes[iIndex]->iBarcodeDataLength + 1);
		memset(pszTemp1, 0, paryResult->ppBarcodes[iIndex]->iBarcodeDataLength + 1);
		memcpy(pszTemp1, paryResult->ppBarcodes[iIndex]->pBarcodeData, paryResult->ppBarcodes[iIndex]->iBarcodeDataLength);
		sprintf(pszTemp, "    Value: %s\r\n", pszTemp1);
		printf(pszTemp);
		free(pszTemp1);
		sprintf(pszTemp, "    Region: {Left: %d, Top: %d, Width: %d, Height: %d}\r\n\r\n",
			paryResult->ppBarcodes[iIndex]->iLeft, paryResult->ppBarcodes[iIndex]->iTop,
			paryResult->ppBarcodes[iIndex]->iWidth, paryResult->ppBarcodes[iIndex]->iHeight);
		printf(pszTemp);
	}

	free(pszTemp);
	DBR_FreeBarcodeResults(&paryResult);

	return 0;
}

Create dbr.i:

/* dbr.i */
 %module dbr
 %{
 /* Put header files here or function declarations like below */
 extern int decode_file(const char *pszImageFile);
 %}

 extern int decode_file(const char *pszImageFile);

Generate dbr.go and dbr_wrap.c with following command:

swig -go -cgo -intgosize 32 dbr.i

Here is the final look:

go dbr structure

Open dbr.go and add the flags of head files and libraries for building:

#cgo CFLAGS: -I .
#cgo LDFLAGS: -L ./bin -lDynamsoftBarcodeReaderx64

Linking Dynamsoft Barcode Reader with Golang

Change directory to %GOPATH% and compile the package with:

go install github.com\dynamsoftsamples\go-barcode-reader\dbr

Alternatively, you can change directory to %GOPATH%\src\github.com\dynamsoftsamples\go-barcode-reader\dbr and compile the package with:

go install

A package dbr.a will be generated at %GOPATH%\pkg\windows_amd64\github.com\dynamsoftsamples\go-barcode-reader

go pkg

Making Golang Barcode Reader on Windows

  1. Create %GOPATH%\github.com\dynamsoftsamples\go-barcode-reader\BarcodeReader\BarcodeReader.go.
    package main
    
    import (
      "github.com/dynamsoftsamples/go-barcode-reader/dbr"
      "os"
    )
    
    func main() {
      if len(os.Args) == 1 { // read the default image
        dbr.Decode_file("AllSupportedBarcodeTypes.tif")
      } else {
        dbr.Decode_file(os.Args[1])
      }
    }
  2. Copy DynamsoftBarcodeReaderx64.dll to %GOPATH%\bin.
  3. Change directory to %GOPATH% and generate the executable file with:
    go install github.com\dynamsoftsamples\go-barcode-reader\BarcodeReader

    Go Barcode Reader

  4. Test the Golang Barcode Reader:

    %GOPATH%bin\BarcodeReader.exe <barcode image>

    go barcode reader

References

Source Code

https://github.com/dynamsoftsamples/go-barcode-reader