---
name: winkod-wine-fix
description: >-
  Диагностика и починка падений Windows-программ (.exe/.msi) под bundled-wine
  проекта Винкод (Winkod, бывш. CebersRun) на macOS Apple Silicon. Брать ВСЕГДА,
  когда программа в бутыли Винкод не стартует, падает на запуске или ругается:
  «Could not load file or assembly», «Wine Mono is not installed»,
  «FileLoadException: manifest does not match», «MSVCR*/MSVCP*/vcruntime*.dll not
  found», «Failed to create Direct3D device», тофу-квадраты вместо текста,
  чёрное окно, мгновенный выход без сообщения, отсутствующая COM/ActiveX
  компонента, не проходит MSI-инсталлятор. Также когда просят «почему не
  запускается exe в Винкоде», «почини WinOLS/PCMflash/ECM под wine», «собери
  лог запуска бутыли». НЕ для: прошивок ЭБУ (.bin → ecu-firmware), PDF
  (pdf-visual-qa), анти-чит/ядерный DRM (нерешаемо под wine), USB-донглов
  vendor-class (Guardant Code — отдельная проблема, wine видит только HID).
metadata:
  type: reference
---

# winkod-wine-fix — починка Windows-программ под bundled-wine Винкод (macOS ARM)

Это macOS-адаптация открытого скилла `wine-exe-fix`
(github.com/bigdata2211it-web/ai-wine-skills, MIT). Оригинал — про Linux
(apt, `dpkg --add-architecture i386`, `.desktop`, отдельный `WINEARCH=win32`
prefix, dxvk/Vulkan). Здесь та же **диагностическая методология**, но под наш
стек: **macOS Apple Silicon + bundled CrossOver wine 11 (wow64, PE-only)**.

## Наш стек — чем отличается от Linux-оригинала

| Аспект | Винкод (наш) |
|---|---|
| wine | CrossOver 11 wow64, x86_64 под Rosetta, bundled в `.app` |
| loader | `/Applications/Винкод.app/Contents/Resources/wine/bin/wineloader` (Mach-O, НЕ Perl-wrapper `bin/wine`) |
| 32-бит prefix | **НЕ существует** — wow64 PE-only. Отдельного `WINEARCH=win32` нет; 32-битные PE крутятся в том же wow64-prefix |
| бутыли | `~/Library/Application Support/Winkod/bottles/<uuid>/` (внутри `drive_c`, `dosdevices`, `system.reg`, `user.reg`) |
| графика | DXMT (D3D11→Metal) / MoltenVK — **не dxvk/Vulkan** |
| `.desktop` | не применимо (macOS); запуск через UI Винкод или bundle-wrapper `.app` |
| шрифты | bundled `share/wine/fonts` + macOS fontconfig; не `apt fonts-*` |

### Обязательное окружение для ручного вызова wineloader
```bash
WINE="/Applications/Винкод.app/Contents/Resources/wine"
PREFIX="$HOME/Library/Application Support/Winkod/bottles/<uuid>"
env -i HOME="$HOME" PATH="$WINE/bin:/usr/bin:/bin" \
  WINEPREFIX="$PREFIX" WINEDEBUG=-all \
  WINEDLLPATH="$WINE/lib/wine" \
  DYLD_FALLBACK_LIBRARY_PATH="$WINE/lib64:$WINE/lib:/usr/lib" \
  "$WINE/bin/wineloader" "C:\\path\\to\\App.exe"
```
`DYLD_FALLBACK_LIBRARY_PATH` с `lib64` обязателен — иначе wine не найдёт
libfreetype/libgnutls/libMoltenVK и т.п. (CrossOver кладёт x86_64-dylib в `lib64/`).
`WINEDEBUG`: `-all` тихо, `warn` показать err/warn, `+module` — что не грузится,
`+reg` — какие ключи реестра щупает.

## Триаж — по порядку, останавливаться на первом, что помогло

`tools/winkod_wine_diag.sh <uuid> [exe-path]` — собирает шаги 1–3 автоматически
(архитектура PE, наличие .NET/VC-рантаймов в prefix, чистый прогон с логом).

### Шаг 1 — разрядность бинарника
```bash
file "/path/App.exe"
```
`PE32 ... Intel i386` = 32-бит, `PE32+ ... x86-64` = 64-бит,
`... Mono/.Net assembly` = .NET (нужен рантайм, шаг 3). Под нашим wow64 и 32-,
и 64-битные PE идут в **один и тот же** prefix — отдельный prefix не нужен.

### Шаг 2 — winetricks в наш prefix
У нас НЕТ системного winetricks по умолчанию. Ставится `brew install winetricks`.
Вызывать с нашим WINE в PATH и нашим WINEPREFIX:
```bash
PATH="$WINE/bin:$PATH" WINEPREFIX="$PREFIX" winetricks --unattended <verb>
```
Гоча: `winetricks dotnet472` иногда зависает в `remove_mono`-цикле — убивать по
PID (`pgrep -af winetricks`; `kill -9`), НЕ `pkill -f winetricks` (матчит себя).

### Шаг 3 — .NET рантайм
Симптом: `err:mscoree:...Wine Mono is not installed` или
`FileNotFoundException: Could not load ... .NET`.
```bash
PATH="$WINE/bin:$PATH" WINEPREFIX="$PREFIX" winetricks --unattended dotnet40
```
`dotnet40` хватает большинству 4.x; эскалировать `dotnet48` только если нужен
4.7.2-only API. Ставить по возрастанию, не перескакивать.

### Шаг 4 — «Could not load assembly» = отсутствует NuGet-DLL (НЕ баг wine)
Сборка приехала без зависимостей рядом с `.exe`. Достать DLL из nuget.org.

**Ключевое правило: версия NuGet-пакета ≠ версия сборки (assembly version).**
В ошибке — **assembly version**; у пакета своя **package version**.
Один `.nupkg` содержит разные DLL под target-framework (`lib/net40/`,
`lib/net45/`, `lib/netstandard2.0/`) с РАЗНЫМИ assembly version.

```bash
curl -sL "https://api.nuget.org/v3-flatcontainer/<pkg-lowercase>/index.json"   # версии
curl -sLo p.nupkg "https://api.nuget.org/v3-flatcontainer/<pkg>/<ver>/<pkg>.<ver>.nupkg"
unzip -o p.nupkg -d pe/
monodis --assembly pe/lib/net40/<Name>.dll | grep -E 'Name:|Version:'         # сверить
cp pe/lib/net40/<Name>.dll "$PREFIX/drive_c/.../app-folder/"
```
`monodis` из `brew install mono` (или слабее: `strings <dll> | grep -E '^[0-9]+\.'`).
Реальный пример: ошибка просит `Newtonsoft.Json 13.0.0.0` → нужен **net40**-вариант
(`13.0.0.0`); net45 даёт `13.0.3.27908` → `FileLoadException: manifest does not match`.
Повторять по одной DLL — следующая всплывает после починки предыдущей.

### Шаг 5 — `FileLoadException: manifest does not match (0x80131040)`
Вариант A (предпочтительно): заменить DLL на ту, чья assembly version точно
совпадает (другой `lib/netXX/` или другая версия пакета).
Вариант B: binding redirect в `<App>.exe.config` (`publicKeyToken` как в ошибке,
`newVersion` = реальная версия положенной DLL):
```xml
<runtime><assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
  <dependentAssembly>
    <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
    <bindingRedirect oldVersion="0.0.0.0-13.0.0.0" newVersion="13.0.0.0"/>
  </dependentAssembly>
</assemblyBinding></runtime>
```

### Шаг 6 — Visual C++ рантайм
`err:module:import_dll Library MSVCR120.dll not found` и т.п. Ставить ТОЛЬКО ту
версию, что в ошибке (все сразу → конфликт MSVCRT):
| DLL | verb |
|---|---|
| MSVCR80/P80 | vcrun2005 |
| MSVCR90/P90 | vcrun2008 |
| MSVCR100/P100 | vcrun2010 |
| MSVCR110/P110 | vcrun2012 |
| MSVCR120/P120 | vcrun2013 |
| MSVCR140/P140/vcruntime140 | vcrun2015 (= 2017/19/22) |
| MSVCP140_1/_2 | vcrun2019 |
Предпочитать bundled-редист программы (`vcredist_x86.exe` в её папке) перед winetricks.

### Шаг 7 — DirectX / графика (отличие от Linux!)
Симптомы: `d3dx9_*.dll`/`d3dcompiler_47.dll` not found, «Failed to create Direct3D
device», чёрное окно. У нас **DXMT/MoltenVK вместо dxvk** — Vulkan через MoltenVK.
```bash
PATH="$WINE/bin:$PATH" WINEPREFIX="$PREFIX" winetricks --unattended \
  d3dx9 d3dcompiler_47 d3dcompiler_43 dxdiag
```
В UI Винкод можно переключить «графический движок» бутыли (auto/DXMT/программный).
Если HW-рендер крашит — переключить на программный и сверить.

### Шаг 8 — шрифты (тофу/съехавшая вёрстка)
```bash
PATH="$WINE/bin:$PATH" WINEPREFIX="$PREFIX" winetricks --unattended corefonts
```
Кириллица/CJK: `cjkfonts`. Если приложение хардкодит шрифт (Segoe UI) —
поставить `.ttf` в `~/Library/Fonts/`, wine подхватит через fontconfig.

### Шаг 9 — MSI / инсталляторы
```bash
WINEPREFIX="$PREFIX" "$WINE/bin/wineloader" msiexec /i "C:\\setup.msi"   # /qn = тихо
```
InstallShield с «ISScript.msi» → сначала `winetricks isscript`. Если инсталлятору
нужна Windows 10 — `winecfg` → версия Windows.

### Шаг 10 — COM / ActiveX
`err:ole:CoGetClassObject no class object` →
`WINEPREFIX="$PREFIX" "$WINE/bin/wineloader" regsvr32 "C:\\comp.dll"`.
Если CLSID нет ни в одной DLL — компонент реально отсутствует (нужен редист вендора).

### Шаг 11 — реестровые ключи, которые приложение щупает
```bash
WINEDEBUG=+reg ... wineloader "App.exe" 2>&1 | grep -iE 'RegOpenKey|RegQueryValue' | head -30
```
Ключи с `STATUS_OBJECT_NAME_NOT_FOUND` — посадить через
`wineloader reg add "HKLM\\Software\\..." /v X /t REG_SZ /d Y /f`.
Бэкап: `cp "$PREFIX/system.reg" "$PREFIX/system.reg.bak"`.

## Чего НЕ чинить под wine (останавливаться, сообщать)
- Анти-чит / ядерный DRM (EAC, BattlEye, Denuvo) — только Windows/VM.
- Copy-protect инсталляторы (SecuROM/StarForce) — нужен no-CD или Windows.
- 16-битные инсталляторы — wow64 не тянет 16-бит.
- **USB-донглы vendor-class (Guardant Code, idVendor 0x0a89)** — wine на macOS
  enumerates через IOHIDManager только HID; vendor-class (0xFF) не виден. Решение
  не в wine: Guardant Net по LAN или эмуляция. См. memory.

## Ключевые уроки
1. loader = `bin/wineloader`, НЕ `bin/wine` (тот — Perl-wrapper CrossOver, игнорит наш WINEPREFIX).
2. `DYLD_FALLBACK_LIBRARY_PATH` должен включать `lib64` — иначе нет freetype/gnutls/MoltenVK.
3. «Could not load assembly» ≠ баг wine — почти всегда отсутствует NuGet-DLL рядом с .exe.
4. package version ≠ assembly version; сверять `monodis`.
5. Один пакет = много `lib/netXX/` DLL с разными версиями — брать под `<supportedRuntime>`.
6. VC++ ставить ровно по ошибке, не пачкой.
7. У нас НЕТ win32-prefix (wow64) и НЕТ dxvk (DXMT/MoltenVK).
8. `WINEDEBUG=+module` — что не грузится; `+reg` — что щупает в реестре.
9. Хранить «правило не светить wine юзеру» (BrandingSweepTests) — в UI это «движок Винкод».

## Persistence
Активен при любой диагностике запуска Windows-программы в бутыли Винкод. Без тумблера.
