Директ Коммандер на Linux: установщик не ставится, а программа работает

Я занимаюсь контекстной рекламой и веб-аналитикой больше 9 лет, успел поработать и инхаус, и в бигтехе на стороне площадки, и пофрилансить. В общем, знаю много разных рецептов блюд ;) Пишу на Хабре в первый раз, так что можете закидывать тапками, к критике отношусь нормально.
А теперь к делу. Я перешёл с вендоуз на Linux Mint и не пожалел: браузеры, таблицы, редакторы, питон, мессенджеры, всё переехало и работает отлично, кроме одной программы.
Директ Коммандер выходит только под Windows и macOS и всё это время смотрел на меня исподтишка, вынуждая переключаться обратно на венду. Казалось бы, можно забить, есть же веб-интерфейс. Но если вы хоть раз правили пятьсот объявлений или собирали большую структуру через UI Директа, вы понимаете, почему именно он оказался последним, что держало меня одной ногой на винде. Эти скачки между осями меня в итоге и добили.
Спойлер: он работает. Логин, загрузка кампаний, правки, отправка на сервер, всё как на винде.

И всё различие между «не работает вообще» и «работает как родное» свелось к одной строчке. Чтобы до этой строчки добраться, я убил пару вечеров и пару миллионов нервных клеток. Ниже история целиком, с тупиками и одним красивым ложным следом. Если нужен только результат, инструкция в конце, листайте сразу вниз.
Попытка первая, наивная
Стандартный способ запустить виндовую программу под Linux это Wine, а самая удобная обёртка над ним сейчас Bottles: создаёт изолированные окружения, сам ставит внутрь шрифты, библиотеки и прочую обвязку.
Ставлю Bottles из Flathub. Создаю бутылку типа «Приложение». Она несколько минут прожёвывает четырнадцать шагов инициализации, ставит Wine Mono, Gecko, шрифты Microsoft и т.д. Скачиваю установщик Коммандера с сайта Яндекса и скармливаю его бутылке.
Установка идёт бодро, доходит до копирования файлов и упирается вот в это:

«Не удалось закрыть Direct Commander. Пожалуйста, закройте Direct Commander вручную и нажмите Повтор, чтобы продолжить».
Ни одного Коммандера не запущено, его в принципе еще нет в бутылке. Пробую «Повтор» несколько раз, диалог возвращается.
Дальше пошёл стандартный набор действий человека, который не хочет верить, что всё настолько тупо.
Убил все процессы Wine, начал заново. То же самое. Посмотрел ps, нашёл там два процесса установщика вместо одного (видимо задублировались), обрадовался, убил оба, запустил заново. Опять то же самое. Подумал, что проверка ищет процесс по имени и находит сама себя, переименовал файл установщика в setup.exe. Дубль процессов исчез, диалог остался.
Перенёс установщик внутрь бутылки, чтобы убрать прослойку flatpak-портала. Не помогло. Сменил версию Windows в настройках бутылки. Не помогло. Сменил сборку Wine. Не помогло. Создал новую бутылку с нуля, чтобы исключить мусор от предыдущих попыток. Ошибка воспроизвелась один в один, с той же секунды.
К этому моменту я уже часа полтора занимался тем, что чинил не то, как оказалось.
Попытка вторая, через “коммерческий” Wine
В сети есть старые статьи, где Коммандер под Linux ставят через CrossOver. Это коммерческий Wine с собственными патчами, у него есть пробный период. Раз уж пошла такая пьянка, ставлю триал.
CrossOver программу не узнал и предложил настроить вручную:

Дальше был отдельный аттракцион. По той самой старой статье CrossOver создаёт под Коммандер окружение Windows XP. Я так и сделал, запустил установку, и установщик поругал меня, что требуется Windows 7 и выше. Статья устарела, Яндекс с тех пор поднял минимальную версию.
Переключил бутылку на Windows 7, запустил снова. Установка пошла, дошла до копирования файлов, и:

Вот тут до меня наконец дошло, что чинить установщик бессмысленно. Проверка внутри него ломается одинаково на любом Wine, включая патченый за деньги. Надо не чинить, а обходить.
А что там внутри
В Bottles есть встроенный анализатор exe-файлов. Скормил ему установщик:

Оказался NSIS - обычный самораспаковывающийся архив со скриптом установки. Это уже хорошо, потому что NSIS предсказуем и вскрывается архиватором.
Распаковал:
7z x ~/Downloads/"Direct Commander-3.153.1-ia32.exe"
А внутри распакованного exe лежит папка со странным именем $PLUGINSDIR (так NSIS называет свой временный каталог, туда установщик складывает всё, что нужно ему самому во время работы), и в ней файл app-32.7z на 128 мегабайт. Распаковал и его - вот тут стало интересно.
Внутри лежат:
libEGL.dlllibGLESv2.dllffmpeg.dllvk_swiftshader.dllпапка
localesфайл
resources/app.asarLICENSE.electron.txt- контрольный в голову!
Директ Коммандер это Electron. То есть Chromium плюс код на JavaScript, упакованные в папку!
Отдельно отмечу для тех, кто будет гуглить: Коммандер давно не приложение на Adobe AIR. В выдаче полно статей, где предлагают сначала поставить под Wine рантайм Adobe AIR, а потом уже Коммандер. Статьи устарели на несколько лет.
Electron’у установщик не нужен в принципе. Ни реестра, ни COM-компонентов, ни системных служб. Положил папку куда угодно, запустил exe, работает. Значит, надо просто достать её и запустить.
Две ловушки при распаковке
Тут я потерял ещё немного времени на ерунде, поэтому предупрежу заранее:
Во-первых: $PLUGINSDIR надо писать в одинарных кавычках. Иначе bash попытается подставить значение несуществующей переменной и передаст архиватору пустоту. Да, я ламер немножко.
Во-вторых: Нельзя писать путь вывода как -o~/dc_app. Тильда в bash раскрывается только в начале слова, а тут слово начинается с -o, так что 7z получает строку ~/dc_app буквально и создаёт в домашней директории папку с именем из одного символа ~ (тильда). Я потом минут десять искал, куда же оно распаковалось, потому что в файловом менеджере такая папка выглядит как издевательство.
Правильно так:
7z x '$PLUGINSDIR/app-32.7z' -o"$HOME/dc_app"
Получилось около 460 мегабайт файлов, в корне лежит Direct Commander.exe. Скопировал всё это в бутылку и запустил.
И ничего не произошло!
Совсем ничего. Ни окна, ни ошибки, ни сообщения. Процесс живёт секунду и исчезает.
В ps процесса нет. wmctrl -l окон не показывает. В dmesg никаких сегфолтов. Запустил из терминала, чтобы увидеть вывод, вывод есть, ошибок нет, а процесса нет. Единственное, что дало зацепку, это код выхода. Ноль.
Это важно, т.к. ноль означает, что процесс завершился штатно, а не упал. Значит искать надо не крэш, а причину, по которой программа решила закрыться. Разные вещи и разные методы поиска.
Лезу внутрь приложения
Раз это Electron, код лежит в resources/app.asar, а asar распаковывается штатной утилитой. Значит в главный файл можно дописать что угодно.
Я распаковал asar в папку app (Electron предпочитает архив, поэтому сам app.asar надо убрать в сторону, иначе папку он проигнорирует) и вписал в начало главного файла перехватчики: обёртки над app.quit() и app.exit() с печатью стека вызовов, слушатели на will-quit, window-all-closed, render-process-gone, child-process-gone, обработчик uncaughtException и пульс, то есть таймер, который раз в секунду печатает строчку.
Заодно попросил создать своё тестовое окно, чтобы понять, может ли Electron вообще нарисовать окно в этом окружении.
Запустил. Результат оказался диагностически идеальным:
=== PATCH ACTIVE ===
HB 1
EVENT ready
HB 2
И тишина. Событие ready наступило, пульс тикнул дважды, то есть программа прожила пару секунд. И при этом ни вызова quit, ни исключения, ни одного события завершения. Даже process.on('exit') не сработал.
Вывод: процесс убивают на нативном уровне, JavaScript в этом не участвует вообще. Значит дело не в логике программы, а в Chromium или в Wine.
Кстати, тестовое окно я всё-таки увидел. Правда позже и при других настройках, но момент был приятный:

Собственный лог программы
Дальше выяснилось, что я зря городил огород. У Коммандера есть свой лог-файл, он лежит внутри бутылки:
drive_c/users/<логин>/AppData/Roaming/direct-commander/logs/20260910.log
Пишется человеческим языком. И там всё видно:
[debug] [main] event:ready
[debug] [main] powerSaveBlockerId is 0
[info] [main] Required space = 524288000, diskSpace available 86406774784
И обрыв.
Инициализация проходит целиком. Проверка свободного места проходит (86 гигабайт против нужных 500 мегабайт). А дальше по коду идёт создание первого окна. То есть программа умирает ровно в тот момент, когда пытается нарисовать окно.
Возвращаюсь к логу Wine и смотрю, что происходит в этот момент:
fixme:dcomp:DCompositionCreateDevice3
[ERROR:direct_composition_support.cc] DCompositionCreateDevice3 failed: Не реализовано.
DirectComposition. Подсистема Windows для композитинга окон, которую Chromium дёргает при создании окна. В Wine она реализована не полностью, вызов возвращает «не реализовано», и Chromium падает, унося с собой весь процесс. Без сообщений, с нулевым кодом выхода, потому что собственный обработчик крэшей у Chromium сам полагается на нативные Windows API, которых тут нет.
Красиво же.
Ложный след, на котором я застрял
Найдя причину, я радостно полез искать флаг Chromium, который отключает эту подсистему. И вот тут потерял еще какое-то количество времени:
Интуитивный
--disable-features=DirectCompositionне работает, это другой механизм.--disable-gpuне помогает.--single-processне помогает.--in-process-gpuне помогает.--use-gl=swiftshaderвообще невалиден для этой сборки, в логе честно пишется, что запрошенная реализация не входит в список разрешённых.
Правильный переключатель называется --disable-direct-composition. Ошибка про DirectComposition из лога пропала, но программа всё равно не запустилась.
Вот здесь было неприятно. Причина найдена, флаг подобран, эффекта ноль. Грусть, печаль, тоска. Я уже был готов забить и поднимать виртуалку.
От отчаяния поменял сборку Wine, ну мало ли. В Bottles их несколько: по умолчанию для новых бутылок подставляется soda - сборка на базе Proton, заточенная под игры. А внутри самого flatpak Bottles лежит обычный системный Wine, в списке раннеров он называется sys-wine-11.0, по пути это /app/bin/wine. Попробовал запустить через него иии…
Окно появилось.

Залогинился, подтянулись кампании, поправил объявление, отправил изменения на сервер. Всё работает, не глючит, баллы API тратятся.
И финальный поворот
Когда эйфория немного отпустила, я решил проверить, что именно из этого было нужно. Разобрал по частям.
Оказалось, что флаг не нужен вообще. На системном Wine программа прекрасно работает без него. А на игровой сборке не спасал даже он.
То есть переменная всё это время была одна. Не флаги, не настройки, не версия Windows, не видеокарта. Только сборка Wine! Я полвечера крутил ручки на приборной панели, пока проблема была в двигателе.
Заодно проверил патч про requestSingleInstanceLock, который я делал по пути, подозревая, что программа считает себя уже запущенной. Тоже не нужен, работает и на нетронутом app.asar.
Мораль для тех, кто будет мучить под Wine что-то другое: не гадайте по флагам. Сначала код выхода, потом собственный лог программы, потом инструментирование главного процесса. И только потом, зная точное место смерти, ищите обход. Я сделал ровно наоборот :)
Как повторить
Теперь коротко и по делу. Понадобится Flatpak (в Mint и Ubuntu уже есть), гигабайта четыре места и минут двадцать.
Ставим Bottles и архиватор:
flatpak install flathub com.usebottles.bottles
sudo apt install -y p7zip-full icoutils
Создаём бутылку. В Bottles жмём плюс, имя Direct (ну или как вам захочется), тип «Приложение». Больше там ничего трогать не надо. Создание займёт несколько минут.


Скачиваем установщик с официального сайта Яндекса, версию для Windows, и кладём в папку загрузок. Запускать его не надо, он нам нужен только как архив.
Распаковываем в два прохода:
mkdir -p ~/dc_extract && cd ~/dc_extract # Создаем папку для извлечения и переходим в неё
7z x ~/Downloads/"Direct Commander-3.153.1-ia32.exe" # Распаковываем exe-шник
7z x '$PLUGINSDIR/app-32.7z' -o"$HOME/dc_app" # Распаковываем внутреннюю папку с самой программой в отдельную папку dc_app
Про кавычки и знак тильду (~) я предупреждал выше, не повторяйте моих ошибок.
Копируем программу в бутылку:
BOTTLE="$HOME/.var/app/com.usebottles.bottles/data/bottles/bottles/Direct" # Если у вас бутылка называется не "Direct", переименуйте название в пути
mkdir -p "$BOTTLE/drive_c/Program Files (x86)/DirectCommander"
cp -r "$HOME/dc_app/." "$BOTTLE/drive_c/Program Files (x86)/DirectCommander/"
Запускаем:
flatpak run --command=bash com.usebottles.bottles -c "
export WINEPREFIX='$BOTTLE'
cd '$BOTTLE/drive_c/Program Files (x86)/DirectCommander'
exec /app/bin/wine 'Direct Commander.exe'
"
Через несколько секунд появится окно с полем для логина. Вход идёт через браузер, как обычно.
Вся соль в /app/bin/wine. Это системная сборка вместо игровой. Если запустить раннером по умолчанию, программа молча умрёт, как у меня в первый раз.
Чтобы не повторять это руками, я завернул всё в скрипт (ниже). Он сам находит свежий установщик в загрузках, распаковывает оба слоя, кладёт файлы в бутылку, вытаскивает иконку прямо из exe-шника и делает идентичный ярлык в меню. Повторный запуск со свежим установщиком будет работать как обновление.
Что работает
Весь алгоритм я проверял несколько раз на чистых бутылках, чтобы удостовериться, что результат воспроизводимый. Коммандер запускался, просил логин, авторизовывался, получал данные с сервера, всё работало в штатном режиме, как обычно, как на винде. Интерфейс рисуется нормально, без артефактов и тормозов, по ощущениям обычное приложение, а не эмуляция.
Что не проверял
А вот чего не получилось проверить пока что, скажу честно: массовый импорт и экспорт файлов кампаний, работу с креативами и картинками, многочасовые сессии и поведение при обрывах связи. Под Wine спотыкаются обычно как раз такие штуки, диалоги работы с файлами и всё, что лезет в системные компоненты. Так что если у вас в работе это критично, заложите время на проверку.
Маленькая подсказка на этот случай: диалоги открытия файлов внутри Коммандера видят файловую систему глазами Wine. Диск C: это внутренности бутылки, а ваш домашний каталог обычно доступен через диск Z:.
Что не работает
Способ неофициальный, поддержки за ним никакой. Но можете завести Issues на гитхабе, я постараюсь помочь.
Главный минус: автообновление работать не будет. Встроенный апдейтер запускает тот самый установщик, который под Wine зацикливается. Обновляться придётся вручную, то есть заново скачать, распаковать, заменить папку. У меня это одна команда, скрипт делает ровно то же, что и при установке, и пользовательские данные не трогает: они лежат отдельно, в AppData/Roaming/direct-commander. Про обновления лучше не забывать, Яндекс периодически требует минимальную версию, иначе закрывает доступ к API.
Если не запустилось
Первым делом запускайте из терминала и смотрите код выхода. Ноль при отсутствующем окне означает штатное завершение, ищите не крэш, а причину выхода.
Дальше смотрите собственный лог программы, он полезнее любых системных:
tail -40 "$BOTTLE"/drive_c/users/*/AppData/Roaming/direct-commander/logs/$(date +%Y%m%d).log
Если он обрывается на строке Required space, вы попали ровно в мою ситуацию: смерть при создании окна. Проверьте, что запускаете через /app/bin/wine, и если это не помогло, добавьте флаг --disable-direct-composition. У меня он не понадобился, но на других сборках может спасти.
И отдельно: не пугайтесь того, что Wine валит в консоль. Строки вида err:module:use_lsteamclient, err:ole:com_get_class_object ... not registered, WSALookupServiceBegin failed with: 8 и россыпь fixme: присутствуют и на полностью рабочем запуске. Я поначалу честно пытался их чинить, пока не понял, что это фоновый шум.
Стоило ли оно того?
Если Коммандер ваш основной инструмент и вы сидите в нём весь день, честнее держать виртуалку с Windows. Ресурсов съест больше, зато совместимость полная и обновления штатные.
Если он нужен регулярно, но не постоянно, а разворачивать виртуалку ради одной программы неохота, то описанный способ вполне рабочий. У меня он запускается с ярлыка как обычное приложение и ведёт себя отлично.
Мне было важно именно это: не держать вторую систему ради одной программы. Пару вечеров возни, и последняя причина оставаться на Windows отпала.
Скрипт установки, обновления и удаления выложил сюда: github.com/cpa-jedi/direct-commander-linux. Там же в README собраны типовые проблемы и то, какие сообщения Wine можно спокойно игнорировать.
Если будете повторять и упрётесь во что-то своё, пишите, попробую помочь.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.