
Миграция приложений для ТСД с Windows CE и .NET Compact Framework на Android
Складские терминалы на Windows CE и Windows Mobile дорабатывают последние годы, а приложение для них написано на VB.NET под .NET Compact Framework в Visual Studio 2008. Разбираем, какие пути перехода на Android-терминалы существуют, как перенести работу со сканером, базу SQL Server Compact и синхронизацию, какой код удастся сохранить и как пройти замену парка без остановки склада.
coredll.dll там не работают. Рабочий путь — вынести правила складских операций в API на ASP.NET Core, а на терминале сделать новый клиент на Kotlin или .NET MAUI, получающий штрихкод через Intent и хранящий задания в SQLite. Переход ведут пилотом на одной зоне склада, старые терминалы работают до конца тиражирования.На многих складах и производствах до сих пор работают терминалы сбора данных на Windows CE, Windows Mobile 6.5 и Windows Embedded Compact. Приложение для них писали десять-пятнадцать лет назад на VB.NET или C# под .NET Compact Framework 3.5: приёмка, отбор, инвентаризация, перемещения. Оно стабильно, но живёт на устройствах, которые уже не выпускаются, и собирается только в Visual Studio 2008 на отдельной виртуальной машине.
Переход на Android-терминалы — это не перекомпиляция, а новый мобильный клиент поверх старых складских процессов. Логику операций и правила учёта обычно удаётся сохранить, а интерфейс, работу со сканером и обмен с сервером придётся сделать заново. Если своей Android-экспертизы в команде нет, клиентскую часть разумно заказать мобильное приложение под Android-терминалы у внешней команды, а за собой оставить сервер, API и знание процессов склада.
Если общие принципы переноса десктопных программ на телефоны вам ещё не знакомы, начните с разбора переноса VB6- и WinForms-приложения на мобильные. Ниже — то, что отличает именно терминалы сбора данных: сканер, ограниченный экран, работа в перчатках, офлайн в дальних зонах склада и управление парком из сотен устройств.
Почему приложения на Windows CE пора переносить именно сейчас?
Платформа закончилась не вчера, но последние два-три года её стало по-настоящему трудно содержать. Сроки поддержки Microsoft уже прошли для всех версий:
- Windows Embedded CE 6.0 — расширенная поддержка закончилась 10 апреля 2018 года.
- Windows Embedded Handheld 6.5 — 14 января 2020 года.
- Windows Embedded Compact 2013 — 11 октября 2023 года, последняя версия линейки; даты есть на странице жизненного цикла Microsoft.
Отсутствие обновлений безопасности — только часть проблемы. На практике мешает другое:
- Железо. Новые терминалы на Windows почти не продаются, запчасти и аккумуляторы к старым моделям приходится искать на вторичном рынке. Каждый разбитый терминал уменьшает парк.
- Сеть. Старые устройства плохо работают с современными настройками Wi-Fi и шифрования. Когда на сервере отключают TLS 1.0 и 1.1, обмен с ним внезапно перестаёт работать.
- Инструменты. Проекты Smart Device поддерживались только до Visual Studio 2008, а Windows Mobile Device Center на современных версиях Windows ставится с трудом. Сборка и отладка держатся на одной виртуальной машине и одном человеке.
- Люди. Новые сотрудники склада привыкли к сенсорным экранам смартфонов, а разработчиков, готовых сопровождать .NET Compact Framework, на рынке почти нет.
Если терминалы ещё работают, это хорошее время для спокойной миграции: можно провести пилот на одной зоне склада, пока старый парк закрывает остальное. Переход в аварийном режиме, когда сломалась половина устройств, обходится заметно дороже.
Что внутри типичного приложения для ТСД на .NET Compact Framework?
Перед выбором технологии стоит разобрать, из чего состоит текущее приложение. У большинства складских клиентов под Windows CE набор частей одинаковый:
- Формы под маленький экран. 240×320 или 480×640, крупные кнопки, переходы по клавишам F1–F4 и Enter. Логика проверок часто зашита прямо в обработчики форм.
- SDK сканера. Symbol/Motorola EMDK for .NET, Intermec или Honeywell: событие чтения штрихкода, настройка символогий, звуковые и вибросигналы.
- Локальная база SQL Server Compact. Файл
.sdfс заданиями, справочником номенклатуры и журналом операций. - Синхронизация. Remote Data Access (RDA), репликация слиянием, веб-сервисы ASMX или обмен файлами через ActiveSync в док-станции.
- Платформенные вызовы. P/Invoke в
coredll.dllдля звука, подсветки, питания, сброса устройства. - Печать. Этикетки на мобильный принтер по Bluetooth командами ZPL или EPL.
' Типичный фрагмент приложения для ТСД на .NET Compact Framework 3.5
Imports System.Data.SqlServerCe
Private Sub OnBarcodeRead(code As String)
Using cn As New SqlCeConnection("Data Source=\Program Files\Wms\wms.sdf")
cn.Open()
Using cmd As New SqlCeCommand(
"SELECT Qty FROM Lines WHERE DocId = @doc AND Barcode = @code", cn)
cmd.Parameters.AddWithValue("@doc", currentDocId)
cmd.Parameters.AddWithValue("@code", code)
Dim qty = cmd.ExecuteScalar()
If qty Is Nothing Then
Beep() ' P/Invoke в coredll.dll
lblStatus.Text = "Нет в документе"
End If
End Using
End Using
End SubВ этом коротком фрагменте сразу видны три вещи, которые на Android не переедут как есть: SQL Server Compact, вызов платформенного API и логика проверки внутри обработчика формы. Полную инвентаризацию удобно вести по чек-листу аудита legacy-кода на VB.NET: он покажет, сколько правил живёт в формах и что придётся выносить.
Какие есть варианты перехода на Android?
Путей пять, и у каждого есть честная ниша. Сроки ниже даны для типичного складского приложения на 10–20 экранов с одной-двумя интеграциями.
| Вариант | Что сохраняется | Срок | Ограничения | Когда подходит |
|---|---|---|---|---|
| Эмулятор терминала или корпоративный браузер | Серверная логика целиком, если клиент был telnet- или веб-приложением | 2–6 недель | Только для «тонких» клиентов; офлайна нет | Приложение уже работало как терминал к серверу |
| Веб-приложение (PWA) | Сервер и правила; интерфейс пишется заново | 2–4 месяца | Ограниченный доступ к сканеру и железу, слабый офлайн | Простые операции при стабильном Wi-Fi по всему складу |
| .NET MAUI | Библиотеки правил на .NET, знания команды | 3–6 месяцев | SDK сканеров приходится подключать через обёртки | Команда сильна в .NET, логики на клиенте много |
| Нативный Android (Kotlin) | Сервер и правила, перенесённые в API | 3–6 месяцев | Отдельный стек и команда | Парк только на Android, важны скорость скана и офлайн |
| Готовая платформа для ТСД | Учётная система; процессы настраиваются под продукт | 1–3 месяца | Лицензии на устройство, нестандартные операции дорогие | Процессы типовые, интеграция с учётной системой есть из коробки |
На практике выбор обычно сводится к двум вопросам. Первый — был ли старый клиент «толстым», с локальной базой и логикой, или терминальным. Второй — насколько уникальны складские операции. Если клиент толстый, а процессы свои, остаются .NET MAUI и нативный Kotlin. Решение лучше подтверждать прототипом одной операции на реальном терминале, а не сравнительной таблицей.
Как перенести работу со сканером штрихкодов?
На Windows CE приложение обычно подписывалось на событие чтения через SDK конкретного производителя. На Android у промышленных терминалов есть три способа получить штрихкод, и выбирать стоит осознанно.
- Эмуляция клавиатуры. Сканер «печатает» код в активное поле ввода и нажимает Enter. Работает без единой строки кода, но легко ломается: код попадает не в то поле, теряются служебные символы, трудно отличить ручной ввод от скана.
- Широковещательное сообщение (Intent). Сервис производителя (у Zebra — DataWedge, у Honeywell — свой профиль сканирования) отправляет приложению Intent с кодом и типом штрихкода. Это основной рабочий вариант: не нужна зависимость от SDK, а настройки символогий живут в профиле. Формат описан в документации Zebra DataWedge.
- SDK производителя. Zebra EMDK, Honeywell Data Collection SDK и аналоги дают полный контроль: включение сканера по событию, настройка декодеров, работа с RFID. Нужен, когда Intent-способа не хватает.
// Android: приём штрихкода от DataWedge через Intent
class ScanReceiver(private val onScan: (String) -> Unit) : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val code = intent.getStringExtra("com.symbol.datawedge.data_string") ?: return
onScan(code)
}
}
// action задаётся в профиле DataWedge для нашего приложения
val receiver = ScanReceiver { code -> viewModel.onBarcode(code) }
ContextCompat.registerReceiver(
this, receiver, IntentFilter("ru.company.wms.SCAN"),
ContextCompat.RECEIVER_EXPORTED
)Чтобы не зависеть от одного производителя, приём скана прячут за собственным интерфейсом вроде BarcodeSource: реализация для Zebra, для Honeywell и для камеры смартфона. Тогда смена закупки терминалов не потребует переписывать экраны операций.
Что делать с SQL Server Compact и синхронизацией?
SQL Server Compact давно без поддержки и на Android не существует. Но главная перемена не в замене одной встроенной базы на другую, а в способе обмена с сервером.
- Локальная база. На устройстве — SQLite: Room в Kotlin или Microsoft.Data.Sqlite в .NET MAUI. Там лежат задания пользователя, нужная часть справочника номенклатуры и очередь операций.
- API вместо RDA и репликации. Устройство не ходит в SQL Server напрямую и не синхронизирует таблицы целиком. Между ним и учётной системой ставят API на ASP.NET Core по HTTPS с авторизацией, проверкой прав и журналом.
- Очередь операций. Каждое действие — «принято 12 штук по строке 5» — получает уникальный идентификатор и уходит на сервер, когда есть связь. Повторная отправка после обрыва не создаёт дублей.
- Инкрементальная загрузка. Справочники забираются по изменениям с момента последнего обмена, а не целиком при каждом входе в сеть.
- Правила конфликтов. Что делать, если задание переназначили, пока сотрудник был в зоне без Wi-Fi. Решение принимают вместе с руководителем склада.
Требования к защите обмена и хранению данных на устройстве удобно передать подрядчику ссылкой на стандарт OWASP MASVS: потерянный терминал с журналом операций и токеном доступа — такой же риск, как потерянный ноутбук.
Какой код на VB.NET можно сохранить?
Меньше, чем хотелось бы, но больше, чем ноль. Всё зависит от того, насколько логика отделена от форм.
- Сохраняется. Классы расчётов и проверок: допуски по количеству, правила подбора партий, формирование кодов этикеток, разбор GS1-штрихкодов. Их переносят в библиотеку на современном .NET и используют в API, а при выборе .NET MAUI — и на устройстве.
- Переписывается. Формы, обработчики клавиш, работа со сканером, P/Invoke в
coredll.dll, доступ к SQL Server Compact и логика обмена. - Переезжает на сервер. Проверки, которые раньше дублировались на терминале и в учётной системе. На сервере они живут в одном месте и одинаково работают для всех клиентов.
Перед выносом правил из форм зафиксируйте их поведение тестами на реальных примерах: какой результат даёт старый код на эталонных документах. Приёмы для кода без швов и зависимостей описаны в статье о тестах для legacy VB.NET. Без этого через месяц после запуска выяснится, что новый клиент по-другому округляет вес или иначе разбирает весовой штрихкод.
Этапы миграции: пошаговый план
Склад нельзя остановить на время переписывания, поэтому миграцию ведут короткими этапами, и старые терминалы работают до последнего.
- Инвентаризация (1–2 недели). Список операций по зонам склада, частота каждой, используемые штрихкоды, принтеры, интеграции, места без Wi-Fi. Результат — приоритизированный перечень операций.
- Выбор технологии и модели терминала (2–3 недели). Прототип одной частой операции, например приёмки, на двух-трёх моделях. Проверка скана, клавиш, печати и работы в слабой сети.
- API для первых операций. Вынос правил из форм в сервис, тесты на поведение, авторизация, журнал. Старый клиент продолжает работать по прежней схеме.
- Пилот на одной зоне (3–6 недель). 5–15 терминалов, реальная смена, сравнение времени операций и ошибок со старым клиентом.
- Тиражирование по операциям и зонам. Следующие операции по одной за итерацию; старые терминалы постепенно выводятся из эксплуатации.
- Сопровождение. Обновления Android и прошивок производителя, новые модели терминалов, мониторинг ошибок и времени отклика API.
Бюджет считают по операциям и объёму логики, которую нужно вынести на сервер, а не по количеству форм старого приложения. Методика расчёта — в статье об оценке стоимости миграции legacy-приложения.
Чек-лист приёмки операции на новом терминале:
- операция проходится одной рукой, без ввода с экранной клавиатуры;
- скан распознаётся с первого раза для всех используемых типов штрихкодов;
- при обрыве Wi-Fi данные не теряются, повтор не создаёт дублей;
- расчёты совпадают со старым клиентом на эталонных документах;
- операция выполняется не медленнее, чем на старом терминале.
Как управлять парком Android-терминалов?
На Windows CE приложение часто ставили вручную через док-станцию, а настройки хранились в реестре каждого устройства. С сотней Android-терминалов так работать нельзя.
- MDM-система. Централизованная установка и обновление приложения, настройка Wi-Fi и профилей сканера, удалённая блокировка потерянного устройства. Корпоративные режимы Android описаны в программе Android Enterprise.
- Режим киоска. Терминал запускает только складское приложение и нужные системные функции, сотрудники не ставят посторонние программы.
- Распространение без магазина. Корпоративное приложение обычно раздаётся через MDM или управляемый Google Play, а не публикуется в открытом доступе.
- Обновления прошивок. Производители терминалов выпускают обновления безопасности и новые версии Android по своему графику; при закупке стоит уточнять срок поддержки модели.
Типичные ошибки при переходе с Windows CE на Android
- Копировать экраны один в один. Формы под 240×320 и клавиши F1–F4 на сенсорном экране неудобны. Операцию проектируют заново, начиная с того, что делает сотрудник руками.
- Опираться на эмуляцию клавиатуры. Это быстро в пилоте и больно в эксплуатации: коды попадают не в то поле, а ручной ввод нельзя отличить от скана.
- Подключать терминал к базе напрямую. Учётные данные SQL Server на сотне устройств — прямой путь к утечке и к зависшим блокировкам при обрывах Wi-Fi.
- Привязаться к одному SDK. Без собственного слоя над сканером смена производителя терминалов превращается в переписывание.
- Забыть про офлайн в дальних зонах. Морозильные камеры, мезонины и уличные площадки часто без устойчивой связи. Их нужно найти на этапе инвентаризации.
- Вывести старые терминалы раньше времени. Пока новые операции не прошли пилот, старый парк — страховка.
Как это делают сегодня
Вопросы и ответы
Можно ли запустить приложение для Windows CE на Android-терминале без переписывания?
Нет. Сборки .NET Compact Framework, вызовы coredll.dll и SDK сканеров для Windows CE на Android не работают, эмулятора Windows CE для промышленного использования нет. Без переписывания можно сохранить только серверную часть, а также перейти на эмулятор терминала, если приложение изначально было telnet- или веб-клиентом.
Что выбрать для нового клиента: .NET MAUI, Kotlin или веб?
Если парк — только Android-терминалы одного-двух производителей и нужен быстрый отклик на скан, разумнее нативный Kotlin. .NET MAUI подходит, когда команда сильна в .NET и хочет переиспользовать библиотеки правил. Веб-приложение в корпоративном браузере годится для простых онлайн-операций при стабильном Wi-Fi.
Чем заменить SQL Server Compact на Android?
На устройстве — SQLite, обычно через Room в Kotlin или Microsoft.Data.Sqlite в .NET. Синхронизацию через RDA или репликацию слиянием заменяют обменом с API по HTTPS: устройство забирает задания и справочники, а операции отправляет очередью с уникальными идентификаторами.
Можно ли сохранить код на VB.NET?
Частично. Классы с расчётами и проверками, не завязанные на формы и P/Invoke, переносятся в библиотеку на современном .NET и используются сервером или клиентом на .NET MAUI. Интерфейс, работа со сканером и локальная база пишутся заново.
Сколько длится миграция приложения для ТСД?
Для типичного складского приложения на 10–20 экранов пилот на одной зоне склада занимает два-три месяца, включая API и обмен. Полная замена парка зависит от числа операций, объёма логики в формах и графика закупки терминалов; оценку делают по инвентаризации операций, а не по числу форм.
Нужно ли менять учётную систему вместе с терминалами?
Обычно нет. Учётная система или WMS на сервере остаётся, меняется мобильный клиент и способ обмена. Если раньше устройство ходило в базу напрямую или через файлы ActiveSync, между ним и сервером появляется API, который потом пригодится и другим клиентам.