Skip to main content
This section provides instructions on how to deploy an FRE 12 for Windows application to Azure Cloud Service. As an example, a Worker processing files by a container in Azure Storage account is represented. To implement this scenario, an Online License that connects to *.abbyy.com license server is used.
ABBYY Licensing Service can work with only one Online License at the same time.
Deploying your application to Cloud Service includes several steps:
  1. Organizing your Online License, local machine and cloud instance using prerequisites
  2. Performing the preparatory steps before deployingyour application
  3. Deploying your application to Cloud Service
See the code samples illustrating how to use the ABBYY FineReader Engine methods for document processing.

Prerequisites

Online License

To use an Online License, you should receive the following information from sales: You need to fulfill the following conditions for using an Online License wherever ABBYY Licensing Service is installed:
  • Active Internet connection
  • Allowed connections to *.abbyy.com on port 443 (HTTPS)
  • GoDaddy root certificate for the Certification Authority (It must be installed in the local machine version of the Trusted Root Certification Authorities certificate store. See the detailed information about the certificate on the GoDaddy website).

Local machine

Before creating your Cloud Service, use the following specification to organize your local machine:
  • Visual Studio 2019 and its modules for developing applications for Azure (check Azure feature in Visual Studio or use Visual Studio Installer to download such modules)
  • Cloud Service emulators and storage for easy debugging (see information here and download via Visual Studio Installer)
  • Template solution for Azure Cloud Service (extended preview) with single Worker role project (see information here)
  • .NET Framework 4.7.2
  • Azure SDK (download here)
  • NuGet package for working with Azure Storage and Blob containers - Azure.Storage.Blobs (download here)
  • ABBYY FineReader Engine wrapper for .NET Framework 4.7 (in the C:\ProgramData\ABBYY\SDK\12\FineReader Engine\Inc\.NET interops folder after developer installation)
  • IFileWriter interface overridden for working with Blob containers (see the sample below)
  • Azure Storage Explorer (optional - download here)

Cloud Service instance

Cloud Service instance is for storing your WorkerRole project in Azure. Use the following specification to organize this instance:
  • .NET Framework version of your Service
  • PowerShell for deploying ABBYY FineReader Engine
  • NuGet 2.8.5.201 or above for uploading Azure SDK
  • Azure SDK for working with an Azure Storage account using PowerShell

Preparatory steps

Preparatory steps are to be done on your local machine. By completing these steps, you will prepare all necessary settings and files to start deploying your application:
  1. Create two archives with the ABBYY FineReader Engine Library and Licensing Service (for example, LibraryPackage.zip and LSPackage. zip). You may create the list of files automatically, with the help of the FREngineDistribution.csv file. Use ABBYY FineReader Engine and License Server from the same package; otherwise, compatibility is not guaranteed.
  2. Create the Azure Storage account (frestorage in this article). All needed instructions you can find on Azure website.
  3. Create three Blob containers inside frestorage:
  • fre-lib - for the ABBYY FineReader Engine files
  • fre-input - for incoming files
  • fre-output - for processing results
  1. Upload LibraryPackage.zip and LSPackage.zip to the fre-lib container in the most convenient way (using .NET, Powershell, Python script, or Azure Storage Explorer/Azure Portal applications).
As an example, a WorkerRole project is used for working in a configured environment. All necessary configuration files (.csdef and Cloud.cscfg) are generated automatically after creating your project.

Deploying ABBYY FineReader Engine to Cloud Service

To deploy ABBYY FineReader Engine to your new WorkerRole project:
  1. Specify the parameters of the Cloud.cscfg as desired.
  2. Specify the parameters of the .csdef file:
  • (optional) your WorkerRole settings
  • (optional) the size of your virtual machine
  • (required) the local storage of your role (LocalStorage section - in this article, storage named LocalStorage1). Set it to 3 GB at least to ensure that the ABBYY FineReader Engine package will be deployed properly.
  • (required) your role startup order (use the code samples and settings from code samples section)
  1. Implement your WorkerRole project to process files from the fre-input container and publish the results of processing to the fre-output container:
  • the OnStart method to prepare your Cloud Service for working. This method is used to initialize the Engine and set up the TLS protocol.
  • the RunAsync method to cyclically process the files taken from the fre-input container. This method detects the files in the fre-input container, processes them in memory, and places them in the fre-output container (see Processor.cs, IFileWriter.cs and Config.cs).
  • the OnStop method to complete working of Cloud Service. This method is used to deinitialize the Engine.

Code Samples

This section includes code samples used to configure role startup order and ABBYY FineReader Engine settings:
  • CleanUpOnStart - deletes the previous version of ABBYY FineReader Engine and simplifies its update procedure (see its listing in the CleanUpOnStart.cmd and CleanUpOnStart.ps1 files below).
  • PreparePoShModules - downloads the SDK needed for Powershell to work with the Azure Storage (see its listing in the PreparePoShModules.cmd and PreparePoShModules.ps1 files below).
  • PrepareLibrary - uploads and unpacks the ABBYY FineReader Engine Library from the fre-lib container to local storage (see its listing in the PrepareLibrary.cmd and PrepareLibrary.ps1 files below).
  • PrepareLS - uploads, unpacks and launches the Licensing Service from the fre-lib container (see its listing in the PrepareLS.cmd and PrepareLS.ps1 files below).
It is required that the scripts listed above are run exactly in the proposed order, before launching your application. To customize the order of running the scripts, specify the following attributes:
  • taskType=“simple” - tasks are executed synchronously, one at a time.
  • executionContext=“elevated” - running a startup script with administrator rights (required to be able to install any application and running LicensingService.exe)
As a result, a folder that Cloud Service can manage will be created. This folder is used for uploading ABBYY FineReader Engine by the scrips listed above and loading the FREngine.dll for its further implementation in the sample.
rem   Script to clean up previous ABBYY FineReader Engine and Licensing Service packages
echo Cleaning up before launching service >> ".\CleanUpOnStart.log" 2>&1
PowerShell -ExecutionPolicy Unrestricted .\StartupScripts\CleanUpOnStart.ps1 >> ".\CleanUpOnStart.log" 2>&1
echo Cleaning up beafore launching service. >> ".\CleanUpOnStart.log" 2>&1
 
exit /B %errorlevel%
Write-Host "CleanUpOnStart.ps1 started...";
# path to local storage (see ServiceDefinition.csdef)
$local_storage_name = "LocalStorage1";
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.WindowsAzure.ServiceRuntime");
$local_storage_path = ([Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment]::GetLocalResource($local_storage_name)).RootPath.TrimEnd('\\');
# stopping Licensing Service process if it was running
$processes = Get-Process;
foreach( $process in $processes ) {
    if( $process.Name -eq "LicesingService" ) {
        Stop-Process $process;
    }
}
 
# cleaning and recreating the fre_packages folder
$destination_path = Join-Path -Path $local_storage_path -ChildPath 'fre_packages';
if( Test-Path -Path $destination_path ) {
    Remove-Item $destination_path -Recurse -Force;
}
New-Item -ItemType Directory -Path $destination_path;
# cleaning and recreating the input folder
$destination_path = Join-Path -Path $local_storage_path -ChildPath 'Input';
if( Test-Path -Path $destination_path ) {
    Remove-Item $destination_path -Recurse -Force;
}
New-Item -ItemType Directory -Path $destination_path;
# cleaning and recreating the results folder
$destination_path = Join-Path -Path $local_storage_path -ChildPath 'Results';
if( Test-Path -Path $destination_path ) {
    Remove-Item $destination_path -Recurse -Force;
}
New-Item -ItemType Directory -Path $destination_path;
Write-Host ("Resource folder was cleaned up.");
rem   Script to prepare modules to be able to connect to Storage account and download ABBYY FineReader Engine and Licensing Service packages
 
echo Preparing PoSh Modules >> ".\PreparePoShModules.log" 2>&1
PowerShell -ExecutionPolicy Unrestricted .\StartupScripts\PreparePoShModules.ps1 >> ".\PreparePoShModules.log" 2>&1
echo PoSh Modules wer prepared. >> ".\PreparePoShModules.log" 2>&1
 
exit /B %errorlevel%
# installing modules
Write-Host "PreparePoShModules.ps1 started...";
# installing NuGet provider for downloading Azure modules
Write-Host "Installing NuGet package provider...";
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force;
 
# installing Azure Storage module for operating the blob
Write-Host "Installing Az.Storage module...";
Install-Module -Name Az.Storage -Scope CurrentUser -Repository PSGallery -Force -AllowClobber;
Write-Host ("PoSh modules were prepared.");
rem   Script to prepare ABBYY FineReader Engine package
echo Preparing Library >> ".\PrepareLibrary.log" 2>&1
PowerShell -ExecutionPolicy Unrestricted .\StartupScripts\PrepareLibrary.ps1 >> ".\PrepareLibrary.log" 2>&1
echo Library was prepared. >> ".\PrepareLibrary.log" 2>&1
exit /B %errorlevel%
Write-Host "PrepareLibrary.ps1 started...";
# container with ABBYY FineReader Engine and Licensing Service packages
$container_name = 'fre-lib';
# storage account connecting string 
$connection_string = "<connection_string_to_frestorage>";
Write-Host "Configuration";
Write-Host ("Container name: " + $container_name);
Write-Host ("Connection string: " + $storage_account);
# connecting to the storage object
Write-Host ("Connecting to storage account...");
$storage_account = New-AzStorageContext -ConnectionString $connection_string;
# getting blobs (basically files)
Write-Host ("Getting blobs from container...");
$fre_blobs = Get-AzStorageBlob -Container $container_name -Context $storage_account;
# path to local storage (see ServiceDefinition.csdef)
$local_storage_name = "LocalStorage1";
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.WindowsAzure.ServiceRuntime");
$local_storage_path = ([Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment]::GetLocalResource($local_storage_name)).RootPath.TrimEnd('\\');
$destination_path = Join-Path -Path $local_storage_path -ChildPath 'fre_packages';
# downloading the only blob needed
Write-Host ("Downloading FRE package from container...");
foreach( $blob in $fre_blobs ) {
    Write-Host $blob.Name;
    if( $blob.Name -eq "LibraryPackage.zip" ) {
        Write-Host ("Downloading blob: " + $blob.Name + "to " + $destination_path);
        Get-AzStorageBlobContent -Container $container_name -Blob $blob.Name `
            -Destination (Join-Path -Path $destination_path -ChildPath $blob.Name) -Context $storage_account;
    } else {
        Write-Host ("Downloading blob " + $blob.Name + " was skipped.");
    }
}
# unzipping the downloaded package (old library will be forcibly rewritten)
Write-Host ("Unzipping library package...");
Expand-Archive -Path (Join-Path -Path $destination_path -ChildPath "LibraryPackage.zip") `
    -DestinationPath (Join-Path -Path $destination_path -ChildPath "LibraryPackage") -Force;
Write-Host ("Library package was prepared.");
rem   Script to prepare Licensing Service package and start LicensingService.exe
echo Preparing LS >> ".\PrepareLS.log" 2>&1
PowerShell -ExecutionPolicy Unrestricted .\StartupScripts\PrepareLS.ps1 >> ".\PrepareLS.log" 2>&1
echo LS was prepared. >> ".\PrepareLS.log" 2>&1
exit /B %errorlevel%
Write-Host "PrepareLS.ps1 started...";
# container with ABBYY FineReader Engine and Licensing Service packages
$container_name = 'fre-lib';
# storage account connecting string
$connection_string = "<connection_string_to_frestorage>";
Write-Host "Configuration";
Write-Host ("Container name: " + $container_name);
Write-Host ("Connection string: " + $storage_account);
# connecting to the storage object
Write-Host ("Connecting to storage account...");
$storage_account = New-AzStorageContext -ConnectionString $connection_string;
# getting blobs (basically files)
Write-Host ("Getting blobs from container...");
$fre_blobs = Get-AzStorageBlob -Container $container_name -Context $storage_account;
# path to local storage (see ServiceDefinition.csdef)
$local_storage_name = "LocalStorage1";
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.WindowsAzure.ServiceRuntime");
$local_storage_path = ([Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment]::GetLocalResource($local_storage_name)).RootPath.TrimEnd('\\');
$destination_path = Join-Path -Path $local_storage_path -ChildPath 'fre_packages'; 
# downloading the Licensing Service package
Write-Host ("Downloading Licensing Service package from container...");
foreach( $blob in $fre_blobs ) {
    Write-Host $blob.Name;
    if( $blob.Name -eq "LSPackage.zip" ) {
        Write-Host ("Downloading blob: " + $blob.Name + "to " + $destination_path);
        Get-AzStorageBlobContent -Container $container_name -Blob $blob.Name `
            -Destination (Join-Path -Path $destination_path -ChildPath $blob.Name) -Context $storage_account;
    } else {
        Write-Host ("Downloading blob " + $blob.Name + " was skipped.");
    }
}
# unzipping the downloaded package
Write-Host ("Unzipping library package...");
Expand-Archive -Path (Join-Path -Path $destination_path -ChildPath "LSPackage.zip") `
    -DestinationPath (Join-Path -Path $destination_path -ChildPath "LSPackage") -Force;
# creating folders for licenses
$program_data_path = [System.Environment]::ExpandEnvironmentVariables("%programdata%");
$path = Join-Path -Path $program_data_path -ChildPath "ABBYY\SDK\12\Licenses";
New-Item -ItemType Directory -Path $path -Force;
# starting Licensing Service as standalone
Write-Host ("Starting Licensing Service...");
 
$licensing_service_app = Join-Path -Path $destination_path -ChildPath "LSPackage\LicensingService.exe";
Start-Process -FilePath $licesing_service_app -ArgumentList "/standalone";
Write-Host ("LS package was prepared and LS was started.");
namespace WorkerRole1.Engine
{
    public class FileWriter : FREngine.IFileWriter, IDisposable
    {
        public FileWriter(string _resultBlobName, string _fileExtension)
        {
            resultBlobName = _resultBlobName;
            fileExtension = _fileExtension;
        }
        public void Open(string fileName, ref int bufferSize)
        {
            stream = new MemoryStream();
        }
        public void Write(byte[] data)
        {
            stream.Write(data, 0, data.Length);
        }
        public void Close() 
        {
            // Creating connection to a new blob in <OutputContainerName>
            // The processing result will be stored there
            BlobClient resultBlobClient = new BlobClient(Config.ConnectionString, Config.OutputContainerName,
                resultBlobName + fileExtension);
            // Rewrite existing file
            resultBlobClient.DeleteIfExists();
            // Setting position to 0 to write file from beginning
            stream.Position = 0;
            resultBlobClient.Upload(stream);
 
            stream.Close();
        }
        public void Dispose()
        {
            // Closing memory stream on disposal to be able to access it after data was written
            stream.Close();
        }
        private string resultBlobName;
        private string fileExtension;
        private MemoryStream stream;
    }
}
namespace WorkerRole1
{
    public class WorkerRole : RoleEntryPoint
    {
        private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
        private readonly ManualResetEvent runCompleteEvent = new ManualResetEvent(false);
        Processor processor = new Processor();
 
        public override void Run()
        {
            Trace.TraceInformation("WorkerRole1 is running");
 
            try
            {
                this.RunAsync(this.cancellationTokenSource.Token).Wait();
            }
            finally
            {
                this.runCompleteEvent.Set();
            }
        }
        public override bool OnStart()
        {
            // Set the maximum number of concurrent connections
            ServicePointManager.DefaultConnectionLimit = 12;
 
            // Set TLS to 12 to be able to connect to storage account
            System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;
 
            // For information on handling configuration changes
            // see the MSDN topic at https://go.microsoft.com/fwlink/?LinkId=166357
 
            bool result = base.OnStart();
            Trace.TraceInformation("WorkerRole1 has been started");
 
            processor.LoadEngine();
            return result;
        }
 
        public override void OnStop()
        {
            processor.UnloadEngine();
            Trace.TraceInformation("WorkerRole1 is stopping");
            this.cancellationTokenSource.Cancel();
            this.runCompleteEvent.WaitOne();
            base.OnStop();
            Trace.TraceInformation("WorkerRole1 has stopped");
        }
 
        private async Task RunAsync(CancellationToken cancellationToken)
        {
            // TODO: Replace the following with your own logic
            while (!cancellationToken.IsCancellationRequested)
            {
                Trace.TraceInformation("Working");
 
                try
                {
                    // Create the container and return a container client object
                    BlobContainerClient inputContainerClient = new BlobContainerClient(Config.ConnectionString, Config.InputContainerName);
                    foreach (BlobItem blobItem in inputContainerClient.GetBlobs())
                    {
                        Trace.TraceInformation("\t" + blobItem.Name);
                        BlobClient blobClient = new BlobClient(Config.ConnectionString, Config.InputContainerName, blobItem.Name);
 
                        Trace.TraceInformation("\t Downloading blob to memory: " + blobItem.Name);
                        MemoryStream memoryStream = new MemoryStream();
                        blobClient.DownloadTo(memoryStream);
                        Trace.TraceInformation("\t Processing in FRE: " + blobItem.Name);
                        string resultBlobName = processor.ProcessImage(memoryStream, blobItem.Name);
                        Trace.TraceInformation("\t Result blob name in output container: " + resultBlobName);
                        Trace.TraceInformation("\t Deleting from input container: " + blobItem.Name);
                        blobClient.Delete();
                    }
                }
                catch (Exception error)
                {
                    Trace.TraceInformation("error: " + error.Message);
                }
 
                await Task.Delay(1000);
            }
        }
    }
}
namespace WorkerRole1
{
    class Processor : IDisposable
    {
        EngineLoader engineLoader = null;
        private void displayMessage(string text)
        {
            File.AppendAllText(".\\FRE.log", text + "\n");
            Trace.TraceInformation("\t" + text );
        }        
        private void loadProfile()
        {
            engineLoader.Engine.LoadPredefinedProfile("DocumentConversion_Accuracy");
        }        
        private void setupFREngine()
        {
            displayMessage("Loading predefined profile...");
            loadProfile();
        }
        public void LoadEngine()
        {
            try {
                if (engineLoader == null)
                {
                    // The same EngineLoader, as in the Hello sample
                    engineLoader = new EngineLoader();
                }
                setupFREngine();
            }
            catch (Exception error)
            {
                displayMessage("error: " + error.Message);
            }
        }
        public void UnloadEngine()
        {
            try
            {
                if (engineLoader != null)
                {
                    engineLoader.Dispose();
                    engineLoader = null;
                }
            }
            catch (Exception error)
            {
                displayMessage("error: " + error.Message);
            }
        }
 
        public string ProcessImage( MemoryStream inputMemoryStream, string inputBlobName )
        {
            FRDocument document = engineLoader.Engine.CreateFRDocument();
            string resultBlobName = "";
            try
            {
               document.PageFlushingPolicy = FREngine.PageFlushingPolicyEnum.PFP_KeepInMemory;
 
                // Add image file to document
                displayMessage("Loading image...");
                IntPtr handle = Marshal.AllocHGlobal(inputMemoryStream.GetBuffer().Length);
                Marshal.Copy(inputMemoryStream.GetBuffer(), 0, handle, inputMemoryStream.GetBuffer().Length);
                document.AddImageFileFromMemory(handle.ToInt64(), null, null);
 
                // Recognize the document
                displayMessage("Recognizing...");
                document.Process(null);
 
                // Save results
                displayMessage("Saving results...");
                // Save results to pdf using 'balanced' scenario
                // FREngine.PDFExportParams pdfParams = engineLoader.Engine.CreatePDFExportParams();
                // pdfParams.Scenario = FREngine.PDFExportScenarioEnum.PES_Balanced;
 
                WorkerRole1.Engine.FileWriter fileWriter = new WorkerRole1.Engine.FileWriter(inputBlobName, ".pdf");
                resultBlobName = inputBlobName + ".pdf";
                document.ExportToMemory(fileWriter, FREngine.FileExportFormatEnum.FEF_PDF, null);
 
            }
            catch (Exception error)
            {
                displayMessage("error: " + error.Message);
            }
            finally
            {
                // Close the document
                document.Close();
            }
 
            return resultBlobName;
        }
 
        public void Dispose()
        {
            UnloadEngine();
        }
    }
}
class Config
{
    // Local storage as defined in ServiceDefinition.csdef
    private static string LocalStorageName = "LocalStorage1";
    // Connecting string to blob container
    public static readonly string ConnectionString = "<connection_string_to_frestorage>";
    // Input and output containers name in your storage
    public static readonly string InputContainerName = "fre-input";
    public static readonly string OutputContainerName = "fre-output";
 
    // Return Customer Project ID for ABBYY FineReader Engine
    public static String GetCustomerProjectId()
    {
        return "<Your_Customer_Project_ID>";
    }
 
    // Return name of license token
    public static String GetLicenseTokenName()
    {
        return "<Token_number>.ABBYY.ActivationToken";
    }
 
    // Return license password
    public static String GetLicensePassword()
    {
        return "<Your_Online_License_token_password>";
    }
    // Return engine path
    public static String GetEngineFolder()
    {
        string engineSubfolder = "fre_packages\\LibraryPackage\\Bin64";
        string engineDllFolder = Path.Combine(RoleEnvironment.GetLocalResource(LocalStorageName).RootPath, engineSubfolder);
        return engineDllFolder;
    }
}