记录一个C#调用MATLAB的玄学问题

1788 0

问题背景:

客户使用MATLAB写了一个计算程序,通过.NET Assembly的方式生成了一个动态链接库以供我系统调用。我先写了一个测试程序,能正常调用他编写的计算程序,完全没有问题。但是当我使用相同方法在我的系统中调用的时候,奇怪的问题发生了,系统抛出了如下的异常。

2021-08-25 09:34:32,757 [1]ERROR

类名:… –函数名: GetPluginInstance System.Reflection.TargetInvocationException: 调用的目标发生了异常。 —> System.TypeInitializationException: “Run.Plugin.C_Run”的类型初始值设定项引发异常。 —> System.TypeInitializationException: “MathWorks.MATLAB.NET.Utility.MWMCR”的类型初始值设定项引发异常。 —> System.Exception: Trouble initializing libraries required by Builder NE.

在 MathWorks.MATLAB.NET.Utility.MWMCR..cctor() — 内部异常堆栈跟踪的结尾 — 在 Run.Plugin.C_Run..cctor() — 内部异常堆栈跟踪的结尾 — 在 Run.Plugin.C_Run..ctor() … 省略无关日志

解决过程及思路:

由于这个问题我之前从来没有遇到过,首先想到的就是去必应和谷歌搜索一下,搜索之后,我发现之前答主描述的虽然报错内容相同,但是前提条件和我是全完不同的,我这是同一台开发机,写的测试用例程序可以调用,正式系统无法调用。

既然网上找不到答案,那么就自己动手。我建了一个新的测试窗体。将系统程序里的所有引用都加上,并且将系统运行环境中的所有文件,全部复制到测试窗体的运行环境中。第一次运行,报错,错误相同。这时候就松了一口气,证明这个玄学问题还是能够复现的。既然能复现,就证明一定有规律。然后,采用控制变量法,首先我将运行环境中的所有文件删除,重新编译测试窗体,运行,能够正确的调用客户编写的计算程序。到这一步,能够证明与系统程序中的引用无关,是系统运行环境中的某个文件与编写的计算程序产生了冲突。

进一步锁定问题后,继续使用控制变量法。恢复运行环境中的所有文件,然后一点点删除,看最终是哪个文件产生的影响。找到了罪魁祸首mclmcrt8_3.dll。将这个文件删除后,系统也能正确调用计算程序了。注意,此时我首先假设的是某一个文件或引用和计算程序产生了冲突。如果使用控制变量法一个个删除后,测试程序都能正确调用计算程序。那证明是某几个文件的组合会对计算程序产生了影响,那事情就复杂的多了。但是解决问题的思路是不变的,当出现了一个可复现的非代码问题时,当经验及搜索引擎无法帮助我们的时候,我们就要采用控制变量法一步一步进行测试。

进一步分析:

按理来说,这个动态链接库是安装MATLAB的MCR时才会出现在安装目录下。那这个动态链接库是怎么出现在我的运行环境中的呢?我回想了一下,是客户之前发给我的,因为之前另一个MATLAB程序曾经报过如下错误。

—> System.DINotFoundException:无法加载,DLL’mclmcrrt8_3.dll’:找不到指定的模块。(异常来自HRESULT:0x8007007E)。

那时候我电脑由于重装没有安装MATLAB的运行环境,所以他直接发我了这个动态链接库。

动态链接库来源找到了,接下来就是下一个疑惑,这个动态链接库讲道理也是现在这个计算程序所需要的,为什么会直接报异常呢?又一顿分析原因,结果是这个动态链接库之前客户发我的是32位版本,现在他发我的计算程序是64位版本。问题至此全部解决。

其它:

附上我调用计算程序所用的代码。

var _pluginAssembly = Assembly.LoadFrom(Application.StartupPath);
var classInstance = _pluginAssembly.CreateInstance("命名空间.类名", 
                                    false,
                                    BindingFlags.Default, 
                                    null, 
                                    null, 
                                    null, 
                                    null);
MWNumericArray parameters = 0;
var methodInfo = classInstance.GetType().
    GetMethod("构造方法名", new Type[] { typeof(MWNumericArray) });
​
methodInfo.Invoke(classInstance, new object[] { parameters });