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.
Prerequisites
- Enable developer mode on Windows 10.
- Install Visual Studio 2015.
- Install Dynamsoft Barcode SDK.
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 <Dynamsoft Barcode SDK>\Redist\C_C++ to DynamsoftBarcodeReader\Debug\DynamsoftBarcodeReader\AppX.
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