internal HidClient(HidDevice device)
        {
            if (device == null)
            {
                throw new ArgumentNullException();
            }
            if (device.IsOpen)
            {
                throw new NotSupportedException();
            }
            device.MonitorDeviceEvents = true;
            device.OpenDevice(DeviceMode.Overlapped, DeviceMode.Overlapped, ShareMode.ShareRead | ShareMode.ShareWrite);
            if (!device.IsOpen)
            {
                throw new Exception("Device could not be opened");
            }
            this.device = device;

            threadCancelSource = new CancellationTokenSource();
            imageQueue         = new KeyRepaintQueue(threadCancelSource.Token);

            this.device.Inserted += Device_Inserted;
            this.device.Removed  += Device_Removed;

            var numberOfWriterThreads = 1;
            var numberOfReadThreads   = 1;
            var numberOfThreads       = numberOfWriterThreads + numberOfReadThreads;

            backgroundTasks = new Task[numberOfThreads];
            for (int i = 0; i < numberOfWriterThreads; i++)
            {
                backgroundTasks[i] = Task.Factory.StartNew(() =>
                {
                    byte[] p1Buffer   = new byte[HidCommunicationHelper.PagePacketSize];
                    byte[] p2Buffer   = new byte[HidCommunicationHelper.PagePacketSize];
                    byte[] emptyImage = new byte[HidClient.rawBitmapDataLength];

                    while (true)
                    {
                        var res = imageQueue.Dequeue();
                        if (!res.success)
                        {
                            break;
                        }

                        if (res.data == null)
                        {
                            res.data = emptyImage;
                        }

                        HidCommunicationHelper.GeneratePage1(p1Buffer, res.keyId, res.data);
                        HidCommunicationHelper.GeneratePage2(p2Buffer, res.keyId, res.data);

                        device.Write(p1Buffer);
                        device.Write(p2Buffer);
                    }
                }, TaskCreationOptions.LongRunning);
            }

            backgroundTasks[numberOfWriterThreads] = Task.Factory.StartNew(() =>
            {
                var cancelToken = threadCancelSource.Token;

                while (true)
                {
                    var rep = device.ReadReport();
                    if (cancelToken.IsCancellationRequested)
                    {
                        return;
                    }
                    ProcessNewStates(rep.Data);
                }
            }, TaskCreationOptions.LongRunning);
        }