背景
wine的安装并不是这篇文章的主题,所以这里我们借用docker-wine来构建运行环境,避免啰哩啰嗦的事情
这个镜像包括了多种运行方式,我根据需要以daemon进行持久化运行
# tty是为了保持运行不退出
# 其他参数是为了通过x11来进行显示,以及软件目录
docker run -d \
--name wine \
--tty=true \
--hostname="$(hostname)" \
--env="DISPLAY" \
--volume="${XAUTHORITY:-${HOME}/.Xauthority}:/root/.Xauthority:ro" \
--volume="/tmp/.X11-unix:/tmp/.X11-unix:ro" \
-v /media/minecraft/Data/software/HALo/:/root/HALo \
scottyhardy/docker-wine \
bash
# 进入到容器内部进行操作
docker exec -it wine bash
程序排错过程
首先配置一下wine
初始化运行wine需要进行一些初步的配置
# 这个参数代表了wine内部系统的根目录,更换之后相当于重新装了个新的wine的环境,默认是这个
export WINEPREFIX=$HOME/.wine
# 64位还是32位,默认64
export WINEARCH=win64
# 配置wine的版本,建议设置
winecfg -v win10
排错开始,一些基本信息
# 我们直接运行程序,并根据程序的报错来确定问题所在
wine xxxx.exe
在我的运行过程中发现的是如下的log,其中会出现一大堆fixme相关的log,这个实际上是wine对于windows的日志系统的操作,这里大部分并没有进行实现(毕竟貌似没啥必要),所以会提示一些日志的方法没有实现,多数情况下可以忽略掉
wine: created the configuration directory '/root/.wine'
002c:fixme:actctx:parse_depend_manifests Could not find dependent assembly L"Microsoft.Windows.Common-Controls" (6.0.0.0)
... # 此处省路一大堆fixme
0078:fixme:nsi:ipv6_forward_enumerate_all not implemented
0078:fixme:nsi:ipv6_forward_enumerate_all not implemented
0078:fixme:nsi:ipv6_forward_enumerate_all not implemented
0078:fixme:nsi:ipv6_forward_enumerate_all not implemented
[ERROR] FATAL UNHANDLED EXCEPTION: System.Runtime.InteropServices.COMException (0x80004005)
at System.Runtime.InteropServices.Marshal.ThrowExceptionForHR (System.Int32 errorCode) [0x0000a] in <e70d6e9587d64cb3abb4b3f99bbf5a0d>:0
at (wrapper cominterop) IWshRuntimeLibrary.IWshShortcut.set_IconLocation(string)
at (wrapper cominterop-invoke) IWshRuntimeLibrary.IWshShortcut.set_IconLocation(string)
at Mediinfo.WinForm.HIS.Main.Program.CreateShortcutOnDesktop () [0x00441] in <792f8817992a498aac36d9ea5e2f5fbc>
WINEDEBUG变量
这里开始引入我们第一个非常重要的环境变量WINEDEBUG
,该变量可以对wine的输出内容进行调整,例如这里我们不想显示fixme相关的内容,就可以设置一下,具体更详细的内容可以参考man wine
中这一部分
export WINEDEBUG=fixme-all
wine xxxx.exe
# 或者这个方法临时进行设置环境变量
WINEDEBUG=fixme-all wine xxxx.exe
常用的几个参数是:
- +relay,会把每一个函数的调用打印出来,会产生巨大的log同时极大的减慢程序的运行速度
- +module,通常建议设置这个,用来寻找dll的问题很方便
# 根据以上建议,我们在调试的时候建议
export WINEDEBUG=fixme-all,+module
# 平时运行建议
export WINEDEBUG=fixme-all
缺失的函数dll
好了,现在回到我们的出问题的部分,可以看到log最后面是打印出来了
[ERROR] FATAL UNHANDLED EXCEPTION: System.Runtime.InteropServices.COMException (0x80004005)
at System.Runtime.InteropServices.Marshal.ThrowExceptionForHR (System.Int32 errorCode) [0x0000a] in <e70d6e9587d64cb3abb4b3f99bbf5a0d>:0
at (wrapper cominterop) IWshRuntimeLibrary.IWshShortcut.set_IconLocation(string)
at (wrapper cominterop-invoke) IWshRuntimeLibrary.IWshShortcut.set_IconLocation(string)
at Mediinfo.WinForm.HIS.Main.Program.CreateShortcutOnDesktop () [0x00441] in <792f8817992a498aac36d9ea5e2f5fbc>
除开我们的程序本身自己的函数来讲,可以看到涉及到系统的函数是IWshRuntimeLibrary
,网上进行寻找发现是属于wshom.ocx
这个dll(?)的,当然另一种更靠谱的方法是github的镜像wine-mirror处进行寻找,函数具体实现可能和原dll中函数名称不一致,查找的时候要注意。
举例我这个函数是这个路径下找到的,可以看到具体的实现,这里提示是实现了程序所需要的函数,但是实际是运行不了的,不在这里继续探究了
接下来我们就要想办法解决这个缺失的dll了
用win自身的dll来替换
wine实现了大量的dll,但仍然有很多函数没有被实现,这个时候就可以考虑用非free的方式来让程序跑起来
通过winetricks
这个工具可以很好的来进行安装各种非free的依赖
既然我们已经知道这个函数是属于wshom.ocx
的,同时wsh是Windows Script Host
,那我们就进行搜索和安装
winetricks dlls list | grep "wsh \| Script \| Host"
#msscript MS Windows Script Control (Microsoft, 2004) [downloadable]
#wsh57 MS Windows Script Host 5.7 (Microsoft, 2007) [downloadable]
winetricks wsh57
# 加上-q安安静静的安装,别烦我
winetricks -q wsh57
这里我们要告诉wine,用winetricks
的dll来替换wine自己的dll,方式有两种:
- 通过
winecfg
的库tab,输入wshom.ocx
来进行添加设置native(windows)
优先 - 通过命令的方式设置,本文章的方法
export WINEDLLOVERRIDES="wshom.ocx=n,b;这里继续写其他要替换的dll"
# 有些dll需要注册一下才行
regsvr32 $WINEPREFIX/drive_c/windows/system32/wshom.ocx
更换32位
再次运行发现如果+module
的话log太多,保存到文件里去寻找
export WINEDEBUG=fixme-all,+module
wine xxxx.exe &> dbg.log
接下来的报错很复杂,最终我将其归结于64位的问题(毕竟System.Int32
),之后整体切换到32位
[ERROR] FATAL UNHANDLED EXCEPTION: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.Runtime.InteropServices.COMException
at System.Runtime.InteropServices.Marshal.ThrowExceptionForHR (System.Int32 errorCode) [0x0000a] in <e70d6e9587d64cb3abb4b3f99bbf5a0d>:0
...
at Mediinfo.WinForm.HIS.Main.Program.CreateShortcutOnDesktop () [0x002ea] in <792f8817992a498aac36d9ea5e2f5fbc>:0
切换32位很简单,只需要重新设置两个环境变量即可,之后就进入一个新的环境了,之后前面该做的事情再重复一遍即可
export WINEPREFIX=/root/.wine32
export WINEARCH=win32
winetricks -q wsh57
export WINEDLLOVERRIDES="wshom.ocx=n;"
regsvr32 $WINEPREFIX/drive_c/windows/system32/wshom.ocx
中文问题
继续运行发现log中有?
在,确认发现是中文问题,这个docker-wine
并没有考虑中文问题,所以进行一下设置
[ERROR] FATAL UNHANDLED EXCEPTION: System.Runtime.InteropServices.COMException (0x8007007B): Unable to save shortcut "C:\users\root\Desktop\??HALO.lnk".
at System.Runtime.InteropServices.Marshal.ThrowExceptionForHR (System.Int32 errorCode) [0x0000a] in <e70d6e9587d64cb3abb4b3f99bbf5a0d>:0
at (wrapper cominterop) IWshRuntimeLibrary.IWshShortcut.Save()
at (wrapper cominterop-invoke) IWshRuntimeLibrary.IWshShortcut.Save()
at Mediinfo.WinForm.HIS.Main.Program.CreateShortcutOnDesktop () [0x00454] in <792f8817992a498aac36d9ea5e2f5fbc>:
echo "zh_CN GB2312
zh_CN.GB18030 GB18030
zh_CN.GBK GBK
zh_CN.UTF-8 UTF-8" >> /etc/locale.gen
locale-gen
# 设置语言和编码,必须设置
export LC_ALL=zh_CN.utf8
.NET(dotnet)
这里的问题我触发出来一次窗口提示,找不到怎么再次弄出来了,但总之是有线索可寻的
[ERROR] FATAL UNHANDLED EXCEPTION: System.TypeInitializationException: The type initializer for 'DevExpress.Utils.Text.FontGuard' threw an exception. ---> System.NullReferenceException: Object reference not set to an instance of an object
at System.Reflection.Emit.ILGenerator.Emit (System.Reflection.Emit.OpCode opcode, System.Reflection.FieldInfo field) [0x0001c] in <e70d6e9587d64cb3abb4b3f99bbf5a0d>:0
at DevExpress.Utils.Text.FontGuard.EmitNativeFontAccessor () [0x00066] in <6dabd2d242dd455a9176a4be43804016>:0
at DevExpress.Utils.Text.FontGuard..cctor () [0x00000] in <6dabd2d242dd455a9176a4be43804016>:0
...
at (wrapper remoting-invoke-with-check) Mediinfo.WinForm.HIS.Main.DengLu..ctor()
at Mediinfo.WinForm.HIS.Main.Program.FormLoginFunc (System.String[] args, System.Boolean switchSystem, System.Boolean againSystem, System.Boolean PICException) [0x00001] in <792f8817992a498aac36d9ea5e2f5fbc>
安装就很简单啦
winetricks -q dotnet40
字体问题
程序启动之后是方块乱码,字体问题有两种方式:
winetricks
可以进行解决,具体可以参考winetricks fonts list
- 直接拷贝windows下
C:/Windows/Fonts/
到$WINEPREFIX/drive_c/windows/
,我用的是这种比较暴力的方式
.NET4.0的bug
这里的报错是.NET4.0自身的一个错误,需要安装kb2468871
补丁
System.TypeInitializationException: The type initializer for 'Mediinfo.ServiceProxy.Core.TokenLocator' threw an exception. ---> System.IO.FileLoadException: Could not load file or assembly 'System.Core, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, Retargetable=Yes' or one of its dependencies. The given assembly name or codebase was invalid. (Exception from HRESULT: 0x80131047)
at Autofac.Builder.RegistrationData..ctor(Service defaultService)
at Autofac.Builder.RegistrationBuilder`3..ctor(Service defaultService, TActivatorData activatorData, TRegistrationStyle style)
at Autofac.Builder.RegistrationBuilder.ForType(Type implementationType)
at Autofac.RegistrationExtensions.RegisterType(ContainerBuilder builder, Type implementationType)
at Mediinfo.ServiceProxy.Core.TokenLocator..ctor() in C:\images\workspace\git4-pipeline-halo-jcjg\04.ServiceProxy\Mediinfo.ServiceProxy.Core\TokenLocator.cs:line 64
at Mediinfo.ServiceProxy.Core.TokenLocator..cctor() in C:\images\workspace\git4-pipeline-halo-jcjg\04.ServiceProxy\Mediinfo.ServiceProxy.Core\TokenLocator.cs:line 23
--- End of inner exception stack trace ---
at Mediinfo.ServiceProxy.Core.TokenLocator.get_Instance()
at Mediinfo.ServiceProxy.Core.ServiceClient.Invoke[T](String controller, String method, ServiceParm[] parms) in C:\images\workspace\git4-pipeline-halo-jcjg\04.ServiceProxy\Mediinfo.ServiceProxy.Core\ServiceClient.cs:line 320
很简单
winetricks -q dotnet40_kb2468871
wine自身的问题
运行后的程序有个问题,画面会进入一个卡死的状态,但实际程序是正常运行的(通过任务切换界面的任务缩略图),此时是能够进行点击交互的。
这个问题是wine的窗口管理问题导致的,可以通过开启虚拟桌面来避开,一般有两种方法:
- winecfg界面在显示tab中开启虚拟桌面
wine explorer /desktop=name,1800x900 xxxx.exe
命令的方法
该程序的总结
echo "zh_CN GB2312
zh_CN.GB18030 GB18030
zh_CN.GBK GBK
zh_CN.UTF-8 UTF-8" >> /etc/locale.gen
locale-gen
export LC_ALL=zh_CN.utf8
export WINEPREFIX=/root/.wine32
export WINEARCH=win32
export WINEDEBUG=fixme-all
winecfg -v win10
winetricks -q dotnet40 dotnet40_kb2468871
winetricks -q wsh57
export WINEDLLOVERRIDES="wshom.ocx=n;"
regsvr32 $WINEPREFIX/drive_c/windows/system32/wshom.ocx
export WINEESYNC=1
export WINE_EXPLORER_FULLSCREEN=1
# export LIBGL_ALWAYS_SOFTWARE=1
export LIBGL_ALWAYS_INDIRECT=1
cd ~/HALo
cp -r Fonts $WINEPREFIX/drive_c/windows/
# LC_ALL=zh_CN.GBK wine Mediinfo.WinForm.HIS.Starter.exe
# winecfg # set virtual desktop
wine explorer /desktop=name,1800x900 Mediinfo.WinForm.HIS.Update.exe
Dockerfile
FROM scottyhardy/docker-wine
RUN echo "zh_CN GB2312\n\
zh_CN.GB18030 GB18030\n\
zh_CN.GBK GBK\n\
zh_CN.UTF-8 UTF-8" >> /etc/locale.gen && \
locale-gen
ENV LC_ALL=zh_CN.utf8 \
WINEPREFIX=/home/wineuser/.wine32 \
WINEARCH=win32 \
WINEDEBUG=fixme-all \
WINEDLLOVERRIDES="wshom.ocx=n;" \
WINE_EXPLORER_FULLSCREEN=1 \
WINEESYNC=1 \
LIBGL_ALWAYS_INDIRECT=1
# WINEESYNC=1 \
COPY HALo/Fonts/* $WINEPREFIX/drive_c/windows/Fonts/
RUN winecfg -v win10 && \
winetricks -q dotnet40 dotnet40_kb2468871 wsh57 && \
regsvr32 $WINEPREFIX/drive_c/windows/system32/wshom.ocx
ENTRYPOINT ["wine","explorer","/desktop=HALo,1800x900","/home/wineuser/HALo/Mediinfo.WinForm.HIS.Update.exe"]
#CMD ["wine","explorer", "/desktop=name,1800x900", "/root/HALo/Mediinfo.WinForm.HIS.Update.exe"]
之后当成命令运行即可
docker run --rm \
--env="DISPLAY" \
--volume="${XAUTHORITY:-${HOME}/.Xauthority}:/root/.Xauthority:ro" \
--volume="/tmp/.X11-unix:/tmp/.X11-unix:ro" \
--volume="/media/minecraft/Data/software/HALo:/home/wineuser/HALo" \
--ipc=host \
halo