Search code examples
c#multithreadingitextbarcodedatamatrix

Generating Datamatrix with iTextSharp using multithreading causes crash


I'm getting a crash in my code when generating a datamatrix for a label. After a lot of testing, I've determined it's due to multi-threading, however I cannot determine why.

I'm using iTextSharp v5.5.13.2(Nuget package). Code that will replicate the error is below:

        for(int i = 0; i < 10; i++)
        {
            ThreadPool.QueueUserWorkItem(x =>
            {
                iTextSharp.text.pdf.BarcodeDatamatrix dataMatrix = new iTextSharp.text.pdf.BarcodeDatamatrix();

                dataMatrix.Height = 18;
                dataMatrix.Width = 18;
                dataMatrix.ForceSquareSize = true;

                dataMatrix.Generate("TestData");
            });
            Console.WriteLine(i);
        }

This is the error:

System.IndexOutOfRangeException: 'Index was outside the bounds of the array.'
This exception was originally thrown at this call stack:
    iTextSharp.text.pdf.BarcodeDatamatrix.B256Encodation(byte[], int, int, byte[], int, int, int, int, int)
    iTextSharp.text.pdf.BarcodeDatamatrix.GetEncodation(byte[], int, int, byte[], int, int, int, bool)
    iTextSharp.text.pdf.BarcodeDatamatrix.Generate(byte[], int, int)
    iTextSharp.text.pdf.BarcodeDatamatrix.Generate(string)
    DataAccessTesting.Form1.Function1_Click.AnonymousMethod__59_0(object) in Form1.cs
    System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(object)
    System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, object, bool)
    System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, object, bool)
    System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
    System.Threading.ThreadPoolWorkQueue.Dispatch()
    ...
    [Call Stack Truncated]

If I remove the multithreading, it runs perfectly fine. So this code works:

        for (int i = 0; i < 10; i++)
        {
            iTextSharp.text.pdf.BarcodeDatamatrix dataMatrix = new iTextSharp.text.pdf.BarcodeDatamatrix();

            dataMatrix.Height = 18;
            dataMatrix.Width = 18;
            dataMatrix.ForceSquareSize = true;

            dataMatrix.Generate("TestData");
            Console.WriteLine(i);
        }

       

I'm using multi threading in the application to generate all the labels and immediately start printing them, even while other labels are still still generating. Forcing them to wait until each of the others has finished processing will significantly slow down the application, so removing the multithreading is a last resort option.

How can I prevent this error from occurring without removing multi threading?

EDIT: I keep getting told I don't have the complete code. I tested again, and by putting the code I added into a button event, I can replicate the error. Here's a screenshot. Complete code for the error


Solution

  • Decompiling the BarcodeDatamatrix in the library confirms that the Generate method is not thread-safe, due to the usage of the following fields (thanks @Enigmativity for the insightful comment):

    private static int[][] f;
    private static int[][] switchMode;
    

    The class at the following location makes these fields (as well as other methods that use them) not static, fixing the thread-safety issue. Important: if the library/NuGet (actually this class) gets updated, this may need to be done again should you want to use the latest version:
    https://pastebin.com/PFnqBYQa

    Alternative solution
    Use the DataMatrix.net library available here or here (old library, not sure which one is better).

    Additional information
    I also tested ZXing.NET but FNC1 characters (ex. group separator \x1D) were not working properly. This works fine with the above library.

    Code samples (for alternative libraries):

    // ZXING: FNC1 not working
    //var dm = new BarcodeWriter
    //{
    //    Format = BarcodeFormat.DATA_MATRIX,
    //    Options =
    //    {
    //        GS1Format = true,
    //        PureBarcode = true
    //    }
    //};
    
    //var gs1Code = "0107612345678900171" + "\x1D" + "00503";
    //var bmp = dm.Write(gs1Code);
    //return bmp;
    
    
    // Datamatrix.NET
    var imageEncoder = new DmtxImageEncoder();
    var options = new DmtxImageEncoderOptions
    {
        ModuleSize = 8,
        MarginSize = 30,
        BackColor = Color.White,
        ForeColor = Color.Black,
        Scheme = DmtxScheme.DmtxSchemeAsciiGS1
    };
    
    var barcode = txt1.Text.Length == 13 
        ? "0" + txt1.Text
        : txt1.Text;
    
    return imageEncoder.EncodeImage("01" + barcode
                                  + "10" + txt2.Text + "\x1D"
                                  + "21" + txt3.Text
                                  , options);