Записная книжка разработчика

Мои проекты

Скриншоты с экрана устройства

| Comments

Для получения скриншотов с экранов устройств была разработана маленькая утилитка, получающая текущее изображение по Rs-232.

Сама утилитка написана на C#, в программу устройства был добавлен небольшой фрагмент, отправляющий в UART изображение из буфера:

switch(buf[1])
{
case 0x03: //read multiple holding registers (0x04)
  ...
  break;
//Другие команды Modbus
...

case 0xFF: //  Команда получения скриншота

...
  return PrintScreen(uartNum);
};

Таким образом, стандартные команды Modbus "расширяются" специальной командой с кодом 0xFF, при получении которой устройство посылает в порт копию экрана (для монохромного экрана 128*64 это составляет ровно 1 кбайт данных).

Утилитка очень простая, небольшое (но, как оказалось, легко решаемое) затруднение вызвал тот факт, что С# работает с COM-портом не так, как другие системы (например, Delphi).

C# создает отдельный поток исполнения для обработчика события приема сообщения COM-порта. А раз это отдельный поток, в нем нельзя отрисовывать изображение (операции с графикой не являются в .Net потокобезопасными).

Проблему можно решить двумя путями: верным и неверным.

Верный путь заключается в применении функции BeginInvoke, которая осуществляет асинхронный вызов делегата в другом потоке. Ниже приведен фрагмент кода:

        public delegate void MessageRecievedDelegate();

        private Bitmap image;
        const int msg_size = 1024;
...

//обработчик приема данных COM-порта, вызывается в отдельном потоке
        private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            #region принимаем сообщение
            try
            {
                if (serialPort1.BytesToRead >= msg_size)
                {
                    serialPort1.Read(buf, 0, msg_size);
                    BeginInvoke(new MessageRecievedDelegate(messageRecieved));
                };
            }
            catch (TimeoutException)
            {
                serialPort1.Close();
            }
            #endregion
        }

//формирование изображения из полученных данных (находится в основном потоке)
public void messageRecieved()
        {
            #region формируем изображение
            image = new Bitmap(128, 64);
            ...
            #endregion

            serialPort1.Close();
        }
...

Второй путь, неверный, был найден (что удивительно), на сайте Microsoft.

public static void Main()
    {
        ...
        Thread readThread = new Thread(Read);

        // Create a new SerialPort object with default settings.
        _serialPort = new SerialPort();
        ...
        _serialPort.Open();
        _continue = true;
        readThread.Start();
         ...
        while (_continue)
        {
            ...

        readThread.Join();
        _serialPort.Close();
    }

public static void Read()
    {
        while (_continue)
        {
            try
            {
                string message = _serialPort.ReadLine();
                Console.WriteLine(message);
            }
            catch (TimeoutException) { }
        }
    }

То есть циклически непрерывно проверяется некоторая переменная _continue, при окончании приема сообщения обработчик порта сбрасывает ее и закрывает порт. Эта конструкция надежно нагружает процессор почти на 100%, практически не позволяя работать ни одному другому приложению.