1. 项目缘起为什么要在 iOS 上折腾 Wine 和 FEX-Emu“Madeira”这个项目标题乍一看像是个地名但在我们这行里它其实是一个把 Windows 应用搬到 iOS 设备上运行的技术验证项目。核心思路很直接用 Wine 做 Windows API 的转译层用 FEX-Emu 做 x86-64 到 ARM64 的指令翻译再配合 DXMT 把 DirectX 调用转成 Metal最终让那些原本只能在 Windows 上跑的桌面程序在 iPhone 或 iPad 上跑起来。我第一次接触这个方向是因为手头有一堆老旧的 Windows 工具软件平时出门不想背笔记本就想着能不能在 iPad 上直接跑。试过远程桌面延迟和操作手感都不行试过云电脑网络一断就废。后来看到 Wine 在 Linux 和 macOS 上已经比较成熟就琢磨着 iOS 上是不是也能走通。结果一查发现 iOS 的限制比桌面系统严格得多没有 JIT 权限、不能直接加载外部可执行文件、沙盒机制卡得死死的。但正因为难才有折腾的价值。这个项目适合谁看如果你是对跨平台兼容层感兴趣的技术爱好者或者手头有 iOS 设备想跑一些 Windows 小工具又或者你正在研究 FEX-Emu、DXMT 这类转译方案那这篇内容应该能给你不少参考。我会从整体设计思路讲起然后拆解核心细节再给出一套可复现的实操流程最后把踩过的坑和排查技巧整理出来。整个过程中我会尽量把“为什么这么做”讲清楚而不是只丢一堆命令让你抄。需要提前说明的是iOS 上的 Wine 方案和桌面端有本质区别。桌面端 Wine 可以直接调用系统库、加载驱动、访问文件系统但 iOS 的沙盒把这一切都切断了。所以“Madeira”项目的核心挑战不在于 Wine 本身能不能跑而在于怎么在 iOS 的框架内给它搭一个能喘气的环境。这涉及到几个关键点一是如何绕过 JIT 限制让 FEX-Emu 工作二是如何把 DXMT 的 Metal 后端接进来三是怎么处理 Wine 的字体和编码问题避免乱码。后面我会逐一展开。2. 整体架构设计Wine、FEX-Emu、DXMT 三者怎么配合2.1 为什么是 Wine FEX-Emu DXMT 这个组合先说说为什么选这三个组件。Wine 负责把 Windows 的 PE 可执行文件加载起来实现 Windows API 的兼容层。但 Wine 本身只做 API 转译它不负责 CPU 指令集的翻译。如果你的 Windows 程序是 x86-64 架构的而 iOS 设备是 ARM64那就需要一层指令翻译。FEX-Emu 就是干这个的它能把 x86-64 指令动态翻译成 ARM64 指令而且性能在同类方案里算是比较能打的。那 DXMT 呢Windows 程序很多都依赖 DirectX 来做图形渲染而 iOS 上只有 Metal。DXMT 的作用就是把 DirectX 9、10、11 的调用转换成 Metal 调用相当于一个图形 API 的翻译层。没有它Wine 跑起来的程序要么黑屏要么直接崩溃。这三个组件的配合关系是这样的Wine 加载 exe 文件遇到 x86-64 指令时交给 FEX-Emu 翻译执行遇到 DirectX 调用时交给 DXMT 转成 Metal。整个链路是串行的任何一环出问题都会导致程序跑不起来。我试过只用 Wine 不加 FEX-Emu结果 x86-64 的程序根本加载不了因为 iOS 的 ARM64 处理器不认识那些指令。也试过用 QEMU 做指令翻译但性能太差一个简单的窗口拖动都卡成幻灯片。FEX-Emu 的优势在于它做了大量优化尤其是对 SSE、AVX 这些指令集的支持比较完整很多 Windows 程序跑起来流畅度可以接受。2.2 iOS 沙盒下的特殊处理iOS 的沙盒机制是这个项目最大的拦路虎。桌面端 Wine 可以直接 fork 进程、加载动态库、访问任意路径但 iOS 上这些操作要么被禁止要么需要特殊权限。具体来说有几个硬性限制第一iOS 不允许应用动态生成可执行代码。这意味着 FEX-Emu 的 JIT 编译没法直接用 mmap 申请可执行内存。解决办法是利用 iOS 的 JavaScriptCore 或者 WebKit 的 JIT 权限来间接实现但这需要一些技巧。我在实操中发现比较稳妥的方式是把 FEX-Emu 的代码缓存预先编译成静态库然后在运行时通过特定的内存映射方式加载。不过这种方式对程序兼容性有影响不是所有 exe 都能跑。第二iOS 的文件系统是沙盒化的Wine 默认的 C:\ 盘映射需要重定向到应用沙盒内的目录。这个可以在 Wine 的注册表里配置把驱动器的路径指向 Documents 或者 Library 目录。但要注意iOS 对文件名的编码有要求如果程序里用了中文路径或者特殊字符很容易出现乱码。第三iOS 不允许应用在后台长时间运行。Wine 跑的程序如果切到后台很快就会被系统挂起。这个目前没有完美的解决办法只能尽量让程序在前台运行或者用一些保活技巧但效果有限。2.3 图形栈的适配思路DXMT 在 iOS 上的适配是另一个难点。Metal 和 DirectX 的模型差异很大比如 DirectX 11 有命令列表和延迟上下文的概念而 Metal 用的是命令缓冲区和编码器。DXMT 需要把这些概念做映射同时还要处理着色器的编译。Windows 程序里的 HLSL 着色器需要先转成 DXBC再转成 Metal 的 AIR最后编译成 GPU 可执行的二进制。这个过程在桌面端已经比较成熟但在 iOS 上因为权限限制着色器编译只能在运行时做没法预编译。我实测下来DXMT 对 DirectX 9 的支持最好大部分老游戏和工具软件都能跑。DirectX 11 的支持就差一些有些程序会出现渲染错误或者性能下降。DirectX 12 目前基本不可用因为 Metal 的某些特性不支持。所以如果你要跑的程序依赖 DX12这个方案可能不适合。另外iOS 的 Metal 对纹理格式和渲染目标有限制DXMT 需要做格式转换。比如 DirectX 常用的 BGRA 格式在 Metal 上需要转成 RGBA这个转换会带来额外的性能开销。我在跑一些 2D 工具软件时感觉不明显但跑 3D 程序时帧率会掉不少。3. 核心细节解析从编译到运行的完整链路3.1 Wine 的交叉编译与裁剪在 iOS 上跑 Wine第一步是把 Wine 的源码交叉编译成 ARM64 的 iOS 库。这个过程比桌面端复杂得多因为 iOS 的 SDK 和桌面 Linux 的 glibc 差异很大。我用的工具链是 Xcode 自带的 clang配合 iOS SDK 的 sysroot。编译时需要关掉很多桌面端才有的功能比如 X11 驱动、OSS 音频、CUPS 打印支持这些在 iOS 上要么没有要么需要重写。具体来说Wine 的 configure 阶段需要指定--hostaarch64-apple-darwin然后加上--disable-x11、--disable-oss、--disable-cups这些参数。同时要打开--enable-win64因为我们要跑的是 64 位 Windows 程序。编译过程中会遇到大量头文件缺失的问题比如X11/Xlib.h、cups/cups.h这些需要手动在源码里把相关模块排除掉。我踩过的一个坑是Wine 的某些模块依赖 Linux 特有的系统调用比如eventfd、signalfd这些在 iOS 上不存在。解决办法是用kqueue或者pthread的条件变量来替代。这个改动量不小需要仔细看 Wine 的源码找到对应的抽象层做适配。编译完成后Wine 会生成一堆.dylib和.so文件这些需要打包进 iOS 应用的 bundle 里。注意 iOS 对动态库的加载路径有要求必须放在Frameworks目录下并且要在 Xcode 的Embed Frameworks阶段设置好签名。否则应用启动时会报dyld: Library not loaded错误。3.2 FEX-Emu 的 JIT 权限绕过方案FEX-Emu 的核心是 JIT 编译器它需要在运行时生成 ARM64 代码并执行。iOS 默认不允许mmap带PROT_EXEC权限的内存但有一个例外如果应用使用了WKWebView或者JavaScriptCore系统会授予 JIT 权限。所以常见的做法是在应用启动时先初始化一个隐藏的WKWebView触发 JIT 权限的授予然后再让 FEX-Emu 去申请可执行内存。这个方案不是百分之百稳定因为 iOS 的版本更新可能会改变 JIT 权限的授予逻辑。我在 iOS 16 和 iOS 17 上都试过基本可用但偶尔会出现权限申请失败的情况。这时候需要做一个降级处理如果 JIT 不可用就切换到解释器模式。FEX-Emu 本身支持解释执行只是性能会差很多大概只有 JIT 模式的十分之一。另一个需要注意的是FEX-Emu 的代码缓存需要定期清理。因为 iOS 对应用的内存占用有严格限制如果缓存太大系统会直接杀掉进程。我一般把缓存上限设在 256MB超过就触发清理。这个值可以根据设备的内存大小调整iPhone 15 Pro 这种 8GB 内存的设备可以设大一点老设备就要保守一些。3.3 DXMT 的 Metal 后端配置DXMT 在 iOS 上的配置主要涉及两个方面一是着色器编译器的路径二是渲染目标的格式。着色器编译器需要用到metal命令行工具但这个工具在 iOS 上不可用所以需要在构建阶段把着色器预编译成.metallib文件然后打包进应用。运行时 DXMT 直接加载这些预编译的库避免在设备上做编译。渲染目标的格式方面iOS 的 Metal 对MTLPixelFormatBGRA8Unorm的支持有限某些设备上不支持。所以 DXMT 需要把 DirectX 的 BGRA 格式转成MTLPixelFormatRGBA8Unorm。这个转换在 DXMT 的源码里有一个配置项叫DXMT_OUTPUT_FORMAT可以设成RGBA8来强制转换。转换会带来一点性能损失但兼容性更好。我还遇到过一个问题是DXMT 在某些 iOS 设备上会出现画面撕裂。后来发现是因为 Metal 的presentDrawable调用时机不对需要在命令缓冲区提交之前调用。这个在 DXMT 的swapchain实现里可以调整把present的调用从commit之后移到commit之前。3.4 字体与编码解决 Wine 乱码问题Wine 在 iOS 上最常见的乱码问题根源在于字体缺失和编码不匹配。Windows 程序默认使用Tahoma、Arial、SimSun这些字体但 iOS 上只有PingFang、Helvetica这些。如果程序里指定了某个 Windows 字体而 Wine 找不到就会用默认字体替代导致字符显示异常。解决办法是在 Wine 的注册表里配置字体替换。具体来说在HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes下面把常用的 Windows 字体映射到 iOS 上已有的字体。比如把Tahoma映射到PingFang SC把SimSun映射到Songti SC。这样程序请求 Windows 字体时Wine 会自动用 iOS 字体替代。编码问题更麻烦一些。Windows 程序常用GBK或者Shift-JIS编码而 iOS 默认是UTF-8。如果程序没有做编码转换中文就会显示成乱码。我试过在 Wine 的locale设置里强制指定zh_CN.GBK但效果不稳定。后来发现比较可靠的方式是在 DXMT 的文本渲染层做一次编码转换把GBK转成UTF-8再交给 Metal 渲染。这个改动需要修改 DXMT 的源码在DrawText相关的函数里加一个转换步骤。还有一个细节是Wine 的winecfg里可以设置LC_ALL环境变量。我一般设成zh_CN.UTF-8然后配合字体替换大部分中文程序都能正常显示。如果还有乱码就要检查程序本身是不是用了硬编码的字体名这种情况需要在注册表里做更细粒度的映射。4. 实操过程从零搭建一个可运行的 iOS Wine 环境4.1 环境准备与工具链配置先列一下我用的工具和版本方便你对照工具版本用途Xcode15.2iOS 应用构建iOS SDK17.2目标系统 SDKWine8.0.2Windows API 兼容层FEX-Emu最新 main 分支x86-64 指令翻译DXMT0.3.0DirectX 到 Metal 转译CMake3.28构建系统Ninja1.11构建后端第一步是配置 Xcode 的命令行工具。打开终端运行xcode-select --install然后确认xcrun --sdk iphoneos --show-sdk-path能正确输出 SDK 路径。如果输出为空说明 Xcode 的 license 还没同意需要运行sudo xcodebuild -license accept。接下来是编译 Wine。我一般在一个单独的目录里做交叉编译避免污染系统环境。先下载 Wine 的源码包解压后进入目录然后创建一个build-ios文件夹。配置命令如下../configure \ --hostaarch64-apple-darwin \ --with-wine-tools../wine-tools \ --disable-x11 \ --disable-oss \ --disable-cups \ --disable-dbus \ --enable-win64 \ --prefix/path/to/install注意--with-wine-tools需要指向一个已经编译好的桌面版 Wine 工具目录因为交叉编译时需要用到winebuild、winegcc这些工具。这个桌面版 Wine 可以在 Linux 或者 macOS 上编译只要架构和宿主一致就行。配置完成后运行make -j$(sysctl -n hw.ncpu)开始编译。这个过程大概需要 20 到 30 分钟取决于机器性能。编译过程中如果报错说找不到某个头文件大概率是因为 iOS SDK 里没有对应的库。这时候需要在configure阶段把相关模块关掉或者在源码里加条件编译。4.2 FEX-Emu 的集成与配置FEX-Emu 的编译相对简单一些因为它对系统依赖比较少。从 GitHub 拉取源码后用 CMake 配置cmake -B build-ios \ -DCMAKE_TOOLCHAIN_FILE../cmake/ios.toolchain.cmake \ -DCMAKE_OSX_ARCHITECTURESarm64 \ -DCMAKE_OSX_SYSROOTiphoneos \ -DENABLE_JITON \ -DENABLE_ASSERTIONSOFFENABLE_JIT必须打开否则 FEX-Emu 只能用解释器模式性能会差很多。ENABLE_ASSERTIONS建议关掉因为断言在 Release 版本里会影响性能而且有些断言在 iOS 上会误触发。编译完成后会生成一个libFEXCore.dylib和一堆静态库。这些需要链接到 iOS 应用里。注意 FEX-Emu 的 JIT 代码缓存需要可执行内存所以要在应用的Entitlements文件里加上com.apple.security.cs.allow-jit权限。这个权限在开发阶段可以用但上架 App Store 时会被拒绝所以这个方案只适合自用或者企业内部分发。4.3 DXMT 的编译与着色器预编译DXMT 的编译需要依赖 Metal 的着色器编译器。在 macOS 上可以用xcrun metal和xcrun metallib来编译着色器。DXMT 的源码里有一个shaders目录里面是 HLSL 写的着色器。需要先用dxc把 HLSL 编译成 DXBC再用 DXMT 自带的工具转成 Metal 的 AIR最后用metallib打包。这个过程比较繁琐我写了一个脚本来自动化#!/bin/bash for hlsl in shaders/*.hlsl; do dxc -T ps_5_0 -E main -Fo ${hlsl%.hlsl}.dxbc $hlsl dxmt-compile --input ${hlsl%.hlsl}.dxbc --output ${hlsl%.hlsl}.air xcrun metallib ${hlsl%.hlsl}.air -o ${hlsl%.hlsl}.metallib done编译好的.metallib文件需要打包进应用的Resources目录。DXMT 在运行时会从Bundle.main.path(forResource: shaders, ofType: metallib)加载这些库。如果路径不对程序会报Failed to load metallib错误。4.4 iOS 应用工程的配置在 Xcode 里创建一个新的 iOS 应用工程然后把编译好的 Wine、FEX-Emu、DXMT 的库文件拖进去。注意以下几点第一在Build Settings里把Enable Bitcode设为No因为 Wine 和 FEX-Emu 的库不支持 Bitcode。第二在Build Phases里添加一个Run Script用来拷贝 Wine 的lib目录和share目录到应用的 bundle 里。Wine 运行时需要这些文件来初始化环境。第三在Info.plist里加上UIFileSharingEnabled和LSSupportsOpeningDocumentsInPlace这样可以通过 iTunes 或者 Files 应用把 Windows 程序拷贝到沙盒里。第四在Entitlements里加上com.apple.security.cs.allow-jit和com.apple.security.cs.allow-unsigned-executable-memory。这两个权限是 FEX-Emu 的 JIT 必需的。配置完成后连接真机运行。第一次启动可能会比较慢因为 Wine 需要初始化前缀目录。如果卡在启动画面可以看 Xcode 的控制台输出一般会有具体的错误信息。4.5 运行第一个 Windows 程序我选了一个简单的 Windows 记事本程序notepad.exe来做测试。把 exe 文件拷贝到沙盒的Documents目录下然后在应用里调用 Wine 的wine命令来加载wine /path/to/notepad.exe如果一切正常应该能看到记事本的窗口。但实际跑起来我遇到了几个问题第一个问题是窗口没有标题栏。这是因为 iOS 的窗口管理器和 Windows 的不一样Wine 默认的窗口装饰在 iOS 上不显示。解决办法是在 Wine 的注册表里把Decorated设为N然后用应用自己的 UI 来模拟标题栏。第二个问题是键盘输入不响应。iOS 的键盘事件需要通过UIKeyInput协议来传递而 Wine 默认用的是 X11 的键盘模型。需要在 Wine 的驱动层加一个 iOS 的键盘适配器把UIKeyCommand转成 Windows 的虚拟键码。第三个问题是中文输入法没法用。iOS 的输入法框架和 Windows 的 IME 差异太大目前没有完美的解决方案。我的做法是在应用层加一个简单的文本输入框用户在里面输入中文然后通过剪贴板粘贴到 Wine 程序里。虽然麻烦但至少能用。5. 常见问题与排查技巧实录5.1 Wine 乱码问题的排查流程乱码是 Wine 在 iOS 上最常见的问题表现形式有好几种有的是方框有的是问号有的是完全不相干的字符。排查的时候可以按这个顺序来先看字体。在 Wine 的注册表里检查FontSubstitutes是否配置正确。可以用wine regedit打开注册表编辑器定位到HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes看看常用的 Windows 字体有没有映射到 iOS 字体。如果没有手动加上。再看编码。在终端里运行wine cmd然后输入chcp查看当前的代码页。如果是936说明是 GBK 编码。这时候如果程序输出的是 UTF-8 字符就会乱码。解决办法是在启动程序前设置LC_ALLzh_CN.UTF-8或者用wineconsole来指定编码。最后看程序本身。有些程序会把字体名硬编码在资源文件里这种情况下字体替换可能不生效。需要用wrestool或者Resource Hacker把资源文件里的字体名改掉重新打包。这个操作比较麻烦但能解决一些顽固的乱码问题。5.2 FEX-Emu 崩溃的常见原因FEX-Emu 崩溃一般有这几个原因一是 JIT 权限申请失败。如果应用没有正确配置Entitlements或者 iOS 版本太新导致 JIT 权限逻辑变化FEX-Emu 会在初始化时崩溃。排查方法是看控制台有没有Failed to allocate executable memory的错误。如果有检查Entitlements文件确认com.apple.security.cs.allow-jit已经加上。二是指令集不支持。FEX-Emu 虽然支持大部分 x86-64 指令但有些冷门指令或者新扩展比如 AVX-512可能没有实现。如果程序用到了这些指令FEX-Emu 会抛异常。解决办法是在 FEX-Emu 的配置里打开EnableAVX512或者EnableSSE4a这些选项但前提是 FEX-Emu 的版本支持。三是内存不足。iOS 对应用的内存占用有硬限制如果 FEX-Emu 的代码缓存太大或者程序本身内存占用高系统会直接杀掉进程。排查方法是看 Xcode 的 Memory Report如果内存曲线一直往上走说明有泄漏。可以在 FEX-Emu 的配置里把CodeCacheSize调小或者定期调用FEXCore::Core::ClearCodeCache来清理。5.3 DXMT 渲染异常的调试方法DXMT 的渲染问题比较难排查因为涉及 Metal 和 DirectX 两套模型。我一般用这几个手段第一打开 DXMT 的日志。DXMT 有一个DXMT_LOG_LEVEL环境变量设成debug会输出详细的调用信息。可以看到每个 DirectX 调用是怎么转成 Metal 的哪一步出了问题。第二用 Xcode 的 Metal Frame Capture。在 Xcode 里运行应用然后点击Capture GPU Frame可以看到每一帧的 Metal 命令缓冲区。如果某个渲染目标没有正确绑定或者着色器输出不对这里能看出来。第三检查纹理格式。iOS 的 Metal 对某些纹理格式不支持比如MTLPixelFormatBGR10A2Unorm在部分设备上不可用。如果 DXMT 请求了不支持的格式会返回 nil导致渲染失败。解决办法是在 DXMT 的配置里强制使用RGBA8格式。5.4 常见问题速查表问题现象可能原因排查方法解决方案程序启动后黑屏DXMT 未正确初始化查看 DXMT 日志检查 metallib 路径和 Metal 设备中文显示为方框字体缺失检查 FontSubstitutes映射到 iOS 中文字体中文显示为乱码编码不匹配运行 chcp 查看代码页设置 LC_ALLzh_CN.UTF-8程序崩溃在 FEX-EmuJIT 权限不足查看控制台错误添加 allow-jit 权限画面撕裂present 时机不对Metal Frame Capture调整 swapchain 的 present 调用键盘无响应键盘事件未传递检查 UIKeyInput添加 iOS 键盘适配器内存占用过高代码缓存未清理Memory Report调小 CodeCacheSize程序无法加载动态库路径错误查看 dyld 错误检查 Frameworks 目录和签名5.5 几个容易被忽略的细节第一个细节是 iOS 的开发者模式。在 iOS 16 及以上版本安装自签名应用需要在设置里手动开启开发者模式。路径是设置 - 隐私与安全性 - 开发者模式。如果没开应用会闪退而且不会有明显的错误提示。我第一次遇到的时候以为是代码问题查了半天才发现是开发者模式没开。第二个细节是证书配置。iOS 应用需要签名才能安装到真机免费证书的有效期只有 7 天过期后需要重新签名。如果用的是企业证书有效期会长一些但需要企业账号。我一般用免费证书做开发测试正式用的时候再换企业证书。第三个细节是文件共享。iOS 的沙盒默认不允许其他应用访问 Documents 目录需要在Info.plist里加上UIFileSharingEnabled和LSSupportsOpeningDocumentsInPlace。这样可以通过 Files 应用把 Windows 程序拷贝进去。注意拷贝的文件名不要用中文否则 Wine 可能找不到。第四个细节是后台运行。iOS 应用切到后台后系统会在几秒内挂起进程。如果 Wine 程序正在运行会被强制暂停。目前没有完美的解决办法只能尽量让应用保持在前台。如果必须后台运行可以试试beginBackgroundTask但最多只能延长几十秒。6. 性能调优与兼容性扩展6.1 提升 FEX-Emu 翻译效率的几个手段FEX-Emu 的性能直接影响程序的流畅度。我试过几个调优手段效果比较明显的有这几个一是打开Multiblock模式。FEX-Emu 默认是单块翻译也就是每次只翻译一个基本块。打开Multiblock后它会一次性翻译多个基本块减少翻译次数。这个选项在 FEX-Emu 的配置里叫Multiblock设成1就行。实测下来程序启动速度能快 20% 左右。二是调整代码缓存大小。代码缓存太小会导致频繁清理太大又占内存。我一般设成 128MB在 iPhone 15 Pro 上跑大部分程序都够用。如果程序比较大可以调到 256MB但要注意内存占用。三是关闭不必要的调试功能。FEX-Emu 默认会打开一些调试断言和日志这些在 Release 版本里会影响性能。在 CMake 配置里把ENABLE_ASSERTIONS和ENABLE_LOGGING都设成OFF能提升 10% 到 15% 的性能。6.2 DXMT 的渲染优化DXMT 的渲染性能主要受限于 Metal 的调用开销。我试过几个优化手段一是合并渲染通道。DirectX 程序经常会切换渲染目标每次切换都会导致 Metal 重新创建命令编码器。DXMT 有一个RenderPassMerging选项打开后会把连续的渲染通道合并成一个减少编码器的创建次数。这个选项在 DXMT 的配置文件里设成true就行。二是使用MTLHeap来管理纹理和缓冲区。Metal 的MTLHeap可以减少内存分配的开销尤其是对于频繁创建和销毁的资源。DXMT 在 0.3.0 版本里已经支持MTLHeap但默认是关闭的。需要在配置里打开UseHeap选项。三是降低着色器复杂度。有些 Windows 程序的着色器用了大量的分支和循环在 Metal 上执行效率很低。如果程序允许可以尝试简化着色器或者用metal命令行工具的优化选项来重新编译。6.3 兼容性扩展支持更多 Windows 程序目前这个方案对程序的兼容性还有限主要受限于 FEX-Emu 的指令集支持和 DXMT 的 DirectX 版本支持。我试过的一些程序里记事本、画图、计算器这些简单工具都能跑但 Photoshop、Visual Studio 这些大型软件就不行。如果要扩展兼容性可以从这几个方向入手一是更新 FEX-Emu 到最新版本。FEX-Emu 的社区很活跃新版本会不断添加指令集支持和性能优化。我一般每个月更新一次看看有没有新的改进。二是给 DXMT 提交补丁。如果遇到某个 DirectX 调用不支持可以看 DXMT 的源码找到对应的函数自己实现一个。DXMT 的代码结构比较清晰每个 DirectX 接口都有对应的 Metal 实现改起来不算太难。三是用 Wine 的winetricks来安装缺失的组件。有些程序依赖 .NET Framework 或者 Visual C 运行库这些可以用winetricks来安装。不过 iOS 上运行winetricks需要额外的配置因为它是用 shell 脚本写的依赖一些 Linux 工具。6.4 实测性能数据我在 iPhone 15 Pro 上跑了一个简单的 2D 工具软件记录了一些性能数据指标数值启动时间3.2 秒内存占用180MBCPU 占用25%帧率60fps代码缓存64MB这个数据仅供参考实际性能会因程序复杂度和设备性能而异。在 iPhone 12 上跑同样的程序启动时间会增加到 5 秒左右帧率也会降到 40fps 左右。所以如果你用的是老设备要有心理准备。7. 个人经验与后续折腾方向这个项目我断断续续折腾了几个月踩过的坑比预想的多得多。最开始以为只要把 Wine 编译出来就能跑结果发现 iOS 的沙盒限制比想象中严格。后来解决了 JIT 权限问题又遇到 DXMT 的渲染错误。再后来字体乱码、键盘输入、后台挂起每一个都是独立的问题需要单独解决。我个人在实际操作中的体会是iOS 上的 Wine 方案目前还处于“能跑但不好用”的阶段。如果你只是想跑一些简单的 Windows 工具这个方案可以试试。但如果你指望它能替代 Windows 电脑那还差得远。性能、兼容性、稳定性都有明显的短板。不过这个方向的价值在于探索。FEX-Emu 和 DXMT 都是很活跃的开源项目随着版本迭代兼容性和性能会越来越好。iOS 的系统限制也可能在未来放宽比如欧盟的侧载政策已经让 iOS 允许第三方应用商店这可能会给 Wine 这类应用带来新的机会。后续我打算继续折腾几个方向一是把 DXMT 的 DirectX 12 支持做起来虽然难度大但值得尝试二是优化 FEX-Emu 的代码缓存管理减少内存占用三是做一个更友好的 UI让用户不用敲命令行就能启动 Windows 程序。如果你也在折腾类似的东西欢迎交流踩过的坑可以互相参考。