How to Create a Universal Barcode Reader on Windows 10 with C/C++ Legacy Code
Probably many Windows developers have upgraded operating systems to Windows 10. On Windows 10, Microsoft suggests developers create Universal Windows Apps (UWP) for a variety of devices, including PC, tablet, mobile phone, Hololens and so on. For development, I wonder whether legacy C/C++ SDKs can also seamlessly work on Windows 10, such as Dynamsoft Barcode SDK, which does not officially support UWP development yet. The answer is yes. Let’s see the tricks.
What You Should Know
Prerequisites
-
Enable developer mode on Windows 10.
-
Install Visual Studio.
Where do We Start?
Follow Microsoft’s tutorial to write your first “Hello World” App:
https://msdn.microsoft.com/en-us/library/windows/apps/dn996906.aspx
What’s the difference between Universal Windows app and desktop app?
Unlike Windows desktop app, Universal app is compiled into a package appx, which contains all resources, configurations and executable file. The application can only run in the context of an app container.
How to Invoke C/C++ APIs of Dynamsoft Barcode SDK in UWP App?
Create a new Universal Windows App.
Add Image, Button and TextBlock to MainPage.xaml:
<ScrollViewer Grid.Row="1" VerticalScrollMode="Auto" VerticalScrollBarVisibility="Auto" VerticalAlignment="Top">
<StackPanel>
<Grid x:Name="Image" Margin="0,0,0,5" VerticalAlignment="Top">
<Image x:Name="PreviewImage" HorizontalAlignment="Left" VerticalAlignment="Top" MaxWidth="600"/>
</Grid>
<StackPanel Orientation="Horizontal" Margin="0, 0, 0, 5" VerticalAlignment="Top">
<Button x:Name="button" Margin="0, 0, 5, 0" Click="button_Click" VerticalAlignment="Top">
<Viewbox MaxHeight="40" MaxWidth="40">
<SymbolIcon Symbol="OpenFile"/>
</Viewbox>
</Button>
<TextBlock x:Name="BarcodeResults" Margin="0,0,0,10" TextWrapping="Wrap" Text="Results:" Height="600" Width="300" VerticalAlignment="Stretch" HorizontalAlignment="Left" />
</StackPanel>
</StackPanel>
</ScrollViewer>
Loading barcode images with FileOpenPicker:
FileOpenPicker^ picker = ref new FileOpenPicker();
picker->FileTypeFilter->Append(".bmp");
picker->SuggestedStartLocation = PickerLocationId::PicturesLibrary;
To get the C++ pointer of the image data buffers from WriteableBitmap, we can refer to the article - Obtaining pointers to data buffers. Here is the code:
byte* GetPointerToPixelData(IBuffer^ pixelBuffer, unsigned int *length)
{
if (length != nullptr)
{
*length = pixelBuffer->Length;
}
// Query the IBufferByteAccess interface.
ComPtr<IBufferByteAccess> bufferByteAccess;
reinterpret_cast<IInspectable*>(pixelBuffer)->QueryInterface(IID_PPV_ARGS(&bufferByteAccess));
// Retrieve the buffer data.
byte* pixels = nullptr;
bufferByteAccess->Buffer(&pixels);
return pixels;
}
We’ll use the API DecodeBuffer, but the underlying required data structure is different. We need to construct the data buffer (BITMAPINFOHEADER + Image data) ourselves:
char *total = (char *)malloc(len + 40);
BITMAPINFOHEADER bitmap_info = { 40, width, height, 0, 32, 0, len, 0, 0, 0, 0 };
memcpy(total, &bitmap_info, 40);
char *data = total + 40;
memcpy(data, buffer, len);
Read barcode image and return results:
iRet = reader.DecodeBuffer((unsigned char*)total, len + 40);
// Output barcode result
pszTemp = (char*)malloc(4096);
if (iRet != DBR_OK)
{
sprintf(pszTemp, "Failed to read barcode: %s\r\n", DBR_GetErrorString(iRet));
free(pszTemp);
return nullptr;
}
pBarcodeResultArray paryResult = NULL;
reader.GetBarcodes(&paryResult);
After getting the barcode results, convert C String to Platform::String^. I found the solution from StackOverflow.
results = ref new Array<String^>(paryResult->iBarcodeCount);
for (iIndex = 0; iIndex < paryResult->iBarcodeCount; iIndex++)
{
sprintf(pszTemp, "Barcode %d:\r\n", iIndex + 1);
sprintf(pszTemp, "%s Page: %d\r\n", pszTemp, paryResult->ppBarcodes[iIndex]->iPageNum);
sprintf(pszTemp, "%s Type: %s\r\n", pszTemp, GetFormatStr(paryResult->ppBarcodes[iIndex]->llFormat));
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, "%s Value: %s\r\n", pszTemp, pszTemp1);
// http://stackoverflow.com/questions/11545951/how-to-convert-from-char-to-platformstring-c-cli
std::string s_str = std::string(pszTemp);
std::wstring wid_str = std::wstring(s_str.begin(), s_str.end());
const wchar_t* w_char = wid_str.c_str();
OutputDebugString(w_char);
barcode_result = ref new String(w_char);
results->set(iIndex, barcode_result);
free(pszTemp1);
}
Copy DynamsoftBarcodeReaderx86.dll from **
Run the UWP Barcode Reader directly from Visual Studio by CTRL+F5 or the Start menu:
See the screenshot:
Known Issues
- Because some CRT functions are not applicable to UWP apps, such as fopen, the API DecodeFile cannot work. Please refer to CRT functions not supported in Universal Windows Platform apps.
- Only 1D barcode detection works.
- Only BMP image file is supported.
Source Code
https://github.com/dynamsoftsamples/universal-windows-barcode-reader